diff options
| author | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
| commit | 80ec1decc49c72efec2a8b87c06245c92c0ab807 (patch) | |
| tree | e3b229aa94e8dcf0590f2317664176e7b8f7607b /src/core/settings/plugins/ifcfg-rh | |
| parent | 65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (diff) | |
New upstream version 1.29.90 upstream/1.29.90
Diffstat (limited to 'src/core/settings/plugins/ifcfg-rh')
233 files changed, 29518 insertions, 0 deletions
diff --git a/src/core/settings/plugins/ifcfg-rh/meson.build b/src/core/settings/plugins/ifcfg-rh/meson.build new file mode 100644 index 00000000..a2f11a92 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/meson.build @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +install_data( + 'nm-ifcfg-rh.conf', + install_dir: dbus_conf_dir, +) + +dbus_sources = gnome.gdbus_codegen( + 'nmdbus-ifcfg-rh', + 'nm-ifcfg-rh.xml', + interface_prefix: 'com.redhat', + namespace: 'NMDBus', +) + +libnmdbus_ifcfg_rh = static_library( + 'nmdbus-ifcfg-rh', + sources: dbus_sources, + dependencies: glib_dep, + c_args: introspection_extra_cflags, +) + +libnms_ifcfg_rh_core = static_library( + 'nms-ifcfg-rh-core', + sources: files( + 'nms-ifcfg-rh-reader.c', + 'nms-ifcfg-rh-utils.c', + 'nms-ifcfg-rh-writer.c', + 'shvar.c', + ), + dependencies: core_default_dep, + c_args: daemon_c_flags, +) + +libnm_settings_plugin_ifcfg_rh = shared_module( + 'nm-settings-plugin-ifcfg-rh', + sources: files( + 'nms-ifcfg-rh-storage.c', + 'nms-ifcfg-rh-plugin.c', + ), + dependencies: core_plugin_dep, + c_args: daemon_c_flags, + link_with: libnms_ifcfg_rh_core, + link_args: ldflags_linker_script_settings, + link_depends: linker_script_settings, + install: true, + install_dir: nm_plugindir, +) + +core_plugins += libnm_settings_plugin_ifcfg_rh + +install_data( + [ + 'nm-ifdown', + 'nm-ifup', + ], + install_dir: nm_libexecdir, + install_mode: 'rwxr-xr-x', +) + +if enable_tests + subdir('tests') +endif diff --git a/src/core/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf b/src/core/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf new file mode 100644 index 00000000..c7a1730a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nm-ifcfg-rh.conf @@ -0,0 +1,11 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy context="default"> + <allow send_destination="com.redhat.ifcfgrh1"/> + </policy> + <policy user="root"> + <allow own="com.redhat.ifcfgrh1"/> + </policy> +</busconfig> diff --git a/src/core/settings/plugins/ifcfg-rh/nm-ifcfg-rh.xml b/src/core/settings/plugins/ifcfg-rh/nm-ifcfg-rh.xml new file mode 100644 index 00000000..033fbfaa --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nm-ifcfg-rh.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<node name="/"> + + <!-- + com.redhat.ifcfgrh1: + + Utility methods for handling NM integration with standard Red Hat ifcfg + files. + --> + <interface name="com.redhat.ifcfgrh1"> + + <!-- + GetIfcfgDetails: + @ifcfg: The full path to an ifcfg file. + @uuid: The UUID of the NM connection backed by this ifcfg file. If the ifcfg file does not contain a UUID tag, this UUID is generated by NM, otherwise the UUID from the ifcfg file is used. + @path: The object path of the NM connected backed by this ifcfg file. + + Given an ifcfg file, return various internal information about it. + --> + <method name="GetIfcfgDetails"> + <arg name="ifcfg" type="s" direction="in"/> + <arg name="uuid" type="s" direction="out"/> + <arg name="path" type="o" direction="out"/> + </method> + </interface> +</node> diff --git a/src/core/settings/plugins/ifcfg-rh/nm-ifdown b/src/core/settings/plugins/ifcfg-rh/nm-ifdown new file mode 100644 index 00000000..e7bd73ae --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nm-ifdown @@ -0,0 +1,3 @@ +#!/bin/sh +nmcli connection load "/etc/sysconfig/network-scripts/ifcfg-$1" && +exec nmcli connection down filename "/etc/sysconfig/network-scripts/ifcfg-$1" diff --git a/src/core/settings/plugins/ifcfg-rh/nm-ifup b/src/core/settings/plugins/ifcfg-rh/nm-ifup new file mode 100644 index 00000000..96637a92 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nm-ifup @@ -0,0 +1,3 @@ +#!/bin/sh +nmcli connection load "/etc/sysconfig/network-scripts/ifcfg-$1" && +exec nmcli connection up filename "/etc/sysconfig/network-scripts/ifcfg-$1" diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h new file mode 100644 index 00000000..79c67e48 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008 - 2013 Red Hat, Inc. + */ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#define IFCFG_TAG "ifcfg-" +#define KEYS_TAG "keys-" +#define ROUTE_TAG "route-" +#define RULE_TAG "rule-" +#define ROUTE6_TAG "route6-" +#define RULE6_TAG "rule6-" + +#define BAK_TAG ".bak" +#define TILDE_TAG "~" +#define ORIG_TAG ".orig" +#define REJ_TAG ".rej" +#define RPMNEW_TAG ".rpmnew" +#define AUGNEW_TAG ".augnew" +#define AUGTMP_TAG ".augtmp" + +#define IFCFG_DIR SYSCONFDIR "/sysconfig/network-scripts" + +#define TYPE_ETHERNET "Ethernet" +#define TYPE_WIRELESS "Wireless" +#define TYPE_INFINIBAND "InfiniBand" +#define TYPE_BRIDGE "Bridge" +#define TYPE_BOND "Bond" +#define TYPE_VLAN "Vlan" +#define TYPE_TEAM "Team" +#define TYPE_TEAM_PORT "TeamPort" + +#define SECRET_FLAG_AGENT "user" +#define SECRET_FLAG_NOT_SAVED "ask" +#define SECRET_FLAG_NOT_REQUIRED "unused" + +/* DCB key names */ +#define KEY_DCB_APP_FCOE_ENABLE "DCB_APP_FCOE_ENABLE" +#define KEY_DCB_APP_FCOE_ADVERTISE "DCB_APP_FCOE_ADVERTISE" +#define KEY_DCB_APP_FCOE_WILLING "DCB_APP_FCOE_WILLING" +#define KEY_DCB_APP_FCOE_MODE "DCB_APP_FCOE_MODE" +#define KEY_DCB_APP_ISCSI_ENABLE "DCB_APP_ISCSI_ENABLE" +#define KEY_DCB_APP_ISCSI_ADVERTISE "DCB_APP_ISCSI_ADVERTISE" +#define KEY_DCB_APP_ISCSI_WILLING "DCB_APP_ISCSI_WILLING" +#define KEY_DCB_APP_FIP_ENABLE "DCB_APP_FIP_ENABLE" +#define KEY_DCB_APP_FIP_ADVERTISE "DCB_APP_FIP_ADVERTISE" +#define KEY_DCB_APP_FIP_WILLING "DCB_APP_FIP_WILLING" +#define KEY_DCB_PFC_ENABLE "DCB_PFC_ENABLE" +#define KEY_DCB_PFC_ADVERTISE "DCB_PFC_ADVERTISE" +#define KEY_DCB_PFC_WILLING "DCB_PFC_WILLING" +#define KEY_DCB_PFC_UP "DCB_PFC_UP" +#define KEY_DCB_PG_ENABLE "DCB_PG_ENABLE" +#define KEY_DCB_PG_ADVERTISE "DCB_PG_ADVERTISE" +#define KEY_DCB_PG_WILLING "DCB_PG_WILLING" +#define KEY_DCB_PG_ID "DCB_PG_ID" +#define KEY_DCB_PG_PCT "DCB_PG_PCT" +#define KEY_DCB_PG_UPPCT "DCB_PG_UPPCT" +#define KEY_DCB_PG_STRICT "DCB_PG_STRICT" +#define KEY_DCB_PG_UP2TC "DCB_PG_UP2TC" + +#endif /* __COMMON_H__ */ diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c new file mode 100644 index 00000000..d114db0f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c @@ -0,0 +1,1253 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Dan Williams <dcbw@redhat.com> + * Søren Sandmann <sandmann@daimi.au.dk> + * Copyright (C) 2007 - 2011 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nms-ifcfg-rh-plugin.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "nm-std-aux/c-list-util.h" +#include "nm-glib-aux/nm-c-list.h" +#include "nm-glib-aux/nm-io-utils.h" +#include "nm-std-aux/nm-dbus-compat.h" +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "nm-config.h" +#include "nm-dbus-manager.h" +#include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-utils.h" +#include "NetworkManagerUtils.h" + +#include "nms-ifcfg-rh-storage.h" +#include "nms-ifcfg-rh-common.h" +#include "nms-ifcfg-rh-utils.h" +#include "nms-ifcfg-rh-reader.h" +#include "nms-ifcfg-rh-writer.h" + +#define IFCFGRH1_BUS_NAME "com.redhat.ifcfgrh1" +#define IFCFGRH1_OBJECT_PATH "/com/redhat/ifcfgrh1" +#define IFCFGRH1_IFACE1_NAME "com.redhat.ifcfgrh1" +#define IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS "GetIfcfgDetails" + +/*****************************************************************************/ + +typedef struct { + NMConfig *config; + + struct { + GDBusConnection *connection; + GCancellable * cancellable; + gulong signal_id; + guint regist_id; + } dbus; + + NMSettUtilStorages storages; + + GHashTable *unmanaged_specs; + GHashTable *unrecognized_specs; + +} NMSIfcfgRHPluginPrivate; + +struct _NMSIfcfgRHPlugin { + NMSettingsPlugin parent; + NMSIfcfgRHPluginPrivate _priv; +}; + +struct _NMSIfcfgRHPluginClass { + NMSettingsPluginClass parent; +}; + +G_DEFINE_TYPE(NMSIfcfgRHPlugin, nms_ifcfg_rh_plugin, NM_TYPE_SETTINGS_PLUGIN) + +#define NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMSIfcfgRHPlugin, NMS_IS_IFCFG_RH_PLUGIN, NMSettingsPlugin) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_SETTINGS +#define _NMLOG(level, ...) \ + G_STMT_START \ + { \ + nm_log((level), \ + (_NMLOG_DOMAIN), \ + NULL, \ + NULL, \ + "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + "ifcfg-rh: " _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END + +/*****************************************************************************/ + +static void _unhandled_specs_reset(NMSIfcfgRHPlugin *self); + +static void _unhandled_specs_merge_storages(NMSIfcfgRHPlugin *self, NMSettUtilStorages *storages); + +/*****************************************************************************/ + +static void +nm_assert_self(NMSIfcfgRHPlugin *self, gboolean unhandled_specs_consistent) +{ + nm_assert(NMS_IS_IFCFG_RH_PLUGIN(self)); + +#if NM_MORE_ASSERTS > 5 + { + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + NMSIfcfgRHStorage * storage; + gsize n_uuid; + gs_unref_hashtable GHashTable *h_unmanaged = NULL; + gs_unref_hashtable GHashTable *h_unrecognized = NULL; + + nm_assert(g_hash_table_size(priv->storages.idx_by_filename) + == c_list_length(&priv->storages._storage_lst_head)); + + h_unmanaged = g_hash_table_new(nm_str_hash, g_str_equal); + h_unrecognized = g_hash_table_new(nm_str_hash, g_str_equal); + + n_uuid = 0; + + c_list_for_each_entry (storage, &priv->storages._storage_lst_head, parent._storage_lst) { + const char *uuid; + const char *filename; + + filename = nms_ifcfg_rh_storage_get_filename(storage); + + nm_assert(filename && NM_STR_HAS_PREFIX(filename, IFCFG_DIR "/")); + + uuid = nms_ifcfg_rh_storage_get_uuid_opt(storage); + + nm_assert((!!uuid) + (!!storage->unmanaged_spec) + (!!storage->unrecognized_spec) == 1); + + nm_assert(storage + == nm_sett_util_storages_lookup_by_filename(&priv->storages, filename)); + + if (uuid) { + NMSettUtilStorageByUuidHead *sbuh; + NMSettUtilStorageByUuidHead *sbuh2; + + if (storage->connection) + nm_assert(nm_streq0(nm_connection_get_uuid(storage->connection), uuid)); + + if (!g_hash_table_lookup_extended(priv->storages.idx_by_uuid, + &uuid, + (gpointer *) &sbuh, + (gpointer *) &sbuh2)) + nm_assert_not_reached(); + + nm_assert(sbuh); + nm_assert(nm_streq(uuid, sbuh->uuid)); + nm_assert(sbuh == sbuh2); + nm_assert(c_list_contains(&sbuh->_storage_by_uuid_lst_head, + &storage->parent._storage_by_uuid_lst)); + + if (c_list_first(&sbuh->_storage_by_uuid_lst_head) + == &storage->parent._storage_by_uuid_lst) + n_uuid++; + } else if (storage->unmanaged_spec) { + nm_assert(strlen(storage->unmanaged_spec) > 0); + g_hash_table_add(h_unmanaged, storage->unmanaged_spec); + } else if (storage->unrecognized_spec) { + nm_assert(strlen(storage->unrecognized_spec) > 0); + g_hash_table_add(h_unrecognized, storage->unrecognized_spec); + } else + nm_assert_not_reached(); + + nm_assert(!storage->connection); + } + + nm_assert(g_hash_table_size(priv->storages.idx_by_uuid) == n_uuid); + + if (unhandled_specs_consistent) { + nm_assert(nm_utils_hashtable_same_keys(h_unmanaged, priv->unmanaged_specs)); + nm_assert(nm_utils_hashtable_same_keys(h_unrecognized, priv->unrecognized_specs)); + } + } +#endif +} + +/*****************************************************************************/ + +static NMSIfcfgRHStorage * +_load_file(NMSIfcfgRHPlugin *self, const char *filename, GError **error) +{ + gs_unref_object NMConnection *connection = NULL; + gs_free_error GError *load_error = NULL; + gs_free char * unhandled_spec = NULL; + gboolean load_error_ignore; + struct stat st; + + if (stat(filename, &st) != 0) { + int errsv = errno; + + if (error) { + nm_utils_error_set_errno(error, errsv, "failure to stat file \%s\": %s", filename); + } else + _LOGT("load[%s]: failure to stat file: %s", filename, nm_strerror_native(errsv)); + return NULL; + } + + connection = connection_from_file(filename, &unhandled_spec, &load_error, &load_error_ignore); + if (load_error) { + if (error) { + nm_utils_error_set(error, + NM_UTILS_ERROR_UNKNOWN, + "failure to read file \"%s\": %s", + filename, + load_error->message); + } else { + _NMLOG(load_error_ignore ? LOGL_TRACE : LOGL_WARN, + "load[%s]: failure to read file: %s", + filename, + load_error->message); + } + return NULL; + } + + if (unhandled_spec) { + const char *unmanaged_spec; + const char *unrecognized_spec; + + if (!nms_ifcfg_rh_utils_parse_unhandled_spec(unhandled_spec, + &unmanaged_spec, + &unrecognized_spec)) { + nm_utils_error_set(error, + NM_UTILS_ERROR_UNKNOWN, + "invalid unhandled spec \"%s\"", + unhandled_spec); + nm_assert_not_reached(); + return NULL; + } + return nms_ifcfg_rh_storage_new_unhandled(self, + filename, + unmanaged_spec, + unrecognized_spec); + } + + return nms_ifcfg_rh_storage_new_connection(self, + filename, + g_steal_pointer(&connection), + &st.st_mtim); +} + +static void +_load_dir(NMSIfcfgRHPlugin *self, NMSettUtilStorages *storages) +{ + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_free_error GError *local = NULL; + const char * f_filename; + GDir * dir; + + dir = g_dir_open(IFCFG_DIR, 0, &local); + if (!dir) { + _LOGT("Could not read directory '%s': %s", IFCFG_DIR, local->message); + return; + } + + dupl_filenames = g_hash_table_new_full(nm_str_hash, g_str_equal, NULL, g_free); + + while ((f_filename = g_dir_read_name(dir))) { + gs_free char * full_path = NULL; + NMSIfcfgRHStorage *storage; + char * full_filename; + + full_path = g_build_filename(IFCFG_DIR, f_filename, NULL); + full_filename = utils_detect_ifcfg_path(full_path, TRUE); + if (!full_filename) + continue; + + if (!g_hash_table_add(dupl_filenames, full_filename)) + continue; + + nm_assert(!nm_sett_util_storages_lookup_by_filename(storages, full_filename)); + + storage = _load_file(self, full_filename, NULL); + if (storage) + nm_sett_util_storages_add_take(storages, storage); + } + g_dir_close(dir); +} + +static void +_storages_consolidate(NMSIfcfgRHPlugin * self, + NMSettUtilStorages * storages_new, + gboolean replace_all, + GHashTable * storages_replaced, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + CList lst_conn_info_deleted = C_LIST_INIT(lst_conn_info_deleted); + gs_unref_ptrarray GPtrArray *storages_modified = NULL; + CList storages_deleted; + NMSIfcfgRHStorage * storage_safe; + NMSIfcfgRHStorage * storage_new; + NMSIfcfgRHStorage * storage_old; + NMSIfcfgRHStorage * storage; + guint i; + + /* when we reload all files, we must signal add/update/modify of profiles one-by-one. + * NMSettings then goes ahead and emits further signals and a lot of things happen. + * + * So, first, emit an update of the unmanaged/unrecognized specs that contains *all* + * the unmanaged/unrecognized devices from before and after. Since both unmanaged/unrecognized + * specs have the meaning of "not doing something", it makes sense that we temporarily + * disable that action for the sum of before and after. */ + _unhandled_specs_merge_storages(self, storages_new); + + storages_modified = g_ptr_array_new_with_free_func(g_object_unref); + c_list_init(&storages_deleted); + + c_list_for_each_entry (storage_old, &priv->storages._storage_lst_head, parent._storage_lst) + storage_old->dirty = TRUE; + + c_list_for_each_entry_safe (storage_new, + storage_safe, + &storages_new->_storage_lst_head, + parent._storage_lst) { + storage_old = nm_sett_util_storages_lookup_by_filename( + &priv->storages, + nms_ifcfg_rh_storage_get_filename(storage_new)); + + nm_sett_util_storages_steal(storages_new, storage_new); + + if (!storage_old || !nms_ifcfg_rh_storage_equal_type(storage_new, storage_old)) { + if (storage_old) { + nm_sett_util_storages_steal(&priv->storages, storage_old); + if (nms_ifcfg_rh_storage_get_uuid_opt(storage_old)) + c_list_link_tail(&storages_deleted, &storage_old->parent._storage_lst); + else + nms_ifcfg_rh_storage_destroy(storage_old); + } + storage_new->dirty = FALSE; + nm_sett_util_storages_add_take(&priv->storages, storage_new); + g_ptr_array_add(storages_modified, g_object_ref(storage_new)); + continue; + } + + storage_old->dirty = FALSE; + nms_ifcfg_rh_storage_copy_content(storage_old, storage_new); + nms_ifcfg_rh_storage_destroy(storage_new); + g_ptr_array_add(storages_modified, g_object_ref(storage_old)); + } + + c_list_for_each_entry_safe (storage_old, + storage_safe, + &priv->storages._storage_lst_head, + parent._storage_lst) { + if (!storage_old->dirty) + continue; + if (replace_all + || (storages_replaced && g_hash_table_contains(storages_replaced, storage_old))) { + nm_sett_util_storages_steal(&priv->storages, storage_old); + if (nms_ifcfg_rh_storage_get_uuid_opt(storage_old)) + c_list_link_tail(&storages_deleted, &storage_old->parent._storage_lst); + else + nms_ifcfg_rh_storage_destroy(storage_old); + } + } + + /* raise events. */ + + for (i = 0; i < storages_modified->len; i++) { + storage = storages_modified->pdata[i]; + storage->dirty = TRUE; + } + + for (i = 0; i < storages_modified->len; i++) { + gs_unref_object NMConnection *connection = NULL; + storage = storages_modified->pdata[i]; + + if (!storage->dirty) { + /* the entry is no longer dirty. In the meantime we already emitted + * another signal for it. */ + continue; + } + storage->dirty = FALSE; + if (storage + != nm_sett_util_storages_lookup_by_filename( + &priv->storages, + nms_ifcfg_rh_storage_get_filename(storage))) { + /* hm? The profile was deleted in the meantime? That is only possible + * if the signal handler called again into the plugin. In any case, the event + * was already emitted. Skip. */ + continue; + } + + connection = nms_ifcfg_rh_storage_steal_connection(storage); + if (!connection) { + nm_assert(!nms_ifcfg_rh_storage_get_uuid_opt(storage)); + continue; + } + + nm_assert(NM_IS_CONNECTION(connection)); + nm_assert(nms_ifcfg_rh_storage_get_uuid_opt(storage)); + callback(NM_SETTINGS_PLUGIN(self), NM_SETTINGS_STORAGE(storage), connection, user_data); + } + + while ( + (storage = c_list_first_entry(&storages_deleted, NMSIfcfgRHStorage, parent._storage_lst))) { + c_list_unlink(&storage->parent._storage_lst); + callback(NM_SETTINGS_PLUGIN(self), NM_SETTINGS_STORAGE(storage), NULL, user_data); + nms_ifcfg_rh_storage_destroy(storage); + } +} + +/*****************************************************************************/ + +static void +load_connections(NMSettingsPlugin * plugin, + NMSettingsPluginConnectionLoadEntry * entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfcfgRHPlugin * self = NMS_IFCFG_RH_PLUGIN(plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = + NM_SETT_UTIL_STORAGES_INIT(storages_new, nms_ifcfg_rh_storage_destroy); + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_unref_hashtable GHashTable *storages_replaced = NULL; + gs_unref_hashtable GHashTable *loaded_uuids = NULL; + const char * loaded_uuid; + GHashTableIter h_iter; + gsize i; + + if (n_entries == 0) + return; + + dupl_filenames = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL); + + loaded_uuids = g_hash_table_new(nm_str_hash, g_str_equal); + + storages_replaced = g_hash_table_new_full(nm_direct_hash, NULL, g_object_unref, NULL); + + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *const entry = &entries[i]; + gs_free_error GError * local = NULL; + const char * full_filename; + const char * uuid; + gs_free char * full_filename_keep = NULL; + NMSettingsPluginConnectionLoadEntry *dupl_content_entry; + gs_unref_object NMSIfcfgRHStorage *storage = NULL; + + if (entry->handled) + continue; + + if (entry->filename[0] != '/') + continue; + + full_filename_keep = utils_detect_ifcfg_path(entry->filename, FALSE); + + if (!full_filename_keep) { + if (nm_utils_file_is_in_path(entry->filename, IFCFG_DIR)) { + nm_utils_error_set(&entry->error, + NM_UTILS_ERROR_UNKNOWN, + ("path is not a valid name for an ifcfg-rh file")); + entry->handled = TRUE; + } + continue; + } + + if ((dupl_content_entry = g_hash_table_lookup(dupl_filenames, full_filename_keep))) { + /* we already visited this file. */ + entry->handled = dupl_content_entry->handled; + if (dupl_content_entry->error) { + g_set_error_literal(&entry->error, + dupl_content_entry->error->domain, + dupl_content_entry->error->code, + dupl_content_entry->error->message); + } + continue; + } + + entry->handled = TRUE; + + full_filename = full_filename_keep; + if (!g_hash_table_insert(dupl_filenames, g_steal_pointer(&full_filename_keep), entry)) + nm_assert_not_reached(); + + storage = _load_file(self, full_filename, &local); + if (!storage) { + if (nm_utils_file_stat(full_filename, NULL) == -ENOENT) { + NMSIfcfgRHStorage *storage2; + + /* the file does not exist. We take that as indication to unload the file + * that was previously loaded... */ + storage2 = nm_sett_util_storages_lookup_by_filename(&priv->storages, full_filename); + if (storage2) + g_hash_table_add(storages_replaced, g_object_ref(storage2)); + continue; + } + g_propagate_error(&entry->error, g_steal_pointer(&local)); + continue; + } + + uuid = nms_ifcfg_rh_storage_get_uuid_opt(storage); + if (uuid) + g_hash_table_add(loaded_uuids, (char *) uuid); + + nm_sett_util_storages_add_take(&storages_new, g_steal_pointer(&storage)); + } + + /* now we visit all UUIDs that are about to change... */ + g_hash_table_iter_init(&h_iter, loaded_uuids); + while (g_hash_table_iter_next(&h_iter, (gpointer *) &loaded_uuid, NULL)) { + NMSIfcfgRHStorage * storage; + NMSettUtilStorageByUuidHead *sbuh; + + sbuh = nm_sett_util_storages_lookup_by_uuid(&priv->storages, loaded_uuid); + if (!sbuh) + continue; + + c_list_for_each_entry (storage, + &sbuh->_storage_by_uuid_lst_head, + parent._storage_by_uuid_lst) { + const char * full_filename = nms_ifcfg_rh_storage_get_filename(storage); + gs_unref_object NMSIfcfgRHStorage *storage_new = NULL; + gs_free_error GError *local = NULL; + + if (g_hash_table_contains(dupl_filenames, full_filename)) { + /* already re-loaded. */ + continue; + } + + /* @storage has a UUID that was just loaded from disk, but we have an entry in cache. + * Reload that file too despite not being told to do so. The reason is to get + * the latest file timestamp so that we get the priorities right. */ + + storage_new = _load_file(self, full_filename, &local); + if (storage_new + && !nm_streq0(loaded_uuid, nms_ifcfg_rh_storage_get_uuid_opt(storage_new))) { + /* the file now references a different UUID. We are not told to reload + * that file, so this means the existing storage (with the previous + * filename and UUID tuple) is no longer valid. */ + g_clear_object(&storage_new); + } + + g_hash_table_add(storages_replaced, g_object_ref(storage)); + if (storage_new) + nm_sett_util_storages_add_take(&storages_new, g_steal_pointer(&storage_new)); + } + } + + nm_clear_pointer(&loaded_uuids, g_hash_table_destroy); + nm_clear_pointer(&dupl_filenames, g_hash_table_destroy); + + _storages_consolidate(self, &storages_new, FALSE, storages_replaced, callback, user_data); +} + +static void +reload_connections(NMSettingsPlugin * plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfcfgRHPlugin * self = NMS_IFCFG_RH_PLUGIN(plugin); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = + NM_SETT_UTIL_STORAGES_INIT(storages_new, nms_ifcfg_rh_storage_destroy); + + nm_assert_self(self, TRUE); + + _load_dir(self, &storages_new); + + _storages_consolidate(self, &storages_new, TRUE, NULL, callback, user_data); + + nm_assert_self(self, FALSE); +} + +static void +load_connections_done(NMSettingsPlugin *plugin) +{ + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN(plugin); + + /* at the beginning of a load, we emit a change signal for unmanaged/unrecognized + * specs that contain the sum of before and after (_unhandled_specs_merge_storages()). + * + * The idea is that while we emit signals about changes to connection, we have + * the sum of all unmanaged/unrecognized devices from before and after. + * + * This if triggered at the end, to reset the specs. */ + _unhandled_specs_reset(self); + + nm_assert_self(self, TRUE); +} + +/*****************************************************************************/ + +static gboolean +add_connection(NMSettingsPlugin * plugin, + NMConnection * connection, + NMSettingsStorage **out_storage, + NMConnection ** out_connection, + GError ** error) +{ + NMSIfcfgRHPlugin * self = NMS_IFCFG_RH_PLUGIN(plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + gs_unref_object NMSIfcfgRHStorage *storage = NULL; + gs_unref_object NMConnection *reread = NULL; + gs_free char * full_filename = NULL; + GError * local = NULL; + gboolean reread_same; + struct timespec mtime; + + nm_assert_self(self, TRUE); + nm_assert(NM_IS_CONNECTION(connection)); + nm_assert(out_storage && !*out_storage); + nm_assert(out_connection && !*out_connection); + + if (!nms_ifcfg_rh_writer_write_connection( + connection, + IFCFG_DIR, + NULL, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA(&priv->storages, NULL), + &full_filename, + &reread, + &reread_same, + &local)) { + _LOGT("commit: %s (%s): failed to add: %s", + nm_connection_get_uuid(connection), + nm_connection_get_id(connection), + local->message); + g_propagate_error(error, local); + return FALSE; + } + + if (!reread || reread_same) + nm_g_object_ref_set(&reread, connection); + + nm_assert(full_filename && full_filename[0] == '/'); + + _LOGT("commit: %s (%s) added as \"%s\"", + nm_connection_get_uuid(reread), + nm_connection_get_id(reread), + full_filename); + + storage = + nms_ifcfg_rh_storage_new_connection(self, + full_filename, + g_steal_pointer(&reread), + nm_sett_util_stat_mtime(full_filename, FALSE, &mtime)); + + nm_sett_util_storages_add_take(&priv->storages, g_object_ref(storage)); + + *out_connection = nms_ifcfg_rh_storage_steal_connection(storage); + *out_storage = NM_SETTINGS_STORAGE(g_steal_pointer(&storage)); + + nm_assert_self(self, TRUE); + + return TRUE; +} + +static gboolean +update_connection(NMSettingsPlugin * plugin, + NMSettingsStorage * storage_x, + NMConnection * connection, + NMSettingsStorage **out_storage, + NMConnection ** out_connection, + GError ** error) +{ + NMSIfcfgRHPlugin * self = NMS_IFCFG_RH_PLUGIN(plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + NMSIfcfgRHStorage * storage = NMS_IFCFG_RH_STORAGE(storage_x); + const char * full_filename; + const char * uuid; + GError * local = NULL; + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same; + struct timespec mtime; + + nm_assert_self(self, TRUE); + nm_assert(NM_IS_CONNECTION(connection)); + nm_assert(NMS_IS_IFCFG_RH_STORAGE(storage)); + nm_assert(_nm_connection_verify(connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert(!error || !*error); + + uuid = nms_ifcfg_rh_storage_get_uuid_opt(storage); + + nm_assert(uuid && nm_streq0(uuid, nm_connection_get_uuid(connection))); + + full_filename = nms_ifcfg_rh_storage_get_filename(storage); + + nm_assert(full_filename); + nm_assert(storage == nm_sett_util_storages_lookup_by_filename(&priv->storages, full_filename)); + + if (!nms_ifcfg_rh_writer_write_connection( + connection, + IFCFG_DIR, + full_filename, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA(&priv->storages, full_filename), + NULL, + &reread, + &reread_same, + &local)) { + _LOGT("commit: failure to write %s (%s) to \"%s\": %s", + nm_connection_get_uuid(connection), + nm_connection_get_id(connection), + full_filename, + local->message); + g_propagate_error(error, local); + return FALSE; + } + + if (!reread || reread_same) + nm_g_object_ref_set(&reread, connection); + + _LOGT("commit: \"%s\": profile %s (%s) written", + full_filename, + uuid, + nm_connection_get_id(connection)); + + storage->stat_mtime = *nm_sett_util_stat_mtime(full_filename, FALSE, &mtime); + + *out_storage = NM_SETTINGS_STORAGE(g_object_ref(storage)); + *out_connection = g_steal_pointer(&reread); + + nm_assert_self(self, TRUE); + + return TRUE; +} + +static gboolean +delete_connection(NMSettingsPlugin *plugin, NMSettingsStorage *storage_x, GError **error) +{ + NMSIfcfgRHPlugin * self = NMS_IFCFG_RH_PLUGIN(plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + NMSIfcfgRHStorage * storage = NMS_IFCFG_RH_STORAGE(storage_x); + const char * operation_message; + const char * full_filename; + + nm_assert_self(self, TRUE); + nm_assert(!error || !*error); + nm_assert(NMS_IS_IFCFG_RH_STORAGE(storage)); + + full_filename = nms_ifcfg_rh_storage_get_filename(storage); + nm_assert(full_filename); + + nm_assert(nms_ifcfg_rh_storage_get_uuid_opt(storage)); + + nm_assert(storage == nm_sett_util_storages_lookup_by_filename(&priv->storages, full_filename)); + + { + gs_free char * keyfile = utils_get_keys_path(full_filename); + gs_free char * routefile = utils_get_route_path(full_filename); + gs_free char * route6file = utils_get_route6_path(full_filename); + const char *const files[] = {full_filename, keyfile, routefile, route6file}; + gboolean any_deleted = FALSE; + gboolean any_failure = FALSE; + int i; + + for (i = 0; i < G_N_ELEMENTS(files); i++) { + int errsv; + + if (unlink(files[i]) == 0) { + any_deleted = TRUE; + continue; + } + errsv = errno; + if (errsv == ENOENT) + continue; + + _LOGW("commit: failure to delete file \"%s\": %s", files[i], nm_strerror_native(errsv)); + any_failure = TRUE; + } + if (any_failure) + operation_message = "failed to delete files from disk"; + else if (any_deleted) + operation_message = "deleted from disk"; + else + operation_message = "does not exist on disk"; + } + + _LOGT("commit: deleted \"%s\", profile %s (%s)", + full_filename, + nms_ifcfg_rh_storage_get_uuid_opt(storage), + operation_message); + + nm_sett_util_storages_steal(&priv->storages, storage); + nms_ifcfg_rh_storage_destroy(storage); + + nm_assert_self(self, TRUE); + + return TRUE; +} + +/*****************************************************************************/ + +static void +_unhandled_specs_reset(NMSIfcfgRHPlugin *self) +{ + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + gs_unref_hashtable GHashTable *unmanaged_specs = NULL; + gs_unref_hashtable GHashTable *unrecognized_specs = NULL; + NMSIfcfgRHStorage * storage; + + unmanaged_specs = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL); + unrecognized_specs = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL); + + c_list_for_each_entry (storage, &priv->storages._storage_lst_head, parent._storage_lst) { + if (storage->unmanaged_spec) + g_hash_table_add(unmanaged_specs, g_strdup(storage->unmanaged_spec)); + if (storage->unrecognized_spec) + g_hash_table_add(unrecognized_specs, g_strdup(storage->unrecognized_spec)); + } + + if (!nm_utils_hashtable_same_keys(unmanaged_specs, priv->unmanaged_specs)) { + g_hash_table_unref(priv->unmanaged_specs); + priv->unmanaged_specs = g_steal_pointer(&unmanaged_specs); + } + if (!nm_utils_hashtable_same_keys(unrecognized_specs, priv->unrecognized_specs)) { + g_hash_table_unref(priv->unrecognized_specs); + priv->unrecognized_specs = g_steal_pointer(&unrecognized_specs); + } + + if (!unmanaged_specs) + _nm_settings_plugin_emit_signal_unmanaged_specs_changed(NM_SETTINGS_PLUGIN(self)); + if (!unrecognized_specs) + _nm_settings_plugin_emit_signal_unrecognized_specs_changed(NM_SETTINGS_PLUGIN(self)); +} + +static void +_unhandled_specs_merge_storages(NMSIfcfgRHPlugin *self, NMSettUtilStorages *storages) +{ + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + gboolean unmanaged_changed = FALSE; + gboolean unrecognized_changed = FALSE; + NMSIfcfgRHStorage * storage; + + c_list_for_each_entry (storage, &storages->_storage_lst_head, parent._storage_lst) { + if (storage->unmanaged_spec + && !g_hash_table_contains(priv->unmanaged_specs, storage->unmanaged_spec)) { + unmanaged_changed = TRUE; + g_hash_table_add(priv->unmanaged_specs, g_strdup(storage->unmanaged_spec)); + } + if (storage->unrecognized_spec + && !g_hash_table_contains(priv->unrecognized_specs, storage->unrecognized_spec)) { + unrecognized_changed = TRUE; + g_hash_table_add(priv->unrecognized_specs, g_strdup(storage->unrecognized_spec)); + } + } + + if (unmanaged_changed) + _nm_settings_plugin_emit_signal_unmanaged_specs_changed(NM_SETTINGS_PLUGIN(self)); + if (unrecognized_changed) + _nm_settings_plugin_emit_signal_unrecognized_specs_changed(NM_SETTINGS_PLUGIN(self)); +} + +static GSList * +_unhandled_specs_from_hashtable(GHashTable *hash) +{ + gs_free const char **keys = NULL; + GSList * list = NULL; + guint i, l; + + keys = nm_utils_strdict_get_keys(hash, TRUE, &l); + for (i = l; i > 0;) { + i--; + list = g_slist_prepend(list, g_strdup(keys[i])); + } + return list; +} + +static GSList * +get_unmanaged_specs(NMSettingsPlugin *plugin) +{ + return _unhandled_specs_from_hashtable( + NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(plugin)->unmanaged_specs); +} + +static GSList * +get_unrecognized_specs(NMSettingsPlugin *plugin) +{ + return _unhandled_specs_from_hashtable( + NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(plugin)->unrecognized_specs); +} + +/*****************************************************************************/ + +static void +impl_ifcfgrh_get_ifcfg_details(NMSIfcfgRHPlugin * self, + GDBusMethodInvocation *context, + const char * in_ifcfg) +{ + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + gs_free char * ifcfg_path = NULL; + NMSIfcfgRHStorage * storage; + const char * uuid; + const char * path; + + if (in_ifcfg[0] != '/') { + g_dbus_method_invocation_return_error(context, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "ifcfg path '%s' is not absolute", + in_ifcfg); + return; + } + + ifcfg_path = utils_detect_ifcfg_path(in_ifcfg, TRUE); + if (!ifcfg_path) { + g_dbus_method_invocation_return_error(context, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "ifcfg path '%s' is not an ifcfg base file", + in_ifcfg); + return; + } + + storage = nm_sett_util_storages_lookup_by_filename(&priv->storages, ifcfg_path); + if (!storage) { + g_dbus_method_invocation_return_error(context, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "ifcfg file '%s' unknown", + in_ifcfg); + return; + } + + uuid = nms_ifcfg_rh_storage_get_uuid_opt(storage); + if (!uuid) { + g_dbus_method_invocation_return_error(context, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "ifcfg file '%s' not managed by NetworkManager", + in_ifcfg); + return; + } + + /* It is ugly that the ifcfg-rh plugin needs to call back into NMSettings this + * way. + * There are alternatives (like invoking a signal), but they are all significant + * extra code (and performance overhead). So the quick and dirty solution here + * is likely to be simpler than getting this right (also from point of readability!). + */ + path = nm_settings_get_dbus_path_for_uuid(nm_settings_get(), uuid); + + if (!path) { + g_dbus_method_invocation_return_error(context, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "unable to get the connection D-Bus path"); + return; + } + + g_dbus_method_invocation_return_value(context, g_variant_new("(so)", uuid, path)); +} + +/*****************************************************************************/ + +static void +_dbus_clear(NMSIfcfgRHPlugin *self) +{ + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + guint id; + + nm_clear_g_signal_handler(priv->dbus.connection, &priv->dbus.signal_id); + + nm_clear_g_cancellable(&priv->dbus.cancellable); + + if ((id = nm_steal_int(&priv->dbus.regist_id))) { + if (!g_dbus_connection_unregister_object(priv->dbus.connection, id)) + _LOGW("dbus: unexpected failure to unregister object"); + } + + g_clear_object(&priv->dbus.connection); +} + +static void +_dbus_connection_closed(GDBusConnection *connection, + gboolean remote_peer_vanished, + GError * error, + gpointer user_data) +{ + _LOGW("dbus: %s bus closed", IFCFGRH1_BUS_NAME); + _dbus_clear(NMS_IFCFG_RH_PLUGIN(user_data)); + + /* Retry or recover? */ +} + +static void +_method_call(GDBusConnection * connection, + const char * sender, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN(user_data); + + if (nm_streq(interface_name, IFCFGRH1_IFACE1_NAME)) { + if (nm_streq(method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) { + const char *ifcfg; + + g_variant_get(parameters, "(&s)", &ifcfg); + impl_ifcfgrh_get_ifcfg_details(self, invocation, ifcfg); + return; + } + } + + g_dbus_method_invocation_return_error(invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s", + method_name); +} + +static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO( + IFCFGRH1_IFACE1_NAME, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS( + NM_DEFINE_GDBUS_METHOD_INFO( + IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS, + .in_args = NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("ifcfg", "s"), ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("uuid", "s"), + NM_DEFINE_GDBUS_ARG_INFO("path", "o"), ), ), ), ); + +static void +_dbus_request_name_done(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GDBusConnection * connection = G_DBUS_CONNECTION(source_object); + NMSIfcfgRHPlugin * self; + NMSIfcfgRHPluginPrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *ret = NULL; + guint32 result; + + ret = g_dbus_connection_call_finish(connection, res, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NMS_IFCFG_RH_PLUGIN(user_data); + priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + + g_clear_object(&priv->dbus.cancellable); + + if (!ret) { + _LOGW("dbus: couldn't acquire D-Bus service: %s", error->message); + _dbus_clear(self); + return; + } + + g_variant_get(ret, "(u)", &result); + + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + _LOGW("dbus: couldn't acquire ifcfgrh1 D-Bus service (already taken)"); + _dbus_clear(self); + return; + } + + { + static const GDBusInterfaceVTable interface_vtable = { + .method_call = _method_call, + }; + + priv->dbus.regist_id = g_dbus_connection_register_object( + connection, + IFCFGRH1_OBJECT_PATH, + interface_info, + NM_UNCONST_PTR(GDBusInterfaceVTable, &interface_vtable), + self, + NULL, + &error); + if (!priv->dbus.regist_id) { + _LOGW("dbus: couldn't register D-Bus service: %s", error->message); + _dbus_clear(self); + return; + } + } + + _LOGD("dbus: acquired D-Bus service %s and exported %s object", + IFCFGRH1_BUS_NAME, + IFCFGRH1_OBJECT_PATH); +} + +static void +_dbus_create_done(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + NMSIfcfgRHPlugin * self; + NMSIfcfgRHPluginPrivate *priv; + gs_free_error GError *error = NULL; + GDBusConnection * connection; + + connection = g_dbus_connection_new_for_address_finish(res, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NMS_IFCFG_RH_PLUGIN(user_data); + priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + + g_clear_object(&priv->dbus.cancellable); + + if (!connection) { + _LOGW("dbus: couldn't initialize system bus: %s", error->message); + return; + } + + priv->dbus.connection = connection; + priv->dbus.cancellable = g_cancellable_new(); + + priv->dbus.signal_id = g_signal_connect(priv->dbus.connection, + "closed", + G_CALLBACK(_dbus_connection_closed), + self); + + g_dbus_connection_call(priv->dbus.connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "RequestName", + g_variant_new("(su)", IFCFGRH1_BUS_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE), + G_VARIANT_TYPE("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->dbus.cancellable, + _dbus_request_name_done, + self); +} + +static void +_dbus_setup(NMSIfcfgRHPlugin *self) +{ + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + gs_free char * address = NULL; + gs_free_error GError *error = NULL; + + _dbus_clear(self); + + if (!NM_MAIN_DBUS_CONNECTION_GET) { + _LOGW("dbus: don't use D-Bus for %s service", IFCFGRH1_BUS_NAME); + return; + } + + /* We use a separate D-Bus connection so that org.freedesktop.NetworkManager and com.redhat.ifcfgrh1 + * are exported by different connections. */ + address = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (address == NULL) { + _LOGW("dbus: failed getting address for system bus: %s", error->message); + return; + } + + priv->dbus.cancellable = g_cancellable_new(); + + g_dbus_connection_new_for_address(address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT + | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + NULL, + priv->dbus.cancellable, + _dbus_create_done, + self); +} + +static void +config_changed_cb(NMConfig * config, + NMConfigData * config_data, + NMConfigChangeFlags changes, + NMConfigData * old_data, + NMSIfcfgRHPlugin * self) +{ + NMSIfcfgRHPluginPrivate *priv; + + /* If the dbus connection for some reason is borked the D-Bus service + * won't be offered. + * + * On SIGHUP and SIGUSR1 try to re-connect to D-Bus. So in the unlikely + * event that the D-Bus connection is broken, that allows for recovery + * without need for restarting NetworkManager. */ + if (!NM_FLAGS_ANY(changes, NM_CONFIG_CHANGE_CAUSE_SIGHUP | NM_CONFIG_CHANGE_CAUSE_SIGUSR1)) + return; + + priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + if (!priv->dbus.connection && !priv->dbus.cancellable) + _dbus_setup(self); +} + +/*****************************************************************************/ + +static void +nms_ifcfg_rh_plugin_init(NMSIfcfgRHPlugin *self) +{ + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + + priv->config = g_object_ref(nm_config_get()); + + priv->unmanaged_specs = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL); + priv->unrecognized_specs = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL); + + priv->storages = (NMSettUtilStorages) NM_SETT_UTIL_STORAGES_INIT(priv->storages, + nms_ifcfg_rh_storage_destroy); +} + +static void +constructed(GObject *object) +{ + NMSIfcfgRHPlugin * self = NMS_IFCFG_RH_PLUGIN(object); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + + G_OBJECT_CLASS(nms_ifcfg_rh_plugin_parent_class)->constructed(object); + + g_signal_connect(priv->config, + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK(config_changed_cb), + self); + + _dbus_setup(self); +} + +static void +dispose(GObject *object) +{ + NMSIfcfgRHPlugin * self = NMS_IFCFG_RH_PLUGIN(object); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self); + + if (priv->config) + g_signal_handlers_disconnect_by_func(priv->config, config_changed_cb, self); + + /* FIXME(shutdown) we need a stop method so that we can unregistering the D-Bus service + * when NMSettings is shutting down, and not when the instance gets destroyed. */ + _dbus_clear(self); + + nm_sett_util_storages_clear(&priv->storages); + + g_clear_object(&priv->config); + + G_OBJECT_CLASS(nms_ifcfg_rh_plugin_parent_class)->dispose(object); + + nm_clear_pointer(&priv->unmanaged_specs, g_hash_table_destroy); + nm_clear_pointer(&priv->unrecognized_specs, g_hash_table_destroy); +} + +static void +nms_ifcfg_rh_plugin_class_init(NMSIfcfgRHPluginClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS(klass); + + object_class->constructed = constructed; + object_class->dispose = dispose; + + plugin_class->plugin_name = "ifcfg-rh"; + plugin_class->get_unmanaged_specs = get_unmanaged_specs; + plugin_class->get_unrecognized_specs = get_unrecognized_specs; + plugin_class->reload_connections = reload_connections; + plugin_class->load_connections = load_connections; + plugin_class->load_connections_done = load_connections_done; + plugin_class->add_connection = add_connection; + plugin_class->update_connection = update_connection; + plugin_class->delete_connection = delete_connection; +} + +/*****************************************************************************/ + +G_MODULE_EXPORT NMSettingsPlugin * + nm_settings_plugin_factory(void) +{ + return g_object_new(NMS_TYPE_IFCFG_RH_PLUGIN, NULL); +} diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h new file mode 100644 index 00000000..dc1a01c8 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Dan Williams <dcbw@redhat.com> + * Søren Sandmann <sandmann@daimi.au.dk> + * Copyright (C) 2007 - 2008 Red Hat, Inc. + */ + +#ifndef __NMS_IFCFG_RH_PLUGIN_H__ +#define __NMS_IFCFG_RH_PLUGIN_H__ + +#define NMS_TYPE_IFCFG_RH_PLUGIN (nms_ifcfg_rh_plugin_get_type()) +#define NMS_IFCFG_RH_PLUGIN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPlugin)) +#define NMS_IFCFG_RH_PLUGIN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass)) +#define NMS_IS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMS_TYPE_IFCFG_RH_PLUGIN)) +#define NMS_IS_IFCFG_RH_PLUGIN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMS_TYPE_IFCFG_RH_PLUGIN)) +#define NMS_IFCFG_RH_PLUGIN_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass)) + +typedef struct _NMSIfcfgRHPlugin NMSIfcfgRHPlugin; +typedef struct _NMSIfcfgRHPluginClass NMSIfcfgRHPluginClass; + +GType nms_ifcfg_rh_plugin_get_type(void); + +#endif /* __NMS_IFCFG_RH_PLUGIN_H__ */ diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c new file mode 100644 index 00000000..209957d9 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -0,0 +1,6642 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008 - 2017 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nms-ifcfg-rh-reader.h" + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/wait.h> +#include <sys/inotify.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <linux/rtnetlink.h> +#include <linux/if_ether.h> + +#include "nm-glib-aux/nm-secret-utils.h" +#include "nm-connection.h" +#include "nm-dbus-interface.h" +#include "nm-setting-connection.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-vlan.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-wired.h" +#include "nm-setting-wireless.h" +#include "nm-setting-ethtool.h" +#include "nm-setting-8021x.h" +#include "nm-setting-bond.h" +#include "nm-setting-team.h" +#include "nm-setting-team-port.h" +#include "nm-setting-bridge.h" +#include "nm-setting-bridge-port.h" +#include "nm-setting-dcb.h" +#include "nm-setting-user.h" +#include "nm-setting-proxy.h" +#include "nm-setting-generic.h" +#include "nm-core-internal.h" +#include "nm-utils.h" +#include "nm-base/nm-ethtool-base.h" + +#include "platform/nm-platform.h" +#include "NetworkManagerUtils.h" + +#include "nms-ifcfg-rh-common.h" +#include "nms-ifcfg-rh-utils.h" +#include "shvar.h" + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_SETTINGS +#define _NMLOG_PREFIX_NAME "ifcfg-rh" +#define _NMLOG(level, ...) \ + G_STMT_START \ + { \ + nm_log((level), \ + (_NMLOG_DOMAIN), \ + NULL, \ + NULL, \ + "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME ": " _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END + +#define PARSE_WARNING(...) \ + _LOGW("%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), " " _NM_UTILS_MACRO_REST(__VA_ARGS__)) + +/*****************************************************************************/ + +static char * +get_full_file_path(const char *ifcfg_path, const char *file_path) +{ + const char * base = file_path; + gs_free char *dirname = NULL; + char * p; + + g_return_val_if_fail(ifcfg_path != NULL, NULL); + g_return_val_if_fail(file_path != NULL, NULL); + + if (file_path[0] == '/') + return g_strdup(file_path); + + p = strrchr(file_path, '/'); + if (p) + base = p + 1; + + dirname = g_path_get_dirname(ifcfg_path); + return g_build_path("/", dirname, base, NULL); +} + +/*****************************************************************************/ + +static NMSettingSecretFlags +_secret_read_ifcfg_flags(shvarFile *ifcfg, const char *flags_key) +{ + NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; + gs_free char * val_free = NULL; + const char * val; + + nm_assert(flags_key); + nm_assert(g_str_has_suffix(flags_key, "_FLAGS")); + + val = svGetValueStr(ifcfg, flags_key, &val_free); + if (val) { + if (strstr(val, SECRET_FLAG_AGENT)) + flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED; + if (strstr(val, SECRET_FLAG_NOT_SAVED)) + flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED; + if (strstr(val, SECRET_FLAG_NOT_REQUIRED)) + flags |= NM_SETTING_SECRET_FLAG_NOT_REQUIRED; + } + return flags; +} + +static void +_secret_read_ifcfg(shvarFile * ifcfg, + shvarFile * keys_ifcfg, + const char * name, + char ** value, + NMSettingSecretFlags *flags) +{ + char flags_key[250]; + + nm_sprintf_buf(flags_key, "%s_FLAGS", name); + + *flags = _secret_read_ifcfg_flags(ifcfg, flags_key); + + if (*flags != NM_SETTING_SECRET_FLAG_NONE) + *value = NULL; + else { + *value = svGetValue_cp(ifcfg, name); + if (!*value && keys_ifcfg) + *value = svGetValue_cp(keys_ifcfg, name); + } +} + +static void +_secret_set_from_ifcfg(gpointer setting, + shvarFile * ifcfg, + shvarFile * keys_ifcfg, + const char *ifcfg_key, + const char *property_name) +{ + nm_auto_free_secret char *secret = NULL; + NMSettingSecretFlags flags; + char flags_key[250]; + + nm_assert(NM_IS_SETTING(setting)); + + _secret_read_ifcfg(ifcfg, keys_ifcfg, ifcfg_key, &secret, &flags); + + g_object_set(setting, + property_name, + secret, + nm_sprintf_buf(flags_key, "%s-flags", property_name), + flags, + NULL); +} + +static gboolean +_secret_password_raw_to_bytes(const char *ifcfg_key, + const char *password_raw, + GBytes ** out_bytes, + GError ** error) +{ + nm_auto_free_secret_buf NMSecretBuf *secret = NULL; + gsize len; + + if (!password_raw) { + NM_SET_OUT(out_bytes, NULL); + return TRUE; + } + + if (password_raw[0] == '0' && password_raw[1] == 'x') + password_raw += 2; + + secret = nm_secret_buf_new(strlen(password_raw) / 2 + 3); + if (!nm_utils_hexstr2bin_full(password_raw, + FALSE, + FALSE, + FALSE, + ":", + 0, + secret->bin, + secret->len, + &len)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid hex password in %s", + ifcfg_key); + return FALSE; + } + + NM_SET_OUT(out_bytes, nm_secret_buf_to_gbytes_take(g_steal_pointer(&secret), len)); + return TRUE; +} + +/*****************************************************************************/ + +static GBytes * +_cert_get_cert_bytes(const char *ifcfg_path, const char *value, GError **error) +{ + gs_free char *path = NULL; + + if (NM_STR_HAS_PREFIX(value, "pkcs11:")) + return _nm_setting_802_1x_cert_value_to_bytes(NM_SETTING_802_1X_CK_SCHEME_PKCS11, + (guint8 *) value, + -1, + error); + + path = get_full_file_path(ifcfg_path, value); + return _nm_setting_802_1x_cert_value_to_bytes(NM_SETTING_802_1X_CK_SCHEME_PATH, + (guint8 *) path, + -1, + error); +} + +static gboolean +_cert_get_cert(shvarFile * ifcfg, + const char * ifcfg_key, + GBytes ** out_cert, + NMSetting8021xCKScheme *out_scheme, + GError ** error) +{ + nm_auto_free_secret char *val_free = NULL; + const char * val; + gs_unref_bytes GBytes *cert = NULL; + GError * local = NULL; + NMSetting8021xCKScheme scheme; + + val = svGetValueStr(ifcfg, ifcfg_key, &val_free); + if (!val) { + NM_SET_OUT(out_cert, NULL); + NM_SET_OUT(out_scheme, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + return TRUE; + } + + cert = _cert_get_cert_bytes(svFileGetName(ifcfg), val, &local); + if (!cert) + goto err; + + scheme = _nm_setting_802_1x_cert_get_scheme(cert, &local); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) + goto err; + + NM_SET_OUT(out_cert, g_steal_pointer(&cert)); + NM_SET_OUT(out_scheme, scheme); + return TRUE; + +err: + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "invalid certificate %s: %s", + ifcfg_key, + local->message); + g_error_free(local); + return FALSE; +} + +static gboolean +_cert_set_from_ifcfg(gpointer setting, + shvarFile * ifcfg, + const char *ifcfg_key, + const char *property_name, + GBytes ** out_cert, + GError ** error) +{ + gs_unref_bytes GBytes *cert = NULL; + + if (!_cert_get_cert(ifcfg, ifcfg_key, &cert, NULL, error)) + return FALSE; + + g_object_set(setting, property_name, cert, NULL); + + NM_SET_OUT(out_cert, g_steal_pointer(&cert)); + return TRUE; +} + +/*****************************************************************************/ + +static void +check_if_bond_slave(shvarFile *ifcfg, NMSettingConnection *s_con) +{ + gs_free char *value = NULL; + const char * v; + const char * master; + + v = svGetValueStr(ifcfg, "MASTER_UUID", &value); + if (!v) + v = svGetValueStr(ifcfg, "MASTER", &value); + + if (v) { + master = nm_setting_connection_get_master(s_con); + if (master) { + PARSE_WARNING("Already configured as slave of %s. Ignoring MASTER{_UUID}=\"%s\"", + master, + v); + return; + } + + g_object_set(s_con, + NM_SETTING_CONNECTION_MASTER, + v, + NM_SETTING_CONNECTION_SLAVE_TYPE, + NM_SETTING_BOND_SETTING_NAME, + NULL); + } + + /* We should be checking for SLAVE=yes as well, but NM used to not set that, + * so for backward-compatibility, we don't check. + */ +} + +static void +check_if_team_slave(shvarFile *ifcfg, NMSettingConnection *s_con) +{ + gs_free char *value = NULL; + const char * v; + const char * master; + + v = svGetValueStr(ifcfg, "TEAM_MASTER_UUID", &value); + if (!v) + v = svGetValueStr(ifcfg, "TEAM_MASTER", &value); + if (!v) + return; + + master = nm_setting_connection_get_master(s_con); + if (master) { + PARSE_WARNING("Already configured as slave of %s. Ignoring TEAM_MASTER{_UUID}=\"%s\"", + master, + v); + return; + } + + g_object_set(s_con, + NM_SETTING_CONNECTION_MASTER, + v, + NM_SETTING_CONNECTION_SLAVE_TYPE, + NM_SETTING_TEAM_SETTING_NAME, + NULL); +} + +static char * +make_connection_name(shvarFile * ifcfg, + const char *ifcfg_name, + const char *suggested, + const char *prefix) +{ + char *full_name = NULL, *name; + + /* If the ifcfg file already has a NAME, always use that */ + name = svGetValueStr_cp(ifcfg, "NAME"); + if (name) + return name; + + /* Otherwise, construct a new NAME */ + if (!prefix) + prefix = "System"; + + /* For cosmetic reasons, if the suggested name is the same as + * the ifcfg files name, don't use it. Mainly for wifi so that + * the SSID is shown in the connection ID instead of just "wlan0". + */ + if (suggested && strcmp(ifcfg_name, suggested)) + full_name = g_strdup_printf("%s %s (%s)", prefix, suggested, ifcfg_name); + else + full_name = g_strdup_printf("%s %s", prefix, ifcfg_name); + + return full_name; +} + +static NMSetting * +make_connection_setting(const char *file, + shvarFile * ifcfg, + const char *type, + const char *suggested, + const char *prefix) +{ + NMSettingConnection * s_con; + NMSettingConnectionLldp lldp; + const char * ifcfg_name = NULL; + gs_free char * new_id = NULL; + const char * uuid; + gs_free char * uuid_free = NULL; + gs_free char * value = NULL; + const char * v; + gs_free char * stable_id = NULL; + const char *const * iter; + int vint64, i_val; + + ifcfg_name = utils_get_ifcfg_name(file, TRUE); + if (!ifcfg_name) + return NULL; + + s_con = NM_SETTING_CONNECTION(nm_setting_connection_new()); + + new_id = make_connection_name(ifcfg, ifcfg_name, suggested, prefix); + g_object_set(s_con, NM_SETTING_CONNECTION_ID, new_id, NULL); + + /* Try for a UUID key before falling back to hashing the file name */ + uuid = svGetValueStr(ifcfg, "UUID", &uuid_free); + if (!uuid) { + uuid_free = nm_utils_uuid_generate_from_string(svFileGetName(ifcfg), + -1, + NM_UTILS_UUID_TYPE_LEGACY, + NULL); + uuid = uuid_free; + } + + g_object_set(s_con, + NM_SETTING_CONNECTION_TYPE, + type, + NM_SETTING_CONNECTION_UUID, + uuid, + NM_SETTING_CONNECTION_STABLE_ID, + svGetValue(ifcfg, "STABLE_ID", &stable_id), + NULL); + + v = svGetValueStr(ifcfg, "DEVICE", &value); + if (v) { + GError *error = NULL; + + /* Only validate for NMU_IFACE_KERNEL, because ifcfg plugin anyway + * doesn't support OVS types. */ + if (nm_utils_ifname_valid(v, NMU_IFACE_KERNEL, &error)) { + g_object_set(s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, v, NULL); + } else { + PARSE_WARNING("invalid DEVICE name '%s': %s", v, error->message); + g_error_free(error); + } + } + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "LLDP", &value); + if (nm_streq0(v, "rx")) + lldp = NM_SETTING_CONNECTION_LLDP_ENABLE_RX; + else + lldp = svParseBoolean(v, NM_SETTING_CONNECTION_LLDP_DEFAULT); + + /* Missing ONBOOT is treated as "ONBOOT=true" by the old network service */ + g_object_set(s_con, + NM_SETTING_CONNECTION_AUTOCONNECT, + svGetValueBoolean(ifcfg, "ONBOOT", TRUE), + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, + (int) svGetValueInt64(ifcfg, + "AUTOCONNECT_PRIORITY", + 10, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT), + NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES, + (int) svGetValueInt64(ifcfg, "AUTOCONNECT_RETRIES", 10, -1, G_MAXINT32, -1), + NM_SETTING_CONNECTION_MULTI_CONNECT, + (int) svGetValueInt64(ifcfg, + "MULTI_CONNECT", + 10, + G_MININT32, + G_MAXINT32, + NM_CONNECTION_MULTI_CONNECT_DEFAULT), + NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, + svGetValueBoolean(ifcfg, + "AUTOCONNECT_SLAVES", + NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT), + NM_SETTING_CONNECTION_LLDP, + lldp, + NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "USERS", &value); + if (v) { + gs_free const char **items = NULL; + + items = nm_utils_strsplit_set(v, " "); + for (iter = items; iter && *iter; iter++) { + if (!nm_setting_connection_add_permission(s_con, "user", *iter, NULL)) + PARSE_WARNING("invalid USERS item '%s'", *iter); + } + } + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "ZONE", &value); + g_object_set(s_con, NM_SETTING_CONNECTION_ZONE, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "SECONDARY_UUIDS", &value); + if (v) { + gs_free const char **items = NULL; + + items = nm_utils_strsplit_set(v, " \t"); + for (iter = items; iter && *iter; iter++) { + if (!nm_setting_connection_add_secondary(s_con, *iter)) + PARSE_WARNING("secondary connection UUID '%s' already added", *iter); + } + } + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "BRIDGE_UUID", &value); + if (!v) + v = svGetValueStr(ifcfg, "BRIDGE", &value); + if (v) { + const char *old_value; + + if ((old_value = nm_setting_connection_get_master(s_con))) { + PARSE_WARNING("Already configured as slave of %s. Ignoring BRIDGE=\"%s\"", + old_value, + v); + } else { + g_object_set(s_con, NM_SETTING_CONNECTION_MASTER, v, NULL); + g_object_set(s_con, + NM_SETTING_CONNECTION_SLAVE_TYPE, + NM_SETTING_BRIDGE_SETTING_NAME, + NULL); + } + } + + check_if_bond_slave(ifcfg, s_con); + check_if_team_slave(ifcfg, s_con); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "OVS_PORT_UUID", &value); + if (!v) + v = svGetValueStr(ifcfg, "OVS_PORT", &value); + if (v) { + const char *old_value; + + if ((old_value = nm_setting_connection_get_master(s_con))) { + PARSE_WARNING("Already configured as slave of %s. Ignoring OVS_PORT=\"%s\"", + old_value, + v); + } else { + g_object_set(s_con, NM_SETTING_CONNECTION_MASTER, v, NULL); + g_object_set(s_con, + NM_SETTING_CONNECTION_SLAVE_TYPE, + NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + } + } + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "VRF_UUID", &value); + if (!v) + v = svGetValueStr(ifcfg, "VRF", &value); + if (v) { + const char *old_value; + + if ((old_value = nm_setting_connection_get_master(s_con))) { + PARSE_WARNING("Already configured as slave of %s. Ignoring VRF{_UUID}=\"%s\"", + old_value, + v); + } else { + g_object_set(s_con, NM_SETTING_CONNECTION_MASTER, v, NULL); + g_object_set(s_con, + NM_SETTING_CONNECTION_SLAVE_TYPE, + NM_SETTING_VRF_SETTING_NAME, + NULL); + } + } + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "GATEWAY_PING_TIMEOUT", &value); + if (v) { + gint64 tmp; + + tmp = _nm_utils_ascii_str_to_int64(v, 10, 0, G_MAXINT32 - 1, -1); + if (tmp >= 0) { + if (tmp > 600) { + tmp = 600; + PARSE_WARNING("invalid GATEWAY_PING_TIMEOUT time"); + } + g_object_set(s_con, NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, (guint) tmp, NULL); + } else + PARSE_WARNING("invalid GATEWAY_PING_TIMEOUT time"); + } + + switch (svGetValueBoolean(ifcfg, "CONNECTION_METERED", -1)) { + case TRUE: + g_object_set(s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_YES, NULL); + break; + case FALSE: + g_object_set(s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_NO, NULL); + break; + } + + vint64 = svGetValueInt64(ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1); + g_object_set(s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (int) vint64, NULL); + + nm_clear_g_free(&value); + v = svGetValue(ifcfg, "DEVTIMEOUT", &value); + if (v) { + v = nm_str_skip_leading_spaces(v); + vint64 = _nm_utils_ascii_str_to_int64(v, 10, 0, ((gint64) G_MAXINT32) / 1000, -1); + if (vint64 != -1) + vint64 *= 1000; + else if (v[0] != '\0') { + char * endptr; + double d; + + d = nm_g_ascii_strtod(v, &endptr); + endptr = nm_str_skip_leading_spaces(endptr); + if (errno == 0 && endptr[0] == '\0' && d >= 0.0) { + d *= 1000.0; + + /* We round. Yes, this is not correct to round IEEE 754 floats in general, + * but sufficient for our case where we know that NetworkManager wrote the + * setting with up to 3 digits for the milliseconds. */ + d += 0.5; + if (d >= 0.0 && d <= (double) G_MAXINT32) + vint64 = (gint64) d; + } + } + if (vint64 == -1) + PARSE_WARNING("invalid DEVTIMEOUT setting"); + else + g_object_set(s_con, NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, (int) vint64, NULL); + } + + nm_clear_g_free(&value); + v = svGetValue(ifcfg, "MUD_URL", &value); + if (v) + g_object_set(s_con, NM_SETTING_CONNECTION_MUD_URL, v, NULL); + + i_val = NM_SETTING_CONNECTION_MDNS_DEFAULT; + if (!svGetValueEnum(ifcfg, "MDNS", nm_setting_connection_mdns_get_type(), &i_val, NULL)) + PARSE_WARNING("invalid MDNS setting"); + g_object_set(s_con, NM_SETTING_CONNECTION_MDNS, i_val, NULL); + + i_val = NM_SETTING_CONNECTION_LLMNR_DEFAULT; + if (!svGetValueEnum(ifcfg, "LLMNR", nm_setting_connection_llmnr_get_type(), &i_val, NULL)) + PARSE_WARNING("invalid LLMNR setting"); + g_object_set(s_con, NM_SETTING_CONNECTION_LLMNR, i_val, NULL); + + return NM_SETTING(s_con); +} + +static gboolean +read_ip4_address(shvarFile * ifcfg, + const char *tag, + gboolean * out_has_key, + guint32 * out_addr, + GError ** error) +{ + gs_free char *value_to_free = NULL; + const char * value; + in_addr_t a; + + nm_assert(ifcfg); + nm_assert(tag); + nm_assert(!error || !*error); + + value = svGetValueStr(ifcfg, tag, &value_to_free); + if (!value) { + NM_SET_OUT(out_has_key, FALSE); + NM_SET_OUT(out_addr, 0); + return TRUE; + } + + if (inet_pton(AF_INET, value, &a) != 1) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid %s IP4 address '%s'", + tag, + value); + return FALSE; + } + + NM_SET_OUT(out_has_key, TRUE); + NM_SET_OUT(out_addr, a); + return TRUE; +} + +static gboolean +is_any_ip4_address_defined(shvarFile *ifcfg, int *idx) +{ + int i, ignore, *ret_idx; + + ret_idx = idx ?: &ignore; + + for (i = -1; i <= 2; i++) { + gs_free char *value = NULL; + char tag[256]; + + if (svGetValueStr(ifcfg, numbered_tag(tag, "IPADDR", i), &value)) { + *ret_idx = i; + return TRUE; + } + + if (svGetValueStr(ifcfg, numbered_tag(tag, "PREFIX", i), &value)) { + *ret_idx = i; + return TRUE; + } + + if (svGetValueStr(ifcfg, numbered_tag(tag, "NETMASK", i), &value)) { + *ret_idx = i; + return TRUE; + } + } + return FALSE; +} + +/* Returns TRUE on missing address or valid address */ +static gboolean +read_full_ip4_address(shvarFile * ifcfg, + gint32 which, + NMIPAddress * base_addr, + NMIPAddress **out_address, + char ** out_gateway, + GError ** error) +{ + char tag[256]; + char prefix_tag[256]; + guint32 ipaddr; + gs_free char *value = NULL; + const char * v; + int prefix = 0; + gboolean has_key; + guint32 a; + char inet_buf[NM_UTILS_INET_ADDRSTRLEN]; + + g_return_val_if_fail(which >= -1, FALSE); + g_return_val_if_fail(ifcfg != 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(!error || !*error, FALSE); + + /* IP address */ + if (!read_ip4_address(ifcfg, numbered_tag(tag, "IPADDR", which), &has_key, &ipaddr, error)) + return FALSE; + if (!has_key) { + if (!base_addr) + return TRUE; + nm_ip_address_get_address_binary(base_addr, &ipaddr); + } + + /* Gateway */ + if (out_gateway && !*out_gateway) { + if (!read_ip4_address(ifcfg, numbered_tag(tag, "GATEWAY", which), &has_key, &a, error)) + return FALSE; + if (has_key) + *out_gateway = nm_utils_inet4_ntop_dup(a); + } + + /* Prefix */ + numbered_tag(prefix_tag, "PREFIX", which); + v = svGetValueStr(ifcfg, prefix_tag, &value); + if (v) { + prefix = _nm_utils_ascii_str_to_int64(v, 10, 0, 32, -1); + if (prefix < 0) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid IP4 prefix '%s'", + v); + return FALSE; + } + } else { + /* Fall back to NETMASK if no PREFIX was specified */ + if (!read_ip4_address(ifcfg, numbered_tag(tag, "NETMASK", which), &has_key, &a, error)) + return FALSE; + if (has_key) + prefix = nm_utils_ip4_netmask_to_prefix(a); + else { + if (base_addr) + prefix = nm_ip_address_get_prefix(base_addr); + else { + /* Try to autodetermine the prefix for the address' class */ + prefix = _nm_utils_ip4_get_default_prefix(ipaddr); + PARSE_WARNING("missing %s, assuming %s/%d", + prefix_tag, + _nm_utils_inet4_ntop(ipaddr, inet_buf), + prefix); + } + } + } + + *out_address = nm_ip_address_new_binary(AF_INET, &ipaddr, prefix, error); + if (*out_address) + return TRUE; + + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +parse_route_line_is_comment(const char *line) +{ + /* we obtained the line from a legacy route file. Here we skip + * empty lines and comments. + * + * initscripts compares: "$line" =~ '^[[:space:]]*(\#.*)?$' + */ + while (nm_utils_is_separator(line[0])) + line++; + if (NM_IN_SET(line[0], '\0', '#')) + return TRUE; + return FALSE; +} + +/*****************************************************************************/ + +typedef enum { + PARSE_LINE_AF_FLAG_FOR_IPV4 = 0x01, + PARSE_LINE_AF_FLAG_FOR_IPV6 = 0x02, +} ParseLineAFFlag; + +typedef struct { + const char *key; + + /* the element is not available in this case. */ + ParseLineAFFlag disabled : 3; + + bool disabled_with_options_route : 1; + + /* whether the element is to be ignored. Ignord is different from + * "disabled", because we still parse the option, but don't use it. */ + ParseLineAFFlag ignore : 3; + + bool int_base_16 : 1; + + /* the type, one of PARSE_LINE_TYPE_* */ + char type; + +} ParseLineInfo; + +typedef struct { + /* whether the command line option was found, and @v is + * initialized. */ + bool has : 1; + + union { + guint8 uint8; + guint32 uint32; + const char *str; + struct { + guint32 uint32; + bool lock : 1; + } uint32_with_lock; + struct { + NMIPAddr addr; + guint8 plen; + bool has_plen : 1; + } addr; + } v; + +} ParseLineData; + +enum { + /* route attributes */ + PARSE_LINE_ATTR_ROUTE_TYPE, + PARSE_LINE_ATTR_ROUTE_TABLE, + PARSE_LINE_ATTR_ROUTE_SRC, + PARSE_LINE_ATTR_ROUTE_FROM, + PARSE_LINE_ATTR_ROUTE_TOS, + PARSE_LINE_ATTR_ROUTE_SCOPE, + PARSE_LINE_ATTR_ROUTE_ONLINK, + PARSE_LINE_ATTR_ROUTE_WINDOW, + PARSE_LINE_ATTR_ROUTE_CWND, + PARSE_LINE_ATTR_ROUTE_INITCWND, + PARSE_LINE_ATTR_ROUTE_INITRWND, + PARSE_LINE_ATTR_ROUTE_MTU, + + /* iproute2 arguments that only matter when parsing the file. */ + PARSE_LINE_ATTR_ROUTE_TO, + PARSE_LINE_ATTR_ROUTE_VIA, + PARSE_LINE_ATTR_ROUTE_METRIC, + + /* iproute2 parameters that are well known and that we silently ignore. */ + PARSE_LINE_ATTR_ROUTE_DEV, +}; + +#define PARSE_LINE_TYPE_UINT8 '8' +#define PARSE_LINE_TYPE_UINT32 'u' +#define PARSE_LINE_TYPE_UINT32_WITH_LOCK 'l' +#define PARSE_LINE_TYPE_ADDR 'a' +#define PARSE_LINE_TYPE_ADDR_WITH_PREFIX 'p' +#define PARSE_LINE_TYPE_IFNAME 'i' +#define PARSE_LINE_TYPE_FLAG 'f' +#define PARSE_LINE_TYPE_ROUTE_SCOPE 'S' +#define PARSE_LINE_TYPE_STRING 's' + +/** + * parse_route_line: + * @line: the line to parse. This is either a line from the route-* or route6-* file, + * or the numbered OPTIONS setting. + * @addr_family: the address family. + * @options_route: (in-out): when line is from the OPTIONS setting, this is a pre-created + * route object that is completed with the settings from options. Otherwise, + * it shall point to %NULL and a new route is created and returned. + * @out_route: (out) (transfer-full) (allow-none): the parsed %NMIPRoute instance. + * In case a @options_route is passed in, it returns the input route that was modified + * in-place. But the caller must unref the returned route in either case. + * @error: the failure description. + * + * Parsing the route options line has two modes: one for the numbered OPTIONS + * setting, and one for initscript's handle_ip_file(), which takes the lines + * and passes them to `ip route add`. The modes are similar, but certain properties + * are not allowed for OPTIONS. + * The mode is differentiated by having an @options_route argument. + * + * Returns: returns a negative errno on failure. On success, it returns 0 + * and @out_route. + */ +static int +parse_route_line(const char *line, + int addr_family, + NMIPRoute * options_route, + NMIPRoute **out_route, + GError ** error) +{ + static const ParseLineInfo parse_infos[] = { + [PARSE_LINE_ATTR_ROUTE_TYPE] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_TYPE, + .type = PARSE_LINE_TYPE_STRING, + }, + [PARSE_LINE_ATTR_ROUTE_TABLE] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_TABLE, + .type = PARSE_LINE_TYPE_UINT32, + }, + [PARSE_LINE_ATTR_ROUTE_SRC] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_SRC, + .type = PARSE_LINE_TYPE_ADDR, + }, + [PARSE_LINE_ATTR_ROUTE_FROM] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_FROM, + .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX, + .disabled = PARSE_LINE_AF_FLAG_FOR_IPV4, + }, + [PARSE_LINE_ATTR_ROUTE_TOS] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_TOS, + .type = PARSE_LINE_TYPE_UINT8, + .int_base_16 = TRUE, + .ignore = PARSE_LINE_AF_FLAG_FOR_IPV6, + }, + [PARSE_LINE_ATTR_ROUTE_SCOPE] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_SCOPE, + .type = PARSE_LINE_TYPE_ROUTE_SCOPE, + .ignore = PARSE_LINE_AF_FLAG_FOR_IPV6, + }, + [PARSE_LINE_ATTR_ROUTE_ONLINK] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_ONLINK, + .type = PARSE_LINE_TYPE_FLAG, + }, + [PARSE_LINE_ATTR_ROUTE_WINDOW] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_WINDOW, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, + }, + [PARSE_LINE_ATTR_ROUTE_CWND] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_CWND, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, + }, + [PARSE_LINE_ATTR_ROUTE_INITCWND] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_INITCWND, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, + }, + [PARSE_LINE_ATTR_ROUTE_INITRWND] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_INITRWND, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, + }, + [PARSE_LINE_ATTR_ROUTE_MTU] = + { + .key = NM_IP_ROUTE_ATTRIBUTE_MTU, + .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, + }, + + [PARSE_LINE_ATTR_ROUTE_TO] = + { + .key = "to", + .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX, + .disabled_with_options_route = TRUE, + }, + [PARSE_LINE_ATTR_ROUTE_VIA] = + { + .key = "via", + .type = PARSE_LINE_TYPE_ADDR, + .disabled_with_options_route = TRUE, + }, + [PARSE_LINE_ATTR_ROUTE_METRIC] = + { + .key = "metric", + .type = PARSE_LINE_TYPE_UINT32, + .disabled_with_options_route = TRUE, + }, + + [PARSE_LINE_ATTR_ROUTE_DEV] = + { + .key = "dev", + .type = PARSE_LINE_TYPE_IFNAME, + .ignore = PARSE_LINE_AF_FLAG_FOR_IPV4 | PARSE_LINE_AF_FLAG_FOR_IPV6, + .disabled_with_options_route = TRUE, + }, + }; + nm_auto_unref_ip_route NMIPRoute *route = NULL; + gs_free const char ** words_free = NULL; + const char *const * words; + const char * s; + gsize i_words; + guint i; + char buf1[256]; + char buf2[256]; + ParseLineData parse_datas[G_N_ELEMENTS(parse_infos)] = {}; + const ParseLineAFFlag af_flag = + (addr_family == AF_INET) ? PARSE_LINE_AF_FLAG_FOR_IPV4 : PARSE_LINE_AF_FLAG_FOR_IPV6; + + nm_assert(line); + nm_assert_addr_family(addr_family); + nm_assert(!options_route || nm_ip_route_get_family(options_route) == addr_family); + + /* initscripts read the legacy route file line-by-line and + * use it as `ip route add $line`, thus doing split+glob. + * Splitting on IFS (which we consider '<space><tab><newline>') + * and globbing (which we obviously don't do). + * + * I think it's a mess, because it doesn't support escaping or + * quoting. In fact, it can only encode benign values. + * + * We also use the same form for the numbered OPTIONS + * variable. I think it's bad not to support any form of + * escaping. But do that for now. + * + * Maybe later we want to support some form of quotation here. + * Which of course, would be incompatible with initscripts. + */ + words_free = nm_utils_strsplit_set(line, " \t\n"); + + words = words_free ?: NM_PTRARRAY_EMPTY(const char *); + + for (i_words = 0; words[i_words];) { + const gsize i_words0 = i_words; + const char *const w = words[i_words0]; + const ParseLineInfo *p_info; + ParseLineData * p_data; + gboolean unqualified_addr = FALSE; + + for (i = 0; i < G_N_ELEMENTS(parse_infos); i++) { + p_info = &parse_infos[i]; + p_data = &parse_datas[i]; + + if ((p_info->disabled & af_flag) + || (p_info->disabled_with_options_route && options_route)) + continue; + + if (!nm_streq(w, p_info->key)) + continue; + + if (p_data->has) { + /* iproute2 for most arguments allows specifying them multiple times. + * Let's not do that. */ + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Duplicate option \"%s\"", + w); + return -EINVAL; + } + + p_data->has = TRUE; + switch (p_info->type) { + case PARSE_LINE_TYPE_UINT8: + i_words++; + goto parse_line_type_uint8; + case PARSE_LINE_TYPE_UINT32: + i_words++; + goto parse_line_type_uint32; + case PARSE_LINE_TYPE_UINT32_WITH_LOCK: + i_words++; + goto parse_line_type_uint32_with_lock; + case PARSE_LINE_TYPE_ADDR: + i_words++; + goto parse_line_type_addr; + case PARSE_LINE_TYPE_ADDR_WITH_PREFIX: + i_words++; + goto parse_line_type_addr_with_prefix; + case PARSE_LINE_TYPE_IFNAME: + i_words++; + goto parse_line_type_ifname; + case PARSE_LINE_TYPE_FLAG: + i_words++; + goto next; + case PARSE_LINE_TYPE_ROUTE_SCOPE: + i_words++; + goto parse_line_type_route_scope; + default: + nm_assert_not_reached(); + } + } + + p_info = &parse_infos[PARSE_LINE_ATTR_ROUTE_TYPE]; + p_data = &parse_datas[PARSE_LINE_ATTR_ROUTE_TYPE]; + if (!p_data->has + && NM_IN_STRSET(w, + "local", + "unicast", + "broadcast" + "multicast", + "throw", + "unreachable", + "prohibit", + "blackhole", + "nat")) { + p_data->has = TRUE; + goto parse_line_type_string; + } + + /* "to" is also accepted unqualified... (once) */ + p_info = &parse_infos[PARSE_LINE_ATTR_ROUTE_TO]; + p_data = &parse_datas[PARSE_LINE_ATTR_ROUTE_TO]; + if (!p_data->has && !(p_info->disabled & af_flag) + && !(p_info->disabled_with_options_route && options_route)) { + unqualified_addr = TRUE; + p_data->has = TRUE; + goto parse_line_type_addr; + } + + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unrecognized argument (\"to\" is duplicate or \"%s\" is garbage)", + w); + return -EINVAL; + +parse_line_type_route_scope: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + if (nm_streq(s, "global")) + p_data->v.uint8 = RT_SCOPE_UNIVERSE; + else if (nm_streq(s, "nowhere")) + p_data->v.uint8 = RT_SCOPE_NOWHERE; + else if (nm_streq(s, "host")) + p_data->v.uint8 = RT_SCOPE_HOST; + else if (nm_streq(s, "link")) + p_data->v.uint8 = RT_SCOPE_LINK; + else if (nm_streq(s, "site")) + p_data->v.uint8 = RT_SCOPE_SITE; + else { + p_data->v.uint8 = _nm_utils_ascii_str_to_int64(s, 0, 0, G_MAXUINT8, 0); + if (errno) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not a valid number", + w); + return -EINVAL; + } + } + i_words++; + goto next; + +parse_line_type_uint8: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + p_data->v.uint8 = + _nm_utils_ascii_str_to_int64(s, p_info->int_base_16 ? 16 : 10, 0, G_MAXUINT8, 0); + if (errno) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not a valid number", + w); + return -EINVAL; + } + i_words++; + goto next; + +parse_line_type_uint32: +parse_line_type_uint32_with_lock: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + if (p_info->type == PARSE_LINE_TYPE_UINT32_WITH_LOCK) { + if (nm_streq(s, "lock")) { + s = words[++i_words]; + if (!s) + goto err_word_missing_argument; + p_data->v.uint32_with_lock.lock = TRUE; + } else + p_data->v.uint32_with_lock.lock = FALSE; + p_data->v.uint32_with_lock.uint32 = + _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXUINT32, 0); + } else { + p_data->v.uint32 = _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXUINT32, 0); + } + if (errno) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not a valid number", + w); + return -EINVAL; + } + i_words++; + goto next; + +parse_line_type_ifname: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + i_words++; + goto next; + +parse_line_type_addr: +parse_line_type_addr_with_prefix: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + { + int prefix = -1; + + if (p_info->type == PARSE_LINE_TYPE_ADDR) { + if (!nm_utils_parse_inaddr_bin(addr_family, s, NULL, &p_data->v.addr.addr)) { + if (p_info == &parse_infos[PARSE_LINE_ATTR_ROUTE_VIA] + && nm_streq(s, "(null)")) { + /* Due to a bug, would older versions of NM write "via (null)" + * (rh#1452648). Workaround that, and accept it.*/ + memset(&p_data->v.addr.addr, 0, sizeof(p_data->v.addr.addr)); + } else { + if (unqualified_addr) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unrecognized argument (inet prefix is expected rather " + "then \"%s\")", + w); + return -EINVAL; + } else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not a valid IPv%c address", + w, + addr_family == AF_INET ? '4' : '6'); + } + return -EINVAL; + } + } + } else { + nm_assert(p_info->type == PARSE_LINE_TYPE_ADDR_WITH_PREFIX); + if (p_info == &parse_infos[PARSE_LINE_ATTR_ROUTE_TO] && nm_streq(s, "default")) { + memset(&p_data->v.addr.addr, 0, sizeof(p_data->v.addr.addr)); + prefix = 0; + } else if (!nm_utils_parse_inaddr_prefix_bin(addr_family, + s, + NULL, + &p_data->v.addr.addr, + &prefix)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Argument for \"%s\" is not ADDR/PREFIX format", + w); + return -EINVAL; + } + } + if (prefix == -1) + p_data->v.addr.has_plen = FALSE; + else { + p_data->v.addr.has_plen = TRUE; + p_data->v.addr.plen = prefix; + } + } + i_words++; + goto next; + +parse_line_type_string: + s = words[i_words]; + if (!s) + goto err_word_missing_argument; + + p_data->v.str = s; + i_words++; + goto next; + +err_word_missing_argument: + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing argument for \"%s\"", + w); + return -EINVAL; +next:; + } + + if (options_route) { + route = options_route; + nm_ip_route_ref(route); + } else { + ParseLineData *data_to = &parse_datas[PARSE_LINE_ATTR_ROUTE_TO]; + ParseLineData *data_via = &parse_datas[PARSE_LINE_ATTR_ROUTE_VIA]; + ParseLineData *data_metric = &parse_datas[PARSE_LINE_ATTR_ROUTE_METRIC]; + guint prefix; + + if (!data_to->has) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing destination prefix"); + return -EINVAL; + } + + prefix = + data_to->v.addr.has_plen ? data_to->v.addr.plen : (addr_family == AF_INET ? 32 : 128); + + route = + nm_ip_route_new_binary(addr_family, + &data_to->v.addr.addr, + prefix, + data_via->has ? &data_via->v.addr.addr : NULL, + data_metric->has ? (gint64) data_metric->v.uint32 : (gint64) -1, + error); + data_to->has = FALSE; + data_via->has = FALSE; + data_metric->has = FALSE; + if (!route) + return -EINVAL; + } + + for (i = 0; i < G_N_ELEMENTS(parse_infos); i++) { + const ParseLineInfo *p_info = &parse_infos[i]; + ParseLineData * p_data = &parse_datas[i]; + + if (!p_data->has) + continue; + + if ((p_info->ignore & af_flag) || (p_info->disabled & af_flag) + || (p_info->disabled_with_options_route && options_route)) + continue; + + switch (p_info->type) { + case PARSE_LINE_TYPE_UINT8: + case PARSE_LINE_TYPE_ROUTE_SCOPE: + nm_ip_route_set_attribute(route, p_info->key, g_variant_new_byte(p_data->v.uint8)); + break; + case PARSE_LINE_TYPE_UINT32: + nm_ip_route_set_attribute(route, p_info->key, g_variant_new_uint32(p_data->v.uint32)); + break; + case PARSE_LINE_TYPE_UINT32_WITH_LOCK: + if (p_data->v.uint32_with_lock.lock) { + nm_ip_route_set_attribute(route, + nm_sprintf_buf(buf1, "lock-%s", p_info->key), + g_variant_new_boolean(TRUE)); + } + nm_ip_route_set_attribute(route, + p_info->key, + g_variant_new_uint32(p_data->v.uint32_with_lock.uint32)); + break; + case PARSE_LINE_TYPE_ADDR: + case PARSE_LINE_TYPE_ADDR_WITH_PREFIX: + nm_ip_route_set_attribute( + route, + p_info->key, + g_variant_new_printf( + "%s%s", + inet_ntop(addr_family, &p_data->v.addr.addr, buf1, sizeof(buf1)), + p_data->v.addr.has_plen + ? nm_sprintf_buf(buf2, "/%u", (unsigned) p_data->v.addr.plen) + : "")); + break; + case PARSE_LINE_TYPE_FLAG: + /* NOTE: the flag (for "onlink") only allows to explicitly set "TRUE". + * There is no way to express an explicit "FALSE" setting + * of this attribute, hence, the file format cannot encode + * that configuration. */ + nm_ip_route_set_attribute(route, p_info->key, g_variant_new_boolean(TRUE)); + break; + case PARSE_LINE_TYPE_STRING: + nm_ip_route_set_attribute(route, p_info->key, g_variant_new_string(p_data->v.str)); + break; + default: + nm_assert_not_reached(); + break; + } + } + + nm_assert(_nm_ip_route_attribute_validate_all(route, NULL)); + + NM_SET_OUT(out_route, g_steal_pointer(&route)); + return 0; +} + +/* Returns TRUE on missing route or valid route */ +static gboolean +read_one_ip4_route(shvarFile *ifcfg, guint32 which, NMIPRoute **out_route, GError **error) +{ + char tag[256]; + char netmask_tag[256]; + guint32 dest; + guint32 next_hop; + guint32 netmask; + gboolean has_key; + const char * v; + gs_free char *value = NULL; + gint64 prefix, metric; + char inet_buf[NM_UTILS_INET_ADDRSTRLEN]; + + g_return_val_if_fail(ifcfg != NULL, FALSE); + g_return_val_if_fail(out_route && !*out_route, FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + /* Destination */ + if (!read_ip4_address(ifcfg, numbered_tag(tag, "ADDRESS", which), &has_key, &dest, error)) + return FALSE; + if (!has_key) { + /* missing route = success */ + *out_route = NULL; + return TRUE; + } + + /* Next hop */ + if (!read_ip4_address(ifcfg, numbered_tag(tag, "GATEWAY", which), NULL, &next_hop, error)) + return FALSE; + /* We don't make distinction between missing GATEWAY IP and 0.0.0.0 */ + + /* Prefix */ + if (!read_ip4_address(ifcfg, + numbered_tag(netmask_tag, "NETMASK", which), + &has_key, + &netmask, + error)) + return FALSE; + if (has_key) { + prefix = nm_utils_ip4_netmask_to_prefix(netmask); + if (netmask != _nm_utils_ip4_prefix_to_netmask(prefix)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid IP4 netmask '%s' \"%s\"", + netmask_tag, + _nm_utils_inet4_ntop(netmask, inet_buf)); + return FALSE; + } + } else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IP4 route element '%s'", + netmask_tag); + return FALSE; + } + + /* Metric */ + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, numbered_tag(tag, "METRIC", which), &value); + if (v) { + metric = _nm_utils_ascii_str_to_int64(v, 10, 0, G_MAXUINT32, -1); + if (metric < 0) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid IP4 route metric '%s'", + v); + return FALSE; + } + } else + metric = -1; + + *out_route = nm_ip_route_new_binary(AF_INET, &dest, prefix, &next_hop, metric, error); + if (!*out_route) + return FALSE; + + /* Options */ + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, numbered_tag(tag, "OPTIONS", which), &value); + if (v) { + if (parse_route_line(v, AF_INET, *out_route, NULL, error) < 0) { + nm_clear_pointer(out_route, nm_ip_route_unref); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +read_route_file_parse(int addr_family, + const char * filename, + const char * contents, + gsize len, + NMSettingIPConfig *s_ip, + GError ** error) +{ + gsize line_num; + + nm_assert(filename); + nm_assert(addr_family == nm_setting_ip_config_get_addr_family(s_ip)); + nm_assert(!error || !*error); + + if (len <= 0) + return TRUE; /* missing/empty = success */ + + line_num = 0; + while (TRUE) { + nm_auto_unref_ip_route NMIPRoute *route = NULL; + gs_free_error GError *local = NULL; + const char * line = contents; + char * eol; + int e; + + eol = strchr(contents, '\n'); + if (eol) { + eol[0] = '\0'; + contents = &eol[1]; + } + + line_num++; + + if (parse_route_line_is_comment(line)) + goto next; + + e = parse_route_line(line, addr_family, NULL, &route, &local); + + if (e < 0) { + if (e == -ERANGE) + PARSE_WARNING("ignoring manual default route: '%s' (%s)", line, filename); + else { + /* we accept all unrecognized lines, because otherwise we would reject the + * entire connection. */ + PARSE_WARNING("ignoring invalid route at \"%s\" (%s:%lu): %s", + line, + filename, + (long unsigned) line_num, + local->message); + } + goto next; + } + + if (!nm_setting_ip_config_add_route(s_ip, route)) + PARSE_WARNING("duplicate IPv%c route", addr_family == AF_INET ? '4' : '6'); + +next: + if (!eol) + return TRUE; + + /* restore original content. */ + eol[0] = '\n'; + } +} + +static gboolean +read_route_file(int addr_family, const char *filename, NMSettingIPConfig *s_ip, GError **error) +{ + gs_free char *contents = NULL; + gsize len; + + nm_assert(filename); + nm_assert(addr_family == nm_setting_ip_config_get_addr_family(s_ip)); + nm_assert(!error || !*error); + + if (!g_file_get_contents(filename, &contents, &len, NULL)) + return TRUE; /* missing/empty = success */ + + return read_route_file_parse(addr_family, filename, contents, len, s_ip, error); +} + +static void +parse_dns_options(NMSettingIPConfig *ip_config, const char *value) +{ + gs_free const char **options = NULL; + const char *const * item; + + g_return_if_fail(ip_config); + + if (!value) + return; + + if (!nm_setting_ip_config_has_dns_options(ip_config)) + nm_setting_ip_config_clear_dns_options(ip_config, TRUE); + + options = nm_utils_strsplit_set(value, " "); + if (options) { + for (item = options; *item; item++) { + if (!nm_setting_ip_config_add_dns_option(ip_config, *item)) + PARSE_WARNING("can't add DNS option '%s'", *item); + } + } +} + +static gboolean +parse_full_ip6_address(shvarFile * ifcfg, + const char * addr_str, + int i, + NMIPAddress **out_address, + GError ** error) +{ + NMIPAddress *addr; + NMIPAddr addr_bin; + int prefix; + + nm_assert(addr_str); + nm_assert(out_address && !*out_address); + nm_assert(!error || !*error); + + if (!nm_utils_parse_inaddr_prefix_bin(AF_INET6, addr_str, NULL, &addr_bin, &prefix)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid IP6 address '%s'", + addr_str); + return FALSE; + } + + if (prefix < 0) + prefix = 64; + + addr = nm_ip_address_new_binary(AF_INET6, &addr_bin, prefix, error); + if (!addr) + return FALSE; + + *out_address = addr; + return TRUE; +} + +static NMSetting * +make_user_setting(shvarFile *ifcfg) +{ + gboolean has_user_data = FALSE; + gs_unref_object NMSettingUser *s_user = NULL; + gs_unref_hashtable GHashTable *keys = NULL; + GHashTableIter iter; + const char * key; + nm_auto_free_gstring GString *str = NULL; + + keys = svGetKeys(ifcfg, SV_KEY_TYPE_USER); + if (!keys) + return NULL; + + g_hash_table_iter_init(&iter, keys); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, NULL)) { + const char * value; + gs_free char *value_to_free = NULL; + + value = svGetValue(ifcfg, key, &value_to_free); + + if (!value) + continue; + + if (!str) + str = g_string_sized_new(100); + else + g_string_set_size(str, 0); + + if (!nms_ifcfg_rh_utils_user_key_decode(key + NM_STRLEN("NM_USER_"), str)) + continue; + + if (!s_user) + s_user = NM_SETTING_USER(nm_setting_user_new()); + + if (nm_setting_user_set_data(s_user, str->str, value, NULL)) + has_user_data = TRUE; + } + + return has_user_data ? NM_SETTING(g_steal_pointer(&s_user)) : NULL; +} + +static void +make_match_setting_prop(const char * v, + NMSettingMatch **s_match, + void (*add_fcn)(NMSettingMatch *s_match, const char *value)) +{ + gs_free const char **strv = NULL; + gsize i; + + strv = nm_utils_escaped_tokens_split(v, NM_ASCII_SPACES); + if (strv) { + for (i = 0; strv[i]; i++) { + if (!(*s_match)) + *s_match = NM_SETTING_MATCH(nm_setting_match_new()); + add_fcn(*s_match, strv[i]); + } + } +} + +static NMSetting * +make_match_setting(shvarFile *ifcfg) +{ + NMSettingMatch *s_match = NULL; + gs_free char * value_ifn = NULL; + gs_free char * value_kcl = NULL; + gs_free char * value_d = NULL; + gs_free char * value_p = NULL; + const char * v; + + v = svGetValueStr(ifcfg, "MATCH_INTERFACE_NAME", &value_ifn); + make_match_setting_prop(v, &s_match, nm_setting_match_add_interface_name); + v = svGetValueStr(ifcfg, "MATCH_KERNEL_COMMAND_LINE", &value_kcl); + make_match_setting_prop(v, &s_match, nm_setting_match_add_kernel_command_line); + v = svGetValueStr(ifcfg, "MATCH_DRIVER", &value_d); + make_match_setting_prop(v, &s_match, nm_setting_match_add_driver); + v = svGetValueStr(ifcfg, "MATCH_PATH", &value_p); + make_match_setting_prop(v, &s_match, nm_setting_match_add_path); + + return NM_SETTING(s_match); +} + +static NMSetting * +make_proxy_setting(shvarFile *ifcfg) +{ + NMSettingProxy * s_proxy = NULL; + gs_free char * value = NULL; + const char * v; + NMSettingProxyMethod method; + + v = svGetValueStr(ifcfg, "PROXY_METHOD", &value); + if (!v) + return NULL; + + if (!g_ascii_strcasecmp(v, "auto")) + method = NM_SETTING_PROXY_METHOD_AUTO; + else + method = NM_SETTING_PROXY_METHOD_NONE; + + s_proxy = (NMSettingProxy *) nm_setting_proxy_new(); + + switch (method) { + case NM_SETTING_PROXY_METHOD_AUTO: + g_object_set(s_proxy, NM_SETTING_PROXY_METHOD, (int) NM_SETTING_PROXY_METHOD_AUTO, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "PAC_URL", &value); + if (v) + g_object_set(s_proxy, NM_SETTING_PROXY_PAC_URL, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "PAC_SCRIPT", &value); + if (v) + g_object_set(s_proxy, NM_SETTING_PROXY_PAC_SCRIPT, v, NULL); + + break; + case NM_SETTING_PROXY_METHOD_NONE: + g_object_set(s_proxy, NM_SETTING_PROXY_METHOD, (int) NM_SETTING_PROXY_METHOD_NONE, NULL); + break; + } + + if (svGetValueBoolean(ifcfg, "BROWSER_ONLY", FALSE)) + g_object_set(s_proxy, NM_SETTING_PROXY_BROWSER_ONLY, TRUE, NULL); + + return NM_SETTING(s_proxy); +} + +static NMSetting * +make_ip4_setting(shvarFile *ifcfg, + shvarFile *network_ifcfg, + gboolean routes_read, + gboolean * out_has_defroute, + GError ** error) +{ + gs_unref_object NMSettingIPConfig *s_ip4 = NULL; + gs_free char * route_path = NULL; + gs_free char * value = NULL; + const char * v; + char * method; + gs_free char * dns_options_free = NULL; + const char * dns_options = NULL; + gs_free char * gateway = NULL; + int i; + guint32 a; + gboolean has_key; + gboolean never_default; + gint64 i64; + int priority; + const char *const * item; + guint32 route_table; + + nm_assert(out_has_defroute && !*out_has_defroute); + + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + + /* First check if DEFROUTE is set for this device; DEFROUTE has the + * opposite meaning from never-default. The default if DEFROUTE is not + * specified is DEFROUTE=yes which means that this connection can be used + * as a default route + */ + i = svGetValueBoolean(ifcfg, "DEFROUTE", -1); + if (i == -1) + never_default = FALSE; + else { + never_default = !i; + *out_has_defroute = TRUE; + } + + /* Then check if GATEWAYDEV; it's global and overrides DEFROUTE */ + if (network_ifcfg) { + gs_free char *gatewaydev_value = NULL; + const char * gatewaydev; + + /* Get the connection ifcfg device name and the global gateway device */ + v = svGetValueStr(ifcfg, "DEVICE", &value); + gatewaydev = svGetValueStr(network_ifcfg, "GATEWAYDEV", &gatewaydev_value); + dns_options = svGetValue(network_ifcfg, "RES_OPTIONS", &dns_options_free); + + /* If there was a global gateway device specified, then only connections + * for that device can be the default connection. + */ + if (gatewaydev && v) + never_default = !!strcmp(v, gatewaydev); + + nm_clear_g_free(&value); + } + + v = svGetValueStr(ifcfg, "BOOTPROTO", &value); + + if (!v || !*v || !g_ascii_strcasecmp(v, "none")) { + if (is_any_ip4_address_defined(ifcfg, NULL)) + method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; + else + method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; + } else if (!g_ascii_strcasecmp(v, "bootp") || !g_ascii_strcasecmp(v, "dhcp")) { + method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; + } else if (!g_ascii_strcasecmp(v, "static")) { + if (is_any_ip4_address_defined(ifcfg, NULL)) + method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; + else + method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; + } else if (!g_ascii_strcasecmp(v, "autoip")) { + method = NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL; + } else if (!g_ascii_strcasecmp(v, "shared")) { + method = NM_SETTING_IP4_CONFIG_METHOD_SHARED; + } else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown BOOTPROTO '%s'", + v); + return NULL; + } + + /* the route table (policy routing) is ignored if we don't handle routes. */ + route_table = svGetValueInt64(ifcfg, "IPV4_ROUTE_TABLE", 10, 0, G_MAXUINT32, 0); + if (route_table != 0 && !routes_read) { + PARSE_WARNING( + "'rule-' or 'rule6-' files are present; Policy routing (IPV4_ROUTE_TABLE) is ignored"); + route_table = 0; + } + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + method, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, + !svGetValueBoolean(ifcfg, "PEERDNS", TRUE), + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, + !svGetValueBoolean(ifcfg, "PEERROUTES", TRUE), + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, + never_default, + NM_SETTING_IP_CONFIG_MAY_FAIL, + !svGetValueBoolean(ifcfg, "IPV4_FAILURE_FATAL", FALSE), + NM_SETTING_IP_CONFIG_ROUTE_METRIC, + svGetValueInt64(ifcfg, "IPV4_ROUTE_METRIC", 10, -1, G_MAXUINT32, -1), + NM_SETTING_IP_CONFIG_ROUTE_TABLE, + (guint) route_table, + NULL); + + if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + return NM_SETTING(g_steal_pointer(&s_ip4)); + + /* Handle DHCP settings */ + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DHCP_HOSTNAME", &value); + if (v) + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DHCP_FQDN", &value); + if (v) { + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, + NULL, + NM_SETTING_IP4_CONFIG_DHCP_FQDN, + v, + NULL); + } + + i64 = svGetValueInt64(ifcfg, "DHCP_HOSTNAME_FLAGS", 10, 0, G_MAXUINT32, -1); + if (i64 > -1) { + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS, (guint) i64, NULL); + } + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, + svGetValueBoolean(ifcfg, "DHCP_SEND_HOSTNAME", TRUE), + NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, + (int) svGetValueInt64(ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0), + NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DHCP_CLIENT_ID", &value); + if (v) + g_object_set(s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DHCP_VENDOR_CLASS_IDENTIFIER", &value); + if (v) + g_object_set(s_ip4, NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DHCP_IAID", &value); + if (v) + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_DHCP_IAID, v, NULL); + + /* Read static IP addresses. + * Read them even for AUTO method - in this case the addresses are + * added to the automatic ones. Note that this is not currently supported by + * the legacy 'network' service (ifup-eth). + */ + for (i = -1;; i++) { + NMIPAddress *addr = NULL; + + /* gateway will only be set if still unset. Hence, we don't leak gateway + * here by calling read_full_ip4_address() repeatedly */ + if (!read_full_ip4_address(ifcfg, i, NULL, &addr, &gateway, error)) + return NULL; + + if (!addr) { + /* The first mandatory variable is 2-indexed (IPADDR2) + * Variables IPADDR, IPADDR0 and IPADDR1 are optional */ + if (i > 1) + break; + continue; + } + + if (!nm_setting_ip_config_add_address(s_ip4, addr)) + PARSE_WARNING("duplicate IP4 address"); + nm_ip_address_unref(addr); + } + + /* Gateway */ + if (!gateway) { + if (network_ifcfg) { + gboolean read_success; + + read_success = read_ip4_address(network_ifcfg, "GATEWAY", &has_key, &a, error); + if (!read_success) + return NULL; + if (has_key) { + if (nm_setting_ip_config_get_num_addresses(s_ip4) == 0) { + gs_free char *f = g_path_get_basename(svFileGetName(ifcfg)); + PARSE_WARNING("ignoring GATEWAY (/etc/sysconfig/network) for %s " + "because the connection has no static addresses", + f); + } else + gateway = nm_utils_inet4_ntop_dup(a); + } + } + } + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL); + + if (gateway && never_default) + PARSE_WARNING("GATEWAY will be ignored when DEFROUTE is disabled"); + + /* We used to skip saving a lot of unused properties for the ipv4 shared method. + * We want now to persist them but... unfortunately loading DNS or DOMAIN options + * would cause a fail in the ipv4 verify() function. As we don't want any regression + * in the unlikely event that someone has a working ifcfg file for an IPv4 shared ip + * connection with a crafted "DNS" entry... don't load it. So we will avoid failing + * the connection) */ + if (!nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { + /* DNS servers + * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting()) + */ + for (i = 1; i <= 10; i++) { + char tag[256]; + + numbered_tag(tag, "DNS", i); + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, tag, &value); + if (v) { + if (nm_utils_ipaddr_is_valid(AF_INET, v)) { + if (!nm_setting_ip_config_add_dns(s_ip4, v)) + PARSE_WARNING("duplicate DNS server %s", tag); + } else if (nm_utils_ipaddr_is_valid(AF_INET6, v)) { + /* Ignore IPv6 addresses */ + } else { + PARSE_WARNING("invalid DNS server address %s", v); + return NULL; + } + } + } + + /* DNS searches */ + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DOMAIN", &value); + if (v) { + gs_free const char **searches = NULL; + + searches = nm_utils_strsplit_set(v, " "); + if (searches) { + for (item = searches; *item; item++) { + if (!nm_setting_ip_config_add_dns_search(s_ip4, *item)) + PARSE_WARNING("duplicate DNS domain '%s'", *item); + } + } + } + } + + /* DNS options */ + nm_clear_g_free(&value); + parse_dns_options(s_ip4, svGetValue(ifcfg, "RES_OPTIONS", &value)); + parse_dns_options(s_ip4, dns_options); + + /* DNS priority */ + priority = svGetValueInt64(ifcfg, "IPV4_DNS_PRIORITY", 10, G_MININT32, G_MAXINT32, 0); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_DNS_PRIORITY, priority, NULL); + + /* Static routes - route-<name> file */ + route_path = utils_get_route_path(svFileGetName(ifcfg)); + + if (routes_read) { + gs_free char *contents = NULL; + gsize len; + + if (!g_file_get_contents(route_path, &contents, &len, NULL)) + len = 0; + + if (utils_has_route_file_new_syntax_content(contents, len)) { + nm_auto_shvar_file_close shvarFile *route_ifcfg = NULL; + + /* Parse route file in new syntax */ + route_ifcfg = svFile_new(route_path, -1, contents); + for (i = 0;; i++) { + nm_auto_unref_ip_route NMIPRoute *route = NULL; + + if (!read_one_ip4_route(route_ifcfg, i, &route, error)) + return NULL; + + if (!route) + break; + + if (!nm_setting_ip_config_add_route(s_ip4, route)) + PARSE_WARNING("duplicate IP4 route"); + } + } else { + if (!read_route_file_parse(AF_INET, route_path, contents, len, s_ip4, error)) + return NULL; + } + } + + /* Legacy value NM used for a while but is incorrect (rh #459370) */ + if (!nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) + && !nm_setting_ip_config_get_num_dns_searches(s_ip4)) { + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "SEARCH", &value); + if (v) { + gs_free const char **searches = NULL; + + searches = nm_utils_strsplit_set(v, " "); + if (searches) { + for (item = searches; *item; item++) { + if (!nm_setting_ip_config_add_dns_search(s_ip4, *item)) + PARSE_WARNING("duplicate DNS search '%s'", *item); + } + } + } + } + + i64 = svGetValueInt64(ifcfg, "ACD_TIMEOUT", 10, -1, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, -2); + if (i64 == -2) { + i64 = svGetValueInt64(ifcfg, + "ARPING_WAIT", + 10, + -1, + NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX / 1000, + -1); + if (i64 > 0) + i64 *= 1000; + } + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT, (int) i64, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DHCP_REJECT_SERVERS", &value); + if (v) { + gs_free const char **strv = NULL; + + strv = nm_utils_escaped_tokens_split(v, NM_ASCII_SPACES); + if (strv) { + for (item = strv; *item; item++) + nm_setting_ip_config_add_dhcp_reject_server(s_ip4, *item); + } + } + + return NM_SETTING(g_steal_pointer(&s_ip4)); +} + +static void +read_aliases(NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *filename) +{ + GDir * dir; + gs_free char *dirname = NULL; + gs_free char *base = NULL; + NMIPAddress * base_addr = NULL; + GError * err = NULL; + + g_return_if_fail(s_ip4 != NULL); + g_return_if_fail(filename != NULL); + + if (nm_setting_ip_config_get_num_addresses(s_ip4) > 0) + base_addr = nm_setting_ip_config_get_address(s_ip4, 0); + + dirname = g_path_get_dirname(filename); + nm_assert(dirname != NULL); + base = g_path_get_basename(filename); + nm_assert(base != NULL); + + dir = g_dir_open(dirname, 0, &err); + if (dir) { + const char * item; + NMIPAddress *addr; + gboolean ok; + + while ((item = g_dir_read_name(dir))) { + nm_auto_shvar_file_close shvarFile *parsed = NULL; + gs_free char * gateway = NULL; + gs_free char * device_value = NULL; + gs_free char * full_path = NULL; + const char * 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 != '_') + break; + } + if (*p) { + PARSE_WARNING("ignoring alias file '%s' with invalid name", full_path); + continue; + } + + parsed = svOpenFile(full_path, &err); + if (!parsed) { + PARSE_WARNING("couldn't parse alias file '%s': %s", full_path, err->message); + g_clear_error(&err); + continue; + } + + device = svGetValueStr(parsed, "DEVICE", &device_value); + if (!device) { + PARSE_WARNING("alias file '%s' has no DEVICE", 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) { + PARSE_WARNING("alias file '%s' has invalid DEVICE (%s) for filename", + full_path, + device); + continue; + } + + addr = NULL; + ok = read_full_ip4_address(parsed, + -1, + base_addr, + &addr, + read_defroute ? &gateway : NULL, + &err); + if (ok) { + nm_ip_address_set_attribute(addr, + NM_IP_ADDRESS_ATTRIBUTE_LABEL, + g_variant_new_string(device)); + if (!nm_setting_ip_config_add_address(s_ip4, addr)) + PARSE_WARNING("duplicate IP4 address in alias file %s", item); + if (nm_streq0(nm_setting_ip_config_get_method(s_ip4), + NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + if (read_defroute) { + int i; + + if (gateway) { + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL); + read_defroute = FALSE; + } + i = svGetValueBoolean(parsed, "DEFROUTE", -1); + if (i != -1) { + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, + (gboolean) !i, + NULL); + read_defroute = FALSE; + } + } + } else { + PARSE_WARNING("error reading IP4 address from alias file '%s': %s", + full_path, + err ? err->message : "no address"); + g_clear_error(&err); + } + nm_ip_address_unref(addr); + } + + g_dir_close(dir); + } else { + PARSE_WARNING("can not read directory '%s': %s", dirname, err->message); + g_error_free(err); + } +} + +static NMSetting * +make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_read, GError **error) +{ + gs_unref_object NMSettingIPConfig *s_ip6 = NULL; + const char * v; + gs_free char * value = NULL; + gboolean ipv6init; + gboolean ipv6forwarding; + gboolean disabled; + gboolean dhcp6 = FALSE; + char * method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; + const char * ipv6addr, *ipv6addr_secondaries; + gs_free char * ipv6addr_to_free = NULL; + gs_free char * ipv6addr_secondaries_to_free = NULL; + gs_free const char ** list = NULL; + const char *const * iter; + guint32 i; + gint64 i64; + int i_val; + GError * local = NULL; + int priority; + gboolean never_default = FALSE; + gboolean ip6_privacy = FALSE, ip6_privacy_prefer_public_ip; + NMSettingIP6ConfigPrivacy ip6_privacy_val; + guint32 route_table; + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + + /* First check if IPV6_DEFROUTE is set for this device; IPV6_DEFROUTE has the + * opposite meaning from never-default. The default if IPV6_DEFROUTE is not + * specified is IPV6_DEFROUTE=yes which means that this connection can be used + * as a default route + */ + never_default = !svGetValueBoolean(ifcfg, "IPV6_DEFROUTE", TRUE); + + /* Then check if IPV6_DEFAULTGW or IPV6_DEFAULTDEV is specified; + * they are global and override IPV6_DEFROUTE + * When both are set, the device specified in IPV6_DEFAULTGW takes preference. + */ + if (network_ifcfg) { + const char * ipv6_defaultgw, *ipv6_defaultdev; + gs_free char *ipv6_defaultgw_to_free = NULL; + gs_free char *ipv6_defaultdev_to_free = NULL; + const char * default_dev = NULL; + + /* Get the connection ifcfg device name and the global default route device */ + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DEVICE", &value); + ipv6_defaultgw = svGetValueStr(network_ifcfg, "IPV6_DEFAULTGW", &ipv6_defaultgw_to_free); + ipv6_defaultdev = svGetValueStr(network_ifcfg, "IPV6_DEFAULTDEV", &ipv6_defaultdev_to_free); + + if (ipv6_defaultgw) { + default_dev = strchr(ipv6_defaultgw, '%'); + if (default_dev) + default_dev++; + } + if (!default_dev) + default_dev = ipv6_defaultdev; + + /* If there was a global default route device specified, then only connections + * for that device can be the default connection. + */ + if (default_dev && v) + never_default = !!strcmp(v, default_dev); + } + + /* Find out method property */ + /* Is IPV6 enabled? Set method to "ignored", when not enabled */ + disabled = svGetValueBoolean(ifcfg, "IPV6_DISABLED", FALSE); + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IPV6INIT", &value); + ipv6init = svGetValueBoolean(ifcfg, "IPV6INIT", FALSE); + if (!v) { + if (network_ifcfg) + ipv6init = svGetValueBoolean(network_ifcfg, "IPV6INIT", FALSE); + } + + if (disabled) + method = NM_SETTING_IP6_CONFIG_METHOD_DISABLED; + else if (!ipv6init) + method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; + else { + ipv6forwarding = svGetValueBoolean(ifcfg, "IPV6FORWARDING", FALSE); + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IPV6_AUTOCONF", &value); + dhcp6 = svGetValueBoolean(ifcfg, "DHCPV6C", FALSE); + + if (!g_strcmp0(v, "shared")) + method = NM_SETTING_IP6_CONFIG_METHOD_SHARED; + else if (svParseBoolean(v, !ipv6forwarding)) + method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + else if (dhcp6) + method = NM_SETTING_IP6_CONFIG_METHOD_DHCP; + else { + /* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */ + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IPV6ADDR", &value); + if (!v) { + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IPV6ADDR_SECONDARIES", &value); + } + + if (!v) + method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; + } + } + /* TODO - handle other methods */ + + /* Read IPv6 Privacy Extensions configuration */ + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IPV6_PRIVACY", &value); + if (v) { + ip6_privacy = svParseBoolean(v, FALSE); + if (!ip6_privacy) + ip6_privacy = (g_strcmp0(v, "rfc4941") == 0) || (g_strcmp0(v, "rfc3041") == 0); + } + ip6_privacy_prefer_public_ip = svGetValueBoolean(ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", FALSE); + ip6_privacy_val = v ? (ip6_privacy ? (ip6_privacy_prefer_public_ip + ? NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR + : NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR) + : NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED) + : NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; + + /* the route table (policy routing) is ignored if we don't handle routes. */ + route_table = svGetValueInt64(ifcfg, "IPV6_ROUTE_TABLE", 10, 0, G_MAXUINT32, 0); + if (route_table != 0 && !routes_read) { + PARSE_WARNING( + "'rule-' or 'rule6-' files are present; Policy routing (IPV6_ROUTE_TABLE) is ignored"); + route_table = 0; + } + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + method, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, + !svGetValueBoolean(ifcfg, "IPV6_PEERDNS", TRUE), + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, + !svGetValueBoolean(ifcfg, "IPV6_PEERROUTES", TRUE), + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, + never_default, + NM_SETTING_IP_CONFIG_MAY_FAIL, + !svGetValueBoolean(ifcfg, "IPV6_FAILURE_FATAL", FALSE), + NM_SETTING_IP_CONFIG_ROUTE_METRIC, + svGetValueInt64(ifcfg, "IPV6_ROUTE_METRIC", 10, -1, G_MAXUINT32, -1), + NM_SETTING_IP_CONFIG_ROUTE_TABLE, + (guint) route_table, + NM_SETTING_IP6_CONFIG_IP6_PRIVACY, + ip6_privacy_val, + NULL); + + /* Don't bother to read IP, DNS and routes when IPv6 is disabled */ + if (NM_IN_STRSET(method, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) + return NM_SETTING(g_steal_pointer(&s_ip6)); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DHCPV6_DUID", &value); + if (v) + g_object_set(s_ip6, NM_SETTING_IP6_CONFIG_DHCP_DUID, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DHCPV6_IAID", &value); + if (v) + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_DHCP_IAID, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DHCPV6_HOSTNAME", &value); + /* Use DHCP_HOSTNAME as fallback if it is in FQDN format and ipv6.method is + * auto or dhcp: this is required to support old ifcfg files + */ + if (!v + && (!strcmp(method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) + || !strcmp(method, NM_SETTING_IP6_CONFIG_METHOD_DHCP))) { + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DHCP_HOSTNAME", &value); + if (v && !strchr(v, '.')) + v = NULL; + } + if (v) + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, + svGetValueBoolean(ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), + NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, + (int) svGetValueInt64(ifcfg, "IPV6_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0), + NM_SETTING_IP6_CONFIG_RA_TIMEOUT, + (int) svGetValueInt64(ifcfg, "IPV6_RA_TIMEOUT", 10, 0, G_MAXINT32, 0), + NULL); + + i64 = svGetValueInt64(ifcfg, "DHCPV6_HOSTNAME_FLAGS", 10, 0, G_MAXUINT32, -1); + if (i64 > -1) { + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS, (guint) i64, NULL); + } + + /* Read static IP addresses. + * Read them even for AUTO and DHCP methods - in this case the addresses are + * added to the automatic ones. Note that this is not currently supported by + * the legacy 'network' service (ifup-eth). + */ + ipv6addr = svGetValueStr(ifcfg, "IPV6ADDR", &ipv6addr_to_free); + ipv6addr_secondaries = + svGetValueStr(ifcfg, "IPV6ADDR_SECONDARIES", &ipv6addr_secondaries_to_free); + + nm_clear_g_free(&value); + value = g_strjoin(ipv6addr && ipv6addr_secondaries ? " " : NULL, + ipv6addr ?: "", + ipv6addr_secondaries ?: "", + NULL); + + list = nm_utils_strsplit_set(value, " "); + for (iter = list, i = 0; iter && *iter; iter++, i++) { + NMIPAddress *addr = NULL; + + if (!parse_full_ip6_address(ifcfg, *iter, i, &addr, error)) + return NULL; + + if (!nm_setting_ip_config_add_address(s_ip6, addr)) + PARSE_WARNING("duplicate IP6 address"); + nm_ip_address_unref(addr); + } + + /* Gateway */ + if (nm_setting_ip_config_get_num_addresses(s_ip6)) { + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IPV6_DEFAULTGW", &value); + if (!v) { + /* If no gateway in the ifcfg, try global /etc/sysconfig/network instead */ + if (network_ifcfg) { + nm_clear_g_free(&value); + v = svGetValueStr(network_ifcfg, "IPV6_DEFAULTGW", &value); + } + } + if (v) { + char *ptr; + if ((ptr = strchr(v, '%')) != NULL) + *ptr = '\0'; /* remove %interface prefix if present */ + if (!nm_utils_ipaddr_is_valid(AF_INET6, v)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid IP6 address '%s'", + v); + return NULL; + } + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, v, NULL); + } + } + + i_val = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64; + if (!svGetValueEnum(ifcfg, + "IPV6_ADDR_GEN_MODE", + nm_setting_ip6_config_addr_gen_mode_get_type(), + &i_val, + &local)) { + PARSE_WARNING("%s", local->message); + g_clear_error(&local); + } + g_object_set(s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, i_val, NULL); + + /* IPv6 tokenized interface identifier */ + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IPV6_TOKEN", &value); + if (v) + g_object_set(s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, v, NULL); + + /* DNS servers + * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting()) + */ + for (i = 1; i <= 10; i++) { + char tag[256]; + + numbered_tag(tag, "DNS", i); + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, tag, &value); + if (!v) { + /* all done */ + break; + } + + if (nm_utils_ipaddr_is_valid(AF_INET6, v)) { + if (!nm_setting_ip_config_add_dns(s_ip6, v)) + PARSE_WARNING("duplicate DNS server %s", tag); + } else if (nm_utils_ipaddr_is_valid(AF_INET, v)) { + /* Ignore IPv4 addresses */ + } else { + PARSE_WARNING("invalid DNS server address %s", v); + return NULL; + } + } + + if (!routes_read) { + /* NOP */ + } else { + gs_free char *route6_path = NULL; + + /* Read static routes from route6-<interface> file */ + route6_path = utils_get_route6_path(svFileGetName(ifcfg)); + if (!read_route_file(AF_INET6, route6_path, s_ip6, error)) + return NULL; + } + + /* DNS searches */ + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IPV6_DOMAIN", &value); + if (v) { + gs_free const char **searches = NULL; + + searches = nm_utils_strsplit_set(v, " "); + if (searches) { + for (iter = searches; *iter; iter++) { + if (!nm_setting_ip_config_add_dns_search(s_ip6, *iter)) + PARSE_WARNING("duplicate DNS domain '%s'", *iter); + } + } + } + + /* DNS options */ + nm_clear_g_free(&value); + parse_dns_options(s_ip6, svGetValue(ifcfg, "IPV6_RES_OPTIONS", &value)); + + /* DNS priority */ + priority = svGetValueInt64(ifcfg, "IPV6_DNS_PRIORITY", 10, G_MININT32, G_MAXINT32, 0); + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_DNS_PRIORITY, priority, NULL); + + return NM_SETTING(g_steal_pointer(&s_ip6)); +} + +static NMSetting * +make_hostname_setting(shvarFile *ifcfg) +{ + NMSetting *setting; + NMTernary from_dhcp; + NMTernary from_dns_lookup; + NMTernary only_from_default; + int priority; + + priority = svGetValueInt64(ifcfg, "HOSTNAME_PRIORITY", 10, G_MININT32, G_MAXINT32, 0); + + from_dhcp = svGetValueTernary(ifcfg, "HOSTNAME_FROM_DHCP"); + from_dns_lookup = svGetValueTernary(ifcfg, "HOSTNAME_FROM_DNS_LOOKUP"); + only_from_default = svGetValueTernary(ifcfg, "HOSTNAME_ONLY_FROM_DEFAULT"); + + /* Create the setting when at least one key is not default*/ + if (priority == 0 && from_dhcp == NM_TERNARY_DEFAULT && from_dns_lookup == NM_TERNARY_DEFAULT + && only_from_default == NM_TERNARY_DEFAULT) + return NULL; + + setting = nm_setting_hostname_new(); + + g_object_set(setting, + NM_SETTING_HOSTNAME_PRIORITY, + priority, + NM_SETTING_HOSTNAME_FROM_DHCP, + from_dhcp, + NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP, + from_dns_lookup, + NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT, + only_from_default, + NULL); + + return setting; +} + +static NMSetting * +make_sriov_setting(shvarFile *ifcfg) +{ + gs_unref_hashtable GHashTable *keys = NULL; + gs_unref_ptrarray GPtrArray *vfs = NULL; + int autoprobe_drivers; + NMSettingSriov * s_sriov; + gint64 total_vfs; + + total_vfs = svGetValueInt64(ifcfg, "SRIOV_TOTAL_VFS", 10, 0, G_MAXUINT32, -1); + + autoprobe_drivers = svGetValueInt64(ifcfg, + "SRIOV_AUTOPROBE_DRIVERS", + 10, + NM_TERNARY_DEFAULT, + NM_TERNARY_TRUE, + -2); + + keys = svGetKeys(ifcfg, SV_KEY_TYPE_SRIOV_VF); + if (keys) { + GHashTableIter iter; + const char * key; + + g_hash_table_iter_init(&iter, keys); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, NULL)) { + gs_free_error GError *error = NULL; + gs_free char * value_to_free = NULL; + const char * value; + NMSriovVF * vf; + + nm_assert(g_str_has_prefix(key, "SRIOV_VF")); + + value = svGetValue(ifcfg, key, &value_to_free); + if (!value) + continue; + + key += NM_STRLEN("SRIOV_VF"); + + vf = _nm_utils_sriov_vf_from_strparts(key, value, TRUE, &error); + if (!vf) { + PARSE_WARNING("ignoring invalid SR-IOV VF '%s %s': %s", key, value, error->message); + continue; + } + if (!vfs) + vfs = g_ptr_array_new_with_free_func((GDestroyNotify) nm_sriov_vf_unref); + g_ptr_array_add(vfs, vf); + } + } + + /* Create the setting when at least one key is set */ + if (total_vfs < 0 && !vfs && autoprobe_drivers < NM_TERNARY_DEFAULT) + return NULL; + + s_sriov = (NMSettingSriov *) nm_setting_sriov_new(); + + autoprobe_drivers = NM_MAX(autoprobe_drivers, NM_TERNARY_DEFAULT); + total_vfs = NM_MAX(total_vfs, 0); + + g_object_set(s_sriov, + NM_SETTING_SRIOV_TOTAL_VFS, + (guint) total_vfs, + NM_SETTING_SRIOV_VFS, + vfs, + NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, + autoprobe_drivers, + NULL); + + return (NMSetting *) s_sriov; +} + +static NMSetting * +make_tc_setting(shvarFile *ifcfg) +{ + NMSettingTCConfig *s_tc = NULL; + char tag[256]; + int i; + + s_tc = (NMSettingTCConfig *) nm_setting_tc_config_new(); + + for (i = 1;; i++) { + NMTCQdisc * qdisc = NULL; + gs_free char *value_to_free = NULL; + const char * value = NULL; + GError * local = NULL; + + value = svGetValueStr(ifcfg, numbered_tag(tag, "QDISC", i), &value_to_free); + if (!value) + break; + + qdisc = nm_utils_tc_qdisc_from_str(value, &local); + if (!qdisc) { + PARSE_WARNING("ignoring bad tc qdisc: '%s': %s", value, local->message); + continue; + } + + if (!nm_setting_tc_config_add_qdisc(s_tc, qdisc)) + PARSE_WARNING("duplicate tc qdisc"); + + nm_tc_qdisc_unref(qdisc); + } + + for (i = 1;; i++) { + NMTCTfilter * tfilter = NULL; + gs_free char *value_to_free = NULL; + const char * value = NULL; + gs_free_error GError *local = NULL; + + value = svGetValueStr(ifcfg, numbered_tag(tag, "FILTER", i), &value_to_free); + if (!value) + break; + + tfilter = nm_utils_tc_tfilter_from_str(value, &local); + if (!tfilter) { + PARSE_WARNING("ignoring bad tc filter: '%s': %s", value, local->message); + continue; + } + + if (!nm_setting_tc_config_add_tfilter(s_tc, tfilter)) + PARSE_WARNING("duplicate tc filter"); + + nm_tc_tfilter_unref(tfilter); + } + + if (nm_setting_tc_config_get_num_qdiscs(s_tc) > 0 + || nm_setting_tc_config_get_num_tfilters(s_tc) > 0) + return NM_SETTING(s_tc); + + g_object_unref(s_tc); + return NULL; +} + +typedef struct { + const char *enable_key; + const char *advertise_key; + const char *willing_key; + const char *flags_prop; +} DcbFlagsProperty; + +enum { + DCB_APP_FCOE_FLAGS = 0, + DCB_APP_ISCSI_FLAGS = 1, + DCB_APP_FIP_FLAGS = 2, + DCB_PFC_FLAGS = 3, + DCB_PG_FLAGS = 4, +}; + +static DcbFlagsProperty dcb_flags_props[] = { + {KEY_DCB_APP_FCOE_ENABLE, + KEY_DCB_APP_FCOE_ADVERTISE, + KEY_DCB_APP_FCOE_WILLING, + NM_SETTING_DCB_APP_FCOE_FLAGS}, + {KEY_DCB_APP_ISCSI_ENABLE, + KEY_DCB_APP_ISCSI_ADVERTISE, + KEY_DCB_APP_ISCSI_WILLING, + NM_SETTING_DCB_APP_ISCSI_FLAGS}, + {KEY_DCB_APP_FIP_ENABLE, + KEY_DCB_APP_FIP_ADVERTISE, + KEY_DCB_APP_FIP_WILLING, + NM_SETTING_DCB_APP_FIP_FLAGS}, + {KEY_DCB_PFC_ENABLE, + KEY_DCB_PFC_ADVERTISE, + KEY_DCB_PFC_WILLING, + NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS}, + {KEY_DCB_PG_ENABLE, + KEY_DCB_PG_ADVERTISE, + KEY_DCB_PG_WILLING, + NM_SETTING_DCB_PRIORITY_GROUP_FLAGS}, + {NULL}, +}; + +static NMSettingDcbFlags +read_dcb_flags(shvarFile *ifcfg, DcbFlagsProperty *property) +{ + NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; + + if (svGetValueBoolean(ifcfg, property->enable_key, FALSE)) + flags |= NM_SETTING_DCB_FLAG_ENABLE; + if (svGetValueBoolean(ifcfg, property->advertise_key, FALSE)) + flags |= NM_SETTING_DCB_FLAG_ADVERTISE; + if (svGetValueBoolean(ifcfg, property->willing_key, FALSE)) + flags |= NM_SETTING_DCB_FLAG_WILLING; + + return flags; +} + +static gboolean +read_dcb_app(shvarFile * ifcfg, + NMSettingDcb * s_dcb, + const char * app, + DcbFlagsProperty *flags_prop, + const char * priority_prop, + GError ** error) +{ + NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; + gs_free char * value = NULL; + const char * v; + gboolean success = TRUE; + int priority = -1; + char key[255]; + + flags = read_dcb_flags(ifcfg, flags_prop); + + /* Priority */ + nm_sprintf_buf(key, "DCB_APP_%s_PRIORITY", app); + v = svGetValueStr(ifcfg, key, &value); + if (v) { + priority = _nm_utils_ascii_str_to_int64(v, 0, 0, 7, -1); + if (priority < 0) { + success = FALSE; + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid %s value '%s' (expected 0 - 7)", + key, + v); + } + + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) + PARSE_WARNING("ignoring DCB %s priority; app not enabled", app); + } + + if (success) { + g_object_set(G_OBJECT(s_dcb), + flags_prop->flags_prop, + flags, + priority_prop, + (guint) priority, + NULL); + } + + return success; +} + +typedef void (*DcbSetBoolFunc)(NMSettingDcb *, guint, gboolean); + +static gboolean +read_dcb_bool_array(shvarFile * ifcfg, + NMSettingDcb * s_dcb, + NMSettingDcbFlags flags, + const char * prop, + const char * desc, + DcbSetBoolFunc set_func, + GError ** error) +{ + gs_free char *value = NULL; + const char * v; + guint i; + + v = svGetValueStr(ifcfg, prop, &value); + if (!v) + return TRUE; + + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) { + PARSE_WARNING("ignoring %s; %s is not enabled", prop, desc); + return TRUE; + } + + if (strlen(v) != 8) { + PARSE_WARNING("%s value '%s' must be 8 characters long", prop, v); + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "boolean array must be 8 characters"); + return FALSE; + } + + /* All characters must be either 0 or 1 */ + for (i = 0; i < 8; i++) { + if (v[i] != '0' && v[i] != '1') { + PARSE_WARNING("invalid %s value '%s': not all 0s and 1s", prop, v); + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "invalid boolean digit"); + return FALSE; + } + set_func(s_dcb, i, (v[i] == '1')); + } + return TRUE; +} + +typedef void (*DcbSetUintFunc)(NMSettingDcb *, guint, guint); + +static gboolean +read_dcb_uint_array(shvarFile * ifcfg, + NMSettingDcb * s_dcb, + NMSettingDcbFlags flags, + const char * prop, + const char * desc, + gboolean f_allowed, + DcbSetUintFunc set_func, + GError ** error) +{ + gs_free char *val = NULL; + guint i; + + val = svGetValueStr_cp(ifcfg, prop); + if (!val) + return TRUE; + + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) { + PARSE_WARNING("ignoring %s; %s is not enabled", prop, desc); + return TRUE; + } + + if (strlen(val) != 8) { + PARSE_WARNING("%s value '%s' must be 8 characters long", prop, val); + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "uint array must be 8 characters"); + return FALSE; + } + + /* All characters must be either 0 - 7 or (optionally) f */ + for (i = 0; i < 8; i++) { + if (val[i] >= '0' && val[i] <= '7') + set_func(s_dcb, i, val[i] - '0'); + else if (f_allowed && (val[i] == 'f' || val[i] == 'F')) + set_func(s_dcb, i, 15); + else { + PARSE_WARNING("invalid %s value '%s': not 0 - 7%s", + prop, + val, + f_allowed ? " or 'f'" : ""); + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "invalid uint digit"); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +read_dcb_percent_array(shvarFile * ifcfg, + NMSettingDcb * s_dcb, + NMSettingDcbFlags flags, + const char * prop, + const char * desc, + gboolean sum_pct, + DcbSetUintFunc set_func, + GError ** error) +{ + gs_free char * val = NULL; + gs_free const char **split = NULL; + const char *const * iter; + guint i, sum = 0; + + val = svGetValueStr_cp(ifcfg, prop); + if (!val) + return TRUE; + + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) { + PARSE_WARNING("ignoring %s; %s is not enabled", prop, desc); + return TRUE; + } + + split = nm_utils_strsplit_set(val, ","); + if (NM_PTRARRAY_LEN(split) != 8) { + PARSE_WARNING("invalid %s percentage list value '%s'", prop, val); + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "percent array must be 8 elements"); + return FALSE; + } + + for (iter = split, i = 0; iter && *iter; iter++, i++) { + int tmp; + + tmp = _nm_utils_ascii_str_to_int64(*iter, 0, 0, 100, -1); + if (tmp < 0) { + PARSE_WARNING("invalid %s percentage value '%s'", prop, *iter); + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "invalid percent element"); + return FALSE; + } + set_func(s_dcb, i, (guint) tmp); + sum += (guint) tmp; + } + + if (sum_pct && (sum != 100)) { + PARSE_WARNING("%s percentages do not equal 100%%", prop); + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "invalid percentage sum"); + return FALSE; + } + + return TRUE; +} + +static gboolean +make_dcb_setting(shvarFile *ifcfg, NMSetting **out_setting, GError **error) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + gboolean dcb_on; + NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE; + + g_return_val_if_fail(out_setting, FALSE); + *out_setting = NULL; + + dcb_on = !!svGetValueBoolean(ifcfg, "DCB", FALSE); + if (!dcb_on) + return TRUE; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new(); + + /* FCOE */ + if (!read_dcb_app(ifcfg, + s_dcb, + "FCOE", + &dcb_flags_props[DCB_APP_FCOE_FLAGS], + NM_SETTING_DCB_APP_FCOE_PRIORITY, + error)) { + return FALSE; + } + if (nm_setting_dcb_get_app_fcoe_flags(s_dcb) & NM_SETTING_DCB_FLAG_ENABLE) { + gs_free char *val = NULL; + + val = svGetValueStr_cp(ifcfg, KEY_DCB_APP_FCOE_MODE); + if (val) { + if (NM_IN_STRSET(val, NM_SETTING_DCB_FCOE_MODE_FABRIC, NM_SETTING_DCB_FCOE_MODE_VN2VN)) + g_object_set(G_OBJECT(s_dcb), NM_SETTING_DCB_APP_FCOE_MODE, val, NULL); + else { + PARSE_WARNING("invalid FCoE mode '%s'", val); + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "invalid FCoE mode"); + return FALSE; + } + } + } + + /* iSCSI */ + if (!read_dcb_app(ifcfg, + s_dcb, + "ISCSI", + &dcb_flags_props[DCB_APP_ISCSI_FLAGS], + NM_SETTING_DCB_APP_ISCSI_PRIORITY, + error)) { + return FALSE; + } + + /* FIP */ + if (!read_dcb_app(ifcfg, + s_dcb, + "FIP", + &dcb_flags_props[DCB_APP_FIP_FLAGS], + NM_SETTING_DCB_APP_FIP_PRIORITY, + error)) { + return FALSE; + } + + /* Priority Flow Control */ + flags = read_dcb_flags(ifcfg, &dcb_flags_props[DCB_PFC_FLAGS]); + g_object_set(G_OBJECT(s_dcb), NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, flags, NULL); + + if (!read_dcb_bool_array(ifcfg, + s_dcb, + flags, + KEY_DCB_PFC_UP, + "PFC", + nm_setting_dcb_set_priority_flow_control, + error)) { + return FALSE; + } + + /* Priority Groups */ + flags = read_dcb_flags(ifcfg, &dcb_flags_props[DCB_PG_FLAGS]); + g_object_set(G_OBJECT(s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, flags, NULL); + + if (!read_dcb_uint_array(ifcfg, + s_dcb, + flags, + KEY_DCB_PG_ID, + "PGID", + TRUE, + nm_setting_dcb_set_priority_group_id, + error)) { + return FALSE; + } + + /* Group bandwidth */ + if (!read_dcb_percent_array(ifcfg, + s_dcb, + flags, + KEY_DCB_PG_PCT, + "PGPCT", + TRUE, + nm_setting_dcb_set_priority_group_bandwidth, + error)) { + return FALSE; + } + + /* Priority bandwidth */ + if (!read_dcb_percent_array(ifcfg, + s_dcb, + flags, + KEY_DCB_PG_UPPCT, + "UPPCT", + FALSE, + nm_setting_dcb_set_priority_bandwidth, + error)) { + return FALSE; + } + + /* Strict Bandwidth */ + if (!read_dcb_bool_array(ifcfg, + s_dcb, + flags, + KEY_DCB_PG_STRICT, + "STRICT", + nm_setting_dcb_set_priority_strict_bandwidth, + error)) { + return FALSE; + } + + if (!read_dcb_uint_array(ifcfg, + s_dcb, + flags, + KEY_DCB_PG_UP2TC, + "UP2TC", + FALSE, + nm_setting_dcb_set_priority_traffic_class, + error)) { + return FALSE; + } + + *out_setting = NM_SETTING(g_steal_pointer(&s_dcb)); + return TRUE; +} + +static gboolean +add_one_wep_key(shvarFile * ifcfg, + const char * shvar_key, + guint8 key_idx, + gboolean passphrase, + NMSettingWirelessSecurity *s_wsec, + GError ** error) +{ + gs_free char *value_free = NULL; + const char * value; + const char * key = NULL; + + g_return_val_if_fail(ifcfg != NULL, FALSE); + g_return_val_if_fail(shvar_key != NULL, FALSE); + g_return_val_if_fail(key_idx <= 3, FALSE); + g_return_val_if_fail(s_wsec != NULL, FALSE); + + value = svGetValueStr(ifcfg, shvar_key, &value_free); + if (!value) + return TRUE; + + /* Validate keys */ + if (passphrase) { + if (value[0] && strlen(value) < 64) + key = value; + } else { + if (NM_IN_SET(strlen(value), 10, 26)) { + /* Hexadecimal WEP key */ + if (NM_STRCHAR_ANY(value, ch, !g_ascii_isxdigit(ch))) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid hexadecimal WEP key"); + return FALSE; + } + key = value; + } else if (!strncmp(value, "s:", 2) && NM_IN_SET(strlen(value), 7, 15)) { + /* ASCII key */ + if (NM_STRCHAR_ANY(value + 2, ch, !g_ascii_isprint(ch))) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid ASCII WEP key"); + return FALSE; + } + + /* Remove 's:' prefix. + * Don't convert to hex string. wpa_supplicant takes 'wep_key0' option over D-Bus as byte array + * and converts it to hex string itself. Even though we convert hex string keys into a bin string + * before passing to wpa_supplicant, this prevents two unnecessary conversions. And mainly, + * ASCII WEP key doesn't change to HEX WEP key in UI, which could confuse users. + */ + key = value + 2; + } + } + + if (!key) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WEP key length"); + return FALSE; + } + + nm_setting_wireless_security_set_wep_key(s_wsec, key_idx, key); + return TRUE; +} + +static gboolean +read_wep_keys(shvarFile * ifcfg, + NMWepKeyType key_type, + guint8 def_idx, + NMSettingWirelessSecurity *s_wsec, + GError ** error) +{ + if (key_type != NM_WEP_KEY_TYPE_PASSPHRASE) { + if (!add_one_wep_key(ifcfg, "KEY1", 0, FALSE, s_wsec, error)) + return FALSE; + if (!add_one_wep_key(ifcfg, "KEY2", 1, FALSE, s_wsec, error)) + return FALSE; + if (!add_one_wep_key(ifcfg, "KEY3", 2, FALSE, s_wsec, error)) + return FALSE; + if (!add_one_wep_key(ifcfg, "KEY4", 3, FALSE, s_wsec, error)) + return FALSE; + if (!add_one_wep_key(ifcfg, "KEY", def_idx, FALSE, s_wsec, error)) + return FALSE; + } + + if (key_type != NM_WEP_KEY_TYPE_KEY) { + if (!add_one_wep_key(ifcfg, "KEY_PASSPHRASE1", 0, TRUE, s_wsec, error)) + return FALSE; + if (!add_one_wep_key(ifcfg, "KEY_PASSPHRASE2", 1, TRUE, s_wsec, error)) + return FALSE; + if (!add_one_wep_key(ifcfg, "KEY_PASSPHRASE3", 2, TRUE, s_wsec, error)) + return FALSE; + if (!add_one_wep_key(ifcfg, "KEY_PASSPHRASE4", 3, TRUE, s_wsec, error)) + return FALSE; + } + + return TRUE; +} + +static NMSetting * +make_wep_setting(shvarFile *ifcfg, const char *file, GError **error) +{ + gs_unref_object NMSettingWirelessSecurity *s_wsec = NULL; + gs_free char * value = NULL; + shvarFile * keys_ifcfg = NULL; + int default_key_idx = 0; + gboolean has_default_key = FALSE; + NMSettingSecretFlags key_flags; + + s_wsec = NM_SETTING_WIRELESS_SECURITY(nm_setting_wireless_security_new()); + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NULL); + + value = svGetValueStr_cp(ifcfg, "DEFAULTKEY"); + if (value) { + default_key_idx = _nm_utils_ascii_str_to_int64(value, 0, 1, 4, 0); + if (default_key_idx == 0) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid default WEP key '%s'", + value); + return NULL; + } + has_default_key = TRUE; + default_key_idx--; /* convert to [0...3] */ + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, + (guint) default_key_idx, + NULL); + nm_clear_g_free(&value); + } + + /* Read WEP key flags */ + key_flags = _secret_read_ifcfg_flags(ifcfg, "WEP_KEY_FLAGS"); + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, key_flags, NULL); + + /* Read keys in the ifcfg file if they are system-owned */ + if (key_flags == NM_SETTING_SECRET_FLAG_NONE) { + NMWepKeyType key_type; + const char * v; + gs_free char *to_free = NULL; + + v = svGetValueStr(ifcfg, "KEY_TYPE", &to_free); + if (!v) + key_type = NM_WEP_KEY_TYPE_UNKNOWN; + else if (nm_streq(v, "key")) + key_type = NM_WEP_KEY_TYPE_KEY; + else if (nm_streq(v, "passphrase")) + key_type = NM_WEP_KEY_TYPE_PASSPHRASE; + else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid KEY_TYPE value '%s'", + v); + return FALSE; + } + + if (!read_wep_keys(ifcfg, key_type, default_key_idx, s_wsec, error)) + return NULL; + + /* Try to get keys from the "shadow" key file */ + keys_ifcfg = utils_get_keys_ifcfg(file, FALSE); + if (keys_ifcfg) { + if (!read_wep_keys(keys_ifcfg, key_type, default_key_idx, s_wsec, error)) { + svCloseFile(keys_ifcfg); + return NULL; + } + svCloseFile(keys_ifcfg); + g_assert(error == NULL || *error == NULL); + } + + g_object_set(G_OBJECT(s_wsec), NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, key_type, NULL); + } + + value = svGetValueStr_cp(ifcfg, "SECURITYMODE"); + if (value) { + gs_free char *lcase = NULL; + + lcase = g_ascii_strdown(value, -1); + nm_clear_g_free(&value); + + if (nm_streq(lcase, "open")) { + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", NULL); + } else if (nm_streq(lcase, "restricted")) { + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", NULL); + } else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WEP authentication algorithm '%s'", + lcase); + return NULL; + } + } + + /* If no WEP keys were given, and the keys are not agent-owned, and no + * default WEP key index was given, then the connection is unencrypted. + */ + if (!nm_setting_wireless_security_get_wep_key(s_wsec, 0) + && !nm_setting_wireless_security_get_wep_key(s_wsec, 1) + && !nm_setting_wireless_security_get_wep_key(s_wsec, 2) + && !nm_setting_wireless_security_get_wep_key(s_wsec, 3) && (has_default_key == FALSE) + && (key_flags == NM_SETTING_SECRET_FLAG_NONE)) { + const char *auth_alg; + + auth_alg = nm_setting_wireless_security_get_auth_alg(s_wsec); + if (auth_alg && !strcmp(auth_alg, "shared")) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "WEP Shared Key authentication is invalid for " + "unencrypted connections"); + return NULL; + } + + /* Unencrypted */ + return NULL; + } + + return NM_SETTING(g_steal_pointer(&s_wsec)); +} + +static gboolean +fill_wpa_ciphers(shvarFile *ifcfg, NMSettingWirelessSecurity *wsec, gboolean group, gboolean adhoc) +{ + gs_free char * value = NULL; + const char * p; + gs_free const char **list = NULL; + const char *const * iter; + int i = 0; + + p = svGetValueStr(ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE", &value); + if (!p) + return TRUE; + + list = nm_utils_strsplit_set(p, " "); + for (iter = list; iter && *iter; iter++, i++) { + if (!strcmp(*iter, "CCMP")) { + if (group) + nm_setting_wireless_security_add_group(wsec, "ccmp"); + else + nm_setting_wireless_security_add_pairwise(wsec, "ccmp"); + } else if (!strcmp(*iter, "TKIP")) { + if (group) + nm_setting_wireless_security_add_group(wsec, "tkip"); + else + nm_setting_wireless_security_add_pairwise(wsec, "tkip"); + } else if (group && !strcmp(*iter, "WEP104")) + nm_setting_wireless_security_add_group(wsec, "wep104"); + else if (group && !strcmp(*iter, "WEP40")) + nm_setting_wireless_security_add_group(wsec, "wep40"); + else { + PARSE_WARNING("ignoring invalid %s cipher '%s'", + group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE", + *iter); + } + } + + return TRUE; +} + +#define WPA_PMK_LEN 32 + +static char * +parse_wpa_psk(shvarFile *ifcfg, const char *file, GBytes *ssid, GError **error) +{ + shvarFile * keys_ifcfg; + gs_free char *psk = NULL; + size_t plen; + + /* Passphrase must be between 10 and 66 characters in length because WPA + * hex keys are exactly 64 characters (no quoting), and WPA passphrases + * are between 8 and 63 characters (inclusive), plus optional quoting if + * the passphrase contains spaces. + */ + + /* Try to get keys from the "shadow" key file */ + keys_ifcfg = utils_get_keys_ifcfg(file, FALSE); + if (keys_ifcfg) { + psk = svGetValueStr_cp(keys_ifcfg, "WPA_PSK"); + svCloseFile(keys_ifcfg); + } + + /* Fall back to the original ifcfg */ + if (!psk) + psk = svGetValueStr_cp(ifcfg, "WPA_PSK"); + + if (!psk) + return NULL; + + plen = strlen(psk); + + if (plen == 64) { + /* Verify the hex PSK; 64 digits */ + if (!NM_STRCHAR_ALL(psk, ch, g_ascii_isxdigit(ch))) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WPA_PSK (contains non-hexadecimal characters)"); + return NULL; + } + } else { + if (plen < 8 || plen > 63) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WPA_PSK (passphrases must be between " + "8 and 63 characters long (inclusive))"); + return NULL; + } + } + + return g_steal_pointer(&psk); +} + +static gboolean +eap_simple_reader(const char * eap_method, + shvarFile * ifcfg, + shvarFile * keys_ifcfg, + NMSetting8021x *s_8021x, + gboolean phase2, + GError ** error) +{ + NMSettingSecretFlags flags; + gs_free char * identity_free = NULL; + nm_auto_free_secret char *password_raw_str = NULL; + gs_unref_bytes GBytes *password_raw_bytes = NULL; + + g_object_set(s_8021x, + NM_SETTING_802_1X_IDENTITY, + svGetValueStr(ifcfg, "IEEE_8021X_IDENTITY", &identity_free), + NULL); + + _secret_set_from_ifcfg(s_8021x, + ifcfg, + keys_ifcfg, + "IEEE_8021X_PASSWORD", + NM_SETTING_802_1X_PASSWORD); + + _secret_read_ifcfg(ifcfg, keys_ifcfg, "IEEE_8021X_PASSWORD_RAW", &password_raw_str, &flags); + if (!_secret_password_raw_to_bytes("IEEE_8021X_PASSWORD_RAW", + password_raw_str, + &password_raw_bytes, + error)) + return FALSE; + + g_object_set(s_8021x, + NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, + flags, + NM_SETTING_802_1X_PASSWORD_RAW, + password_raw_bytes, + NULL); + + return TRUE; +} + +static gboolean +eap_tls_reader(const char * eap_method, + shvarFile * ifcfg, + shvarFile * keys_ifcfg, + NMSetting8021x *s_8021x, + gboolean phase2, + GError ** error) +{ + gs_unref_bytes GBytes *privkey = NULL; + gs_unref_bytes GBytes *client_cert = NULL; + gs_free char * identity_free = NULL; + gs_free char * value_to_free = NULL; + const char * client_cert_var; + const char * client_cert_prop; + NMSetting8021xCKFormat format; + + g_object_set(s_8021x, + NM_SETTING_802_1X_IDENTITY, + svGetValueStr(ifcfg, "IEEE_8021X_IDENTITY", &identity_free), + NULL); + + /* CA certificate */ + if (!_cert_set_from_ifcfg(s_8021x, + ifcfg, + phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT", + phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT : NM_SETTING_802_1X_CA_CERT, + NULL, + error)) + return FALSE; + _secret_set_from_ifcfg( + s_8021x, + ifcfg, + keys_ifcfg, + phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD", + phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD); + + /* Private key */ + if (!_cert_set_from_ifcfg(s_8021x, + ifcfg, + phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", + phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY + : NM_SETTING_802_1X_PRIVATE_KEY, + &privkey, + error)) + return FALSE; + _secret_set_from_ifcfg(s_8021x, + ifcfg, + keys_ifcfg, + phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" + : "IEEE_8021X_PRIVATE_KEY_PASSWORD", + phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD + : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); + + /* Client certificate */ + client_cert_var = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT"; + client_cert_prop = + phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT; + if (!_cert_set_from_ifcfg(s_8021x, + ifcfg, + client_cert_var, + client_cert_prop, + &client_cert, + error)) + return FALSE; + _secret_set_from_ifcfg(s_8021x, + ifcfg, + keys_ifcfg, + phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" + : "IEEE_8021X_CLIENT_CERT_PASSWORD", + phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD + : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD); + + /* In the past when the private key and client certificate + * were the same PKCS #12 file we used to write only the + * private key variable. Still support that even if it means + * that we have to look into the file content, which makes + * the connection not self-contained. + */ + if (!client_cert && privkey && !svGetValue(ifcfg, client_cert_var, &value_to_free)) { + if (phase2) + format = nm_setting_802_1x_get_phase2_private_key_format(s_8021x); + else + format = nm_setting_802_1x_get_private_key_format(s_8021x); + + if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) + g_object_set(s_8021x, client_cert_prop, privkey, NULL); + } + + return TRUE; +} + +static gboolean +parse_8021x_phase2_auth(shvarFile * ifcfg, + shvarFile * keys_ifcfg, + NMSetting8021x *s_8021x, + GError ** error) +{ + gs_free char * inner_auth = NULL; + gs_free char * v_free = NULL; + const char * v; + gs_free const char **list = NULL; + const char *const * iter; + guint num_auth = 0; + guint num_autheap = 0; + + v = svGetValueStr(ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", &v_free); + if (!v) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IEEE_8021X_INNER_AUTH_METHODS"); + return FALSE; + } + + inner_auth = g_ascii_strdown(v, -1); + list = nm_utils_strsplit_set(inner_auth, " "); + for (iter = list; iter && *iter; iter++) { + if (NM_IN_STRSET(*iter, "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5")) { + if (num_auth == 0) { + if (!eap_simple_reader(*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error)) + return FALSE; + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, *iter, NULL); + } + num_auth++; + } else if (nm_streq(*iter, "tls")) { + if (num_auth == 0) { + if (!eap_tls_reader(*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error)) + return FALSE; + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, "tls", NULL); + } + num_auth++; + } else if (NM_IN_STRSET(*iter, "eap-md5", "eap-mschapv2", "eap-otp", "eap-gtc")) { + if (num_autheap == 0) { + if (!eap_simple_reader(*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error)) + return FALSE; + g_object_set(s_8021x, + NM_SETTING_802_1X_PHASE2_AUTHEAP, + (*iter + NM_STRLEN("eap-")), + NULL); + } + num_autheap++; + } else if (nm_streq(*iter, "eap-tls")) { + if (num_autheap == 0) { + if (!eap_tls_reader(*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error)) + return FALSE; + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL); + } + num_autheap++; + } else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'", + *iter); + return FALSE; + } + } + + if (num_auth > 1) + PARSE_WARNING("Discarded extra phase2 authentication methods"); + if (num_auth > 1) + PARSE_WARNING("Discarded extra phase2 EAP authentication methods"); + + if (!num_auth && !num_autheap) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No phase2 authentication method found"); + return FALSE; + } + + return TRUE; +} + +static gboolean +eap_peap_reader(const char * eap_method, + shvarFile * ifcfg, + shvarFile * keys_ifcfg, + NMSetting8021x *s_8021x, + gboolean phase2, + GError ** error) +{ + gs_free char *value = NULL; + const char * v; + + if (!_cert_set_from_ifcfg(s_8021x, + ifcfg, + "IEEE_8021X_CA_CERT", + NM_SETTING_802_1X_CA_CERT, + NULL, + error)) + return FALSE; + _secret_set_from_ifcfg(s_8021x, + ifcfg, + keys_ifcfg, + "IEEE_8021X_CA_CERT_PASSWORD", + NM_SETTING_802_1X_CA_CERT_PASSWORD); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_PEAP_VERSION", &value); + if (v) { + if (!strcmp(v, "0")) + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "0", NULL); + else if (!strcmp(v, "1")) + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "1", NULL); + else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown IEEE_8021X_PEAP_VERSION value '%s'", + v); + return FALSE; + } + } + + if (svGetValueBoolean(ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE)) + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_ANON_IDENTITY", &value); + if (v) + g_object_set(s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, v, NULL); + + if (!parse_8021x_phase2_auth(ifcfg, keys_ifcfg, s_8021x, error)) + return FALSE; + + return TRUE; +} + +static gboolean +eap_ttls_reader(const char * eap_method, + shvarFile * ifcfg, + shvarFile * keys_ifcfg, + NMSetting8021x *s_8021x, + gboolean phase2, + GError ** error) +{ + gs_free char *value = NULL; + const char * v; + + if (!_cert_set_from_ifcfg(s_8021x, + ifcfg, + "IEEE_8021X_CA_CERT", + NM_SETTING_802_1X_CA_CERT, + NULL, + error)) + return FALSE; + _secret_set_from_ifcfg(s_8021x, + ifcfg, + keys_ifcfg, + "IEEE_8021X_CA_CERT_PASSWORD", + NM_SETTING_802_1X_CA_CERT_PASSWORD); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_ANON_IDENTITY", &value); + if (v) + g_object_set(s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, v, NULL); + + if (!parse_8021x_phase2_auth(ifcfg, keys_ifcfg, s_8021x, error)) + return FALSE; + + return TRUE; +} + +static gboolean +eap_fast_reader(const char * eap_method, + shvarFile * ifcfg, + shvarFile * keys_ifcfg, + NMSetting8021x *s_8021x, + gboolean phase2, + GError ** error) +{ + gs_free char * anon_ident = NULL; + gs_free char * pac_file = NULL; + gs_free char * real_pac_path = NULL; + gs_free char * fast_provisioning = NULL; + const char *const *iter; + const char * pac_prov_str; + gboolean allow_unauth = FALSE, allow_auth = FALSE; + + pac_file = svGetValueStr_cp(ifcfg, "IEEE_8021X_PAC_FILE"); + if (pac_file) { + real_pac_path = get_full_file_path(svFileGetName(ifcfg), pac_file); + g_object_set(s_8021x, NM_SETTING_802_1X_PAC_FILE, real_pac_path, NULL); + } + + fast_provisioning = svGetValueStr_cp(ifcfg, "IEEE_8021X_FAST_PROVISIONING"); + if (fast_provisioning) { + gs_free const char **list = NULL; + + list = nm_utils_strsplit_set(fast_provisioning, " \t"); + for (iter = list; iter && *iter; iter++) { + if (strcmp(*iter, "allow-unauth") == 0) + allow_unauth = TRUE; + else if (strcmp(*iter, "allow-auth") == 0) + allow_auth = TRUE; + else { + PARSE_WARNING( + "invalid IEEE_8021X_FAST_PROVISIONING '%s' " + "(space-separated list of these values [allow-auth, allow-unauth] expected)", + *iter); + } + } + } + pac_prov_str = allow_unauth ? (allow_auth ? "3" : "1") : (allow_auth ? "2" : "0"); + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, pac_prov_str, NULL); + + if (!pac_file && !(allow_unauth || allow_auth)) { + g_set_error( + error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "IEEE_8021X_PAC_FILE not provided and EAP-FAST automatic PAC provisioning disabled"); + return FALSE; + } + + anon_ident = svGetValueStr_cp(ifcfg, "IEEE_8021X_ANON_IDENTITY"); + if (anon_ident) + g_object_set(s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL); + + if (!parse_8021x_phase2_auth(ifcfg, keys_ifcfg, s_8021x, error)) + return FALSE; + + return TRUE; +} + +typedef struct { + const char *method; + gboolean (*reader)(const char * eap_method, + shvarFile * ifcfg, + shvarFile * keys_ifcfg, + NMSetting8021x *s_8021x, + gboolean phase2, + GError ** error); + gboolean wifi_phase2_only; +} EAPReader; + +static EAPReader eap_readers[] = {{"md5", eap_simple_reader, TRUE}, + {"pap", eap_simple_reader, TRUE}, + {"chap", eap_simple_reader, TRUE}, + {"mschap", eap_simple_reader, TRUE}, + {"mschapv2", eap_simple_reader, TRUE}, + {"leap", eap_simple_reader, FALSE}, + {"pwd", eap_simple_reader, FALSE}, + {"tls", eap_tls_reader, FALSE}, + {"peap", eap_peap_reader, FALSE}, + {"ttls", eap_ttls_reader, FALSE}, + {"fast", eap_fast_reader, FALSE}, + {NULL, NULL}}; + +static void +read_8021x_list_value(shvarFile * ifcfg, + const char * ifcfg_var_name, + NMSetting8021x *setting, + const char * prop_name) +{ + gs_free char * value = NULL; + gs_free const char **strv = NULL; + const char * v; + + g_return_if_fail(ifcfg != NULL); + g_return_if_fail(ifcfg_var_name != NULL); + g_return_if_fail(prop_name != NULL); + + v = svGetValueStr(ifcfg, ifcfg_var_name, &value); + if (!v) + return; + + strv = nm_utils_strsplit_set(v, " \t"); + if (strv) + g_object_set(setting, prop_name, strv, NULL); +} + +static NMSetting8021x * +fill_8021x(shvarFile *ifcfg, const char *file, const char *key_mgmt, gboolean wifi, GError **error) +{ + nm_auto_shvar_file_close shvarFile *keys_ifcfg = NULL; + gs_unref_object NMSetting8021x *s_8021x = NULL; + gs_free char * value = NULL; + const char * v; + gs_free const char ** list = NULL; + const char *const * iter; + gint64 timeout; + int i_val; + + v = svGetValueStr(ifcfg, "IEEE_8021X_EAP_METHODS", &value); + if (!v) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IEEE_8021X_EAP_METHODS for key management '%s'", + key_mgmt); + return NULL; + } + + list = nm_utils_strsplit_set(v, " "); + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new(); + + /* Read in the lookaside keys_ifcfg file, if present */ + keys_ifcfg = utils_get_keys_ifcfg(file, FALSE); + + /* Validate and handle each EAP method */ + for (iter = list; iter && *iter; iter++) { + EAPReader * eap = &eap_readers[0]; + gboolean found = FALSE; + gs_free char *lower = NULL; + + lower = g_ascii_strdown(*iter, -1); + while (eap->method) { + if (strcmp(eap->method, lower)) + goto next; + + /* Some EAP methods don't provide keying material, thus they + * cannot be used with Wi-Fi unless they are an inner method + * used with TTLS or PEAP or whatever. + */ + if (wifi && eap->wifi_phase2_only) { + PARSE_WARNING("ignored invalid IEEE_8021X_EAP_METHOD '%s'; not allowed for wifi", + lower); + goto next; + } + + /* Parse EAP method specific options */ + if (!(*eap->reader)(lower, ifcfg, keys_ifcfg, s_8021x, FALSE, error)) + return NULL; + + nm_setting_802_1x_add_eap_method(s_8021x, lower); + found = TRUE; + break; + +next: + eap++; + } + + if (!found) + PARSE_WARNING("ignored unknown IEEE_8021X_EAP_METHOD '%s'", lower); + } + + if (nm_setting_802_1x_get_num_eap_methods(s_8021x) == 0) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No valid EAP methods found in IEEE_8021X_EAP_METHODS"); + return NULL; + } + + g_object_set(s_8021x, + NM_SETTING_802_1X_SYSTEM_CA_CERTS, + svGetValueBoolean(ifcfg, "IEEE_8021X_SYSTEM_CA_CERTS", FALSE), + NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_SUBJECT_MATCH", &value); + g_object_set(s_8021x, NM_SETTING_802_1X_SUBJECT_MATCH, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH", &value); + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, v, NULL); + + i_val = NM_SETTING_802_1X_AUTH_FLAGS_NONE; + if (!svGetValueEnum(ifcfg, + "IEEE_8021X_PHASE1_AUTH_FLAGS", + nm_setting_802_1x_auth_flags_get_type(), + &i_val, + error)) + return NULL; + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, (guint) i_val, NULL); + + read_8021x_list_value(ifcfg, + "IEEE_8021X_ALTSUBJECT_MATCHES", + s_8021x, + NM_SETTING_802_1X_ALTSUBJECT_MATCHES); + read_8021x_list_value(ifcfg, + "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", + s_8021x, + NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH", &value); + g_object_set(s_8021x, NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH", &value); + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_DOMAIN_MATCH", &value); + g_object_set(s_8021x, NM_SETTING_802_1X_DOMAIN_MATCH, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_PHASE2_DOMAIN_MATCH", &value); + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH, v, NULL); + + timeout = svGetValueInt64(ifcfg, "IEEE_8021X_AUTH_TIMEOUT", 10, 0, G_MAXINT32, 0); + g_object_set(s_8021x, NM_SETTING_802_1X_AUTH_TIMEOUT, (int) timeout, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_CA_PATH", &value); + g_object_set(s_8021x, NM_SETTING_802_1X_CA_PATH, v, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "IEEE_8021X_PHASE2_CA_PATH", &value); + g_object_set(s_8021x, NM_SETTING_802_1X_PHASE2_CA_PATH, v, NULL); + + g_object_set(s_8021x, + NM_SETTING_802_1X_OPTIONAL, + svGetValueBoolean(ifcfg, "IEEE_8021X_OPTIONAL", FALSE), + NULL); + + _secret_set_from_ifcfg(s_8021x, ifcfg, keys_ifcfg, "IEEE_8021X_PIN", NM_SETTING_802_1X_PIN); + + return g_steal_pointer(&s_8021x); +} + +static NMSetting * +make_wpa_setting(shvarFile * ifcfg, + const char * file, + GBytes * ssid, + gboolean adhoc, + NMSetting8021x **s_8021x, + GError ** error) +{ + gs_unref_object NMSettingWirelessSecurity *wsec = NULL; + gs_free char * value = NULL; + const char * v; + gboolean wpa_psk = FALSE, wpa_sae = FALSE, wpa_owe = FALSE, wpa_eap = FALSE, ieee8021x = FALSE, + wpa3_eap = FALSE; + int i_val; + GError *local = NULL; + + wsec = NM_SETTING_WIRELESS_SECURITY(nm_setting_wireless_security_new()); + + v = svGetValueStr(ifcfg, "KEY_MGMT", &value); + wpa_psk = nm_streq0(v, "WPA-PSK"); + wpa_sae = nm_streq0(v, "SAE"); + wpa_owe = nm_streq0(v, "OWE"); + wpa_eap = nm_streq0(v, "WPA-EAP"); + wpa3_eap = nm_streq0(v, "WPA-EAP-SUITE-B-192"); + ieee8021x = nm_streq0(v, "IEEE8021X"); + if (!wpa_psk && !wpa_sae && !wpa_owe && !wpa_eap && !wpa3_eap && !ieee8021x) + return NULL; /* Not WPA or Dynamic WEP */ + + /* WPS */ + i_val = NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT; + if (!svGetValueEnum(ifcfg, + "WPS_METHOD", + nm_setting_wireless_security_wps_method_get_type(), + &i_val, + error)) + return NULL; + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_WPS_METHOD, (guint) i_val, NULL); + + /* Pairwise and Group ciphers (only relevant for WPA/RSN) */ + if (wpa_psk || wpa_sae || wpa_owe || wpa_eap) { + fill_wpa_ciphers(ifcfg, wsec, FALSE, adhoc); + fill_wpa_ciphers(ifcfg, wsec, TRUE, adhoc); + } + + /* WPA and/or RSN */ + if (adhoc) { + /* Ad-Hoc mode only supports RSN proto */ + nm_setting_wireless_security_add_proto(wsec, "rsn"); + } else { + gs_free char *value2 = NULL; + const char * v2; + + v2 = svGetValueStr(ifcfg, "WPA_ALLOW_WPA", &value2); + if (v2 && svParseBoolean(v2, TRUE)) + nm_setting_wireless_security_add_proto(wsec, "wpa"); + + nm_clear_g_free(&value2); + v2 = svGetValueStr(ifcfg, "WPA_ALLOW_WPA2", &value2); + if (v2 && svParseBoolean(v2, TRUE)) + nm_setting_wireless_security_add_proto(wsec, "rsn"); + } + + if (wpa_psk || wpa_sae) { + NMSettingSecretFlags psk_flags; + + psk_flags = _secret_read_ifcfg_flags(ifcfg, "WPA_PSK_FLAGS"); + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, psk_flags, NULL); + + /* Read PSK if it's system-owned */ + if (psk_flags == NM_SETTING_SECRET_FLAG_NONE) { + gs_free char *psk = NULL; + + psk = parse_wpa_psk(ifcfg, file, ssid, &local); + if (psk) + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_PSK, psk, NULL); + else if (local) { + g_propagate_error(error, local); + return NULL; + } + } + + if (wpa_psk) + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", NULL); + else { + nm_assert(wpa_sae); + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "sae", NULL); + } + } else { + nm_assert(wpa_eap || wpa3_eap || ieee8021x || wpa_owe); + + /* Adhoc mode is mutually exclusive with any 802.1x-based authentication */ + if (adhoc) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", + v); + return NULL; + } + + if (wpa_owe) { + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "owe", NULL); + } else { + *s_8021x = fill_8021x(ifcfg, file, v, TRUE, error); + if (!*s_8021x) + return NULL; + + { + gs_free char *lower = g_ascii_strdown(v, -1); + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, lower, NULL); + } + } + } + + i_val = NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT; + if (!svGetValueEnum(ifcfg, "PMF", nm_setting_wireless_security_pmf_get_type(), &i_val, error)) + return NULL; + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_PMF, i_val, NULL); + + i_val = NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT; + if (!svGetValueEnum(ifcfg, "FILS", nm_setting_wireless_security_fils_get_type(), &i_val, error)) + return NULL; + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_FILS, i_val, NULL); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "SECURITYMODE", &value); + if (NM_IN_STRSET(v, NULL, "open")) + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, v, NULL); + + return (NMSetting *) g_steal_pointer(&wsec); +} + +static NMSetting * +make_leap_setting(shvarFile *ifcfg, const char *file, GError **error) +{ + gs_unref_object NMSettingWirelessSecurity *wsec = NULL; + shvarFile * keys_ifcfg; + gs_free char * value = NULL; + NMSettingSecretFlags flags; + + wsec = NM_SETTING_WIRELESS_SECURITY(nm_setting_wireless_security_new()); + + value = svGetValueStr_cp(ifcfg, "KEY_MGMT"); + if (!value || strcmp(value, "IEEE8021X")) + return NULL; + nm_clear_g_free(&value); + + value = svGetValueStr_cp(ifcfg, "SECURITYMODE"); + if (!value || g_ascii_strcasecmp(value, "leap")) + return NULL; /* Not LEAP */ + nm_clear_g_free(&value); + + flags = _secret_read_ifcfg_flags(ifcfg, "IEEE_8021X_PASSWORD_FLAGS"); + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, flags, NULL); + + /* Read LEAP password if it's system-owned */ + if (flags == NM_SETTING_SECRET_FLAG_NONE) { + value = svGetValueStr_cp(ifcfg, "IEEE_8021X_PASSWORD"); + if (!value) { + /* Try to get keys from the "shadow" key file */ + keys_ifcfg = utils_get_keys_ifcfg(file, FALSE); + if (keys_ifcfg) { + value = svGetValueStr_cp(keys_ifcfg, "IEEE_8021X_PASSWORD"); + svCloseFile(keys_ifcfg); + } + } + if (value && strlen(value)) + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, value, NULL); + nm_clear_g_free(&value); + } + + value = svGetValueStr_cp(ifcfg, "IEEE_8021X_IDENTITY"); + if (!value) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing LEAP identity"); + return NULL; + } + g_object_set(wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, value, NULL); + nm_clear_g_free(&value); + + g_object_set(wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "leap", + NULL); + + return (NMSetting *) g_steal_pointer(&wsec); +} + +static NMSetting * +make_wireless_security_setting(shvarFile * ifcfg, + const char * file, + GBytes * ssid, + gboolean adhoc, + NMSetting8021x **s_8021x, + GError ** error) +{ + NMSetting *wsec; + + g_return_val_if_fail(error && !*error, NULL); + + if (!adhoc) { + wsec = make_leap_setting(ifcfg, file, error); + if (wsec) + return wsec; + else if (*error) + return NULL; + } + + wsec = make_wpa_setting(ifcfg, file, ssid, adhoc, s_8021x, error); + if (wsec) + return wsec; + else if (*error) + return NULL; + + wsec = make_wep_setting(ifcfg, file, error); + if (wsec) + return wsec; + else if (*error) + return NULL; + + return NULL; /* unencrypted */ +} + +static const char ** +transform_hwaddr_blacklist(const char *blacklist) +{ + const char **strv; + gsize i, j; + + strv = nm_utils_strsplit_set(blacklist, " \t"); + if (!strv) + return NULL; + for (i = 0, j = 0; strv[j]; j++) { + const char *s = strv[j]; + + if (!nm_utils_hwaddr_valid(s, ETH_ALEN)) { + PARSE_WARNING("invalid MAC in HWADDR_BLACKLIST '%s'", s); + continue; + } + strv[i++] = s; + } + strv[i] = NULL; + return strv; +} + +static NMSetting * +make_wireless_setting(shvarFile *ifcfg, GError **error) +{ + NMSettingWireless * s_wireless; + const char * cvalue; + char * value = NULL; + gint64 chan = 0; + NMSettingMacRandomization mac_randomization; + NMSettingWirelessPowersave powersave = NM_SETTING_WIRELESS_POWERSAVE_DEFAULT; + NMTernary ternary; + + s_wireless = NM_SETTING_WIRELESS(nm_setting_wireless_new()); + + value = svGetValueStr_cp(ifcfg, "HWADDR"); + if (value) { + value = g_strstrip(value); + g_object_set(s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS, value, NULL); + g_free(value); + } + + value = svGetValueStr_cp(ifcfg, "MACADDR"); + if (value) { + value = g_strstrip(value); + g_object_set(s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, value, NULL); + g_free(value); + } + + value = svGetValueStr_cp(ifcfg, "GENERATE_MAC_ADDRESS_MASK"); + g_object_set(s_wireless, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, value, NULL); + g_free(value); + + cvalue = svGetValueStr(ifcfg, "HWADDR_BLACKLIST", &value); + if (cvalue) { + gs_free const char **strv = NULL; + + strv = transform_hwaddr_blacklist(cvalue); + g_object_set(s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, strv, NULL); + g_free(value); + } + + value = svGetValueStr_cp(ifcfg, "ESSID"); + if (value) { + gs_unref_bytes GBytes *bytes = NULL; + gsize ssid_len = 0; + gsize value_len = strlen(value); + + if (value_len > 2 && (value_len % 2) == 0 && g_str_has_prefix(value, "0x") + && NM_STRCHAR_ALL(&value[2], ch, g_ascii_isxdigit(ch))) { + /* interpret the value as hex-digits iff value starts + * with "0x" followed by pairs of hex digits */ + bytes = nm_utils_hexstr2bin(&value[2]); + } else + bytes = g_bytes_new(value, value_len); + + ssid_len = g_bytes_get_size(bytes); + if (ssid_len > 32 || ssid_len == 0) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)", + value, + ssid_len); + g_free(value); + goto error; + } + + g_object_set(s_wireless, NM_SETTING_WIRELESS_SSID, bytes, NULL); + g_free(value); + } + + value = svGetValueStr_cp(ifcfg, "MODE"); + if (value) { + char * lcase; + const char *mode = NULL; + + lcase = g_ascii_strdown(value, -1); + g_free(value); + + if (!strcmp(lcase, "ad-hoc")) { + mode = "adhoc"; + } else if (!strcmp(lcase, "ap")) { + mode = "ap"; + } else if (!strcmp(lcase, "managed") || !strcmp(lcase, "auto")) { + mode = "infrastructure"; + } else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid mode '%s' (not 'Ad-Hoc', 'Ap', 'Managed', or 'Auto')", + lcase); + g_free(lcase); + goto error; + } + g_free(lcase); + + g_object_set(s_wireless, NM_SETTING_WIRELESS_MODE, mode, NULL); + } + + value = svGetValueStr_cp(ifcfg, "BSSID"); + if (value) { + value = g_strstrip(value); + g_object_set(s_wireless, NM_SETTING_WIRELESS_BSSID, value, NULL); + g_free(value); + } + + value = svGetValueStr_cp(ifcfg, "CHANNEL"); + if (value) { + chan = _nm_utils_ascii_str_to_int64(value, 10, 1, 196, 0); + if (chan == 0) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid wireless channel '%s'", + value); + g_free(value); + goto error; + } + g_object_set(s_wireless, NM_SETTING_WIRELESS_CHANNEL, (guint32) chan, NULL); + g_free(value); + } + + value = svGetValueStr_cp(ifcfg, "BAND"); + if (value) { + if (!strcmp(value, "a")) { + if (chan && chan <= 14) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Band '%s' invalid for channel %u", + value, + (guint32) chan); + g_free(value); + goto error; + } + } else if (!strcmp(value, "bg")) { + if (chan && chan > 14) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Band '%s' invalid for channel %u", + value, + (guint32) chan); + g_free(value); + goto error; + } + } else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid wireless band '%s'", + value); + g_free(value); + goto error; + } + g_object_set(s_wireless, NM_SETTING_WIRELESS_BAND, value, NULL); + g_free(value); + } else if (chan > 0) { + if (chan > 14) + g_object_set(s_wireless, NM_SETTING_WIRELESS_BAND, "a", NULL); + else + g_object_set(s_wireless, NM_SETTING_WIRELESS_BAND, "bg", NULL); + } + + value = svGetValueStr_cp(ifcfg, "MTU"); + if (value) { + int mtu; + + mtu = _nm_utils_ascii_str_to_int64(value, 10, 0, 50000, -1); + if (mtu == -1) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid wireless MTU '%s'", + value); + g_free(value); + goto error; + } + g_object_set(s_wireless, NM_SETTING_WIRELESS_MTU, (guint) mtu, NULL); + g_free(value); + } + + g_object_set(s_wireless, + NM_SETTING_WIRELESS_HIDDEN, + svGetValueBoolean(ifcfg, "SSID_HIDDEN", FALSE), + NULL); + + cvalue = svGetValue(ifcfg, "POWERSAVE", &value); + if (cvalue) { + if (!strcmp(cvalue, "default")) + powersave = NM_SETTING_WIRELESS_POWERSAVE_DEFAULT; + else if (!strcmp(cvalue, "ignore")) + powersave = NM_SETTING_WIRELESS_POWERSAVE_IGNORE; + else if (!strcmp(cvalue, "disable") || !strcmp(cvalue, "no")) + powersave = NM_SETTING_WIRELESS_POWERSAVE_DISABLE; + else if (!strcmp(cvalue, "enable") || !strcmp(cvalue, "yes")) + powersave = NM_SETTING_WIRELESS_POWERSAVE_ENABLE; + else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid POWERSAVE value '%s'", + cvalue); + g_free(value); + goto error; + } + g_free(value); + } + + g_object_set(s_wireless, NM_SETTING_WIRELESS_POWERSAVE, powersave, NULL); + + cvalue = svGetValue(ifcfg, "MAC_ADDRESS_RANDOMIZATION", &value); + if (cvalue) { + if (strcmp(cvalue, "default") == 0) + mac_randomization = NM_SETTING_MAC_RANDOMIZATION_DEFAULT; + else if (strcmp(cvalue, "never") == 0) + mac_randomization = NM_SETTING_MAC_RANDOMIZATION_NEVER; + else if (strcmp(cvalue, "always") == 0) + mac_randomization = NM_SETTING_MAC_RANDOMIZATION_ALWAYS; + else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid MAC_ADDRESS_RANDOMIZATION value '%s'", + cvalue); + g_free(value); + goto error; + } + g_free(value); + } else + mac_randomization = NM_SETTING_MAC_RANDOMIZATION_DEFAULT; + + g_object_set(s_wireless, + NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, + mac_randomization, + NULL); + + ternary = svGetValueTernary(ifcfg, "AP_ISOLATION"); + if (ternary != NM_TERNARY_DEFAULT) { + g_object_set(s_wireless, NM_SETTING_WIRELESS_AP_ISOLATION, ternary, NULL); + } + + return NM_SETTING(s_wireless); + +error: + if (s_wireless) + g_object_unref(s_wireless); + return NULL; +} + +static NMConnection * +wireless_connection_from_ifcfg(const char *file, shvarFile *ifcfg, GError **error) +{ + NMConnection * connection = NULL; + NMSetting * con_setting = NULL; + NMSetting * wireless_setting = NULL; + NMSetting8021x *s_8021x = NULL; + GBytes * ssid; + NMSetting * security_setting = NULL; + gs_free char * ssid_utf8 = NULL; + const char * mode; + gboolean adhoc = FALSE; + GError * local = NULL; + + g_return_val_if_fail(file != NULL, NULL); + g_return_val_if_fail(ifcfg != NULL, NULL); + g_return_val_if_fail(!error || !*error, NULL); + + connection = nm_simple_connection_new(); + + /* Wireless */ + wireless_setting = make_wireless_setting(ifcfg, error); + if (!wireless_setting) { + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, wireless_setting); + + ssid = nm_setting_wireless_get_ssid(NM_SETTING_WIRELESS(wireless_setting)); + mode = nm_setting_wireless_get_mode(NM_SETTING_WIRELESS(wireless_setting)); + if (mode && !strcmp(mode, "adhoc")) + adhoc = TRUE; + + /* Wireless security */ + security_setting = make_wireless_security_setting(ifcfg, file, ssid, adhoc, &s_8021x, &local); + if (local) { + g_object_unref(connection); + g_propagate_error(error, local); + return NULL; + } + if (security_setting) { + nm_connection_add_setting(connection, security_setting); + if (s_8021x) + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + } + + if (ssid) + ssid_utf8 = _nm_utils_ssid_to_utf8(ssid); + + /* Connection */ + con_setting = make_connection_setting(file, + ifcfg, + NM_SETTING_WIRELESS_SETTING_NAME, + nm_str_not_empty(ssid_utf8) ?: "unmanaged", + NULL); + + if (!con_setting) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to create connection setting"); + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, con_setting); + + return connection; +} + +typedef struct { + const char *optname; + union { + guint32 u32; + NMTernary nmternary; + } v; + gboolean has_value; +} NMEthtoolIfcfgOption; + +/* returns an 'iterator' to words + * pointing to the next unprocessed option or NULL + * in case of failure */ +static const char ** +_next_ethtool_options_nmternary(const char ** words, + NMEthtoolType ethtool_type, + NMEthtoolIfcfgOption *out_value) +{ + const char * opt; + const char * opt_val; + const NMEthtoolData *d = NULL; + NMTernary onoff = NM_TERNARY_DEFAULT; + + nm_assert(out_value); + + out_value->has_value = FALSE; + out_value->optname = NULL; + + if (!words || !words[0] || !words[1]) + return NULL; + + opt = *words; + opt_val = *(++words); + + if (nm_streq0(opt_val, "on")) + onoff = NM_TERNARY_TRUE; + else if (nm_streq0(opt_val, "off")) + onoff = NM_TERNARY_FALSE; + + d = nms_ifcfg_rh_utils_get_ethtool_by_name(opt, ethtool_type); + if (!d) { + if (onoff != NM_TERNARY_DEFAULT) { + /* the next value is just the on/off argument. Skip it too. */ + ++words; + } + + /* silently ignore unsupported offloading features. */ + return words; + } + + if (onoff == NM_TERNARY_DEFAULT) { + PARSE_WARNING("Expects on/off argument for feature '%s'", opt); + return words; + } + + out_value->has_value = TRUE; + out_value->optname = d->optname; + out_value->v.nmternary = onoff; + + return ++words; +} + +/* returns an 'iterator' to words + * pointing to the next unprocessed option or NULL + * in case of failure */ +static const char ** +_next_ethtool_options_uint32(const char ** words, + NMEthtoolType ethtool_type, + NMEthtoolIfcfgOption *out_value) +{ + gint64 i64; + const char * opt; + const char * opt_val; + const NMEthtoolData *d = NULL; + + nm_assert(out_value); + + out_value->has_value = FALSE; + out_value->optname = NULL; + + if (!words || !words[0] || !words[1]) + return NULL; + + opt = *words; + opt_val = *(++words); + + i64 = _nm_utils_ascii_str_to_int64(opt_val, 10, 0, G_MAXUINT32, -1); + + d = nms_ifcfg_rh_utils_get_ethtool_by_name(opt, ethtool_type); + if (!d) { + if (i64 != -1) { + /* the next value is just the on/off argument. Skip it too. */ + ++words; + } + + /* silently ignore unsupported offloading features. */ + return words; + } + + out_value->has_value = TRUE; + out_value->optname = d->optname; + out_value->v.u32 = (guint32) i64; + + return ++words; +} + +static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( + _get_ethtool_type_by_name, + NMEthtoolType, + { nm_assert(name); }, + { return NM_ETHTOOL_TYPE_UNKNOWN; }, + {"--coalesce", NM_ETHTOOL_TYPE_COALESCE}, + {"--features", NM_ETHTOOL_TYPE_FEATURE}, + {"--offload", NM_ETHTOOL_TYPE_FEATURE}, + {"--set-ring", NM_ETHTOOL_TYPE_RING}, + {"-C", NM_ETHTOOL_TYPE_COALESCE}, + {"-G", NM_ETHTOOL_TYPE_RING}, + {"-K", NM_ETHTOOL_TYPE_FEATURE}, ); + +static void +parse_ethtool_option(const char * value, + NMSettingWiredWakeOnLan *out_flags, + char ** out_password, + gboolean * out_autoneg, + guint32 * out_speed, + const char ** out_duplex, + NMSettingEthtool ** out_s_ethtool) +{ + guint i; + const char ** w_iter; + NMEthtoolIfcfgOption ifcfg_option; + gs_free const char **words = NULL; + NMEthtoolType ethtool_type = NM_ETHTOOL_TYPE_UNKNOWN; + + words = nm_utils_strsplit_set(value, " \t\n"); + if (!words) + return; + + if (words[0]) + ethtool_type = _get_ethtool_type_by_name(words[0]); + + if (ethtool_type != NM_ETHTOOL_TYPE_UNKNOWN) { + if (!words[1]) { + /* first argument must be the interface name. This is invalid. */ + return; + } + + if (!*out_s_ethtool) + *out_s_ethtool = NM_SETTING_ETHTOOL(nm_setting_ethtool_new()); + + /* skip ethtool type && interface name */ + w_iter = &words[2]; + + while (w_iter && *w_iter) { + if (ethtool_type == NM_ETHTOOL_TYPE_FEATURE) { + w_iter = _next_ethtool_options_nmternary(w_iter, ethtool_type, &ifcfg_option); + + if (ifcfg_option.has_value) { + nm_setting_option_set_boolean(NM_SETTING(*out_s_ethtool), + ifcfg_option.optname, + ifcfg_option.v.nmternary != NM_TERNARY_FALSE); + } + } + if (NM_IN_SET(ethtool_type, NM_ETHTOOL_TYPE_COALESCE, NM_ETHTOOL_TYPE_RING)) { + w_iter = _next_ethtool_options_uint32(w_iter, ethtool_type, &ifcfg_option); + + if (ifcfg_option.has_value) { + nm_setting_option_set_uint32(NM_SETTING(*out_s_ethtool), + ifcfg_option.optname, + ifcfg_option.v.u32); + } + } + } + + return; + } + + /* /sbin/ethtool -s ${REALDEVICE} $opts */ + for (i = 0; words[i];) { + const char *opt = words[i]; + const char *opt_val = words[++i]; + + if (nm_streq(opt, "autoneg")) { + if (!opt_val) { + PARSE_WARNING("Auto-negotiation option missing"); + break; + } + i++; + + if (nm_streq(opt_val, "off")) + *out_autoneg = FALSE; + else if (nm_streq(opt_val, "on")) + *out_autoneg = TRUE; + else + PARSE_WARNING("Auto-negotiation unknown value: %s", opt_val); + continue; + } + + if (nm_streq(opt, "speed")) { + guint32 speed; + + if (!opt_val) { + PARSE_WARNING("Speed option missing"); + break; + } + i++; + + speed = _nm_utils_ascii_str_to_int64(opt_val, 10, 0, G_MAXUINT32, 0); + if (errno == 0) + *out_speed = speed; + else + PARSE_WARNING("Speed value '%s' is invalid", opt_val); + continue; + } + + if (nm_streq(opt, "duplex")) { + if (!opt_val) { + PARSE_WARNING("Duplex option missing"); + break; + } + i++; + + if (nm_streq(opt_val, "half")) + *out_duplex = "half"; + else if (nm_streq(opt_val, "full")) + *out_duplex = "full"; + else + PARSE_WARNING("Duplex unknown value: %s", opt_val); + continue; + } + + if (nm_streq(opt, "wol")) { + NMSettingWiredWakeOnLan wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_NONE; + + if (!opt_val) { + PARSE_WARNING("Wake-on-LAN options missing"); + break; + } + i++; + + for (; *opt_val; opt_val++) { + switch (*opt_val) { + case 'p': + wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_PHY; + break; + case 'u': + wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST; + break; + case 'm': + wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST; + break; + case 'b': + wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST; + break; + case 'a': + wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_ARP; + break; + case 'g': + wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC; + break; + case 's': + break; + case 'd': + wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_NONE; + break; + default: + PARSE_WARNING("unrecognized Wake-on-LAN option '%c'", *opt_val); + } + } + + *out_flags = wol_flags; + continue; + } + + if (nm_streq(opt, "sopass")) { + if (!opt_val) { + PARSE_WARNING("Wake-on-LAN password missing"); + break; + } + i++; + + if (nm_utils_hwaddr_valid(opt_val, ETH_ALEN)) { + nm_clear_g_free(out_password); + *out_password = g_strdup(opt_val); + } else + PARSE_WARNING("Wake-on-LAN password '%s' is invalid", opt_val); + continue; + } + + /* Silently skip unknown options */ + } +} + +static GPtrArray * +read_routing_rules_parse(shvarFile *ifcfg, gboolean routes_read) +{ + gs_unref_ptrarray GPtrArray *arr = NULL; + gs_free const char ** keys = NULL; + guint i, len; + + keys = svGetKeysSorted(ifcfg, SV_KEY_TYPE_ROUTING_RULE4 | SV_KEY_TYPE_ROUTING_RULE6, &len); + if (len == 0) + return NULL; + + if (!routes_read) { + PARSE_WARNING("'rule-' or 'rule6-' files are present; Policy routing rules (ROUTING_RULE*) " + "settings are ignored"); + return NULL; + } + + arr = g_ptr_array_new_full(len, (GDestroyNotify) nm_ip_routing_rule_unref); + for (i = 0; i < len; i++) { + const char * key = keys[i]; + nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL; + gs_free_error GError *local = NULL; + gs_free char * value_to_free = NULL; + const char * value; + gboolean key_is_ipv4; + + key_is_ipv4 = (key[NM_STRLEN("ROUTING_RULE")] == '_'); + nm_assert(key_is_ipv4 == NM_STR_HAS_PREFIX(key, "ROUTING_RULE_")); + nm_assert((!key_is_ipv4) == NM_STR_HAS_PREFIX(key, "ROUTING_RULE6_")); + + value = svGetValueStr(ifcfg, key, &value_to_free); + if (!value) + continue; + + rule = nm_ip_routing_rule_from_string( + value, + NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE + | (key_is_ipv4 ? NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET + : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6), + NULL, + &local); + if (!rule) { + PARSE_WARNING("invalid routing rule %s=\"%s\": %s", key, value, local->message); + continue; + } + + g_ptr_array_add(arr, g_steal_pointer(&rule)); + } + + if (arr->len == 0) + return NULL; + + return g_steal_pointer(&arr); +} + +static void +read_routing_rules(shvarFile * ifcfg, + gboolean routes_read, + NMSettingIPConfig *s_ip4, + NMSettingIPConfig *s_ip6) +{ + gs_unref_ptrarray GPtrArray *routing_rules = NULL; + guint i; + + routing_rules = read_routing_rules_parse(ifcfg, routes_read); + if (!routing_rules) + return; + + for (i = 0; i < routing_rules->len; i++) { + NMIPRoutingRule *rule = routing_rules->pdata[i]; + + nm_setting_ip_config_add_routing_rule( + (nm_ip_routing_rule_get_addr_family(rule) == AF_INET) ? s_ip4 : s_ip6, + rule); + } +} + +static void +parse_ethtool_options(shvarFile *ifcfg, NMConnection *connection) +{ + NMSettingWired *s_wired; + gs_unref_object NMSettingEthtool *s_ethtool = NULL; + NMSettingWiredWakeOnLan wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT; + gs_free char * ethtool_opts_free = NULL; + const char * ethtool_opts; + gs_free char * wol_password = NULL; + gs_free char * wol_value_free = NULL; + const char * tmp; + gboolean autoneg = FALSE; + guint32 speed = 0; + const char * duplex = NULL; + gboolean wired_found = FALSE; + + ethtool_opts = svGetValue(ifcfg, "ETHTOOL_OPTS", ðtool_opts_free); + if (ethtool_opts) { + wired_found = TRUE; + /* WAKE_ON_LAN_IGNORE is inferred from a specified but empty ETHTOOL_OPTS */ + if (!ethtool_opts[0]) + wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; + else { + gs_free const char **opts = NULL; + const char *const * iter; + + opts = nm_utils_strsplit_set(ethtool_opts, ";"); + for (iter = opts; iter && iter[0]; iter++) { + /* in case of repeated wol_passwords, parse_ethtool_option() + * will do the right thing and clear wol_password before resetting. */ + parse_ethtool_option(iter[0], + &wol_flags, + &wol_password, + &autoneg, + &speed, + &duplex, + &s_ethtool); + } + } + } + + /* ETHTOOL_WAKE_ON_LAN = ignore overrides WoL settings in ETHTOOL_OPTS */ + tmp = svGetValue(ifcfg, "ETHTOOL_WAKE_ON_LAN", &wol_value_free); + if (tmp) + wired_found = TRUE; + if (nm_streq0(tmp, "ignore")) + wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; + else if (tmp) + PARSE_WARNING("invalid ETHTOOL_WAKE_ON_LAN value '%s'", tmp); + + if (wol_password && !NM_FLAGS_HAS(wol_flags, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) { + PARSE_WARNING("Wake-on-LAN password not expected"); + nm_clear_g_free(&wol_password); + } + + s_wired = nm_connection_get_setting_wired(connection); + if (!s_wired && wired_found) { + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + } + if (s_wired) { + g_object_set(s_wired, + NM_SETTING_WIRED_WAKE_ON_LAN, + wol_flags, + NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, + wol_password, + NM_SETTING_WIRED_AUTO_NEGOTIATE, + autoneg, + NM_SETTING_WIRED_SPEED, + speed, + NM_SETTING_WIRED_DUPLEX, + duplex, + NULL); + } + + if (s_ethtool) { + nm_connection_add_setting(connection, NM_SETTING(g_steal_pointer(&s_ethtool))); + } +} + +static NMSetting * +make_wired_setting(shvarFile *ifcfg, const char *file, NMSetting8021x **s_8021x, GError **error) +{ + gs_unref_object NMSettingWired *s_wired = NULL; + const char * cvalue; + gs_free char * value = NULL; + gboolean found = FALSE; + + s_wired = NM_SETTING_WIRED(nm_setting_wired_new()); + + cvalue = svGetValue(ifcfg, "MTU", &value); + if (cvalue) { + int mtu; + + mtu = _nm_utils_ascii_str_to_int64(cvalue, 0, 0, 65535, -1); + if (mtu >= 0) + g_object_set(s_wired, NM_SETTING_WIRED_MTU, (guint) mtu, NULL); + else + PARSE_WARNING("invalid MTU '%s'", cvalue); + nm_clear_g_free(&value); + found = TRUE; + } + + value = svGetValue_cp(ifcfg, "HWADDR"); + if (value) { + if (value[0] != '\0') { + value = g_strstrip(value); + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL); + } + nm_clear_g_free(&value); + found = TRUE; + } + + cvalue = svGetValue(ifcfg, "SUBCHANNELS", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + const char *p = cvalue; + gboolean success = TRUE; + + /* basic sanity checks */ + while (*p) { + if (!g_ascii_isxdigit(*p) && (*p != ',') && (*p != '.')) { + PARSE_WARNING("invalid SUBCHANNELS '%s'", cvalue); + success = FALSE; + break; + } + p++; + } + + if (success) { + gs_free const char **chans = NULL; + guint32 num_chans; + + chans = nm_utils_strsplit_set(cvalue, ","); + num_chans = NM_PTRARRAY_LEN(chans); + if (num_chans < 2 || num_chans > 3) { + PARSE_WARNING("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)", + cvalue, + (unsigned) NM_PTRARRAY_LEN(chans)); + } else + g_object_set(s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, chans, NULL); + } + } + nm_clear_g_free(&value); + found = TRUE; + } + + cvalue = svGetValue(ifcfg, "PORTNAME", &value); + if (cvalue) { + if (cvalue[0] != '\0') + nm_setting_wired_add_s390_option(s_wired, "portname", cvalue); + found = TRUE; + nm_clear_g_free(&value); + } + + cvalue = svGetValue(ifcfg, "CTCPROT", &value); + if (cvalue) { + if (cvalue[0] != '\0') + nm_setting_wired_add_s390_option(s_wired, "ctcprot", cvalue); + nm_clear_g_free(&value); + found = TRUE; + } + + cvalue = svGetValue(ifcfg, "NETTYPE", &value); + if (cvalue) { + if (NM_IN_STRSET(cvalue, "qeth", "lcs", "ctc")) + g_object_set(s_wired, NM_SETTING_WIRED_S390_NETTYPE, cvalue, NULL); + else + PARSE_WARNING("unknown s390 NETTYPE '%s'", cvalue); + nm_clear_g_free(&value); + found = TRUE; + } + + cvalue = svGetValue(ifcfg, "OPTIONS", &value); + if (cvalue) + found = TRUE; + if (cvalue && cvalue[0]) { + gs_free const char **options = NULL; + gsize i; + + options = nm_utils_escaped_tokens_split(cvalue, NM_ASCII_SPACES); + for (i = 0; options && options[i]; i++) { + const char *line = options[i]; + const char *equals; + gboolean valid = FALSE; + + equals = strchr(line, '='); + if (equals) { + ((char *) equals)[0] = '\0'; + valid = nm_setting_wired_add_s390_option(s_wired, line, equals + 1); + } + if (!valid) + PARSE_WARNING("invalid s390 OPTION '%s'", line); + } + found = TRUE; + } + nm_clear_g_free(&value); + + cvalue = svGetValueStr(ifcfg, "MACADDR", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + g_object_set(s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cvalue, NULL); + } + nm_clear_g_free(&value); + found = TRUE; + } + + cvalue = svGetValueStr(ifcfg, "GENERATE_MAC_ADDRESS_MASK", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + g_object_set(s_wired, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, cvalue, NULL); + } + nm_clear_g_free(&value); + found = TRUE; + } + + cvalue = svGetValueStr(ifcfg, "HWADDR_BLACKLIST", &value); + if (cvalue) { + gs_free const char **strv = NULL; + + strv = transform_hwaddr_blacklist(cvalue); + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, strv, NULL); + nm_clear_g_free(&value); + found = TRUE; + } + + cvalue = svGetValue(ifcfg, "KEY_MGMT", &value); + if (cvalue) + found = TRUE; + if (cvalue && cvalue[0] != '\0') { + if (!strcmp(cvalue, "IEEE8021X")) { + *s_8021x = fill_8021x(ifcfg, file, cvalue, FALSE, error); + if (!*s_8021x) + return NULL; + } else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown wired KEY_MGMT type '%s'", + cvalue); + return NULL; + } + } + nm_clear_g_free(&value); + + if (!found) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_SETTING_MISSING, + "The setting is missing"); + return NULL; + } + + return (NMSetting *) g_steal_pointer(&s_wired); +} + +static NMConnection * +wired_connection_from_ifcfg(const char *file, shvarFile *ifcfg, GError **error) +{ + NMConnection * connection = NULL; + NMSetting * con_setting = NULL; + NMSetting * wired_setting = NULL; + NMSetting8021x *s_8021x = NULL; + GError * local = NULL; + + g_return_val_if_fail(file != NULL, NULL); + g_return_val_if_fail(ifcfg != NULL, NULL); + + connection = nm_simple_connection_new(); + + con_setting = make_connection_setting(file, ifcfg, NM_SETTING_WIRED_SETTING_NAME, NULL, NULL); + if (!con_setting) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to create connection setting"); + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, con_setting); + + wired_setting = make_wired_setting(ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches(local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error(error, local); + g_object_unref(connection); + return NULL; + } + g_clear_error(&local); + + if (wired_setting) + nm_connection_add_setting(connection, wired_setting); + + if (s_8021x) + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + return connection; +} + +static gboolean +parse_infiniband_p_key(shvarFile *ifcfg, int *out_p_key, char **out_parent, GError **error) +{ + char * device = NULL, *physdev = NULL, *pkey_id = NULL; + char * ifname = NULL; + int id; + gboolean ret = FALSE; + + device = svGetValueStr_cp(ifcfg, "DEVICE"); + if (!device) { + PARSE_WARNING("InfiniBand connection specified PKEY but not DEVICE"); + goto done; + } + + physdev = svGetValueStr_cp(ifcfg, "PHYSDEV"); + if (!physdev) { + PARSE_WARNING("InfiniBand connection specified PKEY but not PHYSDEV"); + goto done; + } + + pkey_id = svGetValueStr_cp(ifcfg, "PKEY_ID"); + if (!pkey_id) { + PARSE_WARNING("InfiniBand connection specified PKEY but not PKEY_ID"); + goto done; + } + + id = _nm_utils_ascii_str_to_int64(pkey_id, 0, 0, 0xFFFF, -1); + if (id == -1) { + PARSE_WARNING("invalid InfiniBand PKEY_ID '%s'", pkey_id); + goto done; + } + id = (id | 0x8000); + + ifname = g_strdup_printf("%s.%04x", physdev, (unsigned) id); + if (strcmp(device, ifname) != 0) { + PARSE_WARNING("InfiniBand DEVICE (%s) does not match PHYSDEV+PKEY_ID (%s)", device, ifname); + goto done; + } + + *out_p_key = id; + *out_parent = g_strdup(physdev); + ret = TRUE; + +done: + g_free(device); + g_free(physdev); + g_free(pkey_id); + g_free(ifname); + + if (!ret) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to create InfiniBand setting"); + } + return ret; +} + +static NMSetting * +make_infiniband_setting(shvarFile *ifcfg, const char *file, GError **error) +{ + NMSettingInfiniband *s_infiniband; + char * value = NULL; + + s_infiniband = NM_SETTING_INFINIBAND(nm_setting_infiniband_new()); + + value = svGetValueStr_cp(ifcfg, "MTU"); + if (value) { + int mtu; + + mtu = _nm_utils_ascii_str_to_int64(value, 0, 0, 65535, -1); + if (mtu >= 0) + g_object_set(s_infiniband, NM_SETTING_INFINIBAND_MTU, (guint) mtu, NULL); + else + PARSE_WARNING("invalid MTU '%s'", value); + g_free(value); + } + + value = svGetValueStr_cp(ifcfg, "HWADDR"); + if (value) { + value = g_strstrip(value); + g_object_set(s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, value, NULL); + g_free(value); + } + + if (svGetValueBoolean(ifcfg, "CONNECTED_MODE", FALSE)) + g_object_set(s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "connected", NULL); + else + g_object_set(s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL); + + if (svGetValueBoolean(ifcfg, "PKEY", FALSE)) { + gs_free char *parent = NULL; + int p_key; + + if (!parse_infiniband_p_key(ifcfg, &p_key, &parent, error)) { + g_object_unref(s_infiniband); + return NULL; + } + + g_object_set(s_infiniband, + NM_SETTING_INFINIBAND_P_KEY, + p_key, + NM_SETTING_INFINIBAND_PARENT, + parent, + NULL); + } + + return (NMSetting *) s_infiniband; +} + +static NMConnection * +infiniband_connection_from_ifcfg(const char *file, shvarFile *ifcfg, GError **error) +{ + NMConnection *connection = NULL; + NMSetting * con_setting = NULL; + NMSetting * infiniband_setting = NULL; + + g_return_val_if_fail(file != NULL, NULL); + g_return_val_if_fail(ifcfg != NULL, NULL); + + connection = nm_simple_connection_new(); + + con_setting = + make_connection_setting(file, ifcfg, NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL); + if (!con_setting) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to create connection setting"); + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, con_setting); + + infiniband_setting = make_infiniband_setting(ifcfg, file, error); + if (!infiniband_setting) { + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, infiniband_setting); + + return connection; +} + +static void +handle_bond_option(NMSettingBond *s_bond, const char *key, const char *value) +{ + gs_free char *sanitized = NULL; + const char * p = value; + + /* Remove any quotes or +/- from arp_ip_target */ + if (nm_streq0(key, NM_SETTING_BOND_OPTION_ARP_IP_TARGET) && value && value[0]) { + char *j; + + if (*p == '\'' || *p == '"') + p++; + j = sanitized = g_malloc(strlen(p) + 1); + while (*p) { + if (*p != '+' && *p != '-' && *p != '\'' && *p != '"') + *j++ = *p; + p++; + } + *j++ = '\0'; + value = sanitized; + } + + if (!_nm_setting_bond_validate_option(key, value, NULL)) { + PARSE_WARNING("invalid bonding option '%s' = %s", key, value); + return; + } + + nm_setting_bond_add_option(s_bond, key, value); +} + +static NMSetting * +make_bond_setting(shvarFile *ifcfg, const char *file, GError **error) +{ + NMSettingBond *s_bond; + gs_free char * value = NULL; + const char * v; + + v = svGetValueStr(ifcfg, "DEVICE", &value); + if (!v) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "mandatory DEVICE keyword missing"); + return NULL; + } + + s_bond = NM_SETTING_BOND(nm_setting_bond_new()); + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "BONDING_OPTS", &value); + if (v) { + gs_free const char **items = NULL; + const char *const * iter; + + items = nm_utils_strsplit_set(v, " "); + for (iter = items; iter && *iter; iter++) { + gs_free char *key = NULL; + const char * val; + + val = strchr(*iter, '='); + if (!val) + continue; + key = g_strndup(*iter, val - *iter); + val++; + if (key[0] && val[0]) + handle_bond_option(s_bond, key, val); + } + } + + return (NMSetting *) s_bond; +} + +static NMConnection * +bond_connection_from_ifcfg(const char *file, shvarFile *ifcfg, GError **error) +{ + NMConnection * connection = NULL; + NMSetting * con_setting = NULL; + NMSetting * bond_setting = NULL; + NMSetting * wired_setting = NULL; + NMSetting8021x *s_8021x = NULL; + GError * local = NULL; + + g_return_val_if_fail(file != NULL, NULL); + g_return_val_if_fail(ifcfg != NULL, NULL); + + connection = nm_simple_connection_new(); + + con_setting = + make_connection_setting(file, ifcfg, NM_SETTING_BOND_SETTING_NAME, NULL, _("Bond")); + if (!con_setting) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to create connection setting"); + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, con_setting); + + bond_setting = make_bond_setting(ifcfg, file, error); + if (!bond_setting) { + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, bond_setting); + + wired_setting = make_wired_setting(ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches(local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error(error, local); + g_object_unref(connection); + return NULL; + } + g_clear_error(&local); + + if (wired_setting) + nm_connection_add_setting(connection, wired_setting); + + if (s_8021x) + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + return connection; +} + +static NMSetting * +make_team_setting(shvarFile *ifcfg, const char *file, GError **error) +{ + NMSetting * s_team; + gs_free char *value_device = NULL; + gs_free char *value = NULL; + + if (!svGetValueStr(ifcfg, "DEVICE", &value_device)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "mandatory DEVICE keyword missing"); + return NULL; + } + + s_team = nm_setting_team_new(); + g_object_set(s_team, NM_SETTING_TEAM_CONFIG, svGetValue(ifcfg, "TEAM_CONFIG", &value), NULL); + return s_team; +} + +static NMConnection * +team_connection_from_ifcfg(const char *file, shvarFile *ifcfg, GError **error) +{ + NMConnection * connection = NULL; + NMSetting * con_setting = NULL; + NMSetting * team_setting = NULL; + NMSetting * wired_setting = NULL; + NMSetting8021x *s_8021x = NULL; + GError * local = NULL; + + g_return_val_if_fail(file != NULL, NULL); + g_return_val_if_fail(ifcfg != NULL, NULL); + + connection = nm_simple_connection_new(); + + con_setting = + make_connection_setting(file, ifcfg, NM_SETTING_TEAM_SETTING_NAME, NULL, _("Team")); + if (!con_setting) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to create connection setting"); + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, con_setting); + + team_setting = make_team_setting(ifcfg, file, error); + if (!team_setting) { + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, team_setting); + + wired_setting = make_wired_setting(ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches(local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error(error, local); + g_object_unref(connection); + return NULL; + } + g_clear_error(&local); + + if (wired_setting) + nm_connection_add_setting(connection, wired_setting); + + if (s_8021x) + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + return connection; +} + +typedef enum { + BRIDGE_OPT_TYPE_MAIN, + BRIDGE_OPT_TYPE_OPTION, + BRIDGE_OPT_TYPE_PORT_MAIN, + BRIDGE_OPT_TYPE_PORT_OPTION, +} BridgeOptType; + +typedef void (*BridgeOptFunc)(NMSetting * setting, + gboolean stp, + const char * key, + const char * value, + BridgeOptType opt_type); + +static void +handle_bridge_option(NMSetting * setting, + gboolean stp, + const char * key, + const char * value, + BridgeOptType opt_type) +{ + static const struct { + const char * key; + const char * property_name; + BridgeOptType opt_type; + gboolean only_with_stp; + gboolean extended_bool; + } m /*etadata*/[] = { + {"DELAY", NM_SETTING_BRIDGE_FORWARD_DELAY, BRIDGE_OPT_TYPE_MAIN, .only_with_stp = TRUE}, + {"priority", NM_SETTING_BRIDGE_PRIORITY, BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE}, + {"hello_time", NM_SETTING_BRIDGE_HELLO_TIME, BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE}, + {"max_age", NM_SETTING_BRIDGE_MAX_AGE, BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE}, + {"ageing_time", NM_SETTING_BRIDGE_AGEING_TIME, BRIDGE_OPT_TYPE_OPTION}, + {"multicast_last_member_count", + NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_COUNT, + BRIDGE_OPT_TYPE_OPTION}, + {"multicast_last_member_interval", + NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL, + BRIDGE_OPT_TYPE_OPTION}, + {"multicast_membership_interval", + NM_SETTING_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL, + BRIDGE_OPT_TYPE_OPTION}, + {"multicast_hash_max", NM_SETTING_BRIDGE_MULTICAST_HASH_MAX, BRIDGE_OPT_TYPE_OPTION}, + {"multicast_querier", NM_SETTING_BRIDGE_MULTICAST_QUERIER, BRIDGE_OPT_TYPE_OPTION}, + {"multicast_querier_interval", + NM_SETTING_BRIDGE_MULTICAST_QUERIER_INTERVAL, + BRIDGE_OPT_TYPE_OPTION}, + {"multicast_query_interval", + NM_SETTING_BRIDGE_MULTICAST_QUERY_INTERVAL, + BRIDGE_OPT_TYPE_OPTION}, + {"multicast_query_response_interval", + NM_SETTING_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL, + BRIDGE_OPT_TYPE_OPTION}, + {"multicast_query_use_ifaddr", + NM_SETTING_BRIDGE_MULTICAST_QUERY_USE_IFADDR, + BRIDGE_OPT_TYPE_OPTION}, + {"multicast_snooping", NM_SETTING_BRIDGE_MULTICAST_SNOOPING, BRIDGE_OPT_TYPE_OPTION}, + {"multicast_router", NM_SETTING_BRIDGE_MULTICAST_ROUTER, BRIDGE_OPT_TYPE_OPTION}, + {"multicast_startup_query_count", + NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT, + BRIDGE_OPT_TYPE_OPTION}, + {"multicast_startup_query_interval", + NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL, + BRIDGE_OPT_TYPE_OPTION}, + {"vlan_filtering", NM_SETTING_BRIDGE_VLAN_FILTERING, BRIDGE_OPT_TYPE_OPTION}, + {"default_pvid", NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, BRIDGE_OPT_TYPE_OPTION}, + {"group_address", NM_SETTING_BRIDGE_GROUP_ADDRESS, BRIDGE_OPT_TYPE_OPTION}, + {"group_fwd_mask", NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, BRIDGE_OPT_TYPE_OPTION}, + {"vlan_protocol", NM_SETTING_BRIDGE_VLAN_PROTOCOL, BRIDGE_OPT_TYPE_OPTION}, + {"vlan_stats_enabled", NM_SETTING_BRIDGE_VLAN_STATS_ENABLED, BRIDGE_OPT_TYPE_OPTION}, + {"priority", NM_SETTING_BRIDGE_PORT_PRIORITY, BRIDGE_OPT_TYPE_PORT_OPTION}, + {"path_cost", NM_SETTING_BRIDGE_PORT_PATH_COST, BRIDGE_OPT_TYPE_PORT_OPTION}, + { + "hairpin_mode", + NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, + BRIDGE_OPT_TYPE_PORT_OPTION, + .extended_bool = TRUE, + }, + }; + const char *error_message = NULL; + int i; + gint64 v; + + for (i = 0; i < G_N_ELEMENTS(m); i++) { + GParamSpec *param_spec; + + if (opt_type != m[i].opt_type) + continue; + if (!nm_streq(key, m[i].key)) + continue; + if (m[i].only_with_stp && !stp) { + PARSE_WARNING("'%s' invalid when STP is disabled", key); + return; + } + + param_spec = g_object_class_find_property(G_OBJECT_GET_CLASS(setting), m[i].property_name); + switch (param_spec->value_type) { + case G_TYPE_BOOLEAN: + if (m[i].extended_bool) { + if (!g_ascii_strcasecmp(value, "on") || !g_ascii_strcasecmp(value, "yes") + || !strcmp(value, "1")) + v = TRUE; + else if (!g_ascii_strcasecmp(value, "off") || !g_ascii_strcasecmp(value, "no")) + v = FALSE; + else { + error_message = "is not a boolean"; + goto warn; + } + } else { + v = _nm_utils_ascii_str_to_int64(value, 10, 0, 1, -1); + if (v == -1) { + error_message = nm_strerror_native(errno); + goto warn; + } + } + if (!nm_g_object_set_property_boolean(G_OBJECT(setting), m[i].property_name, v, NULL)) { + error_message = "number is out of range"; + goto warn; + } + return; + case G_TYPE_UINT: + v = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT, -1); + if (v == -1) { + error_message = nm_strerror_native(errno); + goto warn; + } + if (!nm_g_object_set_property_uint(G_OBJECT(setting), m[i].property_name, v, NULL)) { + error_message = "number is out of range"; + goto warn; + } + return; + case G_TYPE_UINT64: + { + guint64 vu64; + + vu64 = _nm_utils_ascii_str_to_uint64(value, 10, 0, G_MAXUINT64, 0); + if (!nm_g_object_set_property_uint64(G_OBJECT(setting), + m[i].property_name, + vu64, + NULL)) { + error_message = "number is out of range"; + goto warn; + } + } + return; + case G_TYPE_STRING: + nm_g_object_set_property_string(G_OBJECT(setting), m[i].property_name, value, NULL); + return; + default: + nm_assert_not_reached(); + continue; + } + +warn: + PARSE_WARNING("invalid %s value '%s': %s", key, value, error_message); + return; + } + + PARSE_WARNING("unhandled bridge option '%s'", key); +} + +static void +handle_bridging_opts(NMSetting * setting, + gboolean stp, + const char * value, + BridgeOptFunc func, + BridgeOptType opt_type) +{ + gs_free const char **items = NULL; + const char *const * iter; + + items = nm_utils_strsplit_set(value, " "); + for (iter = items; iter && *iter; iter++) { + gs_free char *key = NULL; + const char * val; + + val = strchr(*iter, '='); + if (!val) + continue; + key = g_strndup(*iter, val - *iter); + val++; + if (key[0] && val[0]) + func(setting, stp, key, val, opt_type); + } +} + +static void +read_bridge_vlans(shvarFile *ifcfg, const char *key, NMSetting *setting, const char *property) +{ + gs_unref_ptrarray GPtrArray *array = NULL; + gs_free char * value_to_free = NULL; + const char * value; + + value = svGetValueStr(ifcfg, key, &value_to_free); + if (value) { + gs_free const char **strv = NULL; + const char *const * iter; + GError * local = NULL; + NMBridgeVlan * vlan; + + array = g_ptr_array_new_with_free_func((GDestroyNotify) nm_bridge_vlan_unref); + + strv = nm_utils_escaped_tokens_split(value, ","); + if (strv) { + for (iter = strv; *iter; iter++) { + vlan = nm_bridge_vlan_from_str(*iter, &local); + if (!vlan) { + PARSE_WARNING("invalid bridge VLAN: %s", local->message); + g_clear_error(&local); + continue; + } + g_ptr_array_add(array, vlan); + } + } + nm_clear_g_free(&value_to_free); + } + + g_object_set(setting, property, array, NULL); +} + +static NMSetting * +make_bridge_setting(shvarFile *ifcfg, const char *file, GError **error) +{ + gs_unref_object NMSettingBridge *s_bridge = NULL; + gs_free char * value_to_free = NULL; + const char * value; + gboolean stp = FALSE; + gboolean stp_set = FALSE; + + value = svGetValueStr(ifcfg, "DEVICE", &value_to_free); + if (!value) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "mandatory DEVICE keyword missing"); + return NULL; + } + nm_clear_g_free(&value_to_free); + + s_bridge = NM_SETTING_BRIDGE(nm_setting_bridge_new()); + + value = svGetValueStr(ifcfg, "BRIDGE_MACADDR", &value_to_free); + if (value) { + g_object_set(s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, value, NULL); + nm_clear_g_free(&value_to_free); + } + + value = svGetValueStr(ifcfg, "STP", &value_to_free); + if (value) { + if (!g_ascii_strcasecmp(value, "on") || !g_ascii_strcasecmp(value, "yes")) { + g_object_set(s_bridge, NM_SETTING_BRIDGE_STP, TRUE, NULL); + stp = TRUE; + stp_set = TRUE; + } else if (!g_ascii_strcasecmp(value, "off") || !g_ascii_strcasecmp(value, "no")) { + g_object_set(s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL); + stp_set = TRUE; + } else + PARSE_WARNING("invalid STP value '%s'", value); + nm_clear_g_free(&value_to_free); + } + + if (!stp_set) { + /* Missing or invalid STP property means "no" */ + g_object_set(s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL); + } + + value = svGetValueStr(ifcfg, "DELAY", &value_to_free); + if (value) { + handle_bridge_option(NM_SETTING(s_bridge), stp, "DELAY", value, BRIDGE_OPT_TYPE_MAIN); + nm_clear_g_free(&value_to_free); + } + + value = svGetValueStr(ifcfg, "BRIDGING_OPTS", &value_to_free); + if (value) { + handle_bridging_opts(NM_SETTING(s_bridge), + stp, + value, + handle_bridge_option, + BRIDGE_OPT_TYPE_OPTION); + nm_clear_g_free(&value_to_free); + } + + read_bridge_vlans(ifcfg, "BRIDGE_VLANS", NM_SETTING(s_bridge), NM_SETTING_BRIDGE_VLANS); + + return (NMSetting *) g_steal_pointer(&s_bridge); +} + +static NMConnection * +bridge_connection_from_ifcfg(const char *file, shvarFile *ifcfg, GError **error) +{ + NMConnection * connection = NULL; + NMSetting * con_setting = NULL; + NMSetting * bridge_setting = NULL; + NMSetting * wired_setting = NULL; + NMSetting8021x *s_8021x = NULL; + GError * local = NULL; + + g_return_val_if_fail(file != NULL, NULL); + g_return_val_if_fail(ifcfg != NULL, NULL); + + connection = nm_simple_connection_new(); + + con_setting = + make_connection_setting(file, ifcfg, NM_SETTING_BRIDGE_SETTING_NAME, NULL, _("Bridge")); + if (!con_setting) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to create connection setting"); + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, con_setting); + + bridge_setting = make_bridge_setting(ifcfg, file, error); + if (!bridge_setting) { + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, bridge_setting); + + wired_setting = make_wired_setting(ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches(local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error(error, local); + g_object_unref(connection); + return NULL; + } + g_clear_error(&local); + + if (wired_setting) + nm_connection_add_setting(connection, wired_setting); + + if (s_8021x) + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + return connection; +} + +static NMSetting * +make_bridge_port_setting(shvarFile *ifcfg) +{ + NMSetting * s_port = NULL; + gs_free char *value_to_free = NULL; + const char * value; + + g_return_val_if_fail(ifcfg != NULL, FALSE); + + value = svGetValueStr(ifcfg, "BRIDGE_UUID", &value_to_free); + if (!value) + value = svGetValueStr(ifcfg, "BRIDGE", &value_to_free); + if (value) { + nm_clear_g_free(&value_to_free); + + s_port = nm_setting_bridge_port_new(); + value = svGetValueStr(ifcfg, "BRIDGING_OPTS", &value_to_free); + if (value) { + handle_bridging_opts(s_port, + FALSE, + value, + handle_bridge_option, + BRIDGE_OPT_TYPE_PORT_OPTION); + nm_clear_g_free(&value_to_free); + } + + read_bridge_vlans(ifcfg, "BRIDGE_PORT_VLANS", s_port, NM_SETTING_BRIDGE_PORT_VLANS); + } + + return s_port; +} + +static NMSetting * +make_team_port_setting(shvarFile *ifcfg) +{ + NMSetting * s_port; + gs_free char *value = NULL; + + value = svGetValueStr_cp(ifcfg, "TEAM_PORT_CONFIG"); + if (!value) + return NULL; + + s_port = nm_setting_team_port_new(); + g_object_set(s_port, NM_SETTING_TEAM_PORT_CONFIG, value, NULL); + return s_port; +} + +static gboolean +is_bond_device(const char *name, shvarFile *parsed) +{ + g_return_val_if_fail(name != NULL, FALSE); + g_return_val_if_fail(parsed != NULL, FALSE); + + if (svGetValueBoolean(parsed, "BONDING_MASTER", FALSE)) + return TRUE; + + return FALSE; +} + +static gboolean +is_vlan_device(const char *name, shvarFile *parsed) +{ + g_return_val_if_fail(name != NULL, FALSE); + g_return_val_if_fail(parsed != NULL, FALSE); + + if (svGetValueBoolean(parsed, "VLAN", FALSE)) + return TRUE; + + return FALSE; +} + +static gboolean +is_wifi_device(const char *name, shvarFile *parsed) +{ + const NMPlatformLink *pllink; + + g_return_val_if_fail(name != NULL, FALSE); + g_return_val_if_fail(parsed != NULL, FALSE); + + pllink = nm_platform_link_get_by_ifname(NM_PLATFORM_GET, name); + return pllink && pllink->type == NM_LINK_TYPE_WIFI; +} + +static void +parse_prio_map_list(NMSettingVlan *s_vlan, shvarFile *ifcfg, const char *key, NMVlanPriorityMap map) +{ + gs_free char * value = NULL; + gs_free const char **list = NULL; + const char *const * iter; + const char * v; + + v = svGetValueStr(ifcfg, key, &value); + if (!v) + return; + list = nm_utils_strsplit_set(v, ","); + + for (iter = list; iter && *iter; iter++) { + if (!strchr(*iter, ':')) + continue; + if (!nm_setting_vlan_add_priority_str(s_vlan, map, *iter)) + PARSE_WARNING("invalid %s priority map item '%s'", key, *iter); + } +} + +static NMSetting * +make_vlan_setting(shvarFile *ifcfg, const char *file, GError **error) +{ + gs_unref_object NMSettingVlan *s_vlan = NULL; + gs_free char * parent = NULL; + gs_free char * iface_name = NULL; + gs_free char * value = NULL; + const char * v = NULL; + int vlan_id = -1; + guint32 vlan_flags = 0; + int gvrp, reorder_hdr; + + v = svGetValueStr(ifcfg, "VLAN_ID", &value); + if (v) { + vlan_id = _nm_utils_ascii_str_to_int64(v, 10, 0, 4095, -1); + if (vlan_id == -1) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid VLAN_ID '%s'", + v); + return NULL; + } + } + + /* Need DEVICE if we don't have a separate VLAN_ID property */ + iface_name = svGetValueStr_cp(ifcfg, "DEVICE"); + if (!iface_name && vlan_id < 0) { + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing DEVICE property; cannot determine VLAN ID"); + return NULL; + } + + s_vlan = NM_SETTING_VLAN(nm_setting_vlan_new()); + + /* Parent interface from PHYSDEV takes precedence if it exists */ + parent = svGetValueStr_cp(ifcfg, "PHYSDEV"); + + if (iface_name) { + v = strchr(iface_name, '.'); + if (v) { + /* eth0.43; PHYSDEV is assumed from it if unknown */ + if (!parent) { + parent = g_strndup(iface_name, v - iface_name); + if (g_str_has_prefix(parent, "vlan")) { + /* Like initscripts, if no PHYSDEV and we get an obviously + * invalid parent interface from DEVICE, fail. + */ + nm_clear_g_free(&parent); + } + } + v++; + } else { + /* format like vlan43; PHYSDEV must be set */ + if (g_str_has_prefix(iface_name, "vlan")) + v = iface_name + 4; + } + + if (v) { + int device_vlan_id; + + /* Grab VLAN ID from interface name; this takes precedence over the + * separate VLAN_ID property for backwards compat. + */ + device_vlan_id = _nm_utils_ascii_str_to_int64(v, 10, 0, 4095, -1); + if (device_vlan_id != -1) + vlan_id = device_vlan_id; + } + } + + if (vlan_id < 0) { + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to determine VLAN ID from DEVICE or VLAN_ID"); + return NULL; + } + g_object_set(s_vlan, NM_SETTING_VLAN_ID, vlan_id, NULL); + + if (parent == NULL) { + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to determine VLAN parent from DEVICE or PHYSDEV"); + return NULL; + } + g_object_set(s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL); + + vlan_flags |= NM_VLAN_FLAG_REORDER_HEADERS; + + gvrp = svGetValueBoolean(ifcfg, "GVRP", -1); + if (gvrp > 0) + vlan_flags |= NM_VLAN_FLAG_GVRP; + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "VLAN_FLAGS", &value); + if (v) { + gs_free const char **strv = NULL; + const char *const * ptr; + + strv = nm_utils_strsplit_set(v, ", "); + for (ptr = strv; ptr && *ptr; ptr++) { + if (nm_streq(*ptr, "GVRP") && gvrp == -1) + vlan_flags |= NM_VLAN_FLAG_GVRP; + if (nm_streq(*ptr, "LOOSE_BINDING")) + vlan_flags |= NM_VLAN_FLAG_LOOSE_BINDING; + if (nm_streq(*ptr, "NO_REORDER_HDR")) + vlan_flags &= ~NM_VLAN_FLAG_REORDER_HEADERS; + } + } + + reorder_hdr = svGetValueBoolean(ifcfg, "REORDER_HDR", -1); + if (reorder_hdr != -1 && reorder_hdr != NM_FLAGS_HAS(vlan_flags, NM_VLAN_FLAG_REORDER_HEADERS)) + PARSE_WARNING("REORDER_HDR key is deprecated, use VLAN_FLAGS"); + + if (svGetValueBoolean(ifcfg, "MVRP", FALSE)) + vlan_flags |= NM_VLAN_FLAG_MVRP; + + g_object_set(s_vlan, NM_SETTING_VLAN_FLAGS, vlan_flags, NULL); + + parse_prio_map_list(s_vlan, ifcfg, "VLAN_INGRESS_PRIORITY_MAP", NM_VLAN_INGRESS_MAP); + parse_prio_map_list(s_vlan, ifcfg, "VLAN_EGRESS_PRIORITY_MAP", NM_VLAN_EGRESS_MAP); + + return NM_SETTING(g_steal_pointer(&s_vlan)); +} + +static NMConnection * +vlan_connection_from_ifcfg(const char *file, shvarFile *ifcfg, GError **error) +{ + NMConnection * connection = NULL; + NMSetting * con_setting = NULL; + NMSetting * wired_setting = NULL; + NMSetting * vlan_setting = NULL; + NMSetting8021x *s_8021x = NULL; + GError * local = NULL; + + g_return_val_if_fail(file != NULL, NULL); + g_return_val_if_fail(ifcfg != NULL, NULL); + + connection = nm_simple_connection_new(); + + con_setting = make_connection_setting(file, ifcfg, NM_SETTING_VLAN_SETTING_NAME, NULL, "Vlan"); + if (!con_setting) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to create connection setting"); + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, con_setting); + + vlan_setting = make_vlan_setting(ifcfg, file, error); + if (!vlan_setting) { + g_object_unref(connection); + return NULL; + } + nm_connection_add_setting(connection, vlan_setting); + + wired_setting = make_wired_setting(ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches(local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error(error, local); + g_object_unref(connection); + return NULL; + } + g_clear_error(&local); + + if (wired_setting) + nm_connection_add_setting(connection, wired_setting); + + if (s_8021x) + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + return connection; +} + +static NMConnection * +create_unhandled_connection(const char *filename, + shvarFile * ifcfg, + const char *type, + char ** out_spec) +{ + NMConnection *connection; + NMSetting * s_con; + gs_free char *value = NULL; + const char * v; + + nm_assert(out_spec && !*out_spec); + + connection = nm_simple_connection_new(); + + /* Get NAME, UUID, etc. We need to set a connection type (generic) and add + * an empty type-specific setting as well, to make sure it passes + * nm_connection_verify() later. + */ + s_con = make_connection_setting(filename, ifcfg, NM_SETTING_GENERIC_SETTING_NAME, NULL, NULL); + nm_connection_add_setting(connection, s_con); + + nm_connection_add_setting(connection, nm_setting_generic_new()); + + /* Get a spec */ + v = svGetValueStr(ifcfg, "HWADDR", &value); + if (v) { + gs_free char *lower = g_ascii_strdown(v, -1); + + *out_spec = g_strdup_printf("%s:" NM_MATCH_SPEC_MAC_TAG "%s", type, lower); + return connection; + } + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "SUBCHANNELS", &value); + if (v) { + *out_spec = g_strdup_printf("%s:" NM_MATCH_SPEC_S390_SUBCHANNELS_TAG "%s", type, v); + return connection; + } + + nm_clear_g_free(&value); + v = svGetValueStr(ifcfg, "DEVICE", &value); + if (v) { + *out_spec = g_strdup_printf("%s:" NM_MATCH_SPEC_INTERFACE_NAME_TAG "=%s", type, v); + return connection; + } + + g_object_unref(connection); + return NULL; +} + +static void +check_dns_search_domains(shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6) +{ + if (!s_ip6) + return; + + /* If there is no IPv4 config or it doesn't contain DNS searches, + * read DOMAIN and put the domains into IPv6. + */ + if (!s_ip4 || nm_setting_ip_config_get_num_dns_searches(NM_SETTING_IP_CONFIG(s_ip4)) == 0) { + /* DNS searches */ + gs_free char *value = NULL; + const char * v; + + v = svGetValueStr(ifcfg, "DOMAIN", &value); + if (v) { + gs_free const char **searches = NULL; + const char *const * item; + + searches = nm_utils_strsplit_set(v, " "); + if (searches) { + for (item = searches; *item; item++) { + if (!nm_setting_ip_config_add_dns_search(NM_SETTING_IP_CONFIG(s_ip6), *item)) + PARSE_WARNING("duplicate DNS domain '%s'", *item); + } + } + } + } +} + +static NMConnection * +connection_from_file_full(const char *filename, + const char *network_file, /* for unit tests only */ + const char *test_type, /* for unit tests only */ + char ** out_unhandled, + GError ** error, + gboolean * out_ignore_error) +{ + nm_auto_shvar_file_close shvarFile *main_ifcfg = NULL; + nm_auto_shvar_file_close shvarFile *network_ifcfg = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_free char * type = NULL; + char * devtype, *bootproto; + NMSetting * setting; + NMSetting * s_ip4; + NMSetting * s_ip6; + const char * ifcfg_name = NULL; + gboolean has_ip4_defroute = FALSE; + gboolean has_complex_routes_v4; + gboolean has_complex_routes_v6; + + g_return_val_if_fail(filename != NULL, NULL); + g_return_val_if_fail(out_unhandled && !*out_unhandled, NULL); + + NM_SET_OUT(out_ignore_error, FALSE); + + /* Non-NULL only for unit tests; normally use /etc/sysconfig/network */ + if (!network_file) + network_file = SYSCONFDIR "/sysconfig/network"; + + ifcfg_name = utils_get_ifcfg_name(filename, TRUE); + if (!ifcfg_name) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ignoring connection '%s' because it's not an ifcfg file", + filename); + return NULL; + } + + main_ifcfg = svOpenFile(filename, error); + if (!main_ifcfg) + return NULL; + + network_ifcfg = svOpenFile(network_file, NULL); + + if (!svGetValueBoolean(main_ifcfg, "NM_CONTROLLED", TRUE)) { + connection = create_unhandled_connection(filename, main_ifcfg, "unmanaged", out_unhandled); + if (!connection) { + NM_SET_OUT(out_ignore_error, TRUE); + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "NM_CONTROLLED was false but device was not uniquely identified; device " + "will be managed"); + } + return g_steal_pointer(&connection); + } + + /* iBFT is handled by nm-initrd-generator during boot. */ + bootproto = svGetValueStr_cp(main_ifcfg, "BOOTPROTO"); + if (bootproto && !g_ascii_strcasecmp(bootproto, "ibft")) { + NM_SET_OUT(out_ignore_error, TRUE); + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ignoring iBFT configuration"); + g_free(bootproto); + return NULL; + } + g_free(bootproto); + + devtype = svGetValueStr_cp(main_ifcfg, "DEVICETYPE"); + if (devtype) { + if (!g_ascii_strcasecmp(devtype, TYPE_TEAM)) + type = g_strdup(TYPE_TEAM); + else if (!g_ascii_strcasecmp(devtype, TYPE_TEAM_PORT)) { + gs_free char *device = NULL; + + type = svGetValueStr_cp(main_ifcfg, "TYPE"); + device = svGetValueStr_cp(main_ifcfg, "DEVICE"); + + if (type) { + /* nothing to do */ + } else if (device && is_vlan_device(device, main_ifcfg)) + type = g_strdup(TYPE_VLAN); + else + type = g_strdup(TYPE_ETHERNET); + } + g_free(devtype); + } + if (!type) { + gs_free char *t = NULL; + + /* Team and TeamPort types are also accepted by the mere + * presence of TEAM_CONFIG/TEAM_MASTER. They don't require + * DEVICETYPE. */ + t = svGetValueStr_cp(main_ifcfg, "TEAM_CONFIG"); + if (t) + type = g_strdup(TYPE_TEAM); + } + + if (!type) + type = svGetValueStr_cp(main_ifcfg, "TYPE"); + + if (!type) { + gs_free char *tmp = NULL; + char * device; + + if ((tmp = svGetValueStr_cp(main_ifcfg, "IPV6TUNNELIPV4"))) { + NM_SET_OUT(out_ignore_error, TRUE); + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ignoring unsupported connection due to IPV6TUNNELIPV4"); + return NULL; + } + + device = svGetValueStr_cp(main_ifcfg, "DEVICE"); + if (!device) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "File '%s' had neither TYPE nor DEVICE keys", + filename); + return NULL; + } + + if (!strcmp(device, "lo")) { + NM_SET_OUT(out_ignore_error, TRUE); + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ignoring loopback device config"); + g_free(device); + return NULL; + } + + if (!test_type) { + if (is_bond_device(device, main_ifcfg)) + type = g_strdup(TYPE_BOND); + else if (is_vlan_device(device, main_ifcfg)) + type = g_strdup(TYPE_VLAN); + else if (is_wifi_device(device, main_ifcfg)) + type = g_strdup(TYPE_WIRELESS); + else { + gs_free char *p_path = NULL; + char * p_device; + gsize i; + + /* network-functions detects DEVICETYPE based on the ifcfg-* name and the existence + * of a ifup script: + * [ -z "$DEVICETYPE" ] && DEVICETYPE=$(echo ${DEVICE} | sed "s/[0-9]*$//") + * later... + * OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-${DEVICETYPE}" + * */ +#define IFUP_PATH_PREFIX "/etc/sysconfig/network-scripts/ifup-" + i = strlen(device); + p_path = g_malloc(NM_STRLEN(IFUP_PATH_PREFIX) + i + 1); + p_device = &p_path[NM_STRLEN(IFUP_PATH_PREFIX)]; + memcpy(p_device, device, i + 1); + + /* strip trailing numbers */ + while (i >= 1) { + i--; + if (p_device[i] < '0' || p_device[i] > '9') + break; + p_device[i] = '\0'; + } + + if (nm_streq(p_device, "eth")) + type = g_strdup(TYPE_ETHERNET); + else if (nm_streq(p_device, "wireless")) + type = g_strdup(TYPE_WIRELESS); + else if (p_device[0]) { + memcpy(p_path, IFUP_PATH_PREFIX, NM_STRLEN(IFUP_PATH_PREFIX)); + if (access(p_path, X_OK) == 0) { + /* for all other types, this is not something we want to handle. */ + NM_SET_OUT(out_ignore_error, TRUE); + g_set_error( + error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ignore script for unknown device type which has a matching %s script", + p_path); + return NULL; + } + } + + if (!type) + type = g_strdup(TYPE_ETHERNET); + } + } else { + /* For the unit tests, there won't necessarily be any + * adapters of the connection's type in the system so the + * type can't be tested with ioctls. + */ + type = g_strdup(test_type); + } + + g_free(device); + } else { + /* Check for IBM s390 CTC devices and call them Ethernet */ + if (g_strcmp0(type, "CTC") == 0) { + g_free(type); + type = g_strdup(TYPE_ETHERNET); + } + } + + if (nm_streq0(type, TYPE_ETHERNET)) { + gs_free char *bond_options = NULL; + + if (svGetValueStr(main_ifcfg, "BONDING_OPTS", &bond_options)) { + /* initscripts consider these as bond masters */ + g_free(type); + type = g_strdup(TYPE_BOND); + } + } + + if (svGetValueBoolean(main_ifcfg, "BONDING_MASTER", FALSE) + && g_ascii_strcasecmp(type, TYPE_BOND)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "BONDING_MASTER=yes key only allowed in TYPE=bond connections"); + return NULL; + } + + /* Construct the connection */ + if (!g_ascii_strcasecmp(type, TYPE_ETHERNET)) + connection = wired_connection_from_ifcfg(filename, main_ifcfg, error); + else if (!g_ascii_strcasecmp(type, TYPE_WIRELESS)) + connection = wireless_connection_from_ifcfg(filename, main_ifcfg, error); + else if (!g_ascii_strcasecmp(type, TYPE_INFINIBAND)) + connection = infiniband_connection_from_ifcfg(filename, main_ifcfg, error); + else if (!g_ascii_strcasecmp(type, TYPE_BOND)) + connection = bond_connection_from_ifcfg(filename, main_ifcfg, error); + else if (!g_ascii_strcasecmp(type, TYPE_TEAM)) + connection = team_connection_from_ifcfg(filename, main_ifcfg, error); + else if (!g_ascii_strcasecmp(type, TYPE_VLAN)) + connection = vlan_connection_from_ifcfg(filename, main_ifcfg, error); + else if (!g_ascii_strcasecmp(type, TYPE_BRIDGE)) + connection = bridge_connection_from_ifcfg(filename, main_ifcfg, error); + else { + connection = + create_unhandled_connection(filename, main_ifcfg, "unrecognized", out_unhandled); + if (!connection) { + PARSE_WARNING("connection type was unrecognized but device was not uniquely " + "identified; device may be managed"); + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Failed to read unrecognized connection"); + } + return g_steal_pointer(&connection); + } + + if (!connection) + return NULL; + + parse_ethtool_options(main_ifcfg, connection); + + has_complex_routes_v4 = utils_has_complex_routes(filename, AF_INET); + has_complex_routes_v6 = utils_has_complex_routes(filename, AF_INET6); + + if (has_complex_routes_v4 || has_complex_routes_v6) { + if (has_complex_routes_v4 && !has_complex_routes_v6) + PARSE_WARNING("'rule-' file is present; you will need to use a dispatcher script to " + "apply these routes"); + else if (has_complex_routes_v6 && !has_complex_routes_v4) + PARSE_WARNING("'rule6-' file is present; you will need to use a dispatcher script to " + "apply these routes"); + else + PARSE_WARNING("'rule-' and 'rule6-' files are present; you will need to use a " + "dispatcher script to apply these routes"); + } + + s_ip6 = make_ip6_setting(main_ifcfg, + network_ifcfg, + !has_complex_routes_v4 && !has_complex_routes_v6, + error); + if (!s_ip6) + return NULL; + nm_connection_add_setting(connection, s_ip6); + + s_ip4 = make_ip4_setting(main_ifcfg, + network_ifcfg, + !has_complex_routes_v4 && !has_complex_routes_v6, + &has_ip4_defroute, + error); + if (!s_ip4) + return NULL; + read_aliases(NM_SETTING_IP_CONFIG(s_ip4), + !has_ip4_defroute + && !nm_setting_ip_config_get_gateway(NM_SETTING_IP_CONFIG(s_ip4)), + filename); + nm_connection_add_setting(connection, s_ip4); + + read_routing_rules(main_ifcfg, + !has_complex_routes_v4 && !has_complex_routes_v6, + NM_SETTING_IP_CONFIG(s_ip4), + NM_SETTING_IP_CONFIG(s_ip6)); + + setting = make_sriov_setting(main_ifcfg); + if (setting) + nm_connection_add_setting(connection, setting); + + setting = make_tc_setting(main_ifcfg); + if (setting) + nm_connection_add_setting(connection, setting); + + /* For backwards compatibility, if IPv4 is disabled or the + * config fails for some reason, we read DOMAIN and put the + * values into IPv6 config instead of IPv4. + */ + check_dns_search_domains(main_ifcfg, s_ip4, s_ip6); + + setting = make_proxy_setting(main_ifcfg); + if (setting) + nm_connection_add_setting(connection, setting); + + setting = make_hostname_setting(main_ifcfg); + if (setting) + nm_connection_add_setting(connection, setting); + + setting = make_user_setting(main_ifcfg); + if (setting) + nm_connection_add_setting(connection, setting); + + setting = make_match_setting(main_ifcfg); + if (setting) + nm_connection_add_setting(connection, setting); + + setting = make_bridge_port_setting(main_ifcfg); + if (setting) + nm_connection_add_setting(connection, setting); + + setting = make_team_port_setting(main_ifcfg); + if (setting) + nm_connection_add_setting(connection, setting); + + if (!make_dcb_setting(main_ifcfg, &setting, error)) + return NULL; + if (setting) + nm_connection_add_setting(connection, setting); + + if (!nm_connection_normalize(connection, NULL, NULL, error)) + return NULL; + + return g_steal_pointer(&connection); +} + +NMConnection * +connection_from_file(const char *filename, + char ** out_unhandled, + GError ** error, + gboolean * out_ignore_error) +{ + return connection_from_file_full(filename, NULL, NULL, out_unhandled, error, out_ignore_error); +} + +NMConnection * +nmtst_connection_from_file(const char *filename, + const char *network_file, + const char *test_type, + char ** out_unhandled, + GError ** error) +{ + return connection_from_file_full(filename, network_file, test_type, out_unhandled, error, NULL); +} diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h new file mode 100644 index 00000000..c7dc2240 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008 Red Hat, Inc. + */ + +#ifndef __NMS_IFCFG_RH_READER_H__ +#define __NMS_IFCFG_RH_READER_H__ + +#include "nm-connection.h" + +NMConnection *connection_from_file(const char *filename, + char ** out_unhandled, + GError ** error, + gboolean * out_ignore_error); + +NMConnection *nmtst_connection_from_file(const char *filename, + const char *network_file, + const char *test_type, + char ** out_unhandled, + GError ** error); + +#endif /* __NMS_IFCFG_RH_READER_H__ */ diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c new file mode 100644 index 00000000..3f674dea --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nms-ifcfg-rh-storage.h" + +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "nm-connection.h" +#include "nms-ifcfg-rh-plugin.h" + +/*****************************************************************************/ + +struct _NMSIfcfgRHStorageClass { + NMSettingsStorageClass parent; +}; + +G_DEFINE_TYPE(NMSIfcfgRHStorage, nms_ifcfg_rh_storage, NM_TYPE_SETTINGS_STORAGE) + +/*****************************************************************************/ + +gboolean +nms_ifcfg_rh_storage_equal_type(const NMSIfcfgRHStorage *self_a, const NMSIfcfgRHStorage *self_b) +{ + return (self_a == self_b) + || (self_a && self_b + && nm_streq0(nms_ifcfg_rh_storage_get_uuid_opt(self_a), + nms_ifcfg_rh_storage_get_uuid_opt(self_b)) + && nm_streq0(self_a->unmanaged_spec, self_b->unmanaged_spec) + && nm_streq0(self_a->unrecognized_spec, self_b->unrecognized_spec)); +} + +void +nms_ifcfg_rh_storage_copy_content(NMSIfcfgRHStorage *dst, const NMSIfcfgRHStorage *src) +{ + nm_assert(src != dst); + nm_assert(src && dst); + nm_assert(nms_ifcfg_rh_storage_equal_type(dst, src)); + nm_assert(nms_ifcfg_rh_storage_get_filename(dst) + && nm_streq(nms_ifcfg_rh_storage_get_filename(dst), + nms_ifcfg_rh_storage_get_filename(src))); + + nm_g_object_ref_set(&dst->connection, src->connection); + g_free(dst->unmanaged_spec); + g_free(dst->unrecognized_spec); + dst->unmanaged_spec = g_strdup(src->unmanaged_spec); + dst->unrecognized_spec = g_strdup(src->unrecognized_spec); + dst->stat_mtime = src->stat_mtime; +} + +NMConnection * +nms_ifcfg_rh_storage_steal_connection(NMSIfcfgRHStorage *self) +{ + nm_assert(NMS_IS_IFCFG_RH_STORAGE(self)); + + return g_steal_pointer(&self->connection); +} + +/*****************************************************************************/ + +static int +cmp_fcn(const NMSIfcfgRHStorage *a, const NMSIfcfgRHStorage *b) +{ + nm_assert(NMS_IS_IFCFG_RH_STORAGE(a)); + nm_assert(NMS_IS_IFCFG_RH_STORAGE(b)); + nm_assert(a != b); + + /* newer files are more important. */ + NM_CMP_FIELD(a, b, stat_mtime.tv_sec); + NM_CMP_FIELD(a, b, stat_mtime.tv_nsec); + + NM_CMP_DIRECT_STRCMP(nms_ifcfg_rh_storage_get_filename(a), + nms_ifcfg_rh_storage_get_filename(b)); + + return 0; +} + +/*****************************************************************************/ + +static void +nms_ifcfg_rh_storage_init(NMSIfcfgRHStorage *self) +{} + +static NMSIfcfgRHStorage * +_storage_new(NMSIfcfgRHPlugin *plugin, const char *uuid, const char *filename) +{ + nm_assert(NMS_IS_IFCFG_RH_PLUGIN(plugin)); + nm_assert(!uuid || nm_utils_is_uuid(uuid)); + nm_assert(filename && filename[0] == '/'); + + return g_object_new(NMS_TYPE_IFCFG_RH_STORAGE, + NM_SETTINGS_STORAGE_PLUGIN, + plugin, + NM_SETTINGS_STORAGE_UUID, + uuid, + NM_SETTINGS_STORAGE_FILENAME, + filename, + NULL); +} + +NMSIfcfgRHStorage * +nms_ifcfg_rh_storage_new_connection(NMSIfcfgRHPlugin * plugin, + const char * filename, + NMConnection * connection_take, + const struct timespec *mtime) +{ + NMSIfcfgRHStorage *self; + + nm_assert(NM_IS_CONNECTION(connection_take)); + nm_assert(_nm_connection_verify(connection_take, NULL) == NM_SETTING_VERIFY_SUCCESS); + nmtst_connection_assert_unchanging(connection_take); + + self = _storage_new(plugin, nm_connection_get_uuid(connection_take), filename); + self->connection = connection_take; + if (mtime) + self->stat_mtime = *mtime; + return self; +} + +NMSIfcfgRHStorage * +nms_ifcfg_rh_storage_new_unhandled(NMSIfcfgRHPlugin *plugin, + const char * filename, + const char * unmanaged_spec, + const char * unrecognized_spec) +{ + NMSIfcfgRHStorage *self; + + nm_assert(unmanaged_spec || unrecognized_spec); + + self = _storage_new(plugin, NULL, filename); + self->unmanaged_spec = g_strdup(unmanaged_spec); + self->unrecognized_spec = g_strdup(unrecognized_spec); + return self; +} + +static void +_storage_clear(NMSIfcfgRHStorage *self) +{ + c_list_unlink(&self->parent._storage_lst); + c_list_unlink(&self->parent._storage_by_uuid_lst); + nm_clear_g_free(&self->unmanaged_spec); + nm_clear_g_free(&self->unrecognized_spec); + g_clear_object(&self->connection); +} + +static void +dispose(GObject *object) +{ + NMSIfcfgRHStorage *self = NMS_IFCFG_RH_STORAGE(object); + + _storage_clear(self); + + G_OBJECT_CLASS(nms_ifcfg_rh_storage_parent_class)->dispose(object); +} + +void +nms_ifcfg_rh_storage_destroy(NMSIfcfgRHStorage *self) +{ + _storage_clear(self); + g_object_unref(self); +} + +static void +nms_ifcfg_rh_storage_class_init(NMSIfcfgRHStorageClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMSettingsStorageClass *storage_class = NM_SETTINGS_STORAGE_CLASS(klass); + + object_class->dispose = dispose; + + storage_class->cmp_fcn = (int (*)(NMSettingsStorage *, NMSettingsStorage *)) cmp_fcn; +} diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h new file mode 100644 index 00000000..6fb9838f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Red Hat, Inc. + */ + +#ifndef __NMS_IFCFG_RH_STORAGE_H__ +#define __NMS_IFCFG_RH_STORAGE_H__ + +#include "c-list/src/c-list.h" +#include "settings/nm-settings-storage.h" + +/*****************************************************************************/ + +#define NMS_TYPE_IFCFG_RH_STORAGE (nms_ifcfg_rh_storage_get_type()) +#define NMS_IFCFG_RH_STORAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorage)) +#define NMS_IFCFG_RH_STORAGE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) +#define NMS_IS_IFCFG_RH_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMS_TYPE_IFCFG_RH_STORAGE)) +#define NMS_IS_IFCFG_RH_STORAGE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NMS_TYPE_IFCFG_RH_STORAGE)) +#define NMS_IFCFG_RH_STORAGE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) + +typedef struct { + NMSettingsStorage parent; + + NMConnection *connection; + + char *unmanaged_spec; + char *unrecognized_spec; + + /* The timestamp (stat's mtime) of the file. Newer files have + * higher priority. */ + struct timespec stat_mtime; + + bool dirty : 1; + +} NMSIfcfgRHStorage; + +typedef struct _NMSIfcfgRHStorageClass NMSIfcfgRHStorageClass; + +GType nms_ifcfg_rh_storage_get_type(void); + +struct _NMSIfcfgRHPlugin; + +NMSIfcfgRHStorage *nms_ifcfg_rh_storage_new_connection(struct _NMSIfcfgRHPlugin *plugin, + const char * filename, + NMConnection * connection_take, + const struct timespec * mtime); + +NMSIfcfgRHStorage *nms_ifcfg_rh_storage_new_unhandled(struct _NMSIfcfgRHPlugin *plugin, + const char * filename, + const char * unmanaged_spec, + const char * unrecognized_spec); + +void nms_ifcfg_rh_storage_destroy(NMSIfcfgRHStorage *self); + +/*****************************************************************************/ + +gboolean nms_ifcfg_rh_storage_equal_type(const NMSIfcfgRHStorage *self_a, + const NMSIfcfgRHStorage *self_b); + +void nms_ifcfg_rh_storage_copy_content(NMSIfcfgRHStorage *dst, const NMSIfcfgRHStorage *src); + +NMConnection *nms_ifcfg_rh_storage_steal_connection(NMSIfcfgRHStorage *self); + +/*****************************************************************************/ + +static inline const char * +nms_ifcfg_rh_storage_get_uuid_opt(const NMSIfcfgRHStorage *self) +{ + return nm_settings_storage_get_uuid_opt((const NMSettingsStorage *) self); +} + +static inline const char * +nms_ifcfg_rh_storage_get_filename(const NMSIfcfgRHStorage *self) +{ + return nm_settings_storage_get_filename((const NMSettingsStorage *) self); +} + +#endif /* __NMS_IFCFG_RH_STORAGE_H__ */ diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c new file mode 100644 index 00000000..8da5de47 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -0,0 +1,1119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008 - 2017 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nms-ifcfg-rh-utils.h" + +#include <stdlib.h> + +#include "nm-core-internal.h" +#include "NetworkManagerUtils.h" + +#include "nms-ifcfg-rh-common.h" + +/*****************************************************************************/ + +gboolean +nms_ifcfg_rh_utils_parse_unhandled_spec(const char * unhandled_spec, + const char **out_unmanaged_spec, + const char **out_unrecognized_spec) +{ + if (unhandled_spec) { + if (NM_STR_HAS_PREFIX(unhandled_spec, "unmanaged:")) { + NM_SET_OUT(out_unmanaged_spec, &unhandled_spec[NM_STRLEN("unmanaged:")]); + NM_SET_OUT(out_unrecognized_spec, NULL); + return TRUE; + } + if (NM_STR_HAS_PREFIX(unhandled_spec, "unrecognized:")) { + NM_SET_OUT(out_unmanaged_spec, NULL); + NM_SET_OUT(out_unrecognized_spec, &unhandled_spec[NM_STRLEN("unrecognized:")]); + return TRUE; + } + } + NM_SET_OUT(out_unmanaged_spec, NULL); + NM_SET_OUT(out_unrecognized_spec, NULL); + return FALSE; +} + +/*****************************************************************************/ + +/* + * Check ';[a-fA-F0-9]{8}' file suffix used for temporary files by rpm when + * installing packages. + * + * Implementation taken from upstart. + */ +static gboolean +check_rpm_temp_suffix(const char *path) +{ + const char *ptr; + + g_return_val_if_fail(path != NULL, FALSE); + + /* Matches *;[a-fA-F0-9]{8}; used by rpm */ + ptr = strrchr(path, ';'); + if (ptr && strspn(ptr + 1, "abcdefABCDEF0123456789") == 8 && !ptr[9]) + return TRUE; + return FALSE; +} + +static gboolean +check_suffix(const char *base, const char *tag) +{ + int len, tag_len; + + g_return_val_if_fail(base != NULL, TRUE); + g_return_val_if_fail(tag != NULL, TRUE); + + len = strlen(base); + tag_len = strlen(tag); + if ((len > tag_len) && !g_ascii_strcasecmp(base + len - tag_len, tag)) + return TRUE; + return FALSE; +} + +gboolean +utils_should_ignore_file(const char *filename, gboolean only_ifcfg) +{ + gs_free char *base = NULL; + + g_return_val_if_fail(filename != NULL, TRUE); + + base = g_path_get_basename(filename); + + /* Only handle ifcfg, keys, and routes files */ + if (strncmp(base, IFCFG_TAG, strlen(IFCFG_TAG)) != 0) { + if (only_ifcfg) + return TRUE; + else if (strncmp(base, KEYS_TAG, strlen(KEYS_TAG)) != 0 + && strncmp(base, ROUTE_TAG, strlen(ROUTE_TAG)) != 0 + && strncmp(base, ROUTE6_TAG, strlen(ROUTE6_TAG)) != 0) + return TRUE; + } + + /* But not those that have certain suffixes */ + if (check_suffix(base, BAK_TAG) || check_suffix(base, TILDE_TAG) || check_suffix(base, ORIG_TAG) + || check_suffix(base, REJ_TAG) || check_suffix(base, RPMNEW_TAG) + || check_suffix(base, AUGNEW_TAG) || check_suffix(base, AUGTMP_TAG) + || check_rpm_temp_suffix(base)) + return TRUE; + + return FALSE; +} + +char * +utils_cert_path(const char *parent, const char *suffix, const char *extension) +{ + gs_free char *dir = NULL; + const char * name; + + g_return_val_if_fail(parent, NULL); + g_return_val_if_fail(suffix, NULL); + g_return_val_if_fail(extension, NULL); + + name = utils_get_ifcfg_name(parent, FALSE); + g_return_val_if_fail(name, NULL); + + dir = g_path_get_dirname(parent); + return g_strdup_printf("%s/%s-%s.%s", dir, name, suffix, extension); +} + +const char * +utils_get_ifcfg_name(const char *file, gboolean only_ifcfg) +{ + const char *name; + + g_return_val_if_fail(file != NULL, NULL); + + name = strrchr(file, '/'); + if (!name) + name = file; + else + name++; + if (!*name) + return NULL; + +#define MATCH_TAG_AND_RETURN(name, TAG) \ + G_STMT_START \ + { \ + if (strncmp(name, TAG, NM_STRLEN(TAG)) == 0) { \ + name += NM_STRLEN(TAG); \ + if (name[0] == '\0') \ + return NULL; \ + else \ + return name; \ + } \ + } \ + G_STMT_END + + /* Do not detect alias files and return 'eth0:0' instead of 'eth0'. + * Unfortunately, we cannot be sure that our files don't contain colons, + * so we cannot reject files with colons. + * + * Instead, you must not call utils_get_ifcfg_name() with an alias file + * or files that are ignored. */ + MATCH_TAG_AND_RETURN(name, IFCFG_TAG); + if (!only_ifcfg) { + MATCH_TAG_AND_RETURN(name, KEYS_TAG); + MATCH_TAG_AND_RETURN(name, ROUTE_TAG); + MATCH_TAG_AND_RETURN(name, ROUTE6_TAG); + } + + return NULL; +} + +/* Used to get any ifcfg/extra file path from any other ifcfg/extra path + * in the form <tag><name>. + */ +static char * +utils_get_extra_path(const char *parent, const char *tag) +{ + char * item_path = NULL, *dirname; + const char *name; + + g_return_val_if_fail(parent != NULL, NULL); + g_return_val_if_fail(tag != NULL, NULL); + + dirname = g_path_get_dirname(parent); + if (!dirname) + g_return_val_if_reached(NULL); + + name = utils_get_ifcfg_name(parent, FALSE); + if (name) { + if (!strcmp(dirname, ".")) + item_path = g_strdup_printf("%s%s", tag, name); + else + item_path = g_strdup_printf("%s/%s%s", dirname, tag, name); + } + g_free(dirname); + + return item_path; +} + +char * +utils_get_ifcfg_path(const char *parent) +{ + return utils_get_extra_path(parent, IFCFG_TAG); +} + +char * +utils_get_keys_path(const char *parent) +{ + return utils_get_extra_path(parent, KEYS_TAG); +} + +char * +utils_get_route_path(const char *parent) +{ + return utils_get_extra_path(parent, ROUTE_TAG); +} + +char * +utils_get_route6_path(const char *parent) +{ + return utils_get_extra_path(parent, ROUTE6_TAG); +} + +shvarFile * +utils_get_extra_ifcfg(const char *parent, const char *tag, gboolean should_create) +{ + shvarFile *ifcfg = NULL; + char * path; + + path = utils_get_extra_path(parent, tag); + if (!path) + return NULL; + + if (should_create && !g_file_test(path, G_FILE_TEST_EXISTS)) + ifcfg = svCreateFile(path); + + if (!ifcfg) + ifcfg = svOpenFile(path, NULL); + + g_free(path); + return ifcfg; +} + +shvarFile * +utils_get_keys_ifcfg(const char *parent, gboolean should_create) +{ + return utils_get_extra_ifcfg(parent, KEYS_TAG, should_create); +} + +shvarFile * +utils_get_route_ifcfg(const char *parent, gboolean should_create) +{ + return utils_get_extra_ifcfg(parent, ROUTE_TAG, should_create); +} + +/* Finds out if route file has new or older format + * Returns TRUE - new syntax (ADDRESS<n>=a.b.c.d ...), error opening file or empty + * FALSE - older syntax, i.e. argument to 'ip route add' (1.2.3.0/24 via 11.22.33.44) + */ +gboolean +utils_has_route_file_new_syntax(const char *filename) +{ + gs_free char *contents_data = NULL; + gsize len; + + g_return_val_if_fail(filename != NULL, TRUE); + + if (!g_file_get_contents(filename, &contents_data, &len, NULL)) + return TRUE; + + return utils_has_route_file_new_syntax_content(contents_data, len); +} + +gboolean +utils_has_route_file_new_syntax_content(const char *contents, gsize len) +{ + if (len <= 0) + return TRUE; + + while (TRUE) { + const char *line = contents; + char * eol; + gboolean found = FALSE; + + /* matches regex "^[[:space:]]*ADDRESS[0-9]+=" */ + + eol = (char *) strchr(contents, '\n'); + if (eol) { + eol[0] = '\0'; + contents = &eol[1]; + } + + line = nm_str_skip_leading_spaces(line); + if (NM_STR_HAS_PREFIX(line, "ADDRESS")) { + line += NM_STRLEN("ADDRESS"); + if (g_ascii_isdigit(line[0])) { + while (g_ascii_isdigit((++line)[0])) { + /* pass */ + } + if (line[0] == '=') + found = TRUE; + } + } + + if (eol) { + /* restore the line ending. We don't want to mangle the content from + * POV of the caller. */ + eol[0] = '\n'; + } + + if (found) + return TRUE; + if (!eol) + return FALSE; + } +} + +gboolean +utils_has_complex_routes(const char *filename, int addr_family) +{ + g_return_val_if_fail(filename, TRUE); + + if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET)) { + gs_free char *rules = utils_get_extra_path(filename, RULE_TAG); + + if (g_file_test(rules, G_FILE_TEST_EXISTS)) + return TRUE; + } + + if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) { + gs_free char *rules = utils_get_extra_path(filename, RULE6_TAG); + if (g_file_test(rules, G_FILE_TEST_EXISTS)) + return TRUE; + } + + 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_detect_ifcfg_path(const char *path, gboolean only_ifcfg) +{ + const char *base; + + g_return_val_if_fail(path != NULL, NULL); + + if (utils_should_ignore_file(path, only_ifcfg)) + return NULL; + + base = strrchr(path, '/'); + if (!base) + base = path; + else + base += 1; + + if (NM_STR_HAS_PREFIX(base, IFCFG_TAG)) { + if (base[NM_STRLEN(IFCFG_TAG)] == '\0') + return NULL; + if (utils_is_ifcfg_alias_file(base, NULL)) { + gs_free char *ifcfg = NULL; + char * ptr; + + ifcfg = g_strdup(path); + ptr = strrchr(ifcfg, ':'); + if (ptr && ptr > ifcfg && !strchr(ptr, '/')) { + *ptr = '\0'; + if (g_file_test(ifcfg, G_FILE_TEST_EXISTS)) { + /* the file has a colon, so it is probably an alias. + * To be ~more~ certain that this is an alias file, + * check whether a corresponding base file exists. */ + if (only_ifcfg) + return NULL; + return g_steal_pointer(&ifcfg); + } + } + } + return g_strdup(path); + } + + if (only_ifcfg) + return NULL; + return utils_get_ifcfg_path(path); +} + +void +nms_ifcfg_rh_utils_user_key_encode(const char *key, GString *str_buffer) +{ + gsize i; + + nm_assert(key); + nm_assert(str_buffer); + + for (i = 0; key[i]; i++) { + char ch = key[i]; + + /* we encode the key in only upper case letters, digits, and underscore. + * As we expect lower-case letters to be more common, we encode lower-case + * letters as upper case, and upper-case letters with a leading underscore. */ + + if (ch >= '0' && ch <= '9') { + g_string_append_c(str_buffer, ch); + continue; + } + if (ch >= 'a' && ch <= 'z') { + g_string_append_c(str_buffer, ch - 'a' + 'A'); + continue; + } + if (ch == '.') { + g_string_append(str_buffer, "__"); + continue; + } + if (ch >= 'A' && ch <= 'Z') { + g_string_append_c(str_buffer, '_'); + g_string_append_c(str_buffer, ch); + continue; + } + g_string_append_printf(str_buffer, "_%03o", (unsigned) ch); + } +} + +gboolean +nms_ifcfg_rh_utils_user_key_decode(const char *name, GString *str_buffer) +{ + gsize i; + + nm_assert(name); + nm_assert(str_buffer); + + if (!name[0]) + return FALSE; + + for (i = 0; name[i];) { + char ch = name[i]; + + if (ch >= '0' && ch <= '9') { + g_string_append_c(str_buffer, ch); + i++; + continue; + } + if (ch >= 'A' && ch <= 'Z') { + g_string_append_c(str_buffer, ch - 'A' + 'a'); + i++; + continue; + } + + if (ch == '_') { + ch = name[i + 1]; + if (ch == '_') { + g_string_append_c(str_buffer, '.'); + i += 2; + continue; + } + if (ch >= 'A' && ch <= 'Z') { + g_string_append_c(str_buffer, ch); + i += 2; + continue; + } + if (ch >= '0' && ch <= '7') { + char ch2, ch3; + unsigned v; + + ch2 = name[i + 2]; + if (!(ch2 >= '0' && ch2 <= '7')) + return FALSE; + + ch3 = name[i + 3]; + if (!(ch3 >= '0' && ch3 <= '7')) + return FALSE; + +#define OCTAL_VALUE(ch) ((unsigned) ((ch) - '0')) + v = (OCTAL_VALUE(ch) << 6) + (OCTAL_VALUE(ch2) << 3) + OCTAL_VALUE(ch3); + if (v > 0xFF || v == 0) + return FALSE; + ch = (char) v; + if ((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || (ch == '.') + || (ch >= 'a' && ch <= 'z')) { + /* such characters are not expected to be encoded via + * octal representation. The encoding is invalid. */ + return FALSE; + } + g_string_append_c(str_buffer, ch); + i += 4; + continue; + } + return FALSE; + } + + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +const char *const _nm_ethtool_ifcfg_names[] = { +#define ETHT_NAME(eid, ename) [eid] = "" ename "" + /* indexed by NMEthtoolID */ + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX, "adaptive-rx"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX, "adaptive-tx"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_RX_FRAMES, "rx-frames"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH, "rx-frames-high"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ, "rx-frames-irq"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW, "rx-frames-low"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH, "pkt-rate-high"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW, "pkt-rate-low"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_RX_USECS, "rx-usecs"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH, "rx-usecs-high"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ, "rx-usecs-irq"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW, "rx-usecs-low"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL, "sample-interval"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS, "stats-block-usecs"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_TX_FRAMES, "tx-frames"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH, "tx-frames-high"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ, "tx-frames-irq"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW, "tx-frames-low"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_TX_USECS, "tx-usecs"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH, "tx-usecs-high"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ, "tx-usecs-irq"), + ETHT_NAME(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW, "tx-usecs-low"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, "esp-hw-offload"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, "esp-tx-csum-hw-offload"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_FCOE_MTU, "fcoe-mtu"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_GRO, "gro"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_GSO, "gso"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_HIGHDMA, "highdma"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, "hw-tc-offload"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, "l2-fwd-offload"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_LOOPBACK, "loopback"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_LRO, "lro"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_MACSEC_HW_OFFLOAD, "macsec-hw-offload"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_NTUPLE, "ntuple"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RX, "rx"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RXHASH, "rxhash"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RXVLAN, "rxvlan"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RX_ALL, "rx-all"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RX_FCS, "rx-fcs"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, "rx-gro-hw"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RX_GRO_LIST, "rx-gro-list"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RX_UDP_GRO_FORWARDING, "rx-udp-gro-forwarding"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, "rx-udp_tunnel-port-offload"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, "rx-vlan-filter"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, "rx-vlan-stag-filter"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, "rx-vlan-stag-hw-parse"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_SG, "sg"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, "tls-hw-record"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TLS_HW_RX_OFFLOAD, "tls-hw-rx-offload"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, "tls-hw-tx-offload"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TSO, "tso"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX, "tx"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TXVLAN, "txvlan"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, "tx-checksum-fcoe-crc"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, "tx-checksum-ipv4"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, "tx-checksum-ipv6"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, "tx-checksum-ip-generic"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, "tx-checksum-sctp"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, "tx-esp-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, "tx-fcoe-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, "tx-gre-csum-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, "tx-gre-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_GSO_LIST, "tx-gso-list"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, "tx-gso-partial"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, "tx-gso-robust"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, "tx-ipxip4-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, "tx-ipxip6-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, "tx-nocache-copy"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, "tx-scatter-gather"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, "tx-scatter-gather-fraglist"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, "tx-sctp-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, "tx-tcp6-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, "tx-tcp-ecn-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, "tx-tcp-mangleid-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, "tx-tcp-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION, + "tx-tunnel-remcsum-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, "tx-udp-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, "tx-udp_tnl-segmentation"), + ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, "tx-vlan-stag-hw-insert"), + ETHT_NAME(NM_ETHTOOL_ID_RING_RX, "rx"), + ETHT_NAME(NM_ETHTOOL_ID_RING_RX_JUMBO, "rx-jumbo"), + ETHT_NAME(NM_ETHTOOL_ID_RING_RX_MINI, "rx-mini"), + ETHT_NAME(NM_ETHTOOL_ID_RING_TX, "tx"), +}; + +static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( + _get_ethtoolid_coalesce_by_name, + NMEthtoolID, + { nm_assert(name); }, + { return NM_ETHTOOL_ID_UNKNOWN; }, + {"adaptive-rx", NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX}, + {"adaptive-tx", NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX}, + {"pkt-rate-high", NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH}, + {"pkt-rate-low", NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW}, + {"rx-frames", NM_ETHTOOL_ID_COALESCE_RX_FRAMES}, + {"rx-frames-high", NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH}, + {"rx-frames-irq", NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ}, + {"rx-frames-low", NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW}, + {"rx-usecs", NM_ETHTOOL_ID_COALESCE_RX_USECS}, + {"rx-usecs-high", NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH}, + {"rx-usecs-irq", NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ}, + {"rx-usecs-low", NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW}, + {"sample-interval", NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL}, + {"stats-block-usecs", NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS}, + {"tx-frames", NM_ETHTOOL_ID_COALESCE_TX_FRAMES}, + {"tx-frames-high", NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH}, + {"tx-frames-irq", NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ}, + {"tx-frames-low", NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW}, + {"tx-usecs", NM_ETHTOOL_ID_COALESCE_TX_USECS}, + {"tx-usecs-high", NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH}, + {"tx-usecs-irq", NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ}, + {"tx-usecs-low", NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW}, ); + +static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( + _get_ethtoolid_feature_by_name, + NMEthtoolID, + { nm_assert(name); }, + { return NM_ETHTOOL_ID_UNKNOWN; }, + + /* Map the names from kernel/ethtool/ifcfg to NMEthtoolID. Note that ethtool utility has built-in + * features and NetworkManager's API follows the naming of these built-in features, whenever + * they exist. + * For example, NM's "ethtool.feature-ntuple" corresponds to ethtool utility's "ntuple" + * feature. However the underlying kernel feature is called "rx-ntuple-filter" (as reported + * for ETH_SS_FEATURES). + * + * With ethtool utility, whose command line we attempt to parse here, the user can also + * specify the name of the underlying kernel feature directly. So, check whether that is + * the case and if yes, map them to the corresponding NetworkManager's features. + * + * That is why there are duplicate IDs in this list. */ + {"esp-hw-offload", NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD}, + {"esp-tx-csum-hw-offload", NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD}, + {"fcoe-mtu", NM_ETHTOOL_ID_FEATURE_FCOE_MTU}, + {"gro", NM_ETHTOOL_ID_FEATURE_GRO}, + {"gso", NM_ETHTOOL_ID_FEATURE_GSO}, + {"highdma", NM_ETHTOOL_ID_FEATURE_HIGHDMA}, + {"hw-tc-offload", NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD}, + {"l2-fwd-offload", NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD}, + {"loopback", NM_ETHTOOL_ID_FEATURE_LOOPBACK}, + {"lro", NM_ETHTOOL_ID_FEATURE_LRO}, + {"macsec-hw-offload", NM_ETHTOOL_ID_FEATURE_MACSEC_HW_OFFLOAD}, + {"ntuple", NM_ETHTOOL_ID_FEATURE_NTUPLE}, + {"rx", NM_ETHTOOL_ID_FEATURE_RX}, + {"rx-all", NM_ETHTOOL_ID_FEATURE_RX_ALL}, + {"rx-checksum", NM_ETHTOOL_ID_FEATURE_RX}, // kernel-only name + {"rx-fcs", NM_ETHTOOL_ID_FEATURE_RX_FCS}, + {"rx-gro", NM_ETHTOOL_ID_FEATURE_GRO}, // kernel-only name + {"rx-gro-hw", NM_ETHTOOL_ID_FEATURE_RX_GRO_HW}, + {"rx-gro-list", NM_ETHTOOL_ID_FEATURE_RX_GRO_LIST}, + {"rx-hashing", NM_ETHTOOL_ID_FEATURE_RXHASH}, // kernel-only name + {"rx-lro", NM_ETHTOOL_ID_FEATURE_LRO}, // kernel-only name + {"rx-ntuple-filter", NM_ETHTOOL_ID_FEATURE_NTUPLE}, // kernel-only name + {"rx-udp-gro-forwarding", NM_ETHTOOL_ID_FEATURE_RX_UDP_GRO_FORWARDING}, + {"rx-udp_tunnel-port-offload", NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD}, + {"rx-vlan-filter", NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER}, + {"rx-vlan-hw-parse", NM_ETHTOOL_ID_FEATURE_RXVLAN}, // kernel-only name + {"rx-vlan-stag-filter", NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER}, + {"rx-vlan-stag-hw-parse", NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE}, + {"rxhash", NM_ETHTOOL_ID_FEATURE_RXHASH}, + {"rxvlan", NM_ETHTOOL_ID_FEATURE_RXVLAN}, + {"sg", NM_ETHTOOL_ID_FEATURE_SG}, + {"tls-hw-record", NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD}, + {"tls-hw-rx-offload", NM_ETHTOOL_ID_FEATURE_TLS_HW_RX_OFFLOAD}, + {"tls-hw-tx-offload", NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD}, + {"tso", NM_ETHTOOL_ID_FEATURE_TSO}, + {"tx", NM_ETHTOOL_ID_FEATURE_TX}, + {"tx-checksum-fcoe-crc", NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC}, + {"tx-checksum-ip-generic", NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC}, + {"tx-checksum-ipv4", NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4}, + {"tx-checksum-ipv6", NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6}, + {"tx-checksum-sctp", NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP}, + {"tx-esp-segmentation", NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION}, + {"tx-fcoe-segmentation", NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION}, + {"tx-generic-segmentation", NM_ETHTOOL_ID_FEATURE_GSO}, // kernel-only name + {"tx-gre-csum-segmentation", NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION}, + {"tx-gre-segmentation", NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION}, + {"tx-gso-list", NM_ETHTOOL_ID_FEATURE_TX_GSO_LIST}, + {"tx-gso-partial", NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL}, + {"tx-gso-robust", NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST}, + {"tx-ipxip4-segmentation", NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION}, + {"tx-ipxip6-segmentation", NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION}, + {"tx-nocache-copy", NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY}, + {"tx-scatter-gather", NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER}, + {"tx-scatter-gather-fraglist", NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST}, + {"tx-sctp-segmentation", NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION}, + {"tx-tcp-ecn-segmentation", NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION}, + {"tx-tcp-mangleid-segmentation", NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION}, + {"tx-tcp-segmentation", NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION}, + {"tx-tcp6-segmentation", NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION}, + {"tx-tunnel-remcsum-segmentation", NM_ETHTOOL_ID_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION}, + {"tx-udp-segmentation", NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION}, + {"tx-udp_tnl-csum-segmentation", NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION}, + {"tx-udp_tnl-segmentation", NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION}, + {"tx-vlan-hw-insert", NM_ETHTOOL_ID_FEATURE_TXVLAN}, // kernel-only name + {"tx-vlan-stag-hw-insert", NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT}, + {"txvlan", NM_ETHTOOL_ID_FEATURE_TXVLAN}, ); + +static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( + _get_ethtoolid_ring_by_name, + NMEthtoolID, + { nm_assert(name); }, + { return NM_ETHTOOL_ID_UNKNOWN; }, + {"rx", NM_ETHTOOL_ID_RING_RX}, + {"rx-jumbo", NM_ETHTOOL_ID_RING_RX_JUMBO}, + {"rx-mini", NM_ETHTOOL_ID_RING_RX_MINI}, + {"tx", NM_ETHTOOL_ID_RING_TX}, ); + +const NMEthtoolData * +nms_ifcfg_rh_utils_get_ethtool_by_name(const char *name, NMEthtoolType ethtool_type) +{ + NMEthtoolID id; + + switch (ethtool_type) { + case NM_ETHTOOL_TYPE_COALESCE: + id = _get_ethtoolid_coalesce_by_name(name); + break; + case NM_ETHTOOL_TYPE_FEATURE: + id = _get_ethtoolid_feature_by_name(name); + break; + case NM_ETHTOOL_TYPE_RING: + id = _get_ethtoolid_ring_by_name(name); + break; + default: + nm_assert_not_reached(); + return NULL; + } + + if (id == NM_ETHTOOL_ID_UNKNOWN) + return NULL; + + nm_assert(_NM_INT_NOT_NEGATIVE(id)); + nm_assert(id < G_N_ELEMENTS(nm_ethtool_data)); + nm_assert(nm_ethtool_data[id]); + nm_assert(nm_ethtool_data[id]->id == id); + return nm_ethtool_data[id]; +} + +/*****************************************************************************/ + +gboolean +nms_ifcfg_rh_utils_is_numbered_tag_impl(const char *key, + const char *tag, + gsize tag_len, + gint64 * out_idx) +{ + gint64 idx; + + nm_assert(key); + nm_assert(tag); + nm_assert(tag_len == strlen(tag)); + nm_assert(tag_len > 0); + + if (strncmp(key, tag, tag_len) != 0) + return FALSE; + + key += tag_len; + + if (key[0] == '\0') { + /* The key has no number suffix. We treat this also as a numbered + * tag, and it is for certain tags like "IPADDR", but not so much + * for others like "ROUTING_RULE_". The caller may want to handle + * this case specially. */ + NM_SET_OUT(out_idx, -1); + return TRUE; + } + + if (!NM_STRCHAR_ALL(key, ch, g_ascii_isdigit(ch))) + return FALSE; + + idx = _nm_utils_ascii_str_to_int64(key, 10, 0, G_MAXINT64, -1); + if (idx == -1) + return FALSE; + + NM_SET_OUT(out_idx, idx); + return TRUE; +} + +/*****************************************************************************/ + +#define _KEY_TYPE(key, flags) \ + { \ + .key_name = "" key "", .key_flags = ((NMS_IFCFG_KEY_TYPE_WELL_KNOWN) | (flags)), \ + } + +const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = { + _KEY_TYPE("ACD_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("ADDRESS", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("AP_ISOLATION", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("ARPING_WAIT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("AUTH_RETRIES", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("AUTOCONNECT_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("AUTOCONNECT_RETRIES", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("AUTOCONNECT_SLAVES", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BAND", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BONDING_MASTER", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BONDING_OPTS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BOOTPROTO", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BRIDGE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BRIDGE_MACADDR", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BRIDGE_PORT_VLANS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BRIDGE_UUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BRIDGE_VLANS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BRIDGING_OPTS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BROWSER_ONLY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("BSSID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("CHANNEL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("CIPHER_GROUP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("CIPHER_PAIRWISE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("CONNECTED_MODE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("CONNECTION_METERED", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("CTCPROT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DCB", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_APP_FCOE_ADVERTISE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_APP_FCOE_ENABLE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_APP_FCOE_MODE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DCB_APP_FCOE_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_APP_FCOE_WILLING, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_APP_FIP_ADVERTISE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_APP_FIP_ENABLE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DCB_APP_FIP_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_APP_FIP_WILLING, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_APP_ISCSI_ADVERTISE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_APP_ISCSI_ENABLE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DCB_APP_ISCSI_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_APP_ISCSI_WILLING, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PFC_ADVERTISE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PFC_ENABLE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PFC_UP, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PFC_WILLING, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PG_ADVERTISE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PG_ENABLE, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PG_ID, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PG_PCT, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PG_STRICT, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PG_UP2TC, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PG_UPPCT, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE(KEY_DCB_PG_WILLING, NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DEFAULTKEY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DEFROUTE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DELAY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DEVICE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DEVICETYPE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DEVTIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCPV6C", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCPV6_DUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCPV6_HOSTNAME", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCPV6_HOSTNAME_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCPV6_IAID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCPV6_SEND_HOSTNAME", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCP_CLIENT_ID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCP_FQDN", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCP_HOSTNAME", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCP_HOSTNAME_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCP_IAID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCP_REJECT_SERVERS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCP_SEND_HOSTNAME", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCP_VENDOR_CLASS_IDENTIFIER", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCPv6_DUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DHCPv6_IAID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("DNS", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("DOMAIN", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("ESSID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("ETHTOOL_OPTS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("ETHTOOL_WAKE_ON_LAN", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("FILS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("FILTER", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("GATEWAY", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("GATEWAYDEV", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("GATEWAY_PING_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("GENERATE_MAC_ADDRESS_MASK", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("GVRP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("HOSTNAME_FROM_DHCP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("HOSTNAME_FROM_DNS_LOOKUP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("HOSTNAME_ONLY_FROM_DEFAULT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("HOSTNAME_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("HWADDR", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("HWADDR_BLACKLIST", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_ALTSUBJECT_MATCHES", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_ANON_IDENTITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_AUTH_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_CA_CERT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_CA_CERT_PASSWORD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_CA_CERT_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_CA_PATH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_CLIENT_CERT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_CLIENT_CERT_PASSWORD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_CLIENT_CERT_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_DOMAIN_MATCH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_DOMAIN_SUFFIX_MATCH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_EAP_METHODS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_FAST_PROVISIONING", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_IDENTITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_INNER_AUTH_METHODS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_INNER_CA_CERT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_INNER_CA_CERT_PASSWORD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_INNER_CA_CERT_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_INNER_CLIENT_CERT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_INNER_CLIENT_CERT_PASSWORD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_INNER_CLIENT_CERT_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_INNER_PRIVATE_KEY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_OPTIONAL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PAC_FILE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PASSWORD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PASSWORD_RAW", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PASSWORD_RAW_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PEAP_FORCE_NEW_LABEL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PEAP_VERSION", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PHASE1_AUTH_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PHASE2_CA_PATH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PHASE2_DOMAIN_MATCH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PHASE2_SUBJECT_MATCH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PIN", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PIN_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PRIVATE_KEY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PRIVATE_KEY_PASSWORD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_SUBJECT_MATCH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IEEE_8021X_SYSTEM_CA_CERTS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPADDR", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("IPV4_DHCP_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV4_DNS_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV4_FAILURE_FATAL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV4_ROUTE_METRIC", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV4_ROUTE_TABLE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6ADDR", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6ADDR_SECONDARIES", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6FORWARDING", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6INIT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6TUNNELIPV4", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_ADDR_GEN_MODE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_AUTOCONF", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_DEFAULTDEV", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_DEFAULTGW", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_DEFROUTE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_DHCP_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_DISABLED", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_DNS_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_DOMAIN", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_FAILURE_FATAL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_PEERDNS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_PEERROUTES", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_PRIVACY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_PRIVACY_PREFER_PUBLIC_IP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_RA_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_RES_OPTIONS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_ROUTE_METRIC", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_ROUTE_TABLE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("IPV6_TOKEN", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("KEY", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("KEY_MGMT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("KEY_PASSPHRASE", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("KEY_TYPE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("LLDP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("LLMNR", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MACADDR", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MAC_ADDRESS_RANDOMIZATION", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MASTER", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MASTER_UUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MATCH_DRIVER", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MATCH_INTERFACE_NAME", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MATCH_KERNEL_COMMAND_LINE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MATCH_PATH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MDNS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("METRIC", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("MODE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MTU", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MUD_URL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MULTI_CONNECT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("MVRP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("NAME", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("NETMASK", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("NETTYPE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("NM_CONTROLLED", NMS_IFCFG_KEY_TYPE_IS_PLAIN | NMS_IFCFG_KEY_TYPE_KEEP_WHEN_DIRTY), + _KEY_TYPE("NM_USER_", NMS_IFCFG_KEY_TYPE_IS_PREFIX), + _KEY_TYPE("ONBOOT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("OPTIONS", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("OVS_PORT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("OVS_PORT_UUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("PAC_SCRIPT", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("PAC_URL", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("PEERDNS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("PEERROUTES", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("PHYSDEV", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("PKEY", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("PKEY_ID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("PMF", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("PORTNAME", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("POWERSAVE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("PREFIX", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("PROXY_METHOD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("QDISC", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("REORDER_HDR", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("RES_OPTIONS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("ROUTING_RULE6_", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("ROUTING_RULE_", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("SEARCH", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("SECONDARY_UUIDS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("SECURITYMODE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("SLAVE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("SRIOV_AUTOPROBE_DRIVERS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("SRIOV_TOTAL_VFS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("SRIOV_VF", NMS_IFCFG_KEY_TYPE_IS_NUMBERED), + _KEY_TYPE("SSID_HIDDEN", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("STABLE_ID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("STP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("SUBCHANNELS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("TEAM_CONFIG", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("TEAM_MASTER", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("TEAM_MASTER_UUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("TEAM_PORT_CONFIG", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("TYPE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("USERS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("UUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("VLAN", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("VLAN_EGRESS_PRIORITY_MAP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("VLAN_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("VLAN_ID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("VLAN_INGRESS_PRIORITY_MAP", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("VRF", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("VRF_UUID", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("WEP_KEY_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("WPA_ALLOW_WPA", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("WPA_ALLOW_WPA2", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("WPA_PSK", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("WPA_PSK_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("WPS_METHOD", NMS_IFCFG_KEY_TYPE_IS_PLAIN), + _KEY_TYPE("ZONE", NMS_IFCFG_KEY_TYPE_IS_PLAIN), +}; + +const NMSIfcfgKeyTypeInfo * +nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx) +{ + gssize idx; + + G_STATIC_ASSERT(G_STRUCT_OFFSET(NMSIfcfgKeyTypeInfo, key_name) == 0); + + idx = nm_utils_array_find_binary_search(nms_ifcfg_well_known_keys, + sizeof(nms_ifcfg_well_known_keys[0]), + G_N_ELEMENTS(nms_ifcfg_well_known_keys), + &key, + nm_strcmp_p_with_data, + NULL); + NM_SET_OUT(out_idx, idx); + if (idx < 0) + return NULL; + return &nms_ifcfg_well_known_keys[idx]; +} + +const NMSIfcfgKeyTypeInfo * +nms_ifcfg_rh_utils_is_well_known_key(const char *key) +{ + const NMSIfcfgKeyTypeInfo *ti; + gssize idx; + + nm_assert(key); + + ti = nms_ifcfg_well_known_key_find_info(key, &idx); + + if (ti) { + if (NM_FLAGS_ANY(ti->key_flags, + NMS_IFCFG_KEY_TYPE_IS_PLAIN | NMS_IFCFG_KEY_TYPE_IS_NUMBERED)) { + /* These tags are valid on full match. + * + * Note that numbered tags we also treat as valid if they have no + * suffix. That is correct for "IPADDR", but less so for "ROUTING_RULE_". */ + return ti; + } + nm_assert(NM_FLAGS_HAS(ti->key_flags, NMS_IFCFG_KEY_TYPE_IS_PREFIX)); + /* a prefix tag needs some extra suffix afterwards to be valid. */ + return NULL; + } + + /* Not found. Maybe it's a numbered/prefixed key? With idx we got the index where + * we should insert the key. Since the numbered/prefixed keys share a prefix, we can + * find the possible prefix at the index before the insert position. */ + idx = ~idx; + if (idx == 0) + return NULL; + + ti = &nms_ifcfg_well_known_keys[idx - 1]; + + if (NM_FLAGS_HAS(ti->key_flags, NMS_IFCFG_KEY_TYPE_IS_NUMBERED)) { + if (nms_ifcfg_rh_utils_is_numbered_tag(key, ti->key_name, NULL)) + return ti; + return NULL; + } + + if (NM_FLAGS_HAS(ti->key_flags, NMS_IFCFG_KEY_TYPE_IS_PREFIX)) { + gsize l = strlen(ti->key_name); + + if (strncmp(key, ti->key_name, l) == 0 && key[l] != '\0') + return ti; + return NULL; + } + + return NULL; +} diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h new file mode 100644 index 00000000..36ec9225 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008 - 2017 Red Hat, Inc. + */ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include "nm-connection.h" +#include "nm-base/nm-ethtool-base.h" + +#include "shvar.h" + +/*****************************************************************************/ + +typedef enum { + NMS_IFCFG_KEY_TYPE_UNKNOWN = 0, + NMS_IFCFG_KEY_TYPE_WELL_KNOWN = (1u << 0), + + NMS_IFCFG_KEY_TYPE_IS_PLAIN = (1u << 1), + NMS_IFCFG_KEY_TYPE_IS_NUMBERED = (1u << 2), + NMS_IFCFG_KEY_TYPE_IS_PREFIX = (1u << 3), + + /* by default, well knowns keys that are not explicitly set + * by the writer (the unvisited, dirty ones) are removed. + * With this flag, such keys are kept if they are present. */ + NMS_IFCFG_KEY_TYPE_KEEP_WHEN_DIRTY = (1u << 4), + +} NMSIfcfgKeyTypeFlags; + +typedef struct { + const char * key_name; + NMSIfcfgKeyTypeFlags key_flags; +} NMSIfcfgKeyTypeInfo; + +extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[247]; + +const NMSIfcfgKeyTypeInfo *nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx); + +static inline NMSIfcfgKeyTypeFlags +nms_ifcfg_well_known_key_find_info_flags(const char *key) +{ + const NMSIfcfgKeyTypeInfo *ti; + + ti = nms_ifcfg_well_known_key_find_info(key, NULL); + if (!ti) + return NMS_IFCFG_KEY_TYPE_UNKNOWN; + return ti->key_flags; +} + +/*****************************************************************************/ + +gboolean nms_ifcfg_rh_utils_parse_unhandled_spec(const char * unhandled_spec, + const char **out_unmanaged_spec, + const char **out_unrecognized_spec); + +#define NM_IFCFG_CONNECTION_LOG_PATH(path) ((path) ?: "in-memory") +#define NM_IFCFG_CONNECTION_LOG_FMT "%s (%s,\"%s\")" +#define NM_IFCFG_CONNECTION_LOG_ARG(con) \ + NM_IFCFG_CONNECTION_LOG_PATH( \ + nm_settings_connection_get_filename((NMSettingsConnection *) (con))), \ + nm_settings_connection_get_uuid((NMSettingsConnection *) (con)), \ + nm_settings_connection_get_id((NMSettingsConnection *) (con)) +#define NM_IFCFG_CONNECTION_LOG_FMTD "%s (%s,\"%s\",%p)" +#define NM_IFCFG_CONNECTION_LOG_ARGD(con) \ + NM_IFCFG_CONNECTION_LOG_PATH( \ + nm_settings_connection_get_filename((NMSettingsConnection *) (con))), \ + nm_settings_connection_get_uuid((NMSettingsConnection *) (con)), \ + nm_settings_connection_get_id((NMSettingsConnection *) (con)), (con) + +char *utils_cert_path(const char *parent, const char *suffix, const char *extension); + +const char *utils_get_ifcfg_name(const char *file, gboolean only_ifcfg); + +gboolean utils_should_ignore_file(const char *filename, gboolean only_ifcfg); + +char *utils_get_ifcfg_path(const char *parent); +char *utils_get_keys_path(const char *parent); +char *utils_get_route_path(const char *parent); +char *utils_get_route6_path(const char *parent); + +shvarFile *utils_get_extra_ifcfg(const char *parent, const char *tag, gboolean should_create); +shvarFile *utils_get_keys_ifcfg(const char *parent, gboolean should_create); +shvarFile *utils_get_route_ifcfg(const char *parent, gboolean should_create); + +gboolean utils_has_route_file_new_syntax(const char *filename); +gboolean utils_has_route_file_new_syntax_content(const char *contents, gsize len); +gboolean utils_has_complex_routes(const char *filename, int addr_family); + +gboolean utils_is_ifcfg_alias_file(const char *alias, const char *ifcfg); + +char *utils_detect_ifcfg_path(const char *path, gboolean only_ifcfg); + +void nms_ifcfg_rh_utils_user_key_encode(const char *key, GString *str_buffer); +gboolean nms_ifcfg_rh_utils_user_key_decode(const char *name, GString *str_buffer); + +static inline const char * +_nms_ifcfg_rh_utils_numbered_tag(char *buf, gsize buf_len, const char *tag_name, int which) +{ + gsize l; + +#if NM_MORE_ASSERTS > 5 + nm_assert(NM_FLAGS_ALL(nms_ifcfg_well_known_key_find_info_flags(tag_name), + NMS_IFCFG_KEY_TYPE_WELL_KNOWN | NMS_IFCFG_KEY_TYPE_IS_NUMBERED)); +#endif + + l = g_strlcpy(buf, tag_name, buf_len); + nm_assert(l < buf_len); + if (which != -1) { + buf_len -= l; + l = g_snprintf(&buf[l], buf_len, "%d", which); + nm_assert(l < buf_len); + } + return buf; +} +#define numbered_tag(buf, tag_name, which) \ + ({ \ + _nm_unused char *const _buf = (buf); \ + \ + /* some static assert trying to ensure that the buffer is statically allocated. + * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ + G_STATIC_ASSERT(G_N_ELEMENTS(buf) == sizeof(buf) && sizeof(buf) != sizeof(char *) \ + && sizeof(buf) < G_MAXINT); \ + _nms_ifcfg_rh_utils_numbered_tag(buf, sizeof(buf), "" tag_name "", (which)); \ + }) + +gboolean nms_ifcfg_rh_utils_is_numbered_tag_impl(const char *key, + const char *tag, + gsize tag_len, + gint64 * out_idx); + +static inline gboolean +nms_ifcfg_rh_utils_is_numbered_tag(const char *key, const char *tag, gint64 *out_idx) +{ + nm_assert(tag); + + return nms_ifcfg_rh_utils_is_numbered_tag_impl(key, tag, strlen(tag), out_idx); +} + +#define NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG(key, tag, out_idx) \ + nms_ifcfg_rh_utils_is_numbered_tag_impl(key, tag, NM_STRLEN(tag), out_idx) + +/*****************************************************************************/ + +const NMSIfcfgKeyTypeInfo *nms_ifcfg_rh_utils_is_well_known_key(const char *key); + +/*****************************************************************************/ + +extern const char *const _nm_ethtool_ifcfg_names[_NM_ETHTOOL_ID_NUM]; + +static inline const char * +nms_ifcfg_rh_utils_get_ethtool_name(NMEthtoolID ethtool_id) +{ + nm_assert(ethtool_id >= _NM_ETHTOOL_ID_FIRST && ethtool_id <= _NM_ETHTOOL_ID_LAST); + nm_assert(ethtool_id < G_N_ELEMENTS(_nm_ethtool_ifcfg_names)); + nm_assert(_nm_ethtool_ifcfg_names[ethtool_id]); + + return _nm_ethtool_ifcfg_names[ethtool_id]; +} + +const NMEthtoolData *nms_ifcfg_rh_utils_get_ethtool_by_name(const char * name, + NMEthtoolType ethtool_type); + +#endif /* _UTILS_H_ */ diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c new file mode 100644 index 00000000..a968fce0 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -0,0 +1,3654 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2009 - 2015 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nms-ifcfg-rh-writer.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> + +#include "nm-glib-aux/nm-enum-utils.h" +#include "nm-glib-aux/nm-io-utils.h" +#include "nm-manager.h" +#include "nm-setting-connection.h" +#include "nm-setting-wired.h" +#include "nm-setting-wireless.h" +#include "nm-setting-ethtool.h" +#include "nm-setting-8021x.h" +#include "nm-setting-proxy.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-pppoe.h" +#include "nm-setting-vlan.h" +#include "nm-setting-user.h" +#include "nm-setting-team.h" +#include "nm-setting-team-port.h" +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "NetworkManagerUtils.h" +#include "nm-base/nm-ethtool-base.h" + +#include "nms-ifcfg-rh-common.h" +#include "nms-ifcfg-rh-reader.h" +#include "nms-ifcfg-rh-utils.h" +#include "shvar.h" + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_SETTINGS +#define _NMLOG_PREFIX_NAME "ifcfg-rh" +#define _NMLOG(level, ...) \ + G_STMT_START \ + { \ + nm_log((level), \ + (_NMLOG_DOMAIN), \ + NULL, \ + NULL, \ + "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME ": " _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END + +/*****************************************************************************/ + +static void +save_secret_flags(shvarFile *ifcfg, const char *key, NMSettingSecretFlags flags) +{ + GString *str; + + g_return_if_fail(ifcfg != NULL); + g_return_if_fail(key != NULL); + + if (flags == NM_SETTING_SECRET_FLAG_NONE) + return; + + /* Convert flags bitfield into string representation */ + str = g_string_sized_new(20); + if (flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED) + g_string_append(str, SECRET_FLAG_AGENT); + + if (flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) { + if (str->len) + g_string_append_c(str, ' '); + g_string_append(str, SECRET_FLAG_NOT_SAVED); + } + + if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) { + if (str->len) + g_string_append_c(str, ' '); + g_string_append(str, SECRET_FLAG_NOT_REQUIRED); + } + + svSetValueStr(ifcfg, key, str->len ? str->str : NULL); + g_string_free(str, TRUE); +} + +static void +set_secret(shvarFile * ifcfg, + GHashTable * secrets, + const char * key, + const char * value, + const char * flags_key, + NMSettingSecretFlags flags) +{ + /* Save secret flags */ + save_secret_flags(ifcfg, flags_key, flags); + + /* Only write the secret if it's system owned and supposed to be saved */ + if (flags != NM_SETTING_SECRET_FLAG_NONE) + value = NULL; + + g_hash_table_replace(secrets, g_strdup(key), g_strdup(value)); +} + +static gboolean +write_secrets(shvarFile *ifcfg, GHashTable *secrets, GError **error) +{ + nm_auto_shvar_file_close shvarFile *keyfile = NULL; + gs_free NMUtilsNamedValue *secrets_arr_free = NULL; + NMUtilsNamedValue secrets_arr_static[30]; + const NMUtilsNamedValue * secrets_arr; + guint secrets_len; + GError * local = NULL; + gboolean any_secrets = FALSE; + guint i; + + keyfile = utils_get_keys_ifcfg(svFileGetName(ifcfg), TRUE); + if (!keyfile) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Failure to create secrets file for '%s'", + svFileGetName(ifcfg)); + return FALSE; + } + + secrets_arr = nm_utils_named_values_from_strdict(secrets, + &secrets_len, + secrets_arr_static, + &secrets_arr_free); + for (i = 0; i < secrets_len; i++) { + const char *k = secrets_arr[i].name; + const char *v = secrets_arr[i].value_str; + + if (v) { + svSetValueStr(keyfile, k, v); + any_secrets = TRUE; + } + } + + if (!any_secrets) + (void) unlink(svFileGetName(keyfile)); + else if (!svWriteFileWithoutDirtyWellknown(keyfile, 0600, &local)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Failure to write secrets to '%s': %s", + svFileGetName(keyfile), + local->message); + return FALSE; + } + + return TRUE; +} + +typedef struct { + const NMSetting8021xSchemeVtable *vtable; + const char * ifcfg_rh_key; +} Setting8021xSchemeVtable; + +static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = { +#define _D(_scheme_type, _ifcfg_rh_key) \ + [(_scheme_type)] = { \ + .vtable = &nm_setting_8021x_scheme_vtable[(_scheme_type)], \ + .ifcfg_rh_key = ""_ifcfg_rh_key \ + "", \ + } + _D(NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, "IEEE_8021X_CA_CERT"), + _D(NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, "IEEE_8021X_INNER_CA_CERT"), + _D(NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, "IEEE_8021X_CLIENT_CERT"), + _D(NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, "IEEE_8021X_INNER_CLIENT_CERT"), + _D(NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, "IEEE_8021X_PRIVATE_KEY"), + _D(NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, "IEEE_8021X_INNER_PRIVATE_KEY"), +#undef _D +}; + +static gboolean +write_object(NMSetting8021x * s_8021x, + shvarFile * ifcfg, + GHashTable * secrets, + GHashTable * blobs, + const Setting8021xSchemeVtable *objtype, + gboolean force_write, + GError ** error) +{ + NMSetting8021xCKScheme scheme; + const char * value = NULL; + GBytes * blob = NULL; + const char * password = NULL; + NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; + char secret_name[100]; + char secret_flags[sizeof(secret_name) + NM_STRLEN("_FLAGS")]; + const char * extension; + char * standard_file; + + g_return_val_if_fail(ifcfg != NULL, FALSE); + g_return_val_if_fail(objtype != NULL, FALSE); + + scheme = (*(objtype->vtable->scheme_func))(s_8021x); + switch (scheme) { + case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN: + break; + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + blob = (*(objtype->vtable->blob_func))(s_8021x); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + value = (*(objtype->vtable->path_func))(s_8021x); + break; + case NM_SETTING_802_1X_CK_SCHEME_PKCS11: + value = (*(objtype->vtable->uri_func))(s_8021x); + break; + default: + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Unhandled certificate object scheme"); + return FALSE; + } + + /* Set the password for certificate/private key. */ + nm_sprintf_buf(secret_name, "%s_PASSWORD", objtype->ifcfg_rh_key); + nm_sprintf_buf(secret_flags, "%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key); + password = (*(objtype->vtable->passwd_func))(s_8021x); + flags = (*(objtype->vtable->pwflag_func))(s_8021x); + set_secret(ifcfg, secrets, secret_name, password, secret_flags, flags); + + if (!objtype->vtable->format_func) + extension = "der"; + else if (objtype->vtable->format_func(s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) + extension = "p12"; + else + extension = "pem"; + + /* If the object path was specified, prefer that over any raw cert data that + * may have been sent. + */ + if (value) { + svSetValueStr(ifcfg, objtype->ifcfg_rh_key, value); + return TRUE; + } + + /* If it's raw certificate data, write the data out to the standard file */ + if (blob) { + char *new_file; + + new_file = utils_cert_path(svFileGetName(ifcfg), objtype->vtable->file_suffix, extension); + g_hash_table_replace(blobs, new_file, g_bytes_ref(blob)); + svSetValueStr(ifcfg, objtype->ifcfg_rh_key, new_file); + return TRUE; + } + + /* If certificate/private key wasn't sent, the connection may no longer be + * 802.1x and thus we clear out the paths and certs. + * + * Since no cert/private key is now being used, delete any standard file + * that was created for this connection, but leave other files alone. + * Thus, for example, + * /etc/sysconfig/network-scripts/ca-cert-Test_Write_Wifi_WPA_EAP-TLS.der + * will be deleted, but /etc/pki/tls/cert.pem will not. + */ + standard_file = utils_cert_path(svFileGetName(ifcfg), objtype->vtable->file_suffix, extension); + g_hash_table_replace(blobs, standard_file, NULL); + svSetValue(ifcfg, objtype->ifcfg_rh_key, force_write ? "" : NULL); + return TRUE; +} + +static gboolean +write_blobs(GHashTable *blobs, GError **error) +{ + GHashTableIter iter; + const char * filename; + GBytes * blob; + + if (!blobs) + return TRUE; + + g_hash_table_iter_init(&iter, blobs); + while (g_hash_table_iter_next(&iter, (gpointer *) &filename, (gpointer *) &blob)) { + GError *write_error = NULL; + + if (!blob) { + (void) unlink(filename); + continue; + } + + /* Write the raw certificate data out to the standard file so that we + * can use paths from now on instead of pushing around the certificate + * data itself. + */ + if (!nm_utils_file_set_contents(filename, + (const char *) g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob), + 0600, + NULL, + &write_error)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Could not write certificate to file \"%s\": %s", + filename, + write_error->message); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +write_8021x_certs(NMSetting8021x *s_8021x, + GHashTable * secrets, + GHashTable * blobs, + gboolean phase2, + shvarFile * ifcfg, + GError ** error) +{ + const Setting8021xSchemeVtable *pk_otype = NULL; + gs_free char * value_to_free = NULL; + + /* CA certificate */ + if (!write_object( + s_8021x, + ifcfg, + secrets, + blobs, + phase2 ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], + FALSE, + error)) + return FALSE; + + /* Private key */ + if (phase2) + pk_otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY]; + else + pk_otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; + + /* Save the private key */ + if (!write_object(s_8021x, ifcfg, secrets, blobs, pk_otype, FALSE, error)) + return FALSE; + + /* Save the client certificate. + * If there is a private key, always write a property for the + * client certificate even if it is empty, so that the reader + * doesn't have to read the private key file to determine if it + * is a PKCS #12 one which serves also as client certificate. + */ + if (!write_object( + s_8021x, + ifcfg, + secrets, + blobs, + phase2 ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], + !!svGetValue(ifcfg, pk_otype->ifcfg_rh_key, &value_to_free), + error)) + return FALSE; + + return TRUE; +} + +static gboolean +write_8021x_setting(NMConnection *connection, + shvarFile * ifcfg, + GHashTable * secrets, + GHashTable * blobs, + gboolean wired, + GError ** error) +{ + NMSetting8021x * s_8021x; + NMSetting8021xAuthFlags auth_flags; + const char * value, *match; + gconstpointer ptr; + GBytes * bytes; + char * tmp = NULL; + GString * phase2_auth; + GString * str; + guint32 i, num; + gsize size; + int vint; + + s_8021x = nm_connection_get_setting_802_1x(connection); + if (!s_8021x) + return TRUE; + + /* If wired, write KEY_MGMT */ + if (wired) + svSetValueStr(ifcfg, "KEY_MGMT", "IEEE8021X"); + + /* EAP method */ + if (nm_setting_802_1x_get_num_eap_methods(s_8021x)) { + value = nm_setting_802_1x_get_eap_method(s_8021x, 0); + if (value) + tmp = g_ascii_strup(value, -1); + } + svSetValueStr(ifcfg, "IEEE_8021X_EAP_METHODS", tmp); + g_free(tmp); + + svSetValueStr(ifcfg, "IEEE_8021X_IDENTITY", nm_setting_802_1x_get_identity(s_8021x)); + + svSetValueStr(ifcfg, + "IEEE_8021X_ANON_IDENTITY", + nm_setting_802_1x_get_anonymous_identity(s_8021x)); + + set_secret(ifcfg, + secrets, + "IEEE_8021X_PASSWORD", + nm_setting_802_1x_get_password(s_8021x), + "IEEE_8021X_PASSWORD_FLAGS", + nm_setting_802_1x_get_password_flags(s_8021x)); + + tmp = NULL; + bytes = nm_setting_802_1x_get_password_raw(s_8021x); + if (bytes) { + ptr = g_bytes_get_data(bytes, &size); + tmp = nm_utils_bin2hexstr(ptr, size, -1); + } + set_secret(ifcfg, + secrets, + "IEEE_8021X_PASSWORD_RAW", + tmp, + "IEEE_8021X_PASSWORD_RAW_FLAGS", + nm_setting_802_1x_get_password_raw_flags(s_8021x)); + g_free(tmp); + + svSetValueBoolean_cond_true(ifcfg, + "IEEE_8021X_SYSTEM_CA_CERTS", + nm_setting_802_1x_get_system_ca_certs(s_8021x)); + + value = nm_setting_802_1x_get_phase1_peapver(s_8021x); + svSetValueStr(ifcfg, "IEEE_8021X_PEAP_VERSION", value); + + svSetValueBoolean_cond_true(ifcfg, + "IEEE_8021X_PEAP_FORCE_NEW_LABEL", + nm_streq0(nm_setting_802_1x_get_phase1_peaplabel(s_8021x), "1")); + + svSetValueStr(ifcfg, "IEEE_8021X_PAC_FILE", nm_setting_802_1x_get_pac_file(s_8021x)); + + /* FAST PAC provisioning */ + value = nm_setting_802_1x_get_phase1_fast_provisioning(s_8021x); + if (value) { + if (strcmp(value, "1") == 0) + value = "allow-unauth"; + else if (strcmp(value, "2") == 0) + value = "allow-auth"; + else if (strcmp(value, "3") == 0) + value = "allow-unauth allow-auth"; + else + value = NULL; + } + svSetValueStr(ifcfg, "IEEE_8021X_FAST_PROVISIONING", value); + + /* Phase2 auth methods */ + phase2_auth = g_string_new(NULL); + + value = nm_setting_802_1x_get_phase2_auth(s_8021x); + if (value) { + tmp = g_ascii_strup(value, -1); + g_string_append(phase2_auth, tmp); + g_free(tmp); + } + + value = nm_setting_802_1x_get_phase2_autheap(s_8021x); + if (value) { + if (phase2_auth->len) + g_string_append_c(phase2_auth, ' '); + + tmp = g_ascii_strup(value, -1); + g_string_append_printf(phase2_auth, "EAP-%s", tmp); + g_free(tmp); + } + + auth_flags = nm_setting_802_1x_get_phase1_auth_flags(s_8021x); + if (auth_flags != NM_SETTING_802_1X_AUTH_FLAGS_NONE) { + svSetValueEnum(ifcfg, + "IEEE_8021X_PHASE1_AUTH_FLAGS", + nm_setting_802_1x_auth_flags_get_type(), + auth_flags); + } + + svSetValueStr(ifcfg, + "IEEE_8021X_INNER_AUTH_METHODS", + phase2_auth->len ? phase2_auth->str : NULL); + + g_string_free(phase2_auth, TRUE); + + svSetValueStr(ifcfg, "IEEE_8021X_SUBJECT_MATCH", nm_setting_802_1x_get_subject_match(s_8021x)); + + svSetValueStr(ifcfg, + "IEEE_8021X_PHASE2_SUBJECT_MATCH", + nm_setting_802_1x_get_phase2_subject_match(s_8021x)); + + str = g_string_new(NULL); + num = nm_setting_802_1x_get_num_altsubject_matches(s_8021x); + for (i = 0; i < num; i++) { + if (i > 0) + g_string_append_c(str, ' '); + match = nm_setting_802_1x_get_altsubject_match(s_8021x, i); + g_string_append(str, match); + } + if (str->len > 0) + svSetValueStr(ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES", str->str); + g_string_free(str, TRUE); + + str = g_string_new(NULL); + num = nm_setting_802_1x_get_num_phase2_altsubject_matches(s_8021x); + for (i = 0; i < num; i++) { + if (i > 0) + g_string_append_c(str, ' '); + match = nm_setting_802_1x_get_phase2_altsubject_match(s_8021x, i); + g_string_append(str, match); + } + if (str->len > 0) + svSetValueStr(ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", str->str); + g_string_free(str, TRUE); + + svSetValueStr(ifcfg, + "IEEE_8021X_DOMAIN_SUFFIX_MATCH", + nm_setting_802_1x_get_domain_suffix_match(s_8021x)); + svSetValueStr(ifcfg, + "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH", + nm_setting_802_1x_get_phase2_domain_suffix_match(s_8021x)); + + svSetValueStr(ifcfg, "IEEE_8021X_DOMAIN_MATCH", nm_setting_802_1x_get_domain_match(s_8021x)); + svSetValueStr(ifcfg, + "IEEE_8021X_PHASE2_DOMAIN_MATCH", + nm_setting_802_1x_get_phase2_domain_match(s_8021x)); + + vint = nm_setting_802_1x_get_auth_timeout(s_8021x); + svSetValueInt64_cond(ifcfg, "IEEE_8021X_AUTH_TIMEOUT", vint > 0, vint); + + svSetValueBoolean_cond_true(ifcfg, + "IEEE_8021X_OPTIONAL", + nm_setting_802_1x_get_optional(s_8021x)); + + svSetValue(ifcfg, "IEEE_8021X_CA_PATH", nm_setting_802_1x_get_ca_path(s_8021x)); + svSetValue(ifcfg, "IEEE_8021X_PHASE2_CA_PATH", nm_setting_802_1x_get_phase2_ca_path(s_8021x)); + + set_secret(ifcfg, + secrets, + "IEEE_8021X_PIN", + nm_setting_802_1x_get_pin(s_8021x), + "IEEE_8021X_PIN_FLAGS", + nm_setting_802_1x_get_pin_flags(s_8021x)); + + if (!write_8021x_certs(s_8021x, secrets, blobs, FALSE, ifcfg, error)) + return FALSE; + + /* phase2/inner certs */ + if (!write_8021x_certs(s_8021x, secrets, blobs, TRUE, ifcfg, error)) + return FALSE; + + return TRUE; +} + +static gboolean +write_wireless_security_setting(NMConnection *connection, + shvarFile * ifcfg, + GHashTable * secrets, + gboolean adhoc, + gboolean * no_8021x, + GError ** error) +{ + NMSettingWirelessSecurity * s_wsec; + const char * key_mgmt, *auth_alg, *key, *proto, *cipher; + const char * psk = NULL; + gboolean wep = FALSE, wpa = FALSE, dynamic_wep = FALSE; + NMSettingWirelessSecurityWpsMethod wps_method; + char * tmp; + guint32 i, num; + GString * str; + + s_wsec = nm_connection_get_setting_wireless_security(connection); + if (!s_wsec) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Missing '%s' setting", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + return FALSE; + } + + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + nm_assert(key_mgmt); + + auth_alg = nm_setting_wireless_security_get_auth_alg(s_wsec); + + if (!strcmp(key_mgmt, "none")) { + wep = TRUE; + *no_8021x = TRUE; + } else if (!strcmp(key_mgmt, "wpa-psk")) { + svSetValueStr(ifcfg, "KEY_MGMT", "WPA-PSK"); + wpa = TRUE; + *no_8021x = TRUE; + } else if (!strcmp(key_mgmt, "sae")) { + svSetValueStr(ifcfg, "KEY_MGMT", "SAE"); + wpa = TRUE; + *no_8021x = TRUE; + } else if (!strcmp(key_mgmt, "owe")) { + svSetValueStr(ifcfg, "KEY_MGMT", "OWE"); + wpa = FALSE; + *no_8021x = TRUE; + } else if (!strcmp(key_mgmt, "ieee8021x")) { + svSetValueStr(ifcfg, "KEY_MGMT", "IEEE8021X"); + dynamic_wep = TRUE; + } else if (!strcmp(key_mgmt, "wpa-eap")) { + svSetValueStr(ifcfg, "KEY_MGMT", "WPA-EAP"); + wpa = TRUE; + } else if (!strcmp(key_mgmt, "wpa-eap-suite-b-192")) { + svSetValueStr(ifcfg, "KEY_MGMT", "WPA-EAP-SUITE-B-192"); + wpa = TRUE; + } + + if (auth_alg) { + if (!strcmp(auth_alg, "shared")) + svSetValueStr(ifcfg, "SECURITYMODE", "restricted"); + else if (!strcmp(auth_alg, "open")) + svSetValueStr(ifcfg, "SECURITYMODE", "open"); + else if (!strcmp(auth_alg, "leap")) { + svSetValueStr(ifcfg, "SECURITYMODE", "leap"); + svSetValueStr(ifcfg, + "IEEE_8021X_IDENTITY", + nm_setting_wireless_security_get_leap_username(s_wsec)); + set_secret(ifcfg, + secrets, + "IEEE_8021X_PASSWORD", + nm_setting_wireless_security_get_leap_password(s_wsec), + "IEEE_8021X_PASSWORD_FLAGS", + nm_setting_wireless_security_get_leap_password_flags(s_wsec)); + *no_8021x = TRUE; + } + } + + wps_method = nm_setting_wireless_security_get_wps_method(s_wsec); + if (wps_method != NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT) + svSetValueEnum(ifcfg, + "WPS_METHOD", + nm_setting_wireless_security_wps_method_get_type(), + wps_method); + + /* WEP keys */ + + /* Clear any default key */ + set_secret(ifcfg, secrets, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + + /* Clear existing keys */ + for (i = 0; i < 4; i++) { + char tag[64]; + + numbered_tag(tag, "KEY_PASSPHRASE", i + 1); + set_secret(ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + + numbered_tag(tag, "KEY", i + 1); + set_secret(ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + } + + /* And write the new ones out */ + if (wep) { + NMWepKeyType key_type; + const char * key_type_str = NULL; + + /* Default WEP TX key index */ + svSetValueInt64(ifcfg, + "DEFAULTKEY", + nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec) + 1); + + key_type = nm_setting_wireless_security_get_wep_key_type(s_wsec); + switch (key_type) { + case NM_WEP_KEY_TYPE_KEY: + key_type_str = "key"; + break; + case NM_WEP_KEY_TYPE_PASSPHRASE: + key_type_str = "passphrase"; + break; + case NM_WEP_KEY_TYPE_UNKNOWN: + break; + } + svSetValue(ifcfg, "KEY_TYPE", key_type_str); + + for (i = 0; i < 4; i++) { + key = nm_setting_wireless_security_get_wep_key(s_wsec, i); + if (key) { + gs_free char *ascii_key = NULL; + char tag[64]; + gboolean key_valid = TRUE; + + /* Passphrase needs a different ifcfg key since with WEP, there + * are some passphrases that are indistinguishable from WEP hex + * keys. + */ + if (key_type == NM_WEP_KEY_TYPE_UNKNOWN) { + if (nm_utils_wep_key_valid(key, NM_WEP_KEY_TYPE_KEY)) + key_type = NM_WEP_KEY_TYPE_KEY; + else if (nm_utils_wep_key_valid(key, NM_WEP_KEY_TYPE_PASSPHRASE)) + key_type = NM_WEP_KEY_TYPE_PASSPHRASE; + } + + if (key_type == NM_WEP_KEY_TYPE_PASSPHRASE) + numbered_tag(tag, "KEY_PASSPHRASE", i + 1); + else if (key_type == NM_WEP_KEY_TYPE_KEY) { + numbered_tag(tag, "KEY", i + 1); + + /* Add 's:' prefix for ASCII keys */ + if (strlen(key) == 5 || strlen(key) == 13) { + ascii_key = g_strdup_printf("s:%s", key); + key = ascii_key; + } + } else { + g_warn_if_reached(); + key_valid = FALSE; + } + + if (key_valid) { + set_secret(ifcfg, + secrets, + tag, + key, + "WEP_KEY_FLAGS", + nm_setting_wireless_security_get_wep_key_flags(s_wsec)); + } + } + } + } + + /* WPA protos */ + num = nm_setting_wireless_security_get_num_protos(s_wsec); + for (i = 0; i < num; i++) { + proto = nm_setting_wireless_security_get_proto(s_wsec, i); + if (proto && !strcmp(proto, "wpa")) + svSetValueStr(ifcfg, "WPA_ALLOW_WPA", "yes"); + else if (proto && !strcmp(proto, "rsn")) + svSetValueStr(ifcfg, "WPA_ALLOW_WPA2", "yes"); + } + + /* WPA Pairwise ciphers */ + str = g_string_new(NULL); + num = nm_setting_wireless_security_get_num_pairwise(s_wsec); + for (i = 0; i < num; i++) { + if (i > 0) + g_string_append_c(str, ' '); + cipher = nm_setting_wireless_security_get_pairwise(s_wsec, i); + + /* Don't write out WEP40 or WEP104 if for some reason they are set; they + * are not valid pairwise ciphers. + */ + if (strcmp(cipher, "wep40") && strcmp(cipher, "wep104")) { + tmp = g_ascii_strup(cipher, -1); + g_string_append(str, tmp); + g_free(tmp); + } + } + if (strlen(str->str) && (dynamic_wep == FALSE)) + svSetValueStr(ifcfg, "CIPHER_PAIRWISE", str->str); + g_string_free(str, TRUE); + + /* WPA Group ciphers */ + str = g_string_new(NULL); + num = nm_setting_wireless_security_get_num_groups(s_wsec); + for (i = 0; i < num; i++) { + if (i > 0) + g_string_append_c(str, ' '); + cipher = nm_setting_wireless_security_get_group(s_wsec, i); + tmp = g_ascii_strup(cipher, -1); + g_string_append(str, tmp); + g_free(tmp); + } + if (strlen(str->str) && (dynamic_wep == FALSE)) + svSetValueStr(ifcfg, "CIPHER_GROUP", str->str); + g_string_free(str, TRUE); + + if (wpa) + psk = nm_setting_wireless_security_get_psk(s_wsec); + + set_secret(ifcfg, + secrets, + "WPA_PSK", + psk, + "WPA_PSK_FLAGS", + wpa ? nm_setting_wireless_security_get_psk_flags(s_wsec) + : NM_SETTING_SECRET_FLAG_NONE); + + if (nm_setting_wireless_security_get_pmf(s_wsec) != NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT) { + svSetValueEnum(ifcfg, + "PMF", + nm_setting_wireless_security_pmf_get_type(), + nm_setting_wireless_security_get_pmf(s_wsec)); + } + + if (nm_setting_wireless_security_get_fils(s_wsec) + != NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT) { + svSetValueEnum(ifcfg, + "FILS", + nm_setting_wireless_security_fils_get_type(), + nm_setting_wireless_security_get_fils(s_wsec)); + } + + return TRUE; +} + +static gboolean +write_wireless_setting(NMConnection *connection, + shvarFile * ifcfg, + GHashTable * secrets, + gboolean * no_8021x, + GError ** error) +{ + NMSettingWireless *s_wireless; + GBytes * ssid; + const guint8 * ssid_data; + gsize ssid_len; + const char * mode, *bssid; + const char * device_mac, *cloned_mac; + guint32 mtu, chan, i; + gboolean adhoc = FALSE, hex_ssid = FALSE; + const char *const *macaddr_blacklist; + + s_wireless = nm_connection_get_setting_wireless(connection); + if (!s_wireless) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Missing '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + device_mac = nm_setting_wireless_get_mac_address(s_wireless); + svSetValueStr(ifcfg, "HWADDR", device_mac); + + cloned_mac = nm_setting_wireless_get_cloned_mac_address(s_wireless); + svSetValueStr(ifcfg, "MACADDR", cloned_mac); + + svSetValueStr(ifcfg, + "GENERATE_MAC_ADDRESS_MASK", + nm_setting_wireless_get_generate_mac_address_mask(s_wireless)); + + macaddr_blacklist = nm_setting_wireless_get_mac_address_blacklist(s_wireless); + if (macaddr_blacklist[0]) { + gs_free char *blacklist_str = NULL; + + blacklist_str = g_strjoinv(" ", (char **) macaddr_blacklist); + svSetValueStr(ifcfg, "HWADDR_BLACKLIST", blacklist_str); + } + + mtu = nm_setting_wireless_get_mtu(s_wireless); + svSetValueInt64_cond(ifcfg, "MTU", mtu != 0, mtu); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + if (!ssid) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Missing SSID in '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + ssid_data = g_bytes_get_data(ssid, &ssid_len); + if (!ssid_len || ssid_len > 32) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Invalid SSID in '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + /* If the SSID contains any non-printable characters, we need to use the + * hex notation of the SSID instead. + */ + if (ssid_len > 2 && ssid_data[0] == '0' && ssid_data[1] == 'x') { + hex_ssid = TRUE; + for (i = 2; i < ssid_len; i++) { + if (!g_ascii_isxdigit(ssid_data[i])) { + hex_ssid = FALSE; + break; + } + } + } + if (!hex_ssid) { + for (i = 0; i < ssid_len; i++) { + if (!g_ascii_isprint(ssid_data[i])) { + hex_ssid = TRUE; + break; + } + } + } + + if (hex_ssid) { + GString *str; + + /* Hex SSIDs don't get quoted */ + str = g_string_sized_new(ssid_len * 2 + 3); + g_string_append(str, "0x"); + for (i = 0; i < ssid_len; i++) + g_string_append_printf(str, "%02X", ssid_data[i]); + svSetValueStr(ifcfg, "ESSID", str->str); + g_string_free(str, TRUE); + } else { + char buf[33]; + + nm_assert(ssid_len <= 32); + memcpy(buf, ssid_data, ssid_len); + buf[ssid_len] = '\0'; + svSetValueStr(ifcfg, "ESSID", buf); + } + + mode = nm_setting_wireless_get_mode(s_wireless); + if (!mode) { + /* pass */ + } else if (nm_streq(mode, NM_SETTING_WIRELESS_MODE_INFRA)) + svSetValueStr(ifcfg, "MODE", "Managed"); + else if (nm_streq(mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { + svSetValueStr(ifcfg, "MODE", "Ad-Hoc"); + adhoc = TRUE; + } else if (nm_streq(mode, NM_SETTING_WIRELESS_MODE_AP)) + svSetValueStr(ifcfg, "MODE", "Ap"); + else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Invalid mode '%s' in '%s' setting", + mode, + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + chan = nm_setting_wireless_get_channel(s_wireless); + if (chan) { + svSetValueInt64(ifcfg, "CHANNEL", chan); + } else { + /* Band only set if channel is not, since channel implies band */ + svSetValueStr(ifcfg, "BAND", nm_setting_wireless_get_band(s_wireless)); + } + + bssid = nm_setting_wireless_get_bssid(s_wireless); + svSetValueStr(ifcfg, "BSSID", bssid); + + /* Ensure DEFAULTKEY and SECURITYMODE are cleared unless there's security; + * otherwise there's no way to detect WEP vs. open when WEP keys aren't + * saved. + */ + + if (nm_connection_get_setting_wireless_security(connection)) { + if (!write_wireless_security_setting(connection, ifcfg, secrets, adhoc, no_8021x, error)) + return FALSE; + } else { + /* Clear out wifi security keys */ + set_secret(ifcfg, + secrets, + "IEEE_8021X_PASSWORD", + NULL, + "IEEE_8021X_PASSWORD_FLAGS", + NM_SETTING_SECRET_FLAG_NONE); + + /* Clear existing keys */ + set_secret(ifcfg, secrets, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + for (i = 0; i < 4; i++) { + char tag[64]; + + numbered_tag(tag, "KEY_PASSPHRASE", i + 1); + set_secret(ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + + numbered_tag(tag, "KEY", i + 1); + set_secret(ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + } + + set_secret(ifcfg, secrets, "WPA_PSK", NULL, "WPA_PSK_FLAGS", NM_SETTING_SECRET_FLAG_NONE); + } + + svSetValueStr(ifcfg, "SSID_HIDDEN", nm_setting_wireless_get_hidden(s_wireless) ? "yes" : NULL); + + switch (nm_setting_wireless_get_powersave(s_wireless)) { + case NM_SETTING_WIRELESS_POWERSAVE_IGNORE: + svSetValueStr(ifcfg, "POWERSAVE", "ignore"); + break; + case NM_SETTING_WIRELESS_POWERSAVE_DISABLE: + svSetValueStr(ifcfg, "POWERSAVE", "disable"); + break; + case NM_SETTING_WIRELESS_POWERSAVE_ENABLE: + svSetValueStr(ifcfg, "POWERSAVE", "enable"); + break; + default: + case NM_SETTING_WIRELESS_POWERSAVE_DEFAULT: + break; + } + + switch (nm_setting_wireless_get_mac_address_randomization(s_wireless)) { + case NM_SETTING_MAC_RANDOMIZATION_NEVER: + svSetValueStr(ifcfg, "MAC_ADDRESS_RANDOMIZATION", "never"); + break; + case NM_SETTING_MAC_RANDOMIZATION_ALWAYS: + svSetValueStr(ifcfg, "MAC_ADDRESS_RANDOMIZATION", "always"); + break; + case NM_SETTING_MAC_RANDOMIZATION_DEFAULT: + default: + svSetValueStr(ifcfg, "MAC_ADDRESS_RANDOMIZATION", "default"); + break; + } + + svSetValueTernary(ifcfg, "AP_ISOLATION", nm_setting_wireless_get_ap_isolation(s_wireless)); + + svSetValueStr(ifcfg, "TYPE", TYPE_WIRELESS); + + return TRUE; +} + +static gboolean +write_infiniband_setting(NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingInfiniband *s_infiniband; + const char * mac, *transport_mode, *parent; + guint32 mtu; + int p_key; + + s_infiniband = nm_connection_get_setting_infiniband(connection); + if (!s_infiniband) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Missing '%s' setting", + NM_SETTING_INFINIBAND_SETTING_NAME); + return FALSE; + } + + mac = nm_setting_infiniband_get_mac_address(s_infiniband); + svSetValueStr(ifcfg, "HWADDR", mac); + + mtu = nm_setting_infiniband_get_mtu(s_infiniband); + svSetValueInt64_cond(ifcfg, "MTU", mtu != 0, mtu); + + transport_mode = nm_setting_infiniband_get_transport_mode(s_infiniband); + svSetValueBoolean(ifcfg, "CONNECTED_MODE", nm_streq(transport_mode, "connected")); + + p_key = nm_setting_infiniband_get_p_key(s_infiniband); + if (p_key != -1) { + svSetValueStr(ifcfg, "PKEY", "yes"); + svSetValueInt64(ifcfg, "PKEY_ID", p_key); + + parent = nm_setting_infiniband_get_parent(s_infiniband); + if (parent) + svSetValueStr(ifcfg, "PHYSDEV", parent); + } + + svSetValueStr(ifcfg, "TYPE", TYPE_INFINIBAND); + + return TRUE; +} + +static void +write_hostname_setting(NMConnection *connection, shvarFile *ifcfg) +{ + NMSettingHostname *s_hostname; + NMTernary t; + + s_hostname = _nm_connection_get_setting(connection, NM_TYPE_SETTING_HOSTNAME); + if (!s_hostname) + return; + + svSetValueInt64(ifcfg, "HOSTNAME_PRIORITY", nm_setting_hostname_get_priority(s_hostname)); + + t = nm_setting_hostname_get_from_dhcp(s_hostname); + svSetValueInt64_cond(ifcfg, "HOSTNAME_FROM_DHCP", t != NM_TERNARY_DEFAULT, t); + + t = nm_setting_hostname_get_from_dns_lookup(s_hostname); + svSetValueInt64_cond(ifcfg, "HOSTNAME_FROM_DNS_LOOKUP", t != NM_TERNARY_DEFAULT, t); + + t = nm_setting_hostname_get_only_from_default(s_hostname); + svSetValueInt64_cond(ifcfg, "HOSTNAME_ONLY_FROM_DEFAULT", t != NM_TERNARY_DEFAULT, t); +} + +static gboolean +write_wired_setting(NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingWired * s_wired; + const char *const *s390_subchannels; + guint32 mtu, num_opts, i; + const char *const *macaddr_blacklist; + + s_wired = nm_connection_get_setting_wired(connection); + if (!s_wired) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Missing '%s' setting", + NM_SETTING_WIRED_SETTING_NAME); + return FALSE; + } + + svSetValueStr(ifcfg, "HWADDR", nm_setting_wired_get_mac_address(s_wired)); + + svSetValueStr(ifcfg, "MACADDR", nm_setting_wired_get_cloned_mac_address(s_wired)); + + svSetValueStr(ifcfg, + "GENERATE_MAC_ADDRESS_MASK", + nm_setting_wired_get_generate_mac_address_mask(s_wired)); + + macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist(s_wired); + if (macaddr_blacklist[0]) { + gs_free char *blacklist_str = NULL; + + blacklist_str = g_strjoinv(" ", (char **) macaddr_blacklist); + svSetValueStr(ifcfg, "HWADDR_BLACKLIST", blacklist_str); + } + + mtu = nm_setting_wired_get_mtu(s_wired); + svSetValueInt64_cond(ifcfg, "MTU", mtu != 0, mtu); + + s390_subchannels = nm_setting_wired_get_s390_subchannels(s_wired); + + { + gs_free char *tmp = NULL; + gsize len = NM_PTRARRAY_LEN(s390_subchannels); + + if (len == 2) { + tmp = g_strdup_printf("%s,%s", s390_subchannels[0], s390_subchannels[1]); + } else if (len == 3) { + tmp = g_strdup_printf("%s,%s,%s", + s390_subchannels[0], + s390_subchannels[1], + s390_subchannels[2]); + } + + svSetValueStr(ifcfg, "SUBCHANNELS", tmp); + } + + svSetValueStr(ifcfg, "NETTYPE", nm_setting_wired_get_s390_nettype(s_wired)); + + svSetValueStr(ifcfg, "PORTNAME", nm_setting_wired_get_s390_option_by_key(s_wired, "portname")); + + svSetValueStr(ifcfg, "CTCPROT", nm_setting_wired_get_s390_option_by_key(s_wired, "ctcprot")); + + num_opts = nm_setting_wired_get_num_s390_options(s_wired); + if (s390_subchannels && num_opts) { + nm_auto_free_gstring GString *tmp = NULL; + + for (i = 0; i < num_opts; i++) { + const char *s390_key, *s390_val; + + nm_setting_wired_get_s390_option(s_wired, i, &s390_key, &s390_val); + + /* portname is handled separately */ + if (NM_IN_STRSET(s390_key, "portname", "ctcprot")) + continue; + + if (strchr(s390_key, '=')) { + /* this key cannot be expressed. But after all, it's not valid anyway + * and the connection shouldn't even verify. */ + continue; + } + + if (!tmp) + tmp = g_string_sized_new(30); + else + g_string_append_c(tmp, ' '); + nm_utils_escaped_tokens_escape_gstr(s390_key, NM_ASCII_SPACES, tmp); + g_string_append_c(tmp, '='); + nm_utils_escaped_tokens_escape_gstr(s390_val, NM_ASCII_SPACES, tmp); + } + if (tmp) + svSetValueStr(ifcfg, "OPTIONS", tmp->str); + } + + svSetValueStr(ifcfg, "TYPE", TYPE_ETHERNET); + + return TRUE; +} + +static void +_ethtool_gstring_prepare(GString **str, gboolean *is_first, char cmdline_flag, const char *iface) +{ + if (!*is_first) { + nm_assert(*str && (*str)->len > 0); + return; + } + + if (!*str) + *str = g_string_sized_new(30); + else { + nm_assert((*str)->len > 0); + g_string_append(*str, " ; "); + } + g_string_append_printf(*str, "-%c %s", cmdline_flag, iface); + *is_first = FALSE; +} + +static gboolean +write_ethtool_setting(NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingWired * s_wired; + NMSettingEthtool * s_ethtool; + const char * duplex; + guint32 speed; + GString * str = NULL; + gboolean auto_negotiate; + NMSettingWiredWakeOnLan wol; + const char * wol_password; + + s_wired = nm_connection_get_setting_wired(connection); + s_ethtool = NM_SETTING_ETHTOOL(nm_connection_get_setting(connection, NM_TYPE_SETTING_ETHTOOL)); + + if (!s_wired && !s_ethtool) + return TRUE; + + if (s_wired) { + auto_negotiate = nm_setting_wired_get_auto_negotiate(s_wired); + speed = nm_setting_wired_get_speed(s_wired); + duplex = nm_setting_wired_get_duplex(s_wired); + + /* autoneg off + speed 0 + duplex NULL, means we want NM + * to skip link configuration which is default. So write + * down link config only if we have auto-negotiate true or + * a valid value for one among speed and duplex. + */ + if (auto_negotiate) { + str = g_string_sized_new(64); + g_string_printf(str, "autoneg on"); + } else if (speed || duplex) { + str = g_string_sized_new(64); + g_string_printf(str, "autoneg off"); + } + if (speed) + g_string_append_printf(str, " speed %u", speed); + if (duplex) + g_string_append_printf(str, " duplex %s", duplex); + + wol = nm_setting_wired_get_wake_on_lan(s_wired); + wol_password = nm_setting_wired_get_wake_on_lan_password(s_wired); + + svSetValue(ifcfg, + "ETHTOOL_WAKE_ON_LAN", + wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE ? "ignore" : NULL); + if (!NM_IN_SET(wol, + NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE, + NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT)) { + if (!str) + str = g_string_sized_new(30); + else + g_string_append(str, " "); + + g_string_append(str, "wol "); + + if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY)) + g_string_append(str, "p"); + if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST)) + g_string_append(str, "u"); + if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST)) + g_string_append(str, "m"); + if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST)) + g_string_append(str, "b"); + if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_ARP)) + g_string_append(str, "a"); + if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) + g_string_append(str, "g"); + + if (!NM_FLAGS_ANY(wol, NM_SETTING_WIRED_WAKE_ON_LAN_ALL)) + g_string_append(str, "d"); + + if (wol_password && NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) + g_string_append_printf(str, "s sopass %s", wol_password); + } + } + + if (s_ethtool) { + NMEthtoolID ethtool_id; + NMSettingConnection *s_con; + const char * iface; + gboolean is_first; + guint32 u32; + gboolean b; + + s_con = nm_connection_get_setting_connection(connection); + if (s_con) { + iface = nm_setting_connection_get_interface_name(s_con); + if (iface + && (!iface[0] + || !NM_STRCHAR_ALL(iface, + ch, + (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') + || (ch >= '0' && ch <= '9') || NM_IN_SET(ch, '_')))) + iface = NULL; + } else + iface = NULL; + if (!iface) + iface = "net0"; + + is_first = TRUE; + for (ethtool_id = _NM_ETHTOOL_ID_FEATURE_FIRST; ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST; + ethtool_id++) { + nm_assert(nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id)); + if (!nm_setting_option_get_boolean(NM_SETTING(s_ethtool), + nm_ethtool_data[ethtool_id]->optname, + &b)) + continue; + + _ethtool_gstring_prepare(&str, &is_first, 'K', iface); + g_string_append_c(str, ' '); + g_string_append(str, nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id)); + g_string_append(str, b ? " on" : " off"); + } + + is_first = TRUE; + for (ethtool_id = _NM_ETHTOOL_ID_COALESCE_FIRST; ethtool_id <= _NM_ETHTOOL_ID_COALESCE_LAST; + ethtool_id++) { + nm_assert(nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id)); + if (!nm_setting_option_get_uint32(NM_SETTING(s_ethtool), + nm_ethtool_data[ethtool_id]->optname, + &u32)) + continue; + + _ethtool_gstring_prepare(&str, &is_first, 'C', iface); + g_string_append_c(str, ' '); + g_string_append(str, nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id)); + g_string_append_printf(str, " %" G_GUINT32_FORMAT, u32); + } + + is_first = TRUE; + for (ethtool_id = _NM_ETHTOOL_ID_RING_FIRST; ethtool_id <= _NM_ETHTOOL_ID_RING_LAST; + ethtool_id++) { + nm_assert(nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id)); + if (!nm_setting_option_get_uint32(NM_SETTING(s_ethtool), + nm_ethtool_data[ethtool_id]->optname, + &u32)) + continue; + + _ethtool_gstring_prepare(&str, &is_first, 'G', iface); + g_string_append_c(str, ' '); + g_string_append(str, nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id)); + g_string_append_printf(str, " %" G_GUINT32_FORMAT, u32); + } + } + + if (str) { + svSetValueStr(ifcfg, "ETHTOOL_OPTS", str->str); + g_string_free(str, TRUE); + } + + return TRUE; +} + +static char * +vlan_priority_maplist_to_stringlist(NMSettingVlan *s_vlan, NMVlanPriorityMap map) +{ + char **strlist; + char * value; + + if (map == NM_VLAN_INGRESS_MAP) + g_object_get(G_OBJECT(s_vlan), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP, &strlist, NULL); + else if (map == NM_VLAN_EGRESS_MAP) + g_object_get(G_OBJECT(s_vlan), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP, &strlist, NULL); + else + return NULL; + + if (strlist[0]) + value = g_strjoinv(",", strlist); + else + value = NULL; + g_strfreev(strlist); + + return value; +} + +static gboolean +write_wired_for_virtual(NMConnection *connection, shvarFile *ifcfg) +{ + NMSettingWired *s_wired; + gboolean has_wired = FALSE; + + s_wired = nm_connection_get_setting_wired(connection); + if (s_wired) { + const char *device_mac, *cloned_mac; + guint32 mtu; + + has_wired = TRUE; + + device_mac = nm_setting_wired_get_mac_address(s_wired); + svSetValue(ifcfg, "HWADDR", device_mac ?: ""); + + cloned_mac = nm_setting_wired_get_cloned_mac_address(s_wired); + svSetValueStr(ifcfg, "MACADDR", cloned_mac); + + svSetValueStr(ifcfg, + "GENERATE_MAC_ADDRESS_MASK", + nm_setting_wired_get_generate_mac_address_mask(s_wired)); + + mtu = nm_setting_wired_get_mtu(s_wired); + svSetValueInt64_cond(ifcfg, "MTU", mtu != 0, mtu); + } + return has_wired; +} + +static gboolean +write_vlan_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) +{ + NMSettingVlan *s_vlan; + char * tmp; + guint32 vlan_flags = 0; + gsize s_buf_len; + char s_buf[50], *s_buf_ptr; + + s_vlan = nm_connection_get_setting_vlan(connection); + if (!s_vlan) { + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Missing VLAN setting"); + return FALSE; + } + + svSetValueStr(ifcfg, "VLAN", "yes"); + svSetValueStr(ifcfg, "TYPE", TYPE_VLAN); + svSetValueStr(ifcfg, "PHYSDEV", nm_setting_vlan_get_parent(s_vlan)); + svSetValueInt64(ifcfg, "VLAN_ID", nm_setting_vlan_get_id(s_vlan)); + + vlan_flags = nm_setting_vlan_get_flags(s_vlan); + svSetValueBoolean(ifcfg, "REORDER_HDR", NM_FLAGS_HAS(vlan_flags, NM_VLAN_FLAG_REORDER_HEADERS)); + svSetValueBoolean(ifcfg, "GVRP", NM_FLAGS_HAS(vlan_flags, NM_VLAN_FLAG_GVRP)); + + nm_utils_strbuf_init(s_buf, &s_buf_ptr, &s_buf_len); + + if (NM_FLAGS_HAS(vlan_flags, NM_VLAN_FLAG_LOOSE_BINDING)) + nm_utils_strbuf_append_str(&s_buf_ptr, &s_buf_len, "LOOSE_BINDING"); + if (!NM_FLAGS_HAS(vlan_flags, NM_VLAN_FLAG_REORDER_HEADERS)) + nm_utils_strbuf_append(&s_buf_ptr, &s_buf_len, "%sNO_REORDER_HDR", s_buf[0] ? "," : ""); + + svSetValueStr(ifcfg, "VLAN_FLAGS", s_buf); + + svSetValueBoolean(ifcfg, "MVRP", NM_FLAGS_HAS(vlan_flags, NM_VLAN_FLAG_MVRP)); + + tmp = vlan_priority_maplist_to_stringlist(s_vlan, NM_VLAN_INGRESS_MAP); + svSetValueStr(ifcfg, "VLAN_INGRESS_PRIORITY_MAP", tmp); + g_free(tmp); + + tmp = vlan_priority_maplist_to_stringlist(s_vlan, NM_VLAN_EGRESS_MAP); + svSetValueStr(ifcfg, "VLAN_EGRESS_PRIORITY_MAP", tmp); + g_free(tmp); + + *wired = write_wired_for_virtual(connection, ifcfg); + + return TRUE; +} + +static gboolean +write_bond_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) +{ + NMSettingBond *s_bond; + guint32 i, num_opts; + + s_bond = nm_connection_get_setting_bond(connection); + if (!s_bond) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Missing '%s' setting", + NM_SETTING_BOND_SETTING_NAME); + return FALSE; + } + + num_opts = nm_setting_bond_get_num_options(s_bond); + if (num_opts) { + nm_auto_free_gstring GString *str = NULL; + const char * name, *value; + + str = g_string_sized_new(64); + for (i = 0; i < num_opts; i++) { + if (str->len) + g_string_append_c(str, ' '); + nm_setting_bond_get_option(s_bond, i, &name, &value); + g_string_append_printf(str, "%s=%s", name, value); + } + + svSetValueStr(ifcfg, "BONDING_OPTS", str->str); + } + + svSetValueStr(ifcfg, "TYPE", TYPE_BOND); + svSetValueStr(ifcfg, "BONDING_MASTER", "yes"); + + *wired = write_wired_for_virtual(connection, ifcfg); + + return TRUE; +} + +static gboolean +write_team_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) +{ + NMSettingTeam *s_team; + const char * config; + + s_team = nm_connection_get_setting_team(connection); + if (!s_team) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Missing '%s' setting", + NM_SETTING_TEAM_SETTING_NAME); + return FALSE; + } + + config = nm_setting_team_get_config(s_team); + svSetValueStr(ifcfg, "TEAM_CONFIG", config); + + *wired = write_wired_for_virtual(connection, ifcfg); + + return TRUE; +} + +#define get_setting_default_checked_boolean(dflt, setting, prop) \ + ({ \ + const gboolean _dflt = (dflt); \ + \ + nm_assert(NM_G_PARAM_SPEC_GET_DEFAULT_BOOLEAN( \ + g_object_class_find_property(G_OBJECT_GET_CLASS(setting), prop)) \ + == _dflt); \ + _dflt; \ + }) + +#define get_setting_default_checked_uint(dflt, setting, prop) \ + ({ \ + const guint _dflt = (dflt); \ + \ + nm_assert(NM_G_PARAM_SPEC_GET_DEFAULT_UINT( \ + g_object_class_find_property(G_OBJECT_GET_CLASS(setting), prop)) \ + == _dflt); \ + _dflt; \ + }) + +#define get_setting_default_checked_uint64(dflt, setting, prop) \ + ({ \ + const guint64 _dflt = (dflt); \ + \ + nm_assert(NM_G_PARAM_SPEC_GET_DEFAULT_UINT64( \ + g_object_class_find_property(G_OBJECT_GET_CLASS(setting), prop)) \ + == _dflt); \ + _dflt; \ + }) + +static gboolean +write_bridge_vlans(NMSetting * setting, + const char *property_name, + shvarFile * ifcfg, + const char *key, + GError ** error) +{ + gs_unref_ptrarray GPtrArray *vlans = NULL; + NMBridgeVlan * vlan; + GString * string; + guint i; + + g_object_get(setting, property_name, &vlans, NULL); + + if (!vlans || !vlans->len) + return TRUE; + + string = g_string_new(""); + for (i = 0; i < vlans->len; i++) { + gs_free char *vlan_str = NULL; + + vlan = vlans->pdata[i]; + vlan_str = nm_bridge_vlan_to_str(vlan, error); + if (!vlan_str) + return FALSE; + if (string->len > 0) + g_string_append(string, ","); + nm_utils_escaped_tokens_escape_gstr_assert(vlan_str, ",", string); + } + + svSetValueStr(ifcfg, key, string->str); + g_string_free(string, TRUE); + return TRUE; +} + +static gboolean +write_bridge_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error) +{ + NMSettingBridge *s_bridge; + guint32 u32; + guint64 u64; + guint u; + gboolean b; + const char * s; + GString * opts; + + s_bridge = nm_connection_get_setting_bridge(connection); + if (!s_bridge) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Missing '%s' setting", + NM_SETTING_BRIDGE_SETTING_NAME); + return FALSE; + } + + svSetValueBoolean(ifcfg, "STP", FALSE); + + s = nm_setting_bridge_get_mac_address(s_bridge); + svSetValueStr(ifcfg, "BRIDGE_MACADDR", s); + + /* Bridge options */ + opts = g_string_sized_new(32); + + if (nm_setting_bridge_get_stp(s_bridge)) { + svSetValueStr(ifcfg, "STP", "yes"); + + u32 = nm_setting_bridge_get_forward_delay(s_bridge); + if (u32 + != get_setting_default_checked_uint(NM_BRIDGE_FORWARD_DELAY_DEF, + s_bridge, + NM_SETTING_BRIDGE_FORWARD_DELAY)) + svSetValueInt64(ifcfg, "DELAY", u32); + + g_string_append_printf(opts, "priority=%u", nm_setting_bridge_get_priority(s_bridge)); + + u32 = nm_setting_bridge_get_hello_time(s_bridge); + if (u32 + != get_setting_default_checked_uint(NM_BRIDGE_HELLO_TIME_DEF, + s_bridge, + NM_SETTING_BRIDGE_HELLO_TIME)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "hello_time=%u", u32); + } + + u32 = nm_setting_bridge_get_max_age(s_bridge); + if (u32 + != get_setting_default_checked_uint(NM_BRIDGE_MAX_AGE_DEF, + s_bridge, + NM_SETTING_BRIDGE_MAX_AGE)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "max_age=%u", u32); + } + } + + u = nm_setting_bridge_get_ageing_time(s_bridge); + if (u + != get_setting_default_checked_uint(NM_BRIDGE_AGEING_TIME_DEF, + s_bridge, + NM_SETTING_BRIDGE_AGEING_TIME)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "ageing_time=%u", u); + } + + s = nm_setting_bridge_get_group_address(s_bridge); + if (s) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "group_address=%s", s); + } + + u32 = nm_setting_bridge_get_group_forward_mask(s_bridge); + if (u32 + != get_setting_default_checked_uint(0, s_bridge, NM_SETTING_BRIDGE_GROUP_FORWARD_MASK)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "group_fwd_mask=%u", u32); + } + + u32 = nm_setting_bridge_get_multicast_hash_max(s_bridge); + if (u32 + != get_setting_default_checked_uint(NM_BRIDGE_MULTICAST_HASH_MAX_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_HASH_MAX)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_hash_max=%u", u32); + } + + u32 = nm_setting_bridge_get_multicast_last_member_count(s_bridge); + if (u32 + != get_setting_default_checked_uint(NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_COUNT)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_last_member_count=%u", u32); + } + + u64 = nm_setting_bridge_get_multicast_last_member_interval(s_bridge); + if (u64 + != get_setting_default_checked_uint64(NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_last_member_interval=%" G_GUINT64_FORMAT, u64); + } + + u64 = nm_setting_bridge_get_multicast_membership_interval(s_bridge); + if (u64 + != get_setting_default_checked_uint64(NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_membership_interval=%" G_GUINT64_FORMAT, u64); + } + + b = nm_setting_bridge_get_multicast_querier(s_bridge); + if (b + != get_setting_default_checked_boolean(NM_BRIDGE_MULTICAST_QUERIER_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_QUERIER)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_querier=%u", (guint) b); + } + + u64 = nm_setting_bridge_get_multicast_querier_interval(s_bridge); + if (u64 + != get_setting_default_checked_uint64(NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_QUERIER_INTERVAL)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_querier_interval=%" G_GUINT64_FORMAT, u64); + } + + u64 = nm_setting_bridge_get_multicast_query_interval(s_bridge); + if (u64 + != get_setting_default_checked_uint64(NM_BRIDGE_MULTICAST_QUERY_INTERVAL_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_QUERY_INTERVAL)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_query_interval=%" G_GUINT64_FORMAT, u64); + } + + u64 = nm_setting_bridge_get_multicast_query_response_interval(s_bridge); + if (u64 + != get_setting_default_checked_uint64( + NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_query_response_interval=%" G_GUINT64_FORMAT, u64); + } + + b = nm_setting_bridge_get_multicast_query_use_ifaddr(s_bridge); + if (b + != get_setting_default_checked_boolean(NM_BRIDGE_MULTICAST_QUERY_USE_IFADDR_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_QUERY_USE_IFADDR)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_query_use_ifaddr=%u", (guint) b); + } + + b = nm_setting_bridge_get_multicast_snooping(s_bridge); + if (b + != get_setting_default_checked_boolean(NM_BRIDGE_MULTICAST_SNOOPING_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_SNOOPING)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_snooping=%u", (guint32) b); + } + + u32 = nm_setting_bridge_get_multicast_startup_query_count(s_bridge); + if (u32 + != get_setting_default_checked_uint(NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_startup_query_count=%u", u32); + } + + u64 = nm_setting_bridge_get_multicast_startup_query_interval(s_bridge); + if (u64 + != get_setting_default_checked_uint64(NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_DEF, + s_bridge, + NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_startup_query_interval=%" G_GUINT64_FORMAT, u64); + } + + s = nm_setting_bridge_get_multicast_router(s_bridge); + if (s) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "multicast_router=%s", s); + } + + b = nm_setting_bridge_get_vlan_filtering(s_bridge); + if (b + != get_setting_default_checked_boolean(FALSE, s_bridge, NM_SETTING_BRIDGE_VLAN_FILTERING)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "vlan_filtering=%u", (guint32) b); + } + + u32 = nm_setting_bridge_get_vlan_default_pvid(s_bridge); + if (u32 + != get_setting_default_checked_uint(NM_BRIDGE_VLAN_DEFAULT_PVID_DEF, + s_bridge, + NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "default_pvid=%u", u32); + } + + s = nm_setting_bridge_get_vlan_protocol(s_bridge); + if (s) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "vlan_protocol=%s", s); + } + + b = nm_setting_bridge_get_vlan_stats_enabled(s_bridge); + if (b + != get_setting_default_checked_boolean(NM_BRIDGE_VLAN_STATS_ENABLED_DEF, + s_bridge, + NM_SETTING_BRIDGE_VLAN_STATS_ENABLED)) { + nm_gstring_add_space_delimiter(opts); + g_string_append_printf(opts, "vlan_stats_enabled=%u", (guint) b); + } + + if (opts->len) + svSetValueStr(ifcfg, "BRIDGING_OPTS", opts->str); + g_string_free(opts, TRUE); + + if (!write_bridge_vlans((NMSetting *) s_bridge, + NM_SETTING_BRIDGE_VLANS, + ifcfg, + "BRIDGE_VLANS", + error)) + return FALSE; + + svSetValueStr(ifcfg, "TYPE", TYPE_BRIDGE); + + *wired = write_wired_for_virtual(connection, ifcfg); + + return TRUE; +} + +static gboolean +write_bridge_port_setting(NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingBridgePort *s_port; + guint32 u32; + GString * string; + + s_port = nm_connection_get_setting_bridge_port(connection); + if (!s_port) + return TRUE; + + /* Bridge options */ + string = g_string_sized_new(32); + + u32 = nm_setting_bridge_port_get_priority(s_port); + if (u32 + != get_setting_default_checked_uint(NM_BRIDGE_PORT_PRIORITY_DEF, + s_port, + NM_SETTING_BRIDGE_PORT_PRIORITY)) + g_string_append_printf(string, "priority=%u", u32); + + u32 = nm_setting_bridge_port_get_path_cost(s_port); + if (u32 + != get_setting_default_checked_uint(NM_BRIDGE_PORT_PATH_COST_DEF, + s_port, + NM_SETTING_BRIDGE_PORT_PATH_COST)) { + if (string->len) + g_string_append_c(string, ' '); + g_string_append_printf(string, "path_cost=%u", u32); + } + + if (nm_setting_bridge_port_get_hairpin_mode(s_port)) { + if (string->len) + g_string_append_c(string, ' '); + g_string_append_printf(string, "hairpin_mode=1"); + } + + if (string->len) + svSetValueStr(ifcfg, "BRIDGING_OPTS", string->str); + g_string_free(string, TRUE); + + if (!write_bridge_vlans((NMSetting *) s_port, + NM_SETTING_BRIDGE_PORT_VLANS, + ifcfg, + "BRIDGE_PORT_VLANS", + error)) + return FALSE; + + return TRUE; +} + +static gboolean +write_team_port_setting(NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingTeamPort *s_port; + const char * config; + + s_port = nm_connection_get_setting_team_port(connection); + if (!s_port) + return TRUE; + + config = nm_setting_team_port_get_config(s_port); + svSetValueStr(ifcfg, "TEAM_PORT_CONFIG", config); + + return TRUE; +} + +static void +write_dcb_flags(shvarFile *ifcfg, const char *tag, NMSettingDcbFlags flags) +{ + char prop[NM_STRLEN("DCB_xxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyy")]; + + nm_sprintf_buf(prop, "DCB_%s_ENABLE", tag); + svSetValueStr(ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ENABLE) ? "yes" : NULL); + + nm_sprintf_buf(prop, "DCB_%s_ADVERTISE", tag); + svSetValueStr(ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ADVERTISE) ? "yes" : NULL); + + nm_sprintf_buf(prop, "DCB_%s_WILLING", tag); + svSetValueStr(ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_WILLING) ? "yes" : NULL); +} + +static void +write_dcb_app(shvarFile *ifcfg, const char *tag, NMSettingDcbFlags flags, int priority) +{ + char prop[NM_STRLEN("DCB_xxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyy")]; + + write_dcb_flags(ifcfg, tag, flags); + + if ((flags & NM_SETTING_DCB_FLAG_ENABLE) && (priority >= 0)) { + nm_sprintf_buf(prop, "DCB_%s_PRIORITY", tag); + svSetValueInt64(ifcfg, prop, priority); + } +} + +typedef gboolean (*DcbGetBoolFunc)(NMSettingDcb *, guint); + +static void +write_dcb_bool_array(shvarFile * ifcfg, + const char * key, + NMSettingDcb * s_dcb, + NMSettingDcbFlags flags, + DcbGetBoolFunc get_func) +{ + char str[9]; + guint i; + + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) + return; + + str[8] = 0; + for (i = 0; i < 8; i++) + str[i] = get_func(s_dcb, i) ? '1' : '0'; + svSetValueStr(ifcfg, key, str); +} + +typedef guint (*DcbGetUintFunc)(NMSettingDcb *, guint); + +static void +write_dcb_uint_array(shvarFile * ifcfg, + const char * key, + NMSettingDcb * s_dcb, + NMSettingDcbFlags flags, + DcbGetUintFunc get_func) +{ + char str[9]; + guint i, num; + + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) + return; + + str[8] = 0; + for (i = 0; i < 8; i++) { + num = get_func(s_dcb, i); + if (num < 10) + str[i] = '0' + num; + else if (num == 15) + str[i] = 'f'; + else + g_assert_not_reached(); + } + svSetValueStr(ifcfg, key, str); +} + +static void +write_dcb_percent_array(shvarFile * ifcfg, + const char * key, + NMSettingDcb * s_dcb, + NMSettingDcbFlags flags, + DcbGetUintFunc get_func) +{ + GString *str; + guint i; + + if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) + return; + + str = g_string_sized_new(30); + for (i = 0; i < 8; i++) { + if (str->len) + g_string_append_c(str, ','); + g_string_append_printf(str, "%d", get_func(s_dcb, i)); + } + svSetValueStr(ifcfg, key, str->str); + g_string_free(str, TRUE); +} + +static gboolean +write_dcb_setting(NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingDcb * s_dcb; + NMSettingDcbFlags flags; + + s_dcb = nm_connection_get_setting_dcb(connection); + if (!s_dcb) + return TRUE; + + svSetValueStr(ifcfg, "DCB", "yes"); + + write_dcb_app(ifcfg, + "APP_FCOE", + nm_setting_dcb_get_app_fcoe_flags(s_dcb), + nm_setting_dcb_get_app_fcoe_priority(s_dcb)); + if (nm_setting_dcb_get_app_fcoe_flags(s_dcb) & NM_SETTING_DCB_FLAG_ENABLE) + svSetValueStr(ifcfg, KEY_DCB_APP_FCOE_MODE, nm_setting_dcb_get_app_fcoe_mode(s_dcb)); + + write_dcb_app(ifcfg, + "APP_ISCSI", + nm_setting_dcb_get_app_iscsi_flags(s_dcb), + nm_setting_dcb_get_app_iscsi_priority(s_dcb)); + write_dcb_app(ifcfg, + "APP_FIP", + nm_setting_dcb_get_app_fip_flags(s_dcb), + nm_setting_dcb_get_app_fip_priority(s_dcb)); + + write_dcb_flags(ifcfg, "PFC", nm_setting_dcb_get_priority_flow_control_flags(s_dcb)); + write_dcb_bool_array(ifcfg, + KEY_DCB_PFC_UP, + s_dcb, + nm_setting_dcb_get_priority_flow_control_flags(s_dcb), + nm_setting_dcb_get_priority_flow_control); + + flags = nm_setting_dcb_get_priority_group_flags(s_dcb); + write_dcb_flags(ifcfg, "PG", flags); + write_dcb_uint_array(ifcfg, KEY_DCB_PG_ID, s_dcb, flags, nm_setting_dcb_get_priority_group_id); + write_dcb_percent_array(ifcfg, + KEY_DCB_PG_PCT, + s_dcb, + flags, + nm_setting_dcb_get_priority_group_bandwidth); + write_dcb_percent_array(ifcfg, + KEY_DCB_PG_UPPCT, + s_dcb, + flags, + nm_setting_dcb_get_priority_bandwidth); + write_dcb_bool_array(ifcfg, + KEY_DCB_PG_STRICT, + s_dcb, + flags, + nm_setting_dcb_get_priority_strict_bandwidth); + write_dcb_uint_array(ifcfg, + KEY_DCB_PG_UP2TC, + s_dcb, + flags, + nm_setting_dcb_get_priority_traffic_class); + + return TRUE; +} + +static void +write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg) +{ + guint32 n, i; + nm_auto_free_gstring GString *str = NULL; + const char * master, *master_iface = NULL, *type; + int vint; + gint32 vint32; + NMSettingConnectionMdns mdns; + NMSettingConnectionLlmnr llmnr; + guint32 vuint32; + const char * tmp, *mud_url; + + svSetValueStr(ifcfg, "NAME", nm_setting_connection_get_id(s_con)); + svSetValueStr(ifcfg, "UUID", nm_setting_connection_get_uuid(s_con)); + svSetValueStr(ifcfg, "STABLE_ID", nm_setting_connection_get_stable_id(s_con)); + svSetValueStr(ifcfg, "DEVICE", nm_setting_connection_get_interface_name(s_con)); + svSetValueBoolean(ifcfg, "ONBOOT", nm_setting_connection_get_autoconnect(s_con)); + + vint = nm_setting_connection_get_autoconnect_priority(s_con); + svSetValueInt64_cond(ifcfg, + "AUTOCONNECT_PRIORITY", + vint != NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT, + vint); + + vint = nm_setting_connection_get_autoconnect_retries(s_con); + svSetValueInt64_cond(ifcfg, "AUTOCONNECT_RETRIES", vint != -1, vint); + + vint = nm_setting_connection_get_multi_connect(s_con); + svSetValueInt64_cond(ifcfg, "MULTI_CONNECT", vint != NM_CONNECTION_MULTI_CONNECT_DEFAULT, vint); + + /* Only save the value for master connections */ + type = nm_setting_connection_get_connection_type(s_con); + if (_nm_connection_type_is_master(type)) { + NMSettingConnectionAutoconnectSlaves autoconnect_slaves; + autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves(s_con); + svSetValueStr(ifcfg, + "AUTOCONNECT_SLAVES", + autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES ? "yes" + : autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO ? "no" + : NULL); + } + switch (nm_setting_connection_get_lldp(s_con)) { + case NM_SETTING_CONNECTION_LLDP_ENABLE_RX: + tmp = "rx"; + break; + case NM_SETTING_CONNECTION_LLDP_DISABLE: + tmp = "no"; + break; + default: + tmp = NULL; + } + svSetValueStr(ifcfg, "LLDP", tmp); + + /* Permissions */ + n = nm_setting_connection_get_num_permissions(s_con); + if (n > 0) { + nm_gstring_prepare(&str); + for (i = 0; i < n; i++) { + const char *ptype = NULL; + const char *puser = NULL; + + if (!nm_setting_connection_get_permission(s_con, i, &ptype, &puser, NULL)) + continue; + if (!nm_streq(ptype, NM_SETTINGS_CONNECTION_PERMISSION_USER)) + continue; + + /* Items separated by space for consistency with eg + * IPV6ADDR_SECONDARIES and DOMAIN. + */ + if (str->len) + g_string_append_c(str, ' '); + + g_string_append(str, puser); + } + svSetValueStr(ifcfg, "USERS", str->str); + } + + svSetValueStr(ifcfg, "ZONE", nm_setting_connection_get_zone(s_con)); + + svSetValueStr(ifcfg, "MASTER_UUID", NULL); + svSetValueStr(ifcfg, "MASTER", NULL); + svSetValueStr(ifcfg, "SLAVE", NULL); + svSetValueStr(ifcfg, "BRIDGE_UUID", NULL); + svSetValueStr(ifcfg, "BRIDGE", NULL); + svSetValueStr(ifcfg, "TEAM_MASTER_UUID", NULL); + svSetValueStr(ifcfg, "TEAM_MASTER", NULL); + + mud_url = nm_setting_connection_get_mud_url(s_con); + svSetValue(ifcfg, "MUD_URL", mud_url); + + master = nm_setting_connection_get_master(s_con); + if (master) { + /* The reader prefers the *_UUID variants, however we still try to resolve + * it into an interface name, so that legacy tooling is not confused. */ + if (!nm_utils_get_testing()) { + /* This is conditional for easier testing. */ + master_iface = nm_manager_iface_for_uuid(NM_MANAGER_GET, master); + } + if (!master_iface) { + master_iface = master; + master = NULL; + } + + if (nm_setting_connection_is_slave_type(s_con, NM_SETTING_BOND_SETTING_NAME)) { + svSetValueStr(ifcfg, "MASTER_UUID", master); + svSetValueStr(ifcfg, "MASTER", master_iface); + svSetValueStr(ifcfg, "SLAVE", "yes"); + } else if (nm_setting_connection_is_slave_type(s_con, NM_SETTING_BRIDGE_SETTING_NAME)) { + svSetValueStr(ifcfg, "BRIDGE_UUID", master); + svSetValueStr(ifcfg, "BRIDGE", master_iface); + } else if (nm_setting_connection_is_slave_type(s_con, NM_SETTING_TEAM_SETTING_NAME)) { + svSetValueStr(ifcfg, "TEAM_MASTER_UUID", master); + svSetValueStr(ifcfg, "TEAM_MASTER", master_iface); + if (NM_IN_STRSET(type, NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_VLAN_SETTING_NAME)) + svUnsetValue(ifcfg, "TYPE"); + } else if (nm_setting_connection_is_slave_type(s_con, NM_SETTING_OVS_PORT_SETTING_NAME)) { + svSetValueStr(ifcfg, "OVS_PORT_UUID", master); + svSetValueStr(ifcfg, "OVS_PORT", master_iface); + } else if (nm_setting_connection_is_slave_type(s_con, NM_SETTING_VRF_SETTING_NAME)) { + svSetValueStr(ifcfg, "VRF_UUID", master); + svSetValueStr(ifcfg, "VRF", master_iface); + } else { + _LOGW("don't know how to set master for a %s slave", + nm_setting_connection_get_slave_type(s_con)); + } + } + + if (nm_streq0(type, NM_SETTING_TEAM_SETTING_NAME)) + svSetValueStr(ifcfg, "DEVICETYPE", TYPE_TEAM); + else if (master_iface + && nm_setting_connection_is_slave_type(s_con, NM_SETTING_TEAM_SETTING_NAME)) + svSetValueStr(ifcfg, "DEVICETYPE", TYPE_TEAM_PORT); + + /* secondary connection UUIDs */ + n = nm_setting_connection_get_num_secondaries(s_con); + if (n > 0) { + nm_gstring_prepare(&str); + for (i = 0; i < n; i++) { + const char *uuid; + + /* Items separated by space for consistency with eg + * IPV6ADDR_SECONDARIES and DOMAIN. + */ + if (!(uuid = nm_setting_connection_get_secondary(s_con, i))) + continue; + + if (str->len) + g_string_append_c(str, ' '); + g_string_append(str, uuid); + } + svSetValueStr(ifcfg, "SECONDARY_UUIDS", str->str); + } + + vuint32 = nm_setting_connection_get_gateway_ping_timeout(s_con); + svSetValueInt64_cond(ifcfg, "GATEWAY_PING_TIMEOUT", vuint32 != 0, vuint32); + + switch (nm_setting_connection_get_metered(s_con)) { + case NM_METERED_YES: + svSetValueStr(ifcfg, "CONNECTION_METERED", "yes"); + break; + case NM_METERED_NO: + svSetValueStr(ifcfg, "CONNECTION_METERED", "no"); + break; + case NM_METERED_UNKNOWN: + case NM_METERED_GUESS_YES: + case NM_METERED_GUESS_NO: + break; + } + + vint = nm_setting_connection_get_auth_retries(s_con); + svSetValueInt64_cond(ifcfg, "AUTH_RETRIES", vint >= 0, vint); + + vint32 = nm_setting_connection_get_wait_device_timeout(s_con); + if (vint32 == -1) { + /* pass */ + } else if ((vint32 % 1000) == 0) + svSetValueInt64(ifcfg, "DEVTIMEOUT", vint32 / 1000); + else { + char b[100]; + + svSetValueStr(ifcfg, "DEVTIMEOUT", nm_sprintf_buf(b, "%.3f", ((double) vint) / 1000.0)); + } + + mdns = nm_setting_connection_get_mdns(s_con); + if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) { + svSetValueEnum(ifcfg, "MDNS", nm_setting_connection_mdns_get_type(), mdns); + } + + llmnr = nm_setting_connection_get_llmnr(s_con); + if (llmnr != NM_SETTING_CONNECTION_LLMNR_DEFAULT) { + svSetValueEnum(ifcfg, "LLMNR", nm_setting_connection_llmnr_get_type(), llmnr); + } +} + +static char * +get_route_attributes_string(NMIPRoute *route, int family) +{ + gs_free const char **names = NULL; + GVariant * attr, *lock; + GString * str; + guint i, len; + + names = _nm_ip_route_get_attribute_names(route, TRUE, &len); + if (!len) + return NULL; + + str = g_string_new(""); + + attr = nm_ip_route_get_attribute(route, NM_IP_ROUTE_ATTRIBUTE_TYPE); + if (attr + && nm_ip_route_attribute_validate(NM_IP_ROUTE_ATTRIBUTE_TYPE, attr, family, NULL, NULL)) + g_string_append_printf(str, "%s ", g_variant_get_string(attr, NULL)); + + for (i = 0; i < len; i++) { + if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_TYPE)) + continue; + + attr = nm_ip_route_get_attribute(route, names[i]); + + if (!nm_ip_route_attribute_validate(names[i], attr, family, NULL, NULL)) + continue; + + if (NM_IN_STRSET(names[i], + NM_IP_ROUTE_ATTRIBUTE_WINDOW, + NM_IP_ROUTE_ATTRIBUTE_CWND, + NM_IP_ROUTE_ATTRIBUTE_INITCWND, + NM_IP_ROUTE_ATTRIBUTE_INITRWND, + NM_IP_ROUTE_ATTRIBUTE_MTU)) { + char lock_name[256]; + + nm_sprintf_buf(lock_name, "lock-%s", names[i]); + lock = nm_ip_route_get_attribute(route, lock_name); + + g_string_append_printf(str, + "%s %s%u", + names[i], + (lock && g_variant_get_boolean(lock)) ? "lock " : "", + g_variant_get_uint32(attr)); + } else if (strstr(names[i], "lock-")) { + const char *n = &(names[i])[NM_STRLEN("lock-")]; + + attr = nm_ip_route_get_attribute(route, n); + if (!attr) { + g_string_append_printf(str, "%s lock 0", n); + } else { + /* we also have a corresponding attribute with the numeric value. The + * lock setting is handled above. */ + } + } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_SCOPE)) { + g_string_append_printf(str, "%s %u", names[i], (unsigned) g_variant_get_byte(attr)); + } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_TOS)) { + g_string_append_printf(str, "%s 0x%02x", names[i], (unsigned) g_variant_get_byte(attr)); + } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_TABLE)) { + g_string_append_printf(str, "%s %u", names[i], (unsigned) g_variant_get_uint32(attr)); + } else if (nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_ONLINK)) { + if (g_variant_get_boolean(attr)) + g_string_append(str, "onlink"); + } else if (NM_IN_STRSET(names[i], NM_IP_ROUTE_ATTRIBUTE_SRC, NM_IP_ROUTE_ATTRIBUTE_FROM)) { + char *arg = nm_streq(names[i], NM_IP_ROUTE_ATTRIBUTE_SRC) ? "src" : "from"; + + g_string_append_printf(str, "%s %s", arg, g_variant_get_string(attr, NULL)); + } else { + g_warn_if_reached(); + continue; + } + if (names[i + 1]) + g_string_append_c(str, ' '); + } + + return g_string_free(str, FALSE); +} + +static shvarFile * +write_route_file_svformat(const char *filename, NMSettingIPConfig *s_ip4) +{ + shvarFile *routefile; + guint i, num; + + routefile = utils_get_route_ifcfg(filename, TRUE); + + num = nm_setting_ip_config_get_num_routes(s_ip4); + for (i = 0; i < num; i++) { + char buf[INET_ADDRSTRLEN]; + NMIPRoute * route; + guint32 netmask; + gint64 metric; + char addr_key[64]; + char gw_key[64]; + char netmask_key[64]; + char metric_key[64]; + char options_key[64]; + gs_free char *options = NULL; + + numbered_tag(addr_key, "ADDRESS", i); + numbered_tag(netmask_key, "NETMASK", i); + numbered_tag(gw_key, "GATEWAY", i); + + route = nm_setting_ip_config_get_route(s_ip4, i); + + svSetValueStr(routefile, addr_key, nm_ip_route_get_dest(route)); + + netmask = _nm_utils_ip4_prefix_to_netmask(nm_ip_route_get_prefix(route)); + svSetValueStr(routefile, netmask_key, _nm_utils_inet4_ntop(netmask, buf)); + + svSetValueStr(routefile, gw_key, nm_ip_route_get_next_hop(route)); + + metric = nm_ip_route_get_metric(route); + if (metric != -1) { + svSetValueInt64(routefile, numbered_tag(metric_key, "METRIC", i), metric); + } + + options = get_route_attributes_string(route, AF_INET); + if (options) { + svSetValueStr(routefile, numbered_tag(options_key, "OPTIONS", i), options); + } + } + + return routefile; +} + +static GString * +write_route_file(NMSettingIPConfig *s_ip) +{ + GString * contents; + NMIPRoute *route; + guint32 i, num; + int addr_family; + + addr_family = nm_setting_ip_config_get_addr_family(s_ip); + + num = nm_setting_ip_config_get_num_routes(s_ip); + if (num == 0) + return NULL; + + contents = g_string_new(""); + + for (i = 0; i < num; i++) { + gs_free char *options = NULL; + const char * next_hop; + gint64 metric; + + route = nm_setting_ip_config_get_route(s_ip, i); + next_hop = nm_ip_route_get_next_hop(route); + metric = nm_ip_route_get_metric(route); + options = get_route_attributes_string(route, addr_family); + + g_string_append_printf(contents, + "%s/%u", + nm_ip_route_get_dest(route), + nm_ip_route_get_prefix(route)); + if (next_hop) + g_string_append_printf(contents, " via %s", next_hop); + if (metric >= 0) + g_string_append_printf(contents, " metric %u", (guint) metric); + if (options) { + g_string_append_c(contents, ' '); + g_string_append(contents, options); + } + + g_string_append_c(contents, '\n'); + } + + return contents; +} + +static gboolean +write_proxy_setting(NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingProxy * s_proxy; + NMSettingProxyMethod method; + const char * pac_url, *pac_script; + + s_proxy = nm_connection_get_setting_proxy(connection); + if (!s_proxy) + return TRUE; + + method = nm_setting_proxy_get_method(s_proxy); + switch (method) { + case NM_SETTING_PROXY_METHOD_AUTO: + svSetValueStr(ifcfg, "PROXY_METHOD", "auto"); + + pac_url = nm_setting_proxy_get_pac_url(s_proxy); + if (pac_url) + svSetValueStr(ifcfg, "PAC_URL", pac_url); + + pac_script = nm_setting_proxy_get_pac_script(s_proxy); + if (pac_script) + svSetValueStr(ifcfg, "PAC_SCRIPT", pac_script); + + break; + case NM_SETTING_PROXY_METHOD_NONE: + svSetValueStr(ifcfg, "PROXY_METHOD", "none"); + break; + } + + svSetValueBoolean(ifcfg, "BROWSER_ONLY", nm_setting_proxy_get_browser_only(s_proxy)); + + return TRUE; +} + +static gboolean +write_user_setting(NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingUser * s_user; + guint i, len; + const char *const *keys; + + s_user = NM_SETTING_USER(nm_connection_get_setting(connection, NM_TYPE_SETTING_USER)); + + if (!s_user) + return TRUE; + + keys = nm_setting_user_get_keys(s_user, &len); + if (len) { + nm_auto_free_gstring GString *str = g_string_sized_new(100); + + for (i = 0; i < len; i++) { + const char *key = keys[i]; + + g_string_set_size(str, 0); + g_string_append(str, "NM_USER_"); + nms_ifcfg_rh_utils_user_key_encode(key, str); + svSetValue(ifcfg, str->str, nm_setting_user_get_data(s_user, key)); + } + } + + return TRUE; +} + +static void +write_sriov_setting(NMConnection *connection, shvarFile *ifcfg) +{ + NMSettingSriov *s_sriov; + guint i, num = 0; + NMTernary b; + NMSriovVF * vf; + char key[32]; + char * str; + + s_sriov = NM_SETTING_SRIOV(nm_connection_get_setting(connection, NM_TYPE_SETTING_SRIOV)); + if (!s_sriov) { + return; + } + + svSetValueInt64(ifcfg, "SRIOV_TOTAL_VFS", nm_setting_sriov_get_total_vfs(s_sriov)); + + b = nm_setting_sriov_get_autoprobe_drivers(s_sriov); + if (b != NM_TERNARY_DEFAULT) + svSetValueInt64(ifcfg, "SRIOV_AUTOPROBE_DRIVERS", b); + + num = nm_setting_sriov_get_num_vfs(s_sriov); + for (i = 0; i < num; i++) { + vf = nm_setting_sriov_get_vf(s_sriov, i); + nm_sprintf_buf(key, "SRIOV_VF%u", nm_sriov_vf_get_index(vf)); + str = nm_utils_sriov_vf_to_str(vf, TRUE, NULL); + svSetValueStr(ifcfg, key, str); + g_free(str); + } +} + +static gboolean +write_tc_setting(NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingTCConfig *s_tc; + guint i, num, n; + char tag[64]; + + s_tc = nm_connection_get_setting_tc_config(connection); + if (!s_tc) + return TRUE; + + num = nm_setting_tc_config_get_num_qdiscs(s_tc); + for (n = 1, i = 0; i < num; i++) { + NMTCQdisc * qdisc; + gs_free char *str = NULL; + + qdisc = nm_setting_tc_config_get_qdisc(s_tc, i); + str = nm_utils_tc_qdisc_to_str(qdisc, error); + if (!str) + return FALSE; + + svSetValueStr(ifcfg, numbered_tag(tag, "QDISC", n), str); + n++; + } + + num = nm_setting_tc_config_get_num_tfilters(s_tc); + for (n = 1, i = 0; i < num; i++) { + NMTCTfilter * tfilter; + gs_free char *str = NULL; + + tfilter = nm_setting_tc_config_get_tfilter(s_tc, i); + str = nm_utils_tc_tfilter_to_str(tfilter, error); + if (!str) + return FALSE; + + svSetValueStr(ifcfg, numbered_tag(tag, "FILTER", n), str); + n++; + } + + return TRUE; +} + +static void +write_match_setting(NMConnection *connection, shvarFile *ifcfg) +{ + nm_auto_free_gstring GString *str = NULL; + NMSettingMatch * s_match; + guint i, num; + const char * name; + + s_match = (NMSettingMatch *) nm_connection_get_setting(connection, NM_TYPE_SETTING_MATCH); + if (!s_match) + return; + + num = nm_setting_match_get_num_drivers(s_match); + if (num > 0) { + nm_gstring_prepare(&str); + for (i = 0; i < num; i++) { + name = nm_setting_match_get_driver(s_match, i); + nm_gstring_add_space_delimiter(str); + nm_utils_escaped_tokens_escape_gstr(name, NM_ASCII_SPACES, str); + } + svSetValueStr(ifcfg, "MATCH_DRIVER", str->str); + } + + num = nm_setting_match_get_num_interface_names(s_match); + if (num > 0) { + nm_gstring_prepare(&str); + for (i = 0; i < num; i++) { + name = nm_setting_match_get_interface_name(s_match, i); + nm_gstring_add_space_delimiter(str); + nm_utils_escaped_tokens_escape_gstr(name, NM_ASCII_SPACES, str); + } + svSetValueStr(ifcfg, "MATCH_INTERFACE_NAME", str->str); + } + + num = nm_setting_match_get_num_kernel_command_lines(s_match); + if (num > 0) { + nm_gstring_prepare(&str); + for (i = 0; i < num; i++) { + name = nm_setting_match_get_kernel_command_line(s_match, i); + nm_gstring_add_space_delimiter(str); + nm_utils_escaped_tokens_escape_gstr(name, NM_ASCII_SPACES, str); + } + svSetValueStr(ifcfg, "MATCH_KERNEL_COMMAND_LINE", str->str); + } + + num = nm_setting_match_get_num_paths(s_match); + if (num > 0) { + nm_gstring_prepare(&str); + for (i = 0; i < num; i++) { + name = nm_setting_match_get_path(s_match, i); + nm_gstring_add_space_delimiter(str); + nm_utils_escaped_tokens_escape_gstr(name, NM_ASCII_SPACES, str); + } + svSetValueStr(ifcfg, "MATCH_PATH", str->str); + } +} + +static void +write_res_options(shvarFile *ifcfg, NMSettingIPConfig *s_ip, const char *var) +{ + nm_auto_free_gstring GString *value = NULL; + guint i, num_options; + + if (!nm_setting_ip_config_has_dns_options(s_ip)) + return; + + value = g_string_new(NULL); + num_options = nm_setting_ip_config_get_num_dns_options(s_ip); + for (i = 0; i < num_options; i++) { + if (i > 0) + g_string_append_c(value, ' '); + g_string_append(value, nm_setting_ip_config_get_dns_option(s_ip, i)); + } + + svSetValue(ifcfg, var, value->str); +} + +static void +write_dns_setting(shvarFile *ifcfg, NMConnection *connection, int addr_family) +{ + NMSettingIPConfig *s_ip; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6 = NULL; + guint num4; + guint num6 = 0; + guint num; + guint i; + guint offset; + + if (addr_family == AF_INET6) { + s_ip6 = nm_connection_get_setting_ip6_config(connection); + num6 = s_ip6 ? nm_setting_ip_config_get_num_dns(s_ip6) : 0u; + } + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + num4 = s_ip4 ? nm_setting_ip_config_get_num_dns(s_ip4) : 0u; + + if (addr_family == AF_INET6) { + num = num6; + offset = num4; + s_ip = s_ip6; + } else { + num = num4; + offset = 0; + s_ip = s_ip4; + } + + for (i = 0; i < num; i++) { + char tag[64]; + + svSetValueStr(ifcfg, + numbered_tag(tag, "DNS", offset + i + 1u), + nm_setting_ip_config_get_dns(s_ip, i)); + } +} + +static gboolean +write_ip4_setting(NMConnection *connection, + shvarFile * ifcfg, + shvarFile ** out_route_content_svformat, + GString ** out_route_content, + GError ** error) +{ + NMSettingIPConfig * s_ip4; + const char * value; + char * tmp; + char tag[64]; + int j; + guint i, num, n; + gint64 route_metric; + NMIPRouteTableSyncMode route_table; + int priority; + int timeout; + nm_auto_free_gstring GString *str = NULL; + const char * method = NULL; + gboolean has_netmask; + NMDhcpHostnameFlags flags; + const char *const * strv; + + NM_SET_OUT(out_route_content_svformat, NULL); + NM_SET_OUT(out_route_content, NULL); + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + if (!s_ip4) + return TRUE; + + method = nm_setting_ip_config_get_method(s_ip4); + + /* Missing IP4 setting is assumed to be DHCP */ + if (!method) + method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; + + if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + return TRUE; + + num = nm_setting_ip_config_get_num_addresses(s_ip4); + + if (!strcmp(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) + svSetValueStr(ifcfg, "BOOTPROTO", "dhcp"); + else if (!strcmp(method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + /* Preserve the archaic form of "static" if there actually + * is static configuration. */ + if (g_strcmp0(svGetValue(ifcfg, "BOOTPROTO", &tmp), "static") || !num) + svSetValueStr(ifcfg, "BOOTPROTO", "none"); + g_free(tmp); + } else if (!strcmp(method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) + svSetValueStr(ifcfg, "BOOTPROTO", "autoip"); + else if (!strcmp(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + svSetValueStr(ifcfg, "BOOTPROTO", "shared"); + + has_netmask = !!svFindFirstNumberedKey(ifcfg, "NETMASK"); + + /* Write out IPADDR<n>, PREFIX<n>, GATEWAY<n> for current IP addresses + * without labels. Unset obsolete NETMASK<n>. + */ + for (i = n = 0; i < num; i++) { + NMIPAddress *addr; + guint prefix; + + addr = nm_setting_ip_config_get_address(s_ip4, i); + + if (i > 0) { + GVariant *label; + + label = nm_ip_address_get_attribute(addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL); + if (label) + continue; + } + + if (n == 0) { + /* Instead of index 0 use un-numbered variables. + * It's needed for compatibility with ifup that only recognizes 'GATEAWAY' + * See https://bugzilla.redhat.com/show_bug.cgi?id=771673 + * and https://bugzilla.redhat.com/show_bug.cgi?id=1105770 + */ + j = -1; + } else + j = n; + + svSetValueStr(ifcfg, numbered_tag(tag, "IPADDR", j), nm_ip_address_get_address(addr)); + + prefix = nm_ip_address_get_prefix(addr); + svSetValueInt64(ifcfg, numbered_tag(tag, "PREFIX", j), prefix); + + /* If the legacy "NETMASK" is present, keep it. */ + numbered_tag(tag, "NETMASK", j); + if (has_netmask) { + char buf[INET_ADDRSTRLEN]; + + svSetValueStr(ifcfg, + tag, + _nm_utils_inet4_ntop(_nm_utils_ip4_prefix_to_netmask(prefix), buf)); + } + + n++; + } + + svSetValueStr(ifcfg, "GATEWAY", nm_setting_ip_config_get_gateway(s_ip4)); + + write_dns_setting(ifcfg, connection, AF_INET); + + num = nm_setting_ip_config_get_num_dns_searches(s_ip4); + if (num > 0) { + nm_gstring_prepare(&str); + for (i = 0; i < num; i++) { + nm_gstring_add_space_delimiter(str); + g_string_append(str, nm_setting_ip_config_get_dns_search(s_ip4, i)); + } + svSetValueStr(ifcfg, "DOMAIN", str->str); + } + + /* DEFROUTE; remember that it has the opposite meaning from never-default */ + svSetValueBoolean(ifcfg, "DEFROUTE", !nm_setting_ip_config_get_never_default(s_ip4)); + + /* Missing PEERDNS means TRUE, so write it only when is FALSE */ + svSetValueStr(ifcfg, "PEERDNS", nm_setting_ip_config_get_ignore_auto_dns(s_ip4) ? "no" : NULL); + /* Missing PEERROUTES means TRUE, so write it only when is FALSE */ + svSetValueStr(ifcfg, + "PEERROUTES", + nm_setting_ip_config_get_ignore_auto_routes(s_ip4) ? "no" : NULL); + + value = nm_setting_ip_config_get_dhcp_hostname(s_ip4); + svSetValueStr(ifcfg, "DHCP_HOSTNAME", value); + + value = nm_setting_ip4_config_get_dhcp_fqdn(NM_SETTING_IP4_CONFIG(s_ip4)); + svSetValueStr(ifcfg, "DHCP_FQDN", value); + + flags = nm_setting_ip_config_get_dhcp_hostname_flags(s_ip4); + svSetValueInt64_cond(ifcfg, "DHCP_HOSTNAME_FLAGS", flags != NM_DHCP_HOSTNAME_FLAG_NONE, flags); + + /* Missing DHCP_SEND_HOSTNAME means TRUE, and we prefer not write it explicitly + * in that case, because it is NM-specific variable + */ + svSetValueStr(ifcfg, + "DHCP_SEND_HOSTNAME", + nm_setting_ip_config_get_dhcp_send_hostname(s_ip4) ? NULL : "no"); + + value = nm_setting_ip4_config_get_dhcp_client_id(NM_SETTING_IP4_CONFIG(s_ip4)); + svSetValueStr(ifcfg, "DHCP_CLIENT_ID", value); + + svSetValue( + ifcfg, + "DHCP_VENDOR_CLASS_IDENTIFIER", + nm_setting_ip4_config_get_dhcp_vendor_class_identifier(NM_SETTING_IP4_CONFIG(s_ip4))); + + value = nm_setting_ip_config_get_dhcp_iaid(s_ip4); + svSetValueStr(ifcfg, "DHCP_IAID", value); + + timeout = nm_setting_ip_config_get_dhcp_timeout(s_ip4); + svSetValueInt64_cond(ifcfg, "IPV4_DHCP_TIMEOUT", timeout != 0, timeout); + + svSetValueBoolean(ifcfg, "IPV4_FAILURE_FATAL", !nm_setting_ip_config_get_may_fail(s_ip4)); + + route_metric = nm_setting_ip_config_get_route_metric(s_ip4); + svSetValueInt64_cond(ifcfg, "IPV4_ROUTE_METRIC", route_metric != -1, route_metric); + + route_table = nm_setting_ip_config_get_route_table(s_ip4); + svSetValueInt64_cond(ifcfg, "IPV4_ROUTE_TABLE", route_table != 0, route_table); + + NM_SET_OUT(out_route_content_svformat, write_route_file_svformat(svFileGetName(ifcfg), s_ip4)); + NM_SET_OUT(out_route_content, write_route_file(s_ip4)); + + timeout = nm_setting_ip_config_get_dad_timeout(s_ip4); + if (timeout < 0) { + /* pass */ + } else if (timeout == 0) { + svSetValueStr(ifcfg, "ACD_TIMEOUT", "0"); + svSetValueStr(ifcfg, "ARPING_WAIT", "0"); + } else { + svSetValueInt64(ifcfg, "ACD_TIMEOUT", timeout); + /* Round the value up to next integer for initscripts */ + svSetValueInt64(ifcfg, "ARPING_WAIT", (timeout - 1) / 1000 + 1); + } + + priority = nm_setting_ip_config_get_dns_priority(s_ip4); + if (priority) + svSetValueInt64(ifcfg, "IPV4_DNS_PRIORITY", priority); + + write_res_options(ifcfg, s_ip4, "RES_OPTIONS"); + + strv = nm_setting_ip_config_get_dhcp_reject_servers(s_ip4, &num); + if (num > 0) { + nm_gstring_prepare(&str); + for (i = 0; i < num; i++) { + nm_gstring_add_space_delimiter(str); + nm_utils_escaped_tokens_escape_gstr(strv[i], NM_ASCII_SPACES, str); + } + svSetValueStr(ifcfg, "DHCP_REJECT_SERVERS", str->str); + } + + return TRUE; +} + +static void +write_ip4_aliases(NMConnection *connection, const char *base_ifcfg_path) +{ + NMSettingIPConfig *s_ip4; + gs_free char * base_ifcfg_dir = NULL, *base_ifcfg_name = NULL; + const char * base_name; + int i, num, base_ifcfg_name_len, base_name_len; + GDir * dir; + + 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); + if (!g_str_has_prefix(base_ifcfg_name, IFCFG_TAG)) + g_return_if_reached(); + 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); + } + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + if (!s_ip4) { + /* slave-type: no alias files */ + return; + } + + num = nm_setting_ip_config_get_num_addresses(s_ip4); + for (i = 0; i < num; i++) { + GVariant * label_var; + const char * label, *p; + char * path; + NMIPAddress *addr; + shvarFile * ifcfg; + + addr = nm_setting_ip_config_get_address(s_ip4, i); + + label_var = nm_ip_address_get_attribute(addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL); + if (!label_var) + continue; + label = g_variant_get_string(label_var, NULL); + 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); + + svSetValueStr(ifcfg, "DEVICE", label); + + addr = nm_setting_ip_config_get_address(s_ip4, i); + svSetValueStr(ifcfg, "IPADDR", nm_ip_address_get_address(addr)); + + svSetValueInt64(ifcfg, "PREFIX", nm_ip_address_get_prefix(addr)); + + svWriteFileWithoutDirtyWellknown(ifcfg, 0644, NULL); + svCloseFile(ifcfg); + } +} + +static gboolean +write_ip6_setting(NMConnection *connection, + shvarFile * ifcfg, + GString ** out_route6_content, + GError ** error) +{ + NMSettingIPConfig * s_ip6; + const char * value; + guint i, num; + int priority; + NMIPAddress * addr; + gint64 route_metric; + NMIPRouteTableSyncMode route_table; + GString * ip_str1, *ip_str2, *ip_ptr; + NMSettingIP6ConfigAddrGenMode addr_gen_mode; + NMDhcpHostnameFlags flags; + const char * hostname; + int timeout; + + NM_SET_OUT(out_route6_content, NULL); + + s_ip6 = nm_connection_get_setting_ip6_config(connection); + if (!s_ip6) + return TRUE; + + value = nm_setting_ip_config_get_method(s_ip6); + g_assert(value); + if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + svSetValueStr(ifcfg, "IPV6INIT", "no"); + return TRUE; + } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { + svSetValueStr(ifcfg, "IPV6_DISABLED", "yes"); + svSetValueStr(ifcfg, "IPV6INIT", "no"); + return TRUE; + } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + svSetValueStr(ifcfg, "IPV6INIT", "yes"); + svSetValueStr(ifcfg, "IPV6_AUTOCONF", "yes"); + } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { + svSetValueStr(ifcfg, "IPV6INIT", "yes"); + svSetValueStr(ifcfg, "IPV6_AUTOCONF", "no"); + svSetValueStr(ifcfg, "DHCPV6C", "yes"); + } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + svSetValueStr(ifcfg, "IPV6INIT", "yes"); + svSetValueStr(ifcfg, "IPV6_AUTOCONF", "no"); + } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { + svSetValueStr(ifcfg, "IPV6INIT", "yes"); + svSetValueStr(ifcfg, "IPV6_AUTOCONF", "no"); + } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { + svSetValueStr(ifcfg, "IPV6INIT", "yes"); + svSetValueStr(ifcfg, "IPV6_AUTOCONF", "shared"); + } + + svSetValueStr(ifcfg, + "DHCPV6_DUID", + nm_setting_ip6_config_get_dhcp_duid(NM_SETTING_IP6_CONFIG(s_ip6))); + svSetValueStr(ifcfg, "DHCPV6_IAID", nm_setting_ip_config_get_dhcp_iaid(s_ip6)); + + hostname = nm_setting_ip_config_get_dhcp_hostname(s_ip6); + svSetValueStr(ifcfg, "DHCPV6_HOSTNAME", hostname); + + /* Missing DHCPV6_SEND_HOSTNAME means TRUE, and we prefer not write it + * explicitly in that case, because it is NM-specific variable + */ + if (!nm_setting_ip_config_get_dhcp_send_hostname(s_ip6)) + svSetValueStr(ifcfg, "DHCPV6_SEND_HOSTNAME", "no"); + + timeout = nm_setting_ip6_config_get_ra_timeout(NM_SETTING_IP6_CONFIG(s_ip6)); + svSetValueInt64_cond(ifcfg, "IPV6_RA_TIMEOUT", timeout != 0, timeout); + + timeout = nm_setting_ip_config_get_dhcp_timeout(s_ip6); + svSetValueInt64_cond(ifcfg, "IPV6_DHCP_TIMEOUT", timeout != 0, timeout); + + flags = nm_setting_ip_config_get_dhcp_hostname_flags(s_ip6); + svSetValueInt64_cond(ifcfg, + "DHCPV6_HOSTNAME_FLAGS", + flags != NM_DHCP_HOSTNAME_FLAG_NONE, + flags); + + /* Write out IP addresses */ + num = nm_setting_ip_config_get_num_addresses(s_ip6); + ip_str1 = g_string_new(NULL); + ip_str2 = g_string_new(NULL); + for (i = 0; i < num; i++) { + if (i == 0) + ip_ptr = ip_str1; + else + ip_ptr = ip_str2; + + addr = nm_setting_ip_config_get_address(s_ip6, i); + + if (i > 1) + g_string_append_c(ip_ptr, ' '); /* separate addresses in IPV6ADDR_SECONDARIES */ + g_string_append_printf(ip_ptr, + "%s/%u", + nm_ip_address_get_address(addr), + nm_ip_address_get_prefix(addr)); + } + svSetValueStr(ifcfg, "IPV6ADDR", ip_str1->str); + svSetValueStr(ifcfg, "IPV6ADDR_SECONDARIES", ip_str2->str); + svSetValueStr(ifcfg, "IPV6_DEFAULTGW", nm_setting_ip_config_get_gateway(s_ip6)); + g_string_free(ip_str1, TRUE); + g_string_free(ip_str2, TRUE); + + write_dns_setting(ifcfg, connection, AF_INET6); + + /* Write out DNS domains */ + num = nm_setting_ip_config_get_num_dns_searches(s_ip6); + if (num > 0) { + nm_auto_free_gstring GString *searches = NULL; + + searches = g_string_new(NULL); + for (i = 0; i < num; i++) { + if (searches->len > 0) + g_string_append_c(searches, ' '); + g_string_append(searches, nm_setting_ip_config_get_dns_search(s_ip6, i)); + } + svSetValueStr(ifcfg, "IPV6_DOMAIN", searches->str); + } + + /* handle IPV6_DEFROUTE */ + /* IPV6_DEFROUTE has the opposite meaning from 'never-default' */ + svSetValueBoolean(ifcfg, "IPV6_DEFROUTE", !nm_setting_ip_config_get_never_default(s_ip6)); + + svSetValueStr(ifcfg, + "IPV6_PEERDNS", + nm_setting_ip_config_get_ignore_auto_dns(s_ip6) ? "no" : NULL); + + svSetValueStr(ifcfg, + "IPV6_PEERROUTES", + nm_setting_ip_config_get_ignore_auto_routes(s_ip6) ? "no" : NULL); + + svSetValueStr(ifcfg, + "IPV6_FAILURE_FATAL", + nm_setting_ip_config_get_may_fail(s_ip6) ? "no" : "yes"); + + route_metric = nm_setting_ip_config_get_route_metric(s_ip6); + svSetValueInt64_cond(ifcfg, "IPV6_ROUTE_METRIC", route_metric != -1, route_metric); + + route_table = nm_setting_ip_config_get_route_table(s_ip6); + svSetValueInt64_cond(ifcfg, "IPV6_ROUTE_TABLE", route_table != 0, route_table); + + /* IPv6 Privacy Extensions */ + switch (nm_setting_ip6_config_get_ip6_privacy(NM_SETTING_IP6_CONFIG(s_ip6))) { + case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: + svSetValueStr(ifcfg, "IPV6_PRIVACY", "no"); + break; + case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: + svSetValueStr(ifcfg, "IPV6_PRIVACY", "rfc3041"); + svSetValueStr(ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", "yes"); + break; + case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: + svSetValueStr(ifcfg, "IPV6_PRIVACY", "rfc3041"); + break; + default: + break; + } + + /* IPv6 Address generation mode */ + addr_gen_mode = nm_setting_ip6_config_get_addr_gen_mode(NM_SETTING_IP6_CONFIG(s_ip6)); + if (addr_gen_mode != NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) { + svSetValueEnum(ifcfg, + "IPV6_ADDR_GEN_MODE", + nm_setting_ip6_config_addr_gen_mode_get_type(), + addr_gen_mode); + } + + /* IPv6 tokenized interface identifier */ + value = nm_setting_ip6_config_get_token(NM_SETTING_IP6_CONFIG(s_ip6)); + svSetValueStr(ifcfg, "IPV6_TOKEN", value); + + priority = nm_setting_ip_config_get_dns_priority(s_ip6); + if (priority) + svSetValueInt64(ifcfg, "IPV6_DNS_PRIORITY", priority); + + write_res_options(ifcfg, s_ip6, "IPV6_RES_OPTIONS"); + + NM_SET_OUT(out_route6_content, write_route_file(s_ip6)); + + return TRUE; +} + +static void +write_ip_routing_rules(NMConnection *connection, shvarFile *ifcfg, gboolean route_ignore) +{ + gsize idx; + int is_ipv4; + + if (route_ignore) + return; + + idx = 0; + + for (is_ipv4 = 1; is_ipv4 >= 0; is_ipv4--) { + const int addr_family = is_ipv4 ? AF_INET : AF_INET6; + NMSettingIPConfig *s_ip; + guint i, num; + + s_ip = nm_connection_get_setting_ip_config(connection, addr_family); + if (!s_ip) + continue; + + num = nm_setting_ip_config_get_num_routing_rules(s_ip); + for (i = 0; i < num; i++) { + NMIPRoutingRule * rule = nm_setting_ip_config_get_routing_rule(s_ip, i); + gs_free const char *s = NULL; + char key[64]; + + s = nm_ip_routing_rule_to_string(rule, + NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE, + NULL, + NULL); + if (!s) + continue; + + if (is_ipv4) + numbered_tag(key, "ROUTING_RULE_", ++idx); + else + numbered_tag(key, "ROUTING_RULE6_", ++idx); + svSetValueStr(ifcfg, key, s); + } + } +} + +static char * +escape_id(const char *id) +{ + char *escaped = g_strdup(id); + char *p = escaped; + + /* Escape random stuff */ + while (*p) { + if (*p == ' ') + *p = '_'; + else if (strchr("\\][|/=()!:", *p)) + *p = '-'; + p++; + } + + return escaped; +} + +static gboolean +do_write_construct(NMConnection * connection, + const char * ifcfg_dir, + const char * filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, + shvarFile ** out_ifcfg, + GHashTable ** out_blobs, + GHashTable ** out_secrets, + gboolean * out_route_ignore, + shvarFile ** out_route_content_svformat, + GString ** out_route_content, + GString ** out_route6_content, + GError ** error) +{ + NMSettingConnection * s_con; + nm_auto_shvar_file_close shvarFile *ifcfg = NULL; + gs_free char * ifcfg_name = NULL; + gs_free char * route_path = NULL; + gs_free char * route6_path = NULL; + const char * type; + gs_unref_hashtable GHashTable *blobs = NULL; + gs_unref_hashtable GHashTable *secrets = NULL; + gboolean wired; + gboolean no_8021x; + gboolean route_path_is_svformat; + gboolean has_complex_routes_v4; + gboolean has_complex_routes_v6; + gboolean route_ignore; + nm_auto_shvar_file_close shvarFile *route_content_svformat = NULL; + nm_auto_free_gstring GString *route_content = NULL; + nm_auto_free_gstring GString *route6_content = NULL; + + nm_assert(NM_IS_CONNECTION(connection)); + nm_assert(_nm_connection_verify(connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + + if (!nms_ifcfg_rh_writer_can_write_connection(connection, error)) + return FALSE; + + s_con = nm_connection_get_setting_connection(connection); + + if (filename) { + /* For existing connections, 'filename' should be full path to ifcfg file */ + ifcfg = svOpenFile(filename, error); + if (!ifcfg) + return FALSE; + + ifcfg_name = g_strdup(filename); + } else if (ifcfg_dir) { + gs_free char *escaped = NULL; + int i_path; + + escaped = escape_id(nm_setting_connection_get_id(s_con)); + + for (i_path = 0; i_path < 10000; i_path++) { + gs_free char *path_candidate = NULL; + + if (i_path == 0) + path_candidate = g_strdup_printf("%s/ifcfg-%s", ifcfg_dir, escaped); + else + path_candidate = g_strdup_printf("%s/ifcfg-%s-%d", ifcfg_dir, escaped, i_path); + + if (allow_filename_cb && !allow_filename_cb(path_candidate, allow_filename_user_data)) + continue; + + if (g_file_test(path_candidate, G_FILE_TEST_EXISTS)) + continue; + + ifcfg_name = g_steal_pointer(&path_candidate); + break; + } + + if (!ifcfg_name) { + g_set_error_literal(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Failed to find usable ifcfg file name"); + return FALSE; + } + + ifcfg = svCreateFile(ifcfg_name); + } else + ifcfg = svCreateFile("/tmp/ifcfg-dummy"); + + route_path = utils_get_route_path(svFileGetName(ifcfg)); + if (!route_path) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Could not get route file path for '%s'", + svFileGetName(ifcfg)); + return FALSE; + } + + route6_path = utils_get_route6_path(svFileGetName(ifcfg)); + if (!route6_path) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Could not get route6 file path for '%s'", + svFileGetName(ifcfg)); + return FALSE; + } + + type = nm_setting_connection_get_connection_type(s_con); + if (!type) { + g_set_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "Missing connection type!"); + return FALSE; + } + + secrets = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); + + wired = FALSE; + no_8021x = FALSE; + if (!strcmp(type, NM_SETTING_WIRED_SETTING_NAME)) { + // FIXME: can't write PPPoE at this time + if (nm_connection_get_setting_pppoe(connection)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Can't write connection type '%s'", + NM_SETTING_PPPOE_SETTING_NAME); + return FALSE; + } + + if (!write_wired_setting(connection, ifcfg, error)) + return FALSE; + wired = TRUE; + } else if (!strcmp(type, NM_SETTING_VLAN_SETTING_NAME)) { + if (!write_vlan_setting(connection, ifcfg, &wired, error)) + return FALSE; + } else if (!strcmp(type, NM_SETTING_WIRELESS_SETTING_NAME)) { + if (!write_wireless_setting(connection, ifcfg, secrets, &no_8021x, error)) + return FALSE; + } else if (!strcmp(type, NM_SETTING_INFINIBAND_SETTING_NAME)) { + if (!write_infiniband_setting(connection, ifcfg, error)) + return FALSE; + } else if (!strcmp(type, NM_SETTING_BOND_SETTING_NAME)) { + if (!write_bond_setting(connection, ifcfg, &wired, error)) + return FALSE; + } else if (!strcmp(type, NM_SETTING_TEAM_SETTING_NAME)) { + if (!write_team_setting(connection, ifcfg, &wired, error)) + return FALSE; + } else if (!strcmp(type, NM_SETTING_BRIDGE_SETTING_NAME)) { + if (!write_bridge_setting(connection, ifcfg, &wired, error)) + return FALSE; + } else { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Can't write connection type '%s'", + type); + return FALSE; + } + + if (!no_8021x) { + blobs = + g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_bytes_unref); + if (!write_8021x_setting(connection, ifcfg, secrets, blobs, wired, error)) + return FALSE; + } + + if (!write_bridge_port_setting(connection, ifcfg, error)) + return FALSE; + + if (!write_team_port_setting(connection, ifcfg, error)) + return FALSE; + + if (!write_dcb_setting(connection, ifcfg, error)) + return FALSE; + + if (!write_proxy_setting(connection, ifcfg, error)) + return FALSE; + + if (!write_ethtool_setting(connection, ifcfg, error)) + return FALSE; + + if (!write_user_setting(connection, ifcfg, error)) + return FALSE; + + write_match_setting(connection, ifcfg); + write_hostname_setting(connection, ifcfg); + write_sriov_setting(connection, ifcfg); + + if (!write_tc_setting(connection, ifcfg, error)) + return FALSE; + + route_path_is_svformat = utils_has_route_file_new_syntax(route_path); + + has_complex_routes_v4 = utils_has_complex_routes(ifcfg_name, AF_INET); + has_complex_routes_v6 = utils_has_complex_routes(ifcfg_name, AF_INET6); + + if (has_complex_routes_v4 || has_complex_routes_v6) { + NMSettingIPConfig *s_ip4, *s_ip6; + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + s_ip6 = nm_connection_get_setting_ip6_config(connection); + if ((s_ip4 && nm_setting_ip_config_get_num_routes(s_ip4) > 0) + || (s_ip6 && nm_setting_ip_config_get_num_routes(s_ip6) > 0)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Cannot configure static routes on a connection that has an associated " + "'rule%s-' file", + has_complex_routes_v4 ? "" : "6"); + return FALSE; + } + if ((s_ip4 && nm_setting_ip_config_get_route_table(s_ip4) != 0) + || (s_ip6 && nm_setting_ip_config_get_route_table(s_ip6) != 0)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Cannot configure a route table for policy routing on a connection that " + "has an associated 'rule%s-' file", + has_complex_routes_v4 ? "" : "6"); + return FALSE; + } + if ((s_ip4 && nm_setting_ip_config_get_num_routing_rules(s_ip4) > 0) + || (s_ip6 && nm_setting_ip_config_get_num_routing_rules(s_ip6) > 0)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Cannot configure routing rules on a connection that has an associated " + "'rule%s-' file", + has_complex_routes_v4 ? "" : "6"); + return FALSE; + } + route_ignore = TRUE; + } else + route_ignore = FALSE; + + if (!write_ip4_setting(connection, + ifcfg, + !route_ignore && route_path_is_svformat ? &route_content_svformat : NULL, + !route_ignore && route_path_is_svformat ? NULL : &route_content, + error)) + return FALSE; + + if (!write_ip6_setting(connection, ifcfg, !route_ignore ? &route6_content : NULL, error)) + return FALSE; + + write_ip_routing_rules(connection, ifcfg, route_ignore); + + write_connection_setting(s_con, ifcfg); + + NM_SET_OUT(out_ifcfg, g_steal_pointer(&ifcfg)); + NM_SET_OUT(out_blobs, g_steal_pointer(&blobs)); + NM_SET_OUT(out_secrets, g_steal_pointer(&secrets)); + NM_SET_OUT(out_route_ignore, route_ignore); + NM_SET_OUT(out_route_content_svformat, g_steal_pointer(&route_content_svformat)); + NM_SET_OUT(out_route_content, g_steal_pointer(&route_content)); + NM_SET_OUT(out_route6_content, g_steal_pointer(&route6_content)); + return TRUE; +} + +static gboolean +do_write_to_disk(NMConnection *connection, + shvarFile * ifcfg, + GHashTable * blobs, + GHashTable * secrets, + gboolean route_ignore, + shvarFile * route_content_svformat, + GString * route_content, + GString * route6_content, + GError ** error) +{ + /* From here on, we persist data to disk. Before, it was all in-memory + * only. But we loaded the ifcfg files from disk, and managled our + * new settings (in-memory). */ + + if (!svWriteFileWithoutDirtyWellknown(ifcfg, 0644, error)) + return FALSE; + + write_ip4_aliases(connection, svFileGetName(ifcfg)); + + if (!write_blobs(blobs, error)) + return FALSE; + + if (!write_secrets(ifcfg, secrets, error)) + return FALSE; + + if (!route_ignore) { + gs_free char *route_path = utils_get_route_path(svFileGetName(ifcfg)); + + if (!route_content && !route_content_svformat) + (void) unlink(route_path); + else { + nm_assert(route_content_svformat || route_content); + if (route_content_svformat) { + if (!svWriteFileWithoutDirtyWellknown(route_content_svformat, 0644, error)) + return FALSE; + } else { + if (!g_file_set_contents(route_path, + route_content->str, + route_content->len, + NULL)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Writing route file '%s' failed", + route_path); + return FALSE; + } + } + } + } + + if (!route_ignore) { + gs_free char *route6_path = utils_get_route6_path(svFileGetName(ifcfg)); + + if (!route6_content) + (void) unlink(route6_path); + else { + if (!g_file_set_contents(route6_path, route6_content->str, route6_content->len, NULL)) { + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "Writing route6 file '%s' failed", + route6_path); + return FALSE; + } + } + } + + return TRUE; +} + +gboolean +nms_ifcfg_rh_writer_write_connection(NMConnection * connection, + const char * ifcfg_dir, + const char * filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, + char ** out_filename, + NMConnection ** out_reread, + gboolean * out_reread_same, + GError ** error) +{ + nm_auto_shvar_file_close shvarFile *ifcfg = NULL; + nm_auto_free_gstring GString *route_content = NULL; + gboolean route_ignore = FALSE; + nm_auto_shvar_file_close shvarFile *route_content_svformat = NULL; + nm_auto_free_gstring GString *route6_content = NULL; + gs_unref_hashtable GHashTable *secrets = NULL; + gs_unref_hashtable GHashTable *blobs = NULL; + + nm_assert(!out_reread || !*out_reread); + + if (!do_write_construct(connection, + ifcfg_dir, + filename, + allow_filename_cb, + allow_filename_user_data, + &ifcfg, + &blobs, + &secrets, + &route_ignore, + &route_content_svformat, + &route_content, + &route6_content, + error)) + return FALSE; + + _LOGT("write: write connection %s (%s) to file \"%s\"", + nm_connection_get_id(connection), + nm_connection_get_uuid(connection), + svFileGetName(ifcfg)); + + if (!do_write_to_disk(connection, + ifcfg, + blobs, + secrets, + route_ignore, + route_content_svformat, + route_content, + route6_content, + error)) + return FALSE; + + /* Note that we just wrote the connection to disk, and re-read it from there. + * That is racy if somebody else modifies the connection. + * That race is why we must not tread a failure to re-read the profile + * as an error. + * + * FIXME: a much better solution might be, to re-read the connection only based + * on the in-memory representation of what we collected above. But the reader + * does not yet allow to inject the configuration. */ + if (out_reread || out_reread_same) { + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same = FALSE; + gs_free_error GError *local = NULL; + gs_free char * unhandled = NULL; + + reread = connection_from_file(svFileGetName(ifcfg), &unhandled, &local, NULL); + nm_assert((NM_IS_CONNECTION(reread) && !local) || (!reread && local)); + + if (!reread) { + _LOGW("write: failure to re-read connection \"%s\": %s", + svFileGetName(ifcfg), + local->message); + } else if (unhandled) { + g_clear_object(&reread); + _LOGW("write: failure to re-read connection \"%s\": %s", + svFileGetName(ifcfg), + "connection is unhandled"); + } else { + /* ifcfg-rh doesn't support the 'timestamp' property, let's add it here */ + g_object_set(nm_connection_get_setting_connection(reread), + NM_SETTING_CONNECTION_TIMESTAMP, + nm_setting_connection_get_timestamp( + nm_connection_get_setting_connection(connection)), + NULL); + if (out_reread_same) { + reread_same = + nm_connection_compare(reread, connection, NM_SETTING_COMPARE_FLAG_EXACT); + if (!reread_same) { + _LOGD("write: connection %s (%s) was modified by persisting it to \"%s\" ", + nm_connection_get_id(connection), + nm_connection_get_uuid(connection), + svFileGetName(ifcfg)); + } + } + } + + NM_SET_OUT(out_reread, g_steal_pointer(&reread)); + NM_SET_OUT(out_reread_same, reread_same); + } + + /* Only return the filename if this was a newly written ifcfg */ + if (out_filename && !filename) + *out_filename = g_strdup(svFileGetName(ifcfg)); + + return TRUE; +} + +gboolean +nms_ifcfg_rh_writer_can_write_connection(NMConnection *connection, GError **error) +{ + const char *type, *id; + + type = nm_connection_get_connection_type(connection); + if (NM_IN_STRSET(type, + NM_SETTING_VLAN_SETTING_NAME, + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_INFINIBAND_SETTING_NAME, + NM_SETTING_BOND_SETTING_NAME, + NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_BRIDGE_SETTING_NAME)) + return TRUE; + if (nm_streq0(type, NM_SETTING_WIRED_SETTING_NAME) + && !nm_connection_get_setting_pppoe(connection)) + return TRUE; + + id = nm_connection_get_id(connection); + g_set_error(error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "The ifcfg-rh plugin cannot write the connection %s%s%s (type %s%s%s)", + NM_PRINT_FMT_QUOTE_STRING(id), + NM_PRINT_FMT_QUOTE_STRING(type)); + return FALSE; +} diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h new file mode 100644 index 00000000..d0d8a4cd --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2009 Red Hat, Inc. + */ + +#ifndef __NMS_IFCFG_RH_WRITER_H__ +#define __NMS_IFCFG_RH_WRITER_H__ + +#include "nm-connection.h" + +typedef gboolean (*NMSIfcfgRHWriterAllowFilenameCb)(const char *check_filename, + gpointer allow_filename_user_data); + +gboolean nms_ifcfg_rh_writer_can_write_connection(NMConnection *connection, GError **error); + +gboolean nms_ifcfg_rh_writer_write_connection(NMConnection * connection, + const char * ifcfg_dir, + const char * filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, + char ** out_filename, + NMConnection **out_reread, + gboolean * out_reread_same, + GError ** error); + +#endif /* __NMS_IFCFG_RH_WRITER_H__ */ diff --git a/src/core/settings/plugins/ifcfg-rh/shvar.c b/src/core/settings/plugins/ifcfg-rh/shvar.c new file mode 100644 index 00000000..386bcac7 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/shvar.c @@ -0,0 +1,1585 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 1999, 2000 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "shvar.h" + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "nm-core-internal.h" +#include "nm-core-utils.h" +#include "nm-glib-aux/nm-enum-utils.h" +#include "nm-glib-aux/nm-io-utils.h" +#include "c-list/src/c-list.h" +#include "nms-ifcfg-rh-utils.h" + +/*****************************************************************************/ + +struct _shvarLine { + const char *key; + + CList lst; + + /* We index variables by their key in shvarFile.lst_idx. One shell variable might + * occur multiple times in a file (in which case the last occurrence wins). + * Hence, we need to keep a list of all the same keys. + * + * This is a pointer to the next shadowed line. */ + struct _shvarLine *prev_shadowed; + + /* There are three cases: + * + * 1) the line is not a valid variable assignment (that is, it doesn't + * start with a "FOO=" with possible whitespace prefix). + * In that case, @key and @key_with_prefix are %NULL, and the entire + * original line is in @line. Such entries are ignored for the most part. + * + * 2) if the line can be parsed with a "FOO=" assignment, then @line contains + * the part after '=', @key_with_prefix contains the key "FOO" with possible + * whitespace prefix, and @key points into @key_with_prefix skipping over the + * whitespace. + * + * 3) like 2, but if the value was deleted via svSetValue(), the entry is not removed, + * but only marked for deletion. That is done by clearing @line but preserving + * @key/@key_with_prefix. + * */ + char *line; + char *key_with_prefix; + + /* svSetValue() will clear the dirty flag. */ + bool dirty : 1; +}; + +typedef struct _shvarLine shvarLine; + +struct _shvarFile { + char * fileName; + CList lst_head; + GHashTable *lst_idx; + int fd; + bool modified : 1; +}; + +/*****************************************************************************/ + +static void _line_link_parse(shvarFile *s, const char *value, gsize len); + +/*****************************************************************************/ + +#define ASSERT_key_is_well_known(key) \ + nm_assert(({ \ + const char *_key = (key); \ + gboolean _is_wellknown = TRUE; \ + \ + if (!nms_ifcfg_rh_utils_is_well_known_key(_key)) { \ + _is_wellknown = FALSE; \ + g_critical("ifcfg-rh key \"%s\" is not well-known", _key); \ + } \ + \ + _is_wellknown; \ + })) + +/** + * svParseBoolean: + * @value: the input string + * @fallback: the fallback value + * + * Parses a string and returns the boolean value it contains or, + * in case no valid value is found, the fallback value. Valid values + * are: "yes", "true", "t", "y", "1" and "no", "false", "f", "n", "0". + * + * Returns: the parsed boolean value or @fallback. + */ +int +svParseBoolean(const char *value, int fallback) +{ + if (!value) + return fallback; + + if (!g_ascii_strcasecmp("yes", value) || !g_ascii_strcasecmp("true", value) + || !g_ascii_strcasecmp("t", value) || !g_ascii_strcasecmp("y", value) + || !g_ascii_strcasecmp("1", value)) + return TRUE; + else if (!g_ascii_strcasecmp("no", value) || !g_ascii_strcasecmp("false", value) + || !g_ascii_strcasecmp("f", value) || !g_ascii_strcasecmp("n", value) + || !g_ascii_strcasecmp("0", value)) + return FALSE; + + return fallback; +} + +/*****************************************************************************/ + +static gboolean +_shell_is_name(const char *key, gssize len) +{ + gssize i; + + /* whether @key is a valid identifier (name). */ + if (!key || len == 0) + return FALSE; + if (!g_ascii_isalpha(key[0]) && key[0] != '_') + return FALSE; + for (i = 1; TRUE; i++) { + if (len < 0) { + if (!key[i]) + return TRUE; + } else { + if (i >= len) + return TRUE; + } + if (!g_ascii_isalnum(key[i]) && key[i] != '_') + return FALSE; + } +} + +/*****************************************************************************/ + +/* like g_strescape(), except that it also escapes '\''' *sigh*. + * + * While at it, add $''. */ +static char * +_escape_ansic(const char *source) +{ + const char *p; + char * dest; + char * q; + + nm_assert(source); + + p = (const char *) source; + /* Each source byte needs maximally four destination chars (\777) */ + q = dest = g_malloc(strlen(source) * 4 + 1 + 3); + + *q++ = '$'; + *q++ = '\''; + + while (*p) { + switch (*p) { + case '\b': + *q++ = '\\'; + *q++ = 'b'; + break; + case '\f': + *q++ = '\\'; + *q++ = 'f'; + break; + case '\n': + *q++ = '\\'; + *q++ = 'n'; + break; + case '\r': + *q++ = '\\'; + *q++ = 'r'; + break; + case '\t': + *q++ = '\\'; + *q++ = 't'; + break; + case '\v': + *q++ = '\\'; + *q++ = 'v'; + break; + case '\\': + case '"': + case '\'': + *q++ = '\\'; + *q++ = *p; + break; + default: + if ((*p < ' ') || (*p >= 0177)) { + *q++ = '\\'; + *q++ = '0' + (((*p) >> 6) & 07); + *q++ = '0' + (((*p) >> 3) & 07); + *q++ = '0' + ((*p) & 07); + } else + *q++ = *p; + break; + } + p++; + } + *q++ = '\''; + *q++ = '\0'; + + nm_assert(q - dest <= strlen(source) * 4 + 1 + 3); + + return dest; +} + +/*****************************************************************************/ + +#define _char_req_escape(ch) NM_IN_SET(ch, '"', '\\', '$', '`') +#define _char_req_escape_old(ch) NM_IN_SET(ch, '"', '\\', '\'', '$', '`', '~') +#define _char_req_quotes(ch) NM_IN_SET(ch, ' ', '\'', '~', '\t', '|', '&', ';', '(', ')', '<', '>') + +const char * +svEscape(const char *s, char **to_free) +{ + char *new; + gsize mangle = 0; + gboolean requires_quotes = FALSE; + int newlen; + size_t i, j, slen; + + for (slen = 0; s[slen]; slen++) { + if (_char_req_escape(s[slen])) + mangle++; + else if (_char_req_quotes(s[slen])) + requires_quotes = TRUE; + else if (s[slen] < ' ') { + /* if the string contains newline we can only express it using ANSI C quotation + * (as we don't support line continuation). + * Additionally, ANSI control characters look odd with regular quotation, so handle + * them too. */ + return (*to_free = _escape_ansic(s)); + } + } + if (!mangle && !requires_quotes) { + *to_free = NULL; + return s; + } + + newlen = slen + mangle + 3; /* 3 is extra ""\0 */ + new = g_malloc(newlen); + + j = 0; + new[j++] = '"'; + for (i = 0; i < slen; i++) { + if (_char_req_escape(s[i])) + new[j++] = '\\'; + new[j++] = s[i]; + } + new[j++] = '"'; + new[j++] = '\0'; + + nm_assert(j == slen + mangle + 3); + + *to_free = new; + return new; +} + +static gboolean +_looks_like_old_svescaped(const char *value) +{ + gsize k; + + if (value[0] != '"') + return FALSE; + + for (k = 1;; k++) { + if (value[k] == '\0') + return FALSE; + if (!_char_req_escape_old(value[k])) + continue; + + if (value[k] == '"') + return (value[k + 1] == '\0'); + else if (value[k] == '\\') { + k++; + if (!_char_req_escape_old(value[k])) + return FALSE; + } else + return FALSE; + } +} + +static gboolean +_ch_octal_is(char ch) +{ + return ch >= '0' && ch < '8'; +} + +static guint8 +_ch_octal_get(char ch) +{ + nm_assert(_ch_octal_is(ch)); + return (ch - '0'); +} + +static gboolean +_ch_hex_is(char ch) +{ + return g_ascii_isxdigit(ch); +} + +static guint8 +_ch_hex_get(char ch) +{ + nm_assert(_ch_hex_is(ch)); + return ch <= '9' ? ch - '0' : (ch & 0x4F) - 'A' + 10; +} + +static void +_gstr_init(GString **str, const char *value, gsize i) +{ + nm_assert(str); + nm_assert(value); + + if (!(*str)) { + /* if @str is not yet initialized, it allocates + * a new GString and copies @i characters from + * @value over. + * + * Unescaping usually does not extend the length of a string, + * so we might be tempted to allocate a fixed buffer of length + * (strlen(value)+CONST). + * However, due to $'\Ux' escapes, the maximum length is some + * (FACTOR*strlen(value) + CONST), which is non trivial to get + * right in all cases. Also, we would have to provision for the + * very unlikely extreme case. + * Instead, use a GString buffer which can grow as needed. But for an + * initial guess, strlen(value) is a good start */ + *str = g_string_new_len(NULL, strlen(value) + 3); + if (i) + g_string_append_len(*str, value, i); + } +} + +const char * +svUnescape(const char *value, char **to_free) +{ + gsize i, j; + GString *str = NULL; + int looks_like_old_svescaped = -1; + + /* we handle bash syntax here (note that ifup has #!/bin/bash. + * Thus, see https://www.gnu.org/software/bash/manual/html_node/Quoting.html#Quoting */ + + /* @value shall start with the first character after "FOO=" */ + + nm_assert(value); + nm_assert(to_free); + + /* we don't expect any newlines. They must be filtered out before-hand. + * We also don't support line continuation. */ + nm_assert(!NM_STRCHAR_ANY(value, ch, ch == '\n')); + + i = 0; + while (TRUE) { + if (value[i] == '\0') + goto out_value; + + if (g_ascii_isspace(value[i]) || value[i] == ';') { + gboolean has_semicolon = (value[i] == ';'); + + /* starting with space is only allowed, if the entire + * string consists of spaces (possibly terminated by a comment). + * This disallows for example + * LANG=C ls -1 + * LANG= ls -1 + * but allows + * LANG= #comment + * + * As a special case, we also allow one trailing semicolon, as long + * it is only followed by whitespace or a #-comment. + * FOO=; + * FOO=a; + * FOO=b ; #hallo + */ + j = i + 1; + while (g_ascii_isspace(value[j]) + || (!has_semicolon && (has_semicolon = (value[j] == ';')))) + j++; + if (!NM_IN_SET(value[j], '\0', '#')) + goto out_error; + goto out_value; + } + + if (value[i] == '\\') { + /* backslash escape */ + _gstr_init(&str, value, i); + i++; + if (G_UNLIKELY(value[i] == '\0')) { + /* we don't support line continuation */ + goto out_error; + } + g_string_append_c(str, value[i]); + i++; + goto loop1_next; + } + + if (value[i] == '\'') { + /* single quotes */ + _gstr_init(&str, value, i); + i++; + j = i; + while (TRUE) { + if (value[j] == '\0') { + /* unterminated single quote. We don't support line continuation */ + goto out_error; + } + if (value[j] == '\'') + break; + j++; + } + g_string_append_len(str, &value[i], j - i); + i = j + 1; + goto loop1_next; + } + + if (value[i] == '"') { + /* double quotes */ + _gstr_init(&str, value, i); + i++; + while (TRUE) { + if (value[i] == '"') { + i++; + break; + } + if (value[i] == '\0') { + /* unterminated double quote. We don't support line continuation. */ + goto out_error; + } + if (NM_IN_SET(value[i], '`', '$')) { + /* we don't support shell expansion. */ + goto out_error; + } + if (value[i] == '\\') { + i++; + if (value[i] == '\0') { + /* we don't support line continuation */ + goto out_error; + } + if (NM_IN_SET(value[i], '$', '`', '"', '\\')) { + /* Drop the backslash. */ + } else if (NM_IN_SET(value[i], '\'', '~')) { + /* '\'' and '~' in double quotes are not handled special by shell. + * However, old versions of svEscape() would wrongly use double-quoting + * with backslash escaping for these characters (expecting svUnescape() + * to remove the backslash). + * + * In order to preserve previous behavior, we continue to read such + * strings different then shell does. */ + + /* Actually, we can relax this. Old svEscape() escaped the entire value + * in a particular way with double quotes. + * If the value doesn't exactly look like something as created by svEscape(), + * don't do the compat hack and preserve the backslash. */ + if (looks_like_old_svescaped < 0) + looks_like_old_svescaped = _looks_like_old_svescaped(value); + if (!looks_like_old_svescaped) + g_string_append_c(str, '\\'); + } else + g_string_append_c(str, '\\'); + } + g_string_append_c(str, value[i]); + i++; + } + goto loop1_next; + } + + if (value[i] == '$' && value[i + 1] == '\'') { + /* ANSI-C Quoting */ + _gstr_init(&str, value, i); + i += 2; + while (TRUE) { + char ch; + + if (value[i] == '\'') { + i++; + break; + } + if (value[i] == '\0') { + /* unterminated double quote. We don't support line continuation. */ + goto out_error; + } + if (value[i] == '\\') { + i++; + if (value[i] == '\0') { + /* we don't support line continuation */ + goto out_error; + } + switch (value[i]) { + case 'a': + ch = '\a'; + break; + case 'b': + ch = '\b'; + break; + case 'e': + ch = '\e'; + break; + case 'E': + ch = '\E'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case 'v': + ch = '\v'; + break; + case '?': + ch = '\?'; + break; + case '"': + ch = '"'; + break; + case '\\': + ch = '\\'; + break; + case '\'': + ch = '\''; + break; + default: + if (_ch_octal_is(value[i])) { + guint v; + + v = _ch_octal_get(value[i]); + i++; + if (_ch_octal_is(value[i])) { + v = (v * 8) + _ch_octal_get(value[i]); + i++; + if (_ch_octal_is(value[i])) { + v = (v * 8) + _ch_octal_get(value[i]); + i++; + } + } + /* like bash, we cut too large numbers off. E.g. A=$'\772' becomes 0xfa */ + g_string_append_c(str, (guint8) v); + } else if (NM_IN_SET(value[i], 'x', 'u', 'U')) { + const char escape_type = value[i]; + int max_digits = escape_type == 'x' ? 2 : escape_type == 'u' ? 4 : 8; + guint64 v; + + i++; + if (!_ch_hex_is(value[i])) { + /* missing hex value after "\x" escape. This is treated like no escaping. */ + g_string_append_c(str, '\\'); + g_string_append_c(str, escape_type); + } else { + v = _ch_hex_get(value[i]); + i++; + + while (--max_digits > 0) { + if (!_ch_hex_is(value[i])) + break; + v = v * 16 + _ch_hex_get(value[i]); + i++; + } + if (escape_type == 'x') + g_string_append_c(str, v); + else { + /* we treat the unicode escapes as utf-8 encoded values. */ + g_string_append_unichar(str, v); + } + } + } else { + g_string_append_c(str, '\\'); + g_string_append_c(str, value[i]); + i++; + } + goto loop_ansic_next; + } + } else + ch = value[i]; + g_string_append_c(str, ch); + i++; +loop_ansic_next:; + } + goto loop1_next; + } + + if (NM_IN_SET(value[i], '|', '&', '(', ')', '<', '>')) { + /* shell metacharacters are not supported without quoting. + * Note that ';' is already handled above. */ + goto out_error; + } + + /* an unquoted, regular character. Just consume it directly. */ + if (str) + g_string_append_c(str, value[i]); + i++; + +loop1_next:; + } + + nm_assert_not_reached(); + +out_value: + if (i == 0) { + nm_assert(!str); + *to_free = NULL; + return ""; + } + + if (str) { + if (str->len == 0 || str->str[0] == '\0') { + g_string_free(str, TRUE); + *to_free = NULL; + return ""; + } else { + *to_free = g_string_free(str, FALSE); + return *to_free; + } + } + + if (value[i] != '\0') { + *to_free = g_strndup(value, i); + return *to_free; + } + + *to_free = NULL; + return value; + +out_error: + if (str) + g_string_free(str, TRUE); + *to_free = NULL; + return NULL; +} + +/*****************************************************************************/ + +shvarFile * +svFile_new(const char *name, int fd, const char *content) +{ + shvarFile * s; + const char *p; + const char *q; + + nm_assert(name); + nm_assert(fd >= -1); + + s = g_slice_new(shvarFile); + *s = (shvarFile){ + .fileName = g_strdup(name), + .fd = fd, + .lst_head = C_LIST_INIT(s->lst_head), + .lst_idx = g_hash_table_new(nm_pstr_hash, nm_pstr_equal), + }; + + if (content) { + for (p = content; (q = strchr(p, '\n')) != NULL; p = q + 1) + _line_link_parse(s, p, q - p); + if (p[0]) + _line_link_parse(s, p, strlen(p)); + } + + return s; +} + +const char * +svFileGetName(const shvarFile *s) +{ + nm_assert(s); + + return s->fileName; +} + +void +_nmtst_svFileSetName(shvarFile *s, const char *fileName) +{ + /* changing the file name is not supported for regular + * operation. Only allowed to use in tests, otherwise, + * the filename is immutable. */ + g_free(s->fileName); + s->fileName = g_strdup(fileName); +} + +void +_nmtst_svFileSetModified(shvarFile *s) +{ + /* marking a file as modified is only for testing. */ + s->modified = TRUE; +} + +/*****************************************************************************/ + +static void +ASSERT_shvarLine(const shvarLine *line) +{ +#if NM_MORE_ASSERTS > 5 + const char *s, *s2; + + nm_assert(line); + if (!line->key) { + nm_assert(line->line); + nm_assert(!line->key_with_prefix); + s = nm_str_skip_leading_spaces(line->line); + s2 = strchr(s, '='); + nm_assert(!s2 || !_shell_is_name(s, s2 - s)); + } else { + nm_assert(line->key_with_prefix); + nm_assert(line->key == nm_str_skip_leading_spaces(line->key_with_prefix)); + nm_assert(_shell_is_name(line->key, -1)); + } +#endif +} + +static shvarLine * +line_new_parse(const char *value, gsize len) +{ + shvarLine *line; + gsize k, e; + + nm_assert(value); + + line = g_slice_new(shvarLine); + *line = (shvarLine){ + .lst = C_LIST_INIT(line->lst), + .dirty = TRUE, + }; + + for (k = 0; k < len; k++) { + if (g_ascii_isspace(value[k])) + continue; + + if (g_ascii_isalpha(value[k]) || value[k] == '_') { + for (e = k + 1; e < len; e++) { + if (value[e] == '=') { + nm_assert(_shell_is_name(&value[k], e - k)); + line->line = g_strndup(&value[e + 1], len - e - 1); + line->key_with_prefix = g_strndup(value, e); + line->key = &line->key_with_prefix[k]; + ASSERT_shvarLine(line); + return line; + } + if (!g_ascii_isalnum(value[e]) && value[e] != '_') + break; + } + } + break; + } + line->line = g_strndup(value, len); + ASSERT_shvarLine(line); + return line; +} + +static shvarLine * +line_new_build(const char *key, const char *value) +{ + char * value_escaped = NULL; + shvarLine *line; + char * new_key; + + value = svEscape(value, &value_escaped); + + line = g_slice_new(shvarLine); + new_key = g_strdup(key), *line = (shvarLine){ + .lst = C_LIST_INIT(line->lst), + .line = value_escaped ?: g_strdup(value), + .key_with_prefix = new_key, + .key = new_key, + .dirty = FALSE, + }; + ASSERT_shvarLine(line); + return line; +} + +static gboolean +line_set(shvarLine *line, const char *value) +{ + char * value_escaped = NULL; + gboolean changed = FALSE; + + ASSERT_shvarLine(line); + nm_assert(line->key); + + line->dirty = FALSE; + + if (line->key != line->key_with_prefix) { + memmove(line->key_with_prefix, line->key, strlen(line->key) + 1); + line->key = line->key_with_prefix; + changed = TRUE; + ASSERT_shvarLine(line); + } + + value = svEscape(value, &value_escaped); + + if (line->line) { + if (nm_streq(value, line->line)) { + g_free(value_escaped); + return changed; + } + g_free(line->line); + } + + line->line = value_escaped ?: g_strdup(value); + ASSERT_shvarLine(line); + return TRUE; +} + +static void +line_free(shvarLine *line) +{ + ASSERT_shvarLine(line); + c_list_unlink_stale(&line->lst); + g_free(line->line); + g_free(line->key_with_prefix); + g_slice_free(shvarLine, line); +} + +/*****************************************************************************/ + +static void +_line_link_parse(shvarFile *s, const char *value, gsize len) +{ + shvarLine *line; + + line = line_new_parse(value, len); + if (!line->key) + goto do_link; + + if (G_UNLIKELY(!g_hash_table_insert(s->lst_idx, line, line))) { + shvarLine *existing_key; + shvarLine *existing_val; + + /* Slow-path: we have duplicate keys. Fix the mess we created. + * Unfortunately, g_hash_table_insert() now had to allocate an extra + * array to track the keys/values differently. I wish there was an + * GHashTable API to add a key only if it does not exist yet. */ + + if (!g_hash_table_lookup_extended(s->lst_idx, + line, + (gpointer *) &existing_key, + (gpointer *) &existing_val)) + nm_assert_not_reached(); + + nm_assert(existing_val == line); + nm_assert(existing_key != line); + line->prev_shadowed = existing_key; + g_hash_table_replace(s->lst_idx, line, line); + } + +do_link: + c_list_link_tail(&s->lst_head, &line->lst); +} + +/*****************************************************************************/ + +/* Open the file <name>, returning a shvarFile on success and NULL on failure. + * Add a wrinkle to let the caller specify whether or not to create the file + * (actually, return a structure anyway) if it doesn't exist. + */ +static shvarFile * +svOpenFileInternal(const char *name, gboolean create, GError **error) +{ + gboolean closefd = FALSE; + int errsv = 0; + gs_free char *content = NULL; + gs_free_error GError *local = NULL; + nm_auto_close int fd = -1; + + if (create) + fd = open(name, O_RDWR | O_CLOEXEC); /* NOT O_CREAT */ + if (fd < 0) { + /* try read-only */ + fd = open(name, O_RDONLY | O_CLOEXEC); /* NOT O_CREAT */ + if (fd < 0) + errsv = errno; + else + closefd = TRUE; + } + + if (fd < 0) { + if (create) + return svFile_new(name, -1, NULL); + + g_set_error(error, + G_FILE_ERROR, + g_file_error_from_errno(errsv), + "Could not read file '%s': %s", + name, + nm_strerror_native(errsv)); + return NULL; + } + + if (!nm_utils_fd_get_contents(closefd ? nm_steal_fd(&fd) : fd, + closefd, + 10 * 1024 * 1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &content, + NULL, + NULL, + &local)) { + if (create) + return svFile_new(name, -1, NULL); + + g_set_error(error, + G_FILE_ERROR, + local->domain == G_FILE_ERROR ? local->code : G_FILE_ERROR_FAILED, + "Could not read file '%s': %s", + name, + local->message); + return NULL; + } + + /* closefd is set if we opened the file read-only, so go ahead and + * close it, because we can't write to it anyway */ + nm_assert(closefd || fd >= 0); + return svFile_new(name, !closefd ? nm_steal_fd(&fd) : -1, content); +} + +/* Open the file <name>, return shvarFile on success, NULL on failure */ +shvarFile * +svOpenFile(const char *name, GError **error) +{ + return svOpenFileInternal(name, FALSE, error); +} + +/* Create a new file structure, returning actual data if the file exists, + * and a suitable starting point if it doesn't. + */ +shvarFile * +svCreateFile(const char *name) +{ + return svOpenFileInternal(name, TRUE, NULL); +} + +/*****************************************************************************/ + +static gboolean +_svKeyMatchesType(const char *key, SvKeyType match_key_type) +{ + if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_ANY)) + return TRUE; + +#define _IS_NUMBERED(key, tag) \ + ({ \ + gint64 _idx; \ + \ + NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG(key, tag, &_idx) \ + &&_idx >= 0; \ + }) + + if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) { + if (_IS_NUMBERED(key, "ADDRESS") || _IS_NUMBERED(key, "NETMASK") + || _IS_NUMBERED(key, "GATEWAY") || _IS_NUMBERED(key, "METRIC") + || _IS_NUMBERED(key, "OPTIONS")) + return TRUE; + } + if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) { + if (_IS_NUMBERED(key, "IPADDR") || _IS_NUMBERED(key, "PREFIX") + || _IS_NUMBERED(key, "NETMASK") || _IS_NUMBERED(key, "GATEWAY")) + return TRUE; + } + if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_USER)) { + if (g_str_has_prefix(key, "NM_USER_")) + return TRUE; + } + if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_TC)) { + if (_IS_NUMBERED(key, "QDISC") || _IS_NUMBERED(key, "FILTER")) + return TRUE; + } + if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_SRIOV_VF)) { + if (_IS_NUMBERED(key, "SRIOV_VF")) + return TRUE; + } + if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_ROUTING_RULE4)) { + if (_IS_NUMBERED(key, "ROUTING_RULE_")) + return TRUE; + } + if (NM_FLAGS_HAS(match_key_type, SV_KEY_TYPE_ROUTING_RULE6)) { + if (_IS_NUMBERED(key, "ROUTING_RULE6_")) + return TRUE; + } + + return FALSE; +} + +gint64 +svNumberedParseKey(const char *key) +{ + gint64 idx; + + if (NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG(key, "ROUTING_RULE_", &idx) + || NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG(key, "ROUTING_RULE6_", &idx)) + return idx; + return -1; +} + +GHashTable * +svGetKeys(shvarFile *s, SvKeyType match_key_type) +{ + GHashTable * keys = NULL; + CList * current; + const shvarLine *line; + + nm_assert(s); + + c_list_for_each (current, &s->lst_head) { + line = c_list_entry(current, shvarLine, lst); + if (line->key && line->line && _svKeyMatchesType(line->key, match_key_type)) { + /* we don't clone the keys. The keys are only valid + * until @s gets modified. */ + if (!keys) + keys = g_hash_table_new_full(nm_str_hash, g_str_equal, NULL, NULL); + g_hash_table_add(keys, (gpointer) line->key); + } + } + return keys; +} + +static int +_get_keys_sorted_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + const char *k_a = *((const char *const *) a); + const char *k_b = *((const char *const *) b); + gint64 n_a; + gint64 n_b; + + n_a = svNumberedParseKey(k_a); + n_b = svNumberedParseKey(k_b); + NM_CMP_DIRECT(n_a, n_b); + NM_CMP_RETURN(strcmp(k_a, k_b)); + nm_assert_not_reached(); + return 0; +} + +const char ** +svGetKeysSorted(shvarFile *s, SvKeyType match_key_type, guint *out_len) +{ + gs_unref_hashtable GHashTable *keys_hash = NULL; + + keys_hash = svGetKeys(s, match_key_type); + if (!keys_hash) { + NM_SET_OUT(out_len, 0); + return NULL; + } + return ( + const char **) nm_utils_hash_keys_to_array(keys_hash, _get_keys_sorted_cmp, NULL, out_len); +} + +/*****************************************************************************/ + +const char * +svFindFirstNumberedKey(shvarFile *s, const char *key_prefix) +{ + const shvarLine *line; + + g_return_val_if_fail(s, NULL); + g_return_val_if_fail(key_prefix, NULL); + + c_list_for_each_entry (line, &s->lst_head, lst) { + if (line->key && line->line + && nms_ifcfg_rh_utils_is_numbered_tag(line->key, key_prefix, NULL)) + return line->key; + } + + return NULL; +} + +/*****************************************************************************/ + +static const char * +_svGetValue(shvarFile *s, const char *key, char **to_free) +{ + const shvarLine *line; + const char * v; + + nm_assert(s); + nm_assert(_shell_is_name(key, -1)); + nm_assert(to_free); + + ASSERT_key_is_well_known(key); + + line = g_hash_table_lookup(s->lst_idx, &key); + + if (line && line->line) { + v = svUnescape(line->line, to_free); + if (!v) { + /* a wrongly quoted value is treated like the empty string. + * See also svWriteFile(), which handles unparsable values + * that way. */ + nm_assert(!*to_free); + return ""; + } + return v; + } + *to_free = NULL; + return NULL; +} + +/* Returns the value for key. The value is either owned by @s + * or returned as to_free. This aims to avoid cloning the string. + * + * - like svGetValue_cp(), but avoids cloning the value if possible. + * - like svGetValueStr(), but does not ignore empty string values. + */ +const char * +svGetValue(shvarFile *s, const char *key, char **to_free) +{ + g_return_val_if_fail(s, NULL); + g_return_val_if_fail(key, NULL); + g_return_val_if_fail(to_free, NULL); + + return _svGetValue(s, key, to_free); +} + +/* Returns the value for key. The value is either owned by @s + * or returned as to_free. This aims to avoid cloning the string. + * + * - like svGetValue(), but does not return an empty string. + * - like svGetValueStr_cp(), but avoids cloning the value if possible. + */ +const char * +svGetValueStr(shvarFile *s, const char *key, char **to_free) +{ + const char *value; + + g_return_val_if_fail(s, NULL); + g_return_val_if_fail(key, NULL); + g_return_val_if_fail(to_free, NULL); + + value = _svGetValue(s, key, to_free); + if (!value || !value[0]) { + nm_assert(!*to_free); + return NULL; + } + return value; +} + +/* Returns the value for key. The returned value must be freed + * by the caller. + * + * - like svGetValue(), but always returns a copy of the value. + * - like svGetValueStr_cp(), but does not ignore an empty string. + */ +char * +svGetValue_cp(shvarFile *s, const char *key) +{ + char * to_free; + const char *value; + + g_return_val_if_fail(s, NULL); + g_return_val_if_fail(key, NULL); + + value = _svGetValue(s, key, &to_free); + if (!value) { + nm_assert(!to_free); + return NULL; + } + return to_free ?: g_strdup(value); +} + +/* Returns the value for key. The returned value must be freed + * by the caller. + * If the key is unset or the value an empty string, NULL is returned. + * + * - like svGetValueStr(), but always returns a copy of the value. + * - like svGetValue_cp(), but returns NULL instead of an empty string. + */ +char * +svGetValueStr_cp(shvarFile *s, const char *key) +{ + char * to_free; + const char *value; + + g_return_val_if_fail(s, NULL); + g_return_val_if_fail(key, NULL); + + value = _svGetValue(s, key, &to_free); + if (!value || !value[0]) { + nm_assert(!to_free); + return NULL; + } + return to_free ?: g_strdup(value); +} + +/* svGetValueBoolean: + * @s: fhe file + * @key: the name of the key to read + * @fallback: the fallback value in any error case + * + * Reads a value @key and converts it to a boolean using svParseBoolean(). + * + * Returns: the parsed boolean value or @fallback. + */ +int +svGetValueBoolean(shvarFile *s, const char *key, int fallback) +{ + gs_free char *to_free = NULL; + const char * value; + + value = _svGetValue(s, key, &to_free); + return svParseBoolean(value, fallback); +} + +/* svGetValueTernary: + * @s: fhe file + * @key: the name of the key to read + * + * Reads a value @key and converts it to a NMTernary value. + * + * Returns: the parsed NMTernary + */ +NMTernary +svGetValueTernary(shvarFile *s, const char *key) +{ + return svGetValueBoolean(s, key, NM_TERNARY_DEFAULT); +} + +/* svGetValueInt64: + * @s: fhe file + * @key: the name of the key to read + * @base: the numeric base (usually 10). Setting to 0 means "auto". Usually you want 10. + * @min: the minimum for range-check + * @max: the maximum for range-check + * @fallback: the fallback value in any error case + * + * Reads a value @key and converts it to an integer using _nm_utils_ascii_str_to_int64(). + * In case of error, @errno will be set and @fallback returned. */ +gint64 +svGetValueInt64(shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback) +{ + char * to_free; + const char *value; + gint64 result; + int errsv; + + value = _svGetValue(s, key, &to_free); + if (!value) { + nm_assert(!to_free); + /* indicate that the key does not exist (or has a syntax error + * and svUnescape() failed). */ + errno = ENOKEY; + return fallback; + } + + result = _nm_utils_ascii_str_to_int64(value, base, min, max, fallback); + if (to_free) { + errsv = errno; + g_free(to_free); + errno = errsv; + } + return result; +} + +gboolean +svGetValueEnum(shvarFile *s, const char *key, GType gtype, int *out_value, GError **error) +{ + gs_free char *to_free = NULL; + const char * svalue; + gs_free char *err_token = NULL; + int value; + + svalue = _svGetValue(s, key, &to_free); + if (!svalue) { + /* don't touch out_value. The caller is supposed + * to initialize it with the default value. */ + return TRUE; + } + + if (!nm_utils_enum_from_str(gtype, svalue, &value, &err_token)) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "Invalid token \"%s\" in \"%s\" for %s", + err_token, + svalue, + key); + return FALSE; + } + + NM_SET_OUT(out_value, value); + return TRUE; +} + +/*****************************************************************************/ + +gboolean +svUnsetAll(shvarFile *s, SvKeyType match_key_type) +{ + shvarLine *line; + gboolean changed = FALSE; + + g_return_val_if_fail(s, FALSE); + + c_list_for_each_entry (line, &s->lst_head, lst) { + ASSERT_shvarLine(line); + if (line->key && _svKeyMatchesType(line->key, match_key_type)) { + if (nm_clear_g_free(&line->line)) { + ASSERT_shvarLine(line); + changed = TRUE; + } + } + } + + if (changed) + s->modified = TRUE; + return changed; +} + +gboolean +svUnsetDirtyWellknown(shvarFile *s, NMTernary new_dirty_value) +{ + shvarLine *line; + gboolean changed = FALSE; + + g_return_val_if_fail(s, FALSE); + + c_list_for_each_entry (line, &s->lst_head, lst) { + const NMSIfcfgKeyTypeInfo *ti; + + ASSERT_shvarLine(line); + + if (line->dirty && line->key && line->line + && (ti = nms_ifcfg_rh_utils_is_well_known_key(line->key)) + && !NM_FLAGS_HAS(ti->key_flags, NMS_IFCFG_KEY_TYPE_KEEP_WHEN_DIRTY)) { + if (nm_clear_g_free(&line->line)) { + ASSERT_shvarLine(line); + changed = TRUE; + } + } + + if (new_dirty_value != NM_TERNARY_DEFAULT) + line->dirty = (new_dirty_value != NM_TERNARY_FALSE); + } + + if (changed) + s->modified = TRUE; + return changed; +} + +/* Same as svSetValueStr() but it preserves empty @value -- contrary to + * svSetValueStr() for which "" effectively means to remove the value. */ +gboolean +svSetValue(shvarFile *s, const char *key, const char *value) +{ + shvarLine *line; + shvarLine *l_shadowed; + gboolean changed = FALSE; + + g_return_val_if_fail(s, FALSE); + g_return_val_if_fail(key, FALSE); + + nm_assert(_shell_is_name(key, -1)); + + ASSERT_key_is_well_known(key); + + line = g_hash_table_lookup(s->lst_idx, &key); + if (line && (l_shadowed = line->prev_shadowed)) { + /* if we find multiple entries for the same key, we can + * delete the shadowed ones. */ + line->prev_shadowed = NULL; + changed = TRUE; + do { + shvarLine *l = l_shadowed; + + l_shadowed = l_shadowed->prev_shadowed; + line_free(l); + } while (l_shadowed); + } + + if (!value) { + if (line) { + /* We only clear the value, but leave the line entry. This way, if we + * happen to re-add the value, we write it to the same line again. */ + if (nm_clear_g_free(&line->line)) { + changed = TRUE; + } + } + } else { + if (!line) { + line = line_new_build(key, value); + if (!g_hash_table_add(s->lst_idx, line)) + nm_assert_not_reached(); + c_list_link_tail(&s->lst_head, &line->lst); + changed = TRUE; + } else { + if (line_set(line, value)) + changed = TRUE; + } + } + + if (changed) + s->modified = TRUE; + return changed; +} + +/* Set the variable <key> equal to the value <value>. + * If <key> does not exist, and the <current> pointer is set, append + * the key=value pair after that line. Otherwise, append the pair + * to the bottom of the file. + */ +gboolean +svSetValueStr(shvarFile *s, const char *key, const char *value) +{ + return svSetValue(s, key, value && value[0] ? value : NULL); +} + +gboolean +svSetValueInt64(shvarFile *s, const char *key, gint64 value) +{ + char buf[NM_DECIMAL_STR_MAX(value)]; + + return svSetValue(s, key, nm_sprintf_buf(buf, "%" G_GINT64_FORMAT, value)); +} + +gboolean +svSetValueInt64_cond(shvarFile *s, const char *key, gboolean do_set, gint64 value) +{ + if (do_set) + return svSetValueInt64(s, key, value); + else + return svUnsetValue(s, key); +} + +gboolean +svSetValueBoolean(shvarFile *s, const char *key, gboolean value) +{ + return svSetValue(s, key, value ? "yes" : "no"); +} + +gboolean +svSetValueTernary(shvarFile *s, const char *key, NMTernary value) +{ + if (NM_IN_SET(value, NM_TERNARY_TRUE, NM_TERNARY_FALSE)) + return svSetValueBoolean(s, key, (gboolean) value); + else + return svUnsetValue(s, key); +} + +gboolean +svSetValueBoolean_cond_true(shvarFile *s, const char *key, gboolean value) +{ + return svSetValue(s, key, value ? "yes" : NULL); +} + +gboolean +svSetValueEnum(shvarFile *s, const char *key, GType gtype, int value) +{ + gs_free char *v = NULL; + + v = _nm_utils_enum_to_str_full(gtype, value, " ", NULL); + return svSetValueStr(s, key, v); +} + +gboolean +svUnsetValue(shvarFile *s, const char *key) +{ + return svSetValue(s, key, NULL); +} + +/*****************************************************************************/ + +/* Write the current contents iff modified. Returns FALSE on error + * and TRUE on success. Do not write if no values have been modified. + * The mode argument is only used if creating the file, not if + * re-writing an existing file, and is passed unchanged to the + * open() syscall. + */ +gboolean +svWriteFile(shvarFile *s, int mode, GError **error) +{ + FILE * f; + int tmpfd; + CList *current; + int errsv; + + if (s->modified) { + if (s->fd == -1) + s->fd = open(s->fileName, O_WRONLY | O_CREAT | O_CLOEXEC, mode); + if (s->fd == -1) { + errsv = errno; + g_set_error(error, + G_FILE_ERROR, + g_file_error_from_errno(errsv), + "Could not open file '%s' for writing: %s", + s->fileName, + nm_strerror_native(errsv)); + return FALSE; + } + if (ftruncate(s->fd, 0) < 0) { + errsv = errno; + g_set_error(error, + G_FILE_ERROR, + g_file_error_from_errno(errsv), + "Could not overwrite file '%s': %s", + s->fileName, + nm_strerror_native(errsv)); + return FALSE; + } + + tmpfd = fcntl(s->fd, F_DUPFD_CLOEXEC, 0); + if (tmpfd == -1) { + errsv = errno; + g_set_error(error, + G_FILE_ERROR, + g_file_error_from_errno(errsv), + "Internal error writing file '%s': %s", + s->fileName, + nm_strerror_native(errsv)); + return FALSE; + } + f = fdopen(tmpfd, "w"); + if (!f) { + errsv = errno; + g_set_error(error, + G_FILE_ERROR, + g_file_error_from_errno(errsv), + "Internal error writing file '%s': %s", + s->fileName, + nm_strerror_native(errsv)); + return FALSE; + } + fseek(f, 0, SEEK_SET); + c_list_for_each (current, &s->lst_head) { + const shvarLine *line = c_list_entry(current, shvarLine, lst); + const char * str; + char * s_tmp; + gboolean valid_value; + + ASSERT_shvarLine(line); + + if (!line->key) { + str = nm_str_skip_leading_spaces(line->line); + if (NM_IN_SET(str[0], '\0', '#')) + fprintf(f, "%s\n", line->line); + else + fprintf(f, "#NM: %s\n", line->line); + continue; + } + + if (!line->line) + continue; + + /* we check that the assignment can be properly unescaped. */ + valid_value = !!svUnescape(line->line, &s_tmp); + g_free(s_tmp); + + if (valid_value) + fprintf(f, "%s=%s\n", line->key_with_prefix, line->line); + else { + fprintf(f, "%s=\n", line->key); + fprintf(f, "#NM: %s=%s\n", line->key_with_prefix, line->line); + } + } + fclose(f); + } + + return TRUE; +} + +void +svCloseFile(shvarFile *s) +{ + shvarLine *line; + + g_return_if_fail(s != NULL); + + if (s->fd >= 0) + nm_close(s->fd); + g_free(s->fileName); + g_hash_table_destroy(s->lst_idx); + while ((line = c_list_first_entry(&s->lst_head, shvarLine, lst))) + line_free(line); + g_slice_free(shvarFile, s); +} diff --git a/src/core/settings/plugins/ifcfg-rh/shvar.h b/src/core/settings/plugins/ifcfg-rh/shvar.h new file mode 100644 index 00000000..6965d873 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/shvar.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 1999 Red Hat, Inc. + */ + +#ifndef _SHVAR_H +#define _SHVAR_H + +typedef struct _shvarFile shvarFile; + +typedef enum { + SV_KEY_TYPE_ANY = (1LL << 0), + SV_KEY_TYPE_ROUTE_SVFORMAT = (1LL << 1), + SV_KEY_TYPE_IP4_ADDRESS = (1LL << 2), + SV_KEY_TYPE_TC = (1LL << 3), + SV_KEY_TYPE_USER = (1LL << 4), + SV_KEY_TYPE_SRIOV_VF = (1LL << 5), + SV_KEY_TYPE_ROUTING_RULE4 = (1LL << 6), + SV_KEY_TYPE_ROUTING_RULE6 = (1LL << 7), +} SvKeyType; + +const char *svFileGetName(const shvarFile *s); + +void _nmtst_svFileSetName(shvarFile *s, const char *fileName); +void _nmtst_svFileSetModified(shvarFile *s); + +/*****************************************************************************/ + +shvarFile *svFile_new(const char *name, int fd, const char *content); + +/* Create the file <name>, return a shvarFile (never fails) */ +shvarFile *svCreateFile(const char *name); + +/* Open the file <name>, return shvarFile on success, NULL on failure */ +shvarFile *svOpenFile(const char *name, GError **error); + +/*****************************************************************************/ + +const char *svFindFirstNumberedKey(shvarFile *s, const char *key_prefix); + +/* Get the value associated with the key, and leave the current pointer + * pointing at the line containing the value. The char* returned MUST + * be freed by the caller. + */ +const char *svGetValue(shvarFile *s, const char *key, char **to_free); +char * svGetValue_cp(shvarFile *s, const char *key); + +const char *svGetValueStr(shvarFile *s, const char *key, char **to_free); +char * svGetValueStr_cp(shvarFile *s, const char *key); + +int svParseBoolean(const char *value, int def); + +gint64 svNumberedParseKey(const char *key); + +GHashTable *svGetKeys(shvarFile *s, SvKeyType match_key_type); + +const char **svGetKeysSorted(shvarFile *s, SvKeyType match_key_type, guint *out_len); + +/* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true") + * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false") + * return <def> otherwise + */ +int svGetValueBoolean(shvarFile *s, const char *key, int def); + +NMTernary svGetValueTernary(shvarFile *s, const char *key); + +gint64 +svGetValueInt64(shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback); + +gboolean svGetValueEnum(shvarFile *s, const char *key, GType gtype, int *out_value, GError **error); + +/* Set the variable <key> equal to the value <value>. + * If <key> does not exist, and the <current> pointer is set, append + * the key=value pair after that line. Otherwise, prepend the pair + * to the top of the file. + */ +gboolean svSetValue(shvarFile *s, const char *key, const char *value); +gboolean svSetValueStr(shvarFile *s, const char *key, const char *value); +gboolean svSetValueBoolean(shvarFile *s, const char *key, gboolean value); +gboolean svSetValueBoolean_cond_true(shvarFile *s, const char *key, gboolean value); +gboolean svSetValueInt64(shvarFile *s, const char *key, gint64 value); +gboolean svSetValueInt64_cond(shvarFile *s, const char *key, gboolean do_set, gint64 value); +gboolean svSetValueEnum(shvarFile *s, const char *key, GType gtype, int value); +gboolean svSetValueTernary(shvarFile *s, const char *key, NMTernary value); + +gboolean svUnsetValue(shvarFile *s, const char *key); +gboolean svUnsetAll(shvarFile *s, SvKeyType match_key_type); +gboolean svUnsetDirtyWellknown(shvarFile *s, NMTernary new_dirty_value); + +/* Write the current contents iff modified. Returns FALSE on error + * and TRUE on success. Do not write if no values have been modified. + * The mode argument is only used if creating the file, not if + * re-writing an existing file, and is passed unchanged to the + * open() syscall. + */ +gboolean svWriteFile(shvarFile *s, int mode, GError **error); + +static inline gboolean +svWriteFileWithoutDirtyWellknown(shvarFile *s, int mode, GError **error) +{ + svUnsetDirtyWellknown(s, NM_TERNARY_FALSE); + return svWriteFile(s, mode, error); +} + +/* Close the file descriptor (if open) and free the shvarFile. */ +void svCloseFile(shvarFile *s); + +const char *svEscape(const char *s, char **to_free); +const char *svUnescape(const char *s, char **to_free); + +static inline void +_nm_auto_shvar_file_close(shvarFile **p_s) +{ + if (*p_s) { + int errsv = errno; + + svCloseFile(*p_s); + errno = errsv; + } +} +#define nm_auto_shvar_file_close nm_auto(_nm_auto_shvar_file_close) + +#endif /* _SHVAR_H */ diff --git a/src/core/settings/plugins/ifcfg-rh/tests/meson.build b/src/core/settings/plugins/ifcfg-rh/tests/meson.build new file mode 100644 index 00000000..077e4782 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/meson.build @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +exe = executable( + 'test-ifcfg-rh', + 'test-ifcfg-rh.c', + dependencies: libNetworkManagerTest_dep, + c_args: test_c_flags, + link_with: libnms_ifcfg_rh_core, +) + +test( + 'ifcfg-rh/test-ifcfg-rh', + test_script, + timeout: 90, + args: test_args + [exe.full_path()], +) diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected new file mode 100644 index 00000000..d81d9187 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected @@ -0,0 +1,8 @@ +HWADDR=00:22:15:59:62:97 +TYPE=Ethernet +BRIDGING_OPTS="priority=28 hairpin_mode=1" +NAME="System test-bridge-component" +UUID=${UUID} +DEVICE=eth0 +ONBOOT=no +BRIDGE=br0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected new file mode 100644 index 00000000..2e69c5e7 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected @@ -0,0 +1,16 @@ +HWADDR=00:22:15:59:62:97 +TYPE=Ethernet +NAME="System test-bridge-component" +UUID=${UUID} +DEVICE=eth0 +ONBOOT=no +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected new file mode 100644 index 00000000..59a6f79b --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected @@ -0,0 +1,22 @@ +HWADDR=00:11:22:33:44:EE +TYPE=Ethernet +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=PEAP +IEEE_8021X_IDENTITY="Jara Cimrman" +IEEE_8021X_PASSWORD_FLAGS="user ask" +IEEE_8021X_PEAP_VERSION=1 +IEEE_8021X_INNER_AUTH_METHODS=GTC +IEEE_8021X_SUBJECT_MATCH=server1.yourdomain.tld +IEEE_8021X_PHASE2_SUBJECT_MATCH=server2.yourdomain.tld +IEEE_8021X_ALTSUBJECT_MATCHES="a.yourdomain.tld b.yourdomain.tld c.yourdomain.tld" +IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES="x.yourdomain.tld y.yourdomain.tld" +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="System test-wired-802-1X-subj-matches" +UUID=${UUID} +DEVICE=eth0 +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected new file mode 100644 index 00000000..a48be78a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected @@ -0,0 +1,34 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +NM_USER__M_Y___053=val=MY.+ +NM_USER__M_Y___055=val=MY.- +NM_USER__M_Y___057=val=MY./ +NM_USER__M_Y__8_053_V=val=MY.8+V +NM_USER__M_Y__8_055_V=val=MY.8-V +NM_USER__M_Y__8_057_V=val=MY.8/V +NM_USER__M_Y__8_075_V=val=MY.8=V +NM_USER__M_Y__8_V=val=MY.8V +NM_USER__M_Y__8_137_V=val=MY.8_V +NM_USER__M_Y___075=val=MY.= +NM_USER__M_Y___A_V=val=MY.AV +NM_USER__M_Y___137=val=MY._ +NM_USER_MY___AV=val=my.Av +NM_USER_MY___137V=val=my._v +NM_USER_MY__KEYS__1=val=my.keys.1 +NM_USER_MY__OTHER___K_E_Y__42=val=my.other.KEY.42 +NM_USER_MY__V_053=val=my.v+ +NM_USER_MY__V_137_137AL3=val=my.v__al3 +NM_USER_MY__VAL1= +NM_USER_MY__VAL2=val=my.val2 +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test User 1" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected new file mode 100644 index 00000000..b288c04a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected @@ -0,0 +1,17 @@ +BONDING_OPTS="mode=balance-rr downdelay=5 miimon=100 updelay=10" +TYPE=Bond +BONDING_MASTER=yes +HWADDR= +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=none +IPADDR=1.1.1.3 +PREFIX=24 +GATEWAY=1.1.1.1 +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Test Write Bond Main" +UUID=${UUID} +DEVICE=bond0 +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected new file mode 100644 index 00000000..a8ff8df3 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected @@ -0,0 +1,9 @@ +HWADDR=31:33:33:37:BE:CD +MTU=1492 +TYPE=Ethernet +BRIDGING_OPTS="priority=50 path_cost=33" +BRIDGE_PORT_VLANS="1 untagged,2 pvid,4-4094 untagged" +NAME="Test Write Bridge Component" +UUID=${UUID} +ONBOOT=yes +BRIDGE=br0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected new file mode 100644 index 00000000..80e96921 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected @@ -0,0 +1,11 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Test Write Permissions" +UUID=${UUID} +ONBOOT=yes +USERS="blahblah foobar asdfasdf" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected new file mode 100644 index 00000000..716eedc2 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected @@ -0,0 +1,15 @@ +TYPE=Ethernet +PROXY_METHOD=auto +PAC_URL=https://wpad.neverland.org/wpad.dat +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Proxy Basic" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected new file mode 100644 index 00000000..0c2fa035 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected @@ -0,0 +1,19 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +ROUTING_RULE_1="priority 10 from 0.0.0.0/0 table 1" +ROUTING_RULE_2="priority 10 to 192.167.8.0/24 table 2" +ROUTING_RULE6_3="priority 10 from ::/0 table 10" +ROUTING_RULE6_4="priority 10 to 1:2:3::5/24 table 22" +ROUTING_RULE6_5="priority 10 to 1:3:3::5 table 55" +NAME="Test Write Routing Rules" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected new file mode 100644 index 00000000..2df1fbb3 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected @@ -0,0 +1,9 @@ +CONNECTED_MODE=no +TYPE=InfiniBand +TEAM_PORT_CONFIG="{\"inf1\": {\"prio\": -10, \"sticky\": true}}" +NAME="Test Write Team Infiniband Port" +UUID=${UUID} +DEVICE=inf1 +ONBOOT=yes +TEAM_MASTER=team0 +DEVICETYPE=TeamPort diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected new file mode 100644 index 00000000..ff55cefe --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected @@ -0,0 +1,6 @@ +TEAM_PORT_CONFIG="{\"p4p1\": {\"prio\": -10, \"sticky\": true}}" +NAME="Test Write Team Port" +UUID=${UUID} +ONBOOT=yes +TEAM_MASTER=team0 +DEVICETYPE=TeamPort diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected new file mode 100644 index 00000000..9c2a1ff0 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected @@ -0,0 +1,21 @@ +VLAN=yes +TYPE=Vlan +PHYSDEV=eth0 +VLAN_ID=444 +REORDER_HDR=yes +GVRP=no +MVRP=no +HWADDR= +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write VLAN reorder_hdr" +UUID=${UUID} +ONBOOT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_AP_Mode.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_AP_Mode.cexpected new file mode 100644 index 00000000..ccec2b81 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_AP_Mode.cexpected @@ -0,0 +1,19 @@ +ESSID=MySSID +MODE=Ap +CHANNEL=196 +MAC_ADDRESS_RANDOMIZATION=default +AP_ISOLATION=yes +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wi-Fi AP Mode" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected new file mode 100644 index 00000000..ddbd986f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected @@ -0,0 +1,18 @@ +ESSID="Test SSID" +MODE=Managed +BAND=a +MAC_ADDRESS_RANDOMIZATION=default +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wi-Fi Band A" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected new file mode 100644 index 00000000..495a24d3 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected @@ -0,0 +1,17 @@ +ESSID="Test SSID" +SSID_HIDDEN=yes +MAC_ADDRESS_RANDOMIZATION=default +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wi-Fi Hidden" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected new file mode 100644 index 00000000..aec6918a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected @@ -0,0 +1,18 @@ +MACADDR=random +ESSID="Test SSID" +MODE=Managed +MAC_ADDRESS_RANDOMIZATION=always +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wi-Fi MAC always" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected new file mode 100644 index 00000000..9d47163f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected @@ -0,0 +1,17 @@ +ESSID="Test SSID" +MODE=Managed +MAC_ADDRESS_RANDOMIZATION=default +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wi-Fi MAC default" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected new file mode 100644 index 00000000..43c07ddb --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected @@ -0,0 +1,18 @@ +MACADDR=permanent +ESSID="Test SSID" +MODE=Managed +MAC_ADDRESS_RANDOMIZATION=never +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wi-Fi MAC missing" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected new file mode 100644 index 00000000..21f2e2de --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected @@ -0,0 +1,18 @@ +MACADDR=permanent +ESSID="Test SSID" +MODE=Managed +MAC_ADDRESS_RANDOMIZATION=never +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wi-Fi MAC never" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected new file mode 100644 index 00000000..d3db19b2 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected @@ -0,0 +1,16 @@ +ESSID=blahblah +MODE=Managed +KEY_MGMT=IEEE8021X +SECURITYMODE=leap +IEEE_8021X_IDENTITY="Bill Smith" +MAC_ADDRESS_RANDOMIZATION=default +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Test Write Wifi LEAP" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected new file mode 100644 index 00000000..32db7262 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected @@ -0,0 +1,15 @@ +ESSID=blahblah104 +MODE=Managed +SECURITYMODE=open +DEFAULTKEY=1 +MAC_ADDRESS_RANDOMIZATION=default +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Test Write Wifi WEP 104 ASCII" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected new file mode 100644 index 00000000..214f5da5 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected @@ -0,0 +1,15 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +ETHTOOL_OPTS="autoneg off speed 10 duplex half" +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wired Auto-Negotiate" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected new file mode 100644 index 00000000..cd8fc96f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected @@ -0,0 +1,22 @@ +HWADDR=31:33:33:37:BE:CD +MTU=1492 +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=none +IPADDR=1.1.1.3 +PREFIX=24 +IPADDR1=1.1.1.5 +PREFIX1=24 +GATEWAY=1.1.1.1 +DNS1=4.2.2.1 +DNS2=4.2.2.2 +DOMAIN="foobar.com lab.foobar.com" +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +ACD_TIMEOUT=400 +ARPING_WAIT=1 +IPV6INIT=no +NAME="Test Write Wired Static Routes" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected new file mode 100644 index 00000000..de66dcdd --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected @@ -0,0 +1,15 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +ETHTOOL_OPTS="wol umgs sopass 00:00:00:11:22:33" +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wired Wake-on-LAN" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected new file mode 100644 index 00000000..9499278e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected @@ -0,0 +1,13 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +MATCH_DRIVER="!virtio e1000e" +MATCH_INTERFACE_NAME="ens* eth\\ 1? !veth*" +MATCH_KERNEL_COMMAND_LINE=!ip= +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Test Write Wired with Match setting" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected new file mode 100644 index 00000000..44eb777c --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected @@ -0,0 +1,23 @@ +VLAN=yes +TYPE=Vlan +PHYSDEV=eth9 +VLAN_ID=43 +REORDER_HDR=yes +GVRP=yes +VLAN_FLAGS=LOOSE_BINDING +MVRP=no +VLAN_INGRESS_PRIORITY_MAP=0:1,2:5 +VLAN_EGRESS_PRIORITY_MAP=3:1,12:3,14:7 +HWADDR= +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=none +IPADDR=192.168.43.149 +PREFIX=24 +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Vlan test-vlan-interface" +UUID=${UUID} +DEVICE=vlan43 +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0 new file mode 100644 index 00000000..4f9d645c --- /dev/null +++ b/src/core/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/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:1 new file mode 100644 index 00000000..37c0df11 --- /dev/null +++ b/src/core/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/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:2 new file mode 100644 index 00000000..0c3b6a7e --- /dev/null +++ b/src/core/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/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:99 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:99 new file mode 100644 index 00000000..5df3f154 --- /dev/null +++ b/src/core/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/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1 new file mode 100644 index 00000000..f6fefc0d --- /dev/null +++ b/src/core/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/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1:1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1:1 new file mode 100644 index 00000000..21971611 --- /dev/null +++ b/src/core/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/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem2 new file mode 100644 index 00000000..92ed461e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem2 @@ -0,0 +1,12 @@ +TYPE=Ethernet +DEVICE=aliasem2 +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/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem2:1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem2:1 new file mode 100644 index 00000000..ce0f23ba --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem2:1 @@ -0,0 +1,3 @@ +# bad: wrong DEVICE +DEVICE=aliasem0:1 +IPADDR=192.168.1.20 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3 new file mode 100644 index 00000000..b7bdd781 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3 @@ -0,0 +1,11 @@ +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 +IPV6INIT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:1 new file mode 100644 index 00000000..5e151875 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:1 @@ -0,0 +1,4 @@ +DEVICE=aliasem3:1 +IPADDR=192.168.1.6 +DEFROUTE=yes +GATEWAY=192.168.1.1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected new file mode 100644 index 00000000..56e233cc --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected @@ -0,0 +1,41 @@ +TYPE=Ethernet +DCB=yes +DCB_APP_FCOE_ENABLE=yes +DCB_APP_FCOE_ADVERTISE=yes +DCB_APP_FCOE_WILLING=yes +DCB_APP_FCOE_PRIORITY=5 +DCB_APP_FCOE_MODE=fabric +DCB_APP_ISCSI_ENABLE=yes +DCB_APP_ISCSI_ADVERTISE=yes +DCB_APP_ISCSI_WILLING=yes +DCB_APP_ISCSI_PRIORITY=1 +DCB_APP_FIP_ENABLE=yes +DCB_APP_FIP_ADVERTISE=yes +DCB_APP_FIP_WILLING=yes +DCB_APP_FIP_PRIORITY=3 +DCB_PFC_ENABLE=yes +DCB_PFC_ADVERTISE=yes +DCB_PFC_WILLING=yes +DCB_PFC_UP=11010110 +DCB_PG_ENABLE=yes +DCB_PG_ADVERTISE=yes +DCB_PG_WILLING=yes +DCB_PG_ID=4f6f173f +DCB_PG_PCT=10,20,15,10,2,3,35,5 +DCB_PG_UPPCT=10,20,30,40,50,10,0,25 +DCB_PG_STRICT=10110001 +DCB_PG_UP2TC=34721056 +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME=dcb-test +UUID=${UUID} +DEVICE=eth0 +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1 new file mode 100644 index 00000000..ecb36c37 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1 @@ -0,0 +1,14 @@ +DNS1="192.0.2.1" +IPADDR="102.0.2.2" +GATEWAY="192.0.2.1" +NETMASK="255.254.0.0" +BOOTPROTO="static" +DEVICE="eth1" +ONBOOT="yes" +IPV6INIT="yes" + +#bogus +PREFIX1=25 +NETMASK0=255.255.0.0 + +#end diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected new file mode 100644 index 00000000..87493ac2 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected @@ -0,0 +1,22 @@ +DNS1=192.0.2.1 +IPADDR=102.0.2.2 +GATEWAY=192.0.2.1 +NETMASK=255.254.0.0 +DEVICE=eth1 +ONBOOT=yes +IPV6INIT=yes + +#bogus + +#end +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +PREFIX=15 +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +NAME="System netmask-1" +UUID=${UUID} diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected new file mode 100644 index 00000000..ead3a047 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected @@ -0,0 +1,13 @@ +ESSID=blahblah +MODE=Managed +MAC_ADDRESS_RANDOMIZATION=default +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="random wifi connection" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected new file mode 100644 index 00000000..7bc1ae6c --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected @@ -0,0 +1,15 @@ +ESSID=SomeSSID +MODE=Managed +MAC_ADDRESS_RANDOMIZATION=default +TYPE=Wireless +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="random wifi connection 2" +UUID=${UUID} +ONBOOT=yes +USERS=superman +DEFAULTKEY=1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected new file mode 100644 index 00000000..b01372af --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected @@ -0,0 +1,13 @@ +VLAN=yes +PHYSDEV=enp31s0f1 +VLAN_ID=142 +REORDER_HDR=yes +GVRP=no +MVRP=no +HWADDR= +NAME=team-slave-enp31s0f1-142 +UUID=74f435bb-ede4-415a-9d48-f580b60eba04 +DEVICE=enp31s0f1-142 +ONBOOT=no +TEAM_MASTER=team142 +DEVICETYPE=TeamPort diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type new file mode 100644 index 00000000..8d295c76 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type @@ -0,0 +1,7 @@ +DEVICE=bond0 +NM_CONTROLLED=yes +TYPE=Ethernet +BONDING_OPTS="miimon=213 mode=4 lacp_rate=1" +BONDING_MASTER=yes +ONBOOT=yes +BOOTPROTO=none diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-main b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-main new file mode 100644 index 00000000..4c4834d5 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-main @@ -0,0 +1,5 @@ +DEVICE=bond0 +ONBOOT=no +TYPE=Bond +BOOTPROTO=dhcp +BONDING_OPTS="miimon=100" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-mode-numeric b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-mode-numeric new file mode 100644 index 00000000..9989f58b --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-mode-numeric @@ -0,0 +1,5 @@ +DEVICE=bond0 +ONBOOT=no +TYPE=Bond +BOOTPROTO=dhcp +BONDING_OPTS="mode=4 miimon=100" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-slave b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-slave new file mode 100644 index 00000000..c0de645d --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-slave @@ -0,0 +1,6 @@ +DEVICE=eth0 +HWADDR=00:22:15:59:62:97 +ONBOOT=no +MASTER=bond0 +# This should be ignored +BOOTPROTO=dhcp diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-slave-ib b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-slave-ib new file mode 100644 index 00000000..5bd9a3a8 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-slave-ib @@ -0,0 +1,7 @@ +TYPE=InfiniBand +DEVICE=ib0 +HWADDR=80:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22 +ONBOOT=no +MASTER=bond0 +# This should be ignored +BOOTPROTO=dhcp diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-component b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-component new file mode 100644 index 00000000..24b51221 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-component @@ -0,0 +1,5 @@ +DEVICE=eth0 +HWADDR=00:22:15:59:62:97 +ONBOOT=no +BRIDGE=br0 +BRIDGING_OPTS="priority=28 hairpin_mode=1 path_cost=100" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main new file mode 100644 index 00000000..1788efe2 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main @@ -0,0 +1,8 @@ +DEVICE=br0 +ONBOOT=no +TYPE=Bridge +BOOTPROTO=dhcp +STP=on +DELAY=2 +BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352 multicast_snooping=0 group_fwd_mask=24 vlan_filtering=1 default_pvid=99" +MACADDR=00:16:41:11:22:33 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-missing-stp b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-missing-stp new file mode 100644 index 00000000..7135cd42 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-missing-stp @@ -0,0 +1,5 @@ +DEVICE=br0 +ONBOOT=no +TYPE=Bridge +BOOTPROTO=dhcp + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb new file mode 100644 index 00000000..6e6589d3 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb @@ -0,0 +1,43 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +GATEWAY=1.1.1.1 + +# dcb global switch +# use DCB=no to turn it off +DCB=yes + +# application settings +DCB_APP_FCOE_PRIORITY=7 +DCB_APP_FCOE_ENABLE=yes +DCB_APP_FCOE_ADVERTISE=yes +DCB_APP_FCOE_WILLING=yes + +DCB_APP_ISCSI_PRIORITY=6 +DCB_APP_ISCSI_ENABLE=yes +DCB_APP_ISCSI_ADVERTISE=yes +DCB_APP_ISCSI_WILLING=yes + +DCB_APP_FIP_PRIORITY=2 +DCB_APP_FIP_ENABLE=yes +DCB_APP_FIP_ADVERTISE=yes +DCB_APP_FIP_WILLING=yes + +# priority group settings +DCB_PG_UP2TC=76543210 +DCB_PG_PCT=25,0,0,75,0,0,0,0 +DCB_PG_ID=0000111f +DCB_PG_UPPCT=5,10,30,25,10,50,5,0 +DCB_PG_STRICT=00110101 +DCB_PG_ENABLE=yes +DCB_PG_ADVERTISE=yes +DCB_PG_WILLING=yes + +# priority flow control settings +DCB_PFC_UP=10011010 +DCB_PFC_ENABLE=yes +DCB_PFC_ADVERTISE=yes +DCB_PFC_WILLING=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-bad-booleans b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-bad-booleans new file mode 100644 index 00000000..2f46a082 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-bad-booleans @@ -0,0 +1,12 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +GATEWAY=1.1.1.1 + +DCB=yes +DCB_PG_STRICT=02030101 +DCB_PG_ENABLE=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-bad-percent b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-bad-percent new file mode 100644 index 00000000..0c5d7b88 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-bad-percent @@ -0,0 +1,12 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +GATEWAY=1.1.1.1 + +DCB=yes +DCB_PG_PCT=25,0,0,75,0,0,110,0 +DCB_PG_ENABLE=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-bad-uints b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-bad-uints new file mode 100644 index 00000000..8334999b --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-bad-uints @@ -0,0 +1,12 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +GATEWAY=1.1.1.1 + +DCB=yes +DCB_PG_UP2TC=96543210 +DCB_PG_ENABLE=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-default-app-priorities b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-default-app-priorities new file mode 100644 index 00000000..5bdcb63c --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-default-app-priorities @@ -0,0 +1,7 @@ +TYPE=Ethernet +DEVICE=eth0 +DCB=yes +DCB_APP_FCOE_ENABLE=yes +DCB_APP_ISCSI_ENABLE=yes +DCB_APP_FIP_ENABLE=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-pgpct-not-100 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-pgpct-not-100 new file mode 100644 index 00000000..95eb7c5b --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-pgpct-not-100 @@ -0,0 +1,12 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +GATEWAY=1.1.1.1 + +DCB=yes +DCB_PG_PCT=25,0,0,3,75,0,25,0 +DCB_PG_ENABLE=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-short-booleans b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-short-booleans new file mode 100644 index 00000000..fbe1cbbc --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-short-booleans @@ -0,0 +1,12 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +GATEWAY=1.1.1.1 + +DCB=yes +DCB_PG_STRICT=0111010 +DCB_PG_ENABLE=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-short-percent b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-short-percent new file mode 100644 index 00000000..07ba6b64 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-short-percent @@ -0,0 +1,12 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +GATEWAY=1.1.1.1 + +DCB=yes +DCB_PG_PCT=25,0,0,75,0,0,0 +DCB_PG_ENABLE=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-short-uints b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-short-uints new file mode 100644 index 00000000..05ae39cb --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb-short-uints @@ -0,0 +1,12 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +GATEWAY=1.1.1.1 + +DCB=yes +DCB_PG_UP2TC=7654321 +DCB_PG_ENABLE=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dns-options b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dns-options new file mode 100644 index 00000000..cea2471a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dns-options @@ -0,0 +1,16 @@ +TYPE=Ethernet +DEVICE=eth2 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +USERCTL=yes +NM_CONTROLLED=yes +PEERDNS=yes +DNS1=10.2.0.4 +DOMAIN="lorem.com ipsum.org dolor.edu" +RES_OPTIONS="ndots:3 single-request-reopen" +IPV6_RES_OPTIONS="inet6" +IPV6INIT=yes +IPV6_AUTOCONF=no +IPV6ADDR="1001:abba::1234/56" +IPV6ADDR_SECONDARIES="2001:abba::2234/64 3001:abba::3234/96" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-fcoe-fabric b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-fcoe-fabric new file mode 100644 index 00000000..cde1c28d --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-fcoe-fabric @@ -0,0 +1,6 @@ +TYPE=Ethernet +DEVICE=eth0 +DCB=yes +DCB_APP_FCOE_ENABLE=yes +DCB_APP_FCOE_MODE=fabric + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-fcoe-vn2vn b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-fcoe-vn2vn new file mode 100644 index 00000000..20c08451 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-fcoe-vn2vn @@ -0,0 +1,6 @@ +TYPE=Ethernet +DEVICE=eth0 +DCB=yes +DCB_APP_FCOE_ENABLE=yes +DCB_APP_FCOE_MODE=vn2vn + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft new file mode 100644 index 00000000..d3e4edca --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft @@ -0,0 +1,10 @@ +# Generated by dracut initrd +DEVICE="ibft0" +ONBOOT=yes +NETBOOT=yes +UUID="2aa8c19d-9744-4ca4-b74f-c37e73f2918e" +BOOTPROTO=ibft +HWADDR="00:33:21:98:b9:f0" +TYPE=Ethernet +NAME="ibft0" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband new file mode 100644 index 00000000..6c36f1de --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-infiniband @@ -0,0 +1,8 @@ +TYPE=InfiniBand +DEVICE=ib0 +HWADDR=80:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22 +CONNECTED_MODE=yes +MTU=65520 +IPADDR=192.168.2.2 +NETMASK=255.255.255.0 +GATEWAY=192.168.2.1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected new file mode 100644 index 00000000..cae51b89 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected @@ -0,0 +1,11 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6_DISABLED=yes +IPV6INIT=no +NAME="Test Write Wired Disabled IP6" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-minimal b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-minimal new file mode 100644 index 00000000..63bcc840 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-minimal @@ -0,0 +1,4 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +DEVICE=eth0 +HWADDR=00:16:41:11:22:33 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-misc-variables b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-misc-variables new file mode 100644 index 00000000..1b10cddb --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-misc-variables @@ -0,0 +1,4 @@ +DEVICE=eth0 +HWADDR_BLACKLIST="00:16:41:11:22:88 00:16:41:11:22:99 XX:aa:invalid 6A:5D:5A:FA:DD:F0" +AUTOCONNECT_RETRIES=100 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-nm-controlled b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-nm-controlled new file mode 100644 index 00000000..a38f1f5f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-nm-controlled @@ -0,0 +1,9 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:f8:9f +BOOTPROTO=dhcp +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +NM_CONTROLLED=no + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-nm-controlled-unrecognized b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-nm-controlled-unrecognized new file mode 100644 index 00000000..58263ece --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-nm-controlled-unrecognized @@ -0,0 +1,6 @@ +TYPE=IPoAC +NAME=PigeonNet +DEVICE=ipoac0 +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-noip b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-noip new file mode 100644 index 00000000..b5028daa --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-noip @@ -0,0 +1,3 @@ +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-onboot-no b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-onboot-no new file mode 100644 index 00000000..b9491711 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-onboot-no @@ -0,0 +1,5 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +ONBOOT=no + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-permissions b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-permissions new file mode 100644 index 00000000..5b413aa9 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-permissions @@ -0,0 +1,8 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +USERS="dcbw ssmith johnny5" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-read-proxy-basic b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-read-proxy-basic new file mode 100644 index 00000000..e2cf6c21 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-read-proxy-basic @@ -0,0 +1,15 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +NM_CONTROLLED=yes +PEERDNS=no + +# proxy configuration +PROXY_METHOD=auto +PAC_URL=http://wpad.mycompany.com/wpad.dat +BROWSER_ONLY=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sit-ignore b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sit-ignore new file mode 100644 index 00000000..a2581db6 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sit-ignore @@ -0,0 +1,12 @@ +# this ifcfg-file represents a "sit" type without explicit TYPE. +# Such connection types are not supported by NetworkManager and +# the connection should be ignored based on the presence of +# IPV6TUNNELIPV4. + +DEVICE=sit1 +BOOTPROTO=none +ONBOOT=yes +IPV6INIT=yes +IPV6TUNNELIPV4=5.4.3.6 +IPV6TUNNELIPV4LOCAL=172.17.1.9 +IPV6ADDR=2001:470:2:3:4::2/64 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov new file mode 100644 index 00000000..142f56e4 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov @@ -0,0 +1,19 @@ +TYPE=Ethernet +DEVICE=eth0 +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 +NAME=ethernet-sriov +UUID=acc703b8-e751-44ce-b456-1550bdf2057e +SRIOV_TOTAL_VFS=16 +SRIOV_AUTOPROBE_DRIVERS=0 +SRIOV_VF15="max-tx-rate=200 mac=01:23:45:67:89:ab vlans=2" +SRIOV_VF12="trust=false min-tx-rate=100 vlans=1.200.ad" +SRIOV_VF3="mac=55:44:33:22:11:00 spoof-check=true" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected new file mode 100644 index 00000000..c882c479 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected @@ -0,0 +1,18 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +SRIOV_TOTAL_VFS=64 +SRIOV_AUTOPROBE_DRIVERS=1 +SRIOV_VF2="mac=55:55:55:55:55:55 vlans=3.10.ad;10" +SRIOV_VF19=spoof-check=true +BOOTPROTO=none +IPADDR=1.1.1.3 +PREFIX=24 +GATEWAY=1.1.1.1 +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Test Write SR-IOV config" +UUID=${UUID} +DEVICE=eth0 +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy new file mode 100644 index 00000000..2173729d --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy @@ -0,0 +1,12 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +DEVICE=eth0 +HWADDR=00:16:41:11:22:33 +NAME="test-static-routes-legacy" +TYPE=Ethernet +BOOTPROTO=dhcp +DEFROUTE=yes +UUID=ba60d05a-7898-820d-c2db-427a88f8f2a5 +ONBOOT=yes +IPV6INIT=no +PEERDNS=yes +PEERROUTES=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected new file mode 100644 index 00000000..a28c5c1c --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected @@ -0,0 +1,12 @@ +HWADDR=00:16:41:11:22:33 +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME=test-static-routes-legacy +UUID=ba60d05a-7898-820d-c2db-427a88f8f2a5 +DEVICE=eth0 +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc new file mode 100644 index 00000000..d0a3c254 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc @@ -0,0 +1,16 @@ +TYPE=Ethernet +DEVICE=eth0 +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 +QDISC1="root fq_codel" +FILTER1="parent 1234: matchall action simple sdata Hello" +NAME=ethernet-tc +UUID=a42c8d4e-11a2-4144-92d2-5cbce8c6b2c4 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected new file mode 100644 index 00000000..a67ca598 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected @@ -0,0 +1,16 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +QDISC1="parent 2468:2 pfifo_fast" +FILTER1="parent 1234: matchall action simple sdata Hello" +BOOTPROTO=none +IPADDR=1.1.1.3 +PREFIX=24 +GATEWAY=1.1.1.1 +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Test Write TC config" +UUID=${UUID} +DEVICE=eth0 +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1 new file mode 100644 index 00000000..209447b8 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1 @@ -0,0 +1,6 @@ +DEVICE=team0 +ONBOOT=no +DEVICETYPE=Team +BOOTPROTO=dhcp +TEAM_CONFIG="{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2 new file mode 100644 index 00000000..26e448cc --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2 @@ -0,0 +1,5 @@ +DEVICE=team0 +ONBOOT=no +BOOTPROTO=dhcp +TEAM_CONFIG="{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-invalid b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-invalid new file mode 100644 index 00000000..41c0f25c --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-invalid @@ -0,0 +1,4 @@ +DEVICE=team0 +ONBOOT=no +BOOTPROTO=dhcp +TEAM_CONFIG="{ foobar }x" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1 new file mode 100644 index 00000000..80355c26 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1 @@ -0,0 +1,5 @@ +TYPE=Ethernet +TEAM_PORT_CONFIG="{\"p4p1\": {\"prio\": -10, \"sticky\": true}}" +DEVICE=p4p1 +TEAM_MASTER=team0 +DEVICETYPE=TeamPort diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2 new file mode 100644 index 00000000..4284737a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2 @@ -0,0 +1,4 @@ +TYPE=Ethernet +TEAM_PORT_CONFIG="{\"p4p1\": {\"prio\": -10, \"sticky\": true}}" +DEVICE=p4p1 +TEAM_MASTER=team0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-empty-config b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-empty-config new file mode 100644 index 00000000..04df6938 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-empty-config @@ -0,0 +1,4 @@ +TYPE=Ethernet +DEVICE=p4p1 +TEAM_MASTER=team0 +DEVICETYPE=TeamPort diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-unrecognized b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-unrecognized new file mode 100644 index 00000000..18da66c1 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-unrecognized @@ -0,0 +1,7 @@ +TYPE=PPP-over-InfiniBand +NAME=U Can't Touch This +DEVICE=pppoipoib0 +HWADDR=00:11:22:33 +BOOTPROTO=dhcp +ONBOOT=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-variables-corner-cases-1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-variables-corner-cases-1 new file mode 100644 index 00000000..af62dcfc --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-variables-corner-cases-1 @@ -0,0 +1,8 @@ +DEVICE=eth0 +HWADDR=00:16:41:11:22:33 + +# Variable with only single character (double quote) +NAME=" + +# Variable with only single character (quote) +ZONE=' diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-flags-1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-flags-1 new file mode 100644 index 00000000..5ff87891 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-flags-1 @@ -0,0 +1,14 @@ +VLAN=yes +TYPE=Vlan +DEVICE=super-vlan +VLAN_ID=44 +PHYSDEV=eth9 +REORDER_HDR=yes +GVRP=no +VLAN_FLAGS="GVRP LOOSE_BINDING" +VLAN_INGRESS_PRIORITY_MAP=0:1,2:5 +VLAN_EGRESS_PRIORITY_MAP=12:3,14:7,3:1 +ONBOOT=yes +BOOTPROTO=static +IPADDR=192.168.43.149 +NETMASK=255.255.255.0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-flags-2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-flags-2 new file mode 100644 index 00000000..2c01be71 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-flags-2 @@ -0,0 +1,10 @@ +VLAN=yes +TYPE=Vlan +DEVICE=super-vlan +VLAN_ID=44 +PHYSDEV=eth9 +VLAN_FLAGS="GVRP LOOSE_BINDING" +ONBOOT=yes +BOOTPROTO=static +IPADDR=192.168.43.149 +NETMASK=255.255.255.0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-interface b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-interface new file mode 100644 index 00000000..d8d9193f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-interface @@ -0,0 +1,11 @@ +VLAN=yes +TYPE=Vlan +DEVICE=vlan43 +PHYSDEV=eth9 +VLAN_FLAGS=GVRP,LOOSE_BINDING +VLAN_INGRESS_PRIORITY_MAP=0:1,2:5 +VLAN_EGRESS_PRIORITY_MAP=12:3,14:7,3:1 +ONBOOT=yes +BOOTPROTO=static +IPADDR=192.168.43.149 +NETMASK=255.255.255.0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-only-device b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-only-device new file mode 100644 index 00000000..4ba06f28 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-only-device @@ -0,0 +1,4 @@ +VLAN=yes +TYPE=Vlan +DEVICE=eth0.9 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-only-vlanid b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-only-vlanid new file mode 100644 index 00000000..622d41ec --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-only-vlanid @@ -0,0 +1,6 @@ +VLAN=yes +TYPE=Vlan +PHYSDEV=eth9 +VLAN_ID=43 +ONBOOT=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-physdev b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-physdev new file mode 100644 index 00000000..446c2a62 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-physdev @@ -0,0 +1,6 @@ +VLAN=yes +TYPE=Vlan +DEVICE=vlan0.3 +PHYSDEV=eth0 +VLAN_ID=3 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-reorder-hdr-1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-reorder-hdr-1 new file mode 100644 index 00000000..0dc53971 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-reorder-hdr-1 @@ -0,0 +1,6 @@ +VLAN=yes +TYPE=Vlan +DEVICE=vlan0.3 +PHYSDEV=eth0 +VLAN_ID=3 +REORDER_HDR=0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-reorder-hdr-2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-reorder-hdr-2 new file mode 100644 index 00000000..d98a9d36 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-reorder-hdr-2 @@ -0,0 +1,6 @@ +VLAN=yes +TYPE=Vlan +DEVICE=vlan0.3 +PHYSDEV=eth0 +VLAN_ID=3 +VLAN_FLAGS="LOOSE_BINDING,NO_REORDER_HDR" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-trailing-spaces b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-trailing-spaces new file mode 100644 index 00000000..4a31e475 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-trailing-spaces @@ -0,0 +1,11 @@ +DEVICE="vlan201" +ONBOOT=yes +NETBOOT=yes +BOOTPROTO=none +IPADDR="10.130.70.7" +NETMASK="255.255.0.0" +TYPE=Vlan +NAME="vlan201" +VLAN=yes +PHYSDEV="enccw0.0.fb00" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-band-a b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-band-a new file mode 100644 index 00000000..43286111 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-band-a @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +BAND=a +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-band-a-channel-mismatch b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-band-a-channel-mismatch new file mode 100644 index 00000000..8905ba0b --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-band-a-channel-mismatch @@ -0,0 +1,9 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=9 +BAND=a +MODE=Managed + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-band-bg-channel-mismatch b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-band-bg-channel-mismatch new file mode 100644 index 00000000..e02e21ad --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-band-bg-channel-mismatch @@ -0,0 +1,9 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=183 +BAND=bg +MODE=Managed + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-dynamic-wep-leap b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-dynamic-wep-leap new file mode 100644 index 00000000..d9c95cc2 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-dynamic-wep-leap @@ -0,0 +1,17 @@ +ESSID="sdasdsdg" +MODE=Managed +KEY_MGMT=IEEE8021X +CIPHER_GROUP="WEP40 WEP104" +TYPE=Wireless +IEEE_8021X_EAP_METHODS=LEAP +IEEE_8021X_IDENTITY="bill smith" +BOOTPROTO=dhcp +DEFROUTE=yes +PEERDNS=yes +PEERROUTES=yes +IPV4_FAILURE_FATAL=yes +IPV6INIT=no +NAME="Test Dynamic WEP LEAP" +UUID=aca7a23c-d934-49a3-8bfb-ad66f846c57b +ONBOOT=yes +USERS=dcbw diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-hidden b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-hidden new file mode 100644 index 00000000..08b9a730 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-hidden @@ -0,0 +1,10 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +BOOTPROTO=dhcp +ONBOOT=yes +ESSID=blahblah +MODE=Managed +SSID_HIDDEN=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap new file mode 100644 index 00000000..50f4fd31 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap @@ -0,0 +1,17 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +KEY_MGMT=IEEE8021X +SECURITYMODE=LEAP +IEEE_8021X_IDENTITY="Bill Smith" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap-agent b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap-agent new file mode 100644 index 00000000..991ba67a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap-agent @@ -0,0 +1,17 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +KEY_MGMT=IEEE8021X +SECURITYMODE=LEAP +IEEE_8021X_IDENTITY="Bill Smith" +IEEE_8021X_PASSWORD_FLAGS=user diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap-always-ask b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap-always-ask new file mode 100644 index 00000000..5bb02b5c --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-leap-always-ask @@ -0,0 +1,17 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +KEY_MGMT=IEEE8021X +SECURITYMODE=LEAP +IEEE_8021X_IDENTITY="Bill Smith" +IEEE_8021X_PASSWORD_FLAGS="user ask" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-always b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-always new file mode 100644 index 00000000..9dcb5bf8 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-always @@ -0,0 +1,7 @@ +TYPE=Wireless +DEVICE=eth2 +BOOTPROTO=dhcp +ESSID=blahblah +ONBOOT=yes +MAC_ADDRESS_RANDOMIZATION=always + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-default b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-default new file mode 100644 index 00000000..3709c493 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-default @@ -0,0 +1,6 @@ +TYPE=Wireless +DEVICE=eth2 +BOOTPROTO=dhcp +ESSID=blahblah +ONBOOT=yes +MAC_ADDRESS_RANDOMIZATION=default diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-missing b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-missing new file mode 100644 index 00000000..f896eb0f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-missing @@ -0,0 +1,6 @@ +TYPE=Wireless +DEVICE=eth2 +BOOTPROTO=dhcp +ESSID=blahblah +ONBOOT=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-never b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-never new file mode 100644 index 00000000..aa12d749 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-mac-random-never @@ -0,0 +1,6 @@ +TYPE=Wireless +DEVICE=eth2 +BOOTPROTO=dhcp +ESSID=blahblah +ONBOOT=yes +MAC_ADDRESS_RANDOMIZATION=never diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open new file mode 100644 index 00000000..b089103d --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open @@ -0,0 +1,16 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +AUTOCONNECT_PRIORITY=-1 +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +IPV4_ROUTE_METRIC=104 +IPV6_ROUTE_METRIC=106 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-auto b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-auto new file mode 100644 index 00000000..42ee200a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-auto @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Auto +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex new file mode 100644 index 00000000..32d63342 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=0x626cxx +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-hex b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-hex new file mode 100644 index 00000000..dcb46da7 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-hex @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=0x626c6168626c6168 +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-hex b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-hex new file mode 100644 index 00000000..37bb085b --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-hex @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=0x626c6168626c6168626c6168626c6168626c6168626c6168626c6168626c6168AA +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted new file mode 100644 index 00000000..0b46acd6 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID="foo\"bar\\foo\"bar\\foo\"bar\\foo\"bar\\1" +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-quoted b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-quoted new file mode 100644 index 00000000..08496bb5 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-open-ssid-quoted @@ -0,0 +1,13 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID="foo\"bar\\" +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-owe b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-owe new file mode 100644 index 00000000..354046ad --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-owe @@ -0,0 +1,5 @@ +TYPE=Wireless +DEVICE=wlan1 +ESSID=blahblah_owe +MODE=Managed +KEY_MGMT=OWE diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-sae b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-sae new file mode 100644 index 00000000..68afbe97 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-sae @@ -0,0 +1,5 @@ +TYPE=Wireless +DEVICE=wlan1 +ESSID=blahblah +MODE=Managed +KEY_MGMT=SAE diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep new file mode 100644 index 00000000..e1ce20d6 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep @@ -0,0 +1,14 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +SECURITYMODE=restricted diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-104-ascii b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-104-ascii new file mode 100644 index 00000000..250efa13 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-104-ascii @@ -0,0 +1,14 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +SECURITYMODE=open diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-40-ascii b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-40-ascii new file mode 100644 index 00000000..250efa13 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-40-ascii @@ -0,0 +1,14 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +SECURITYMODE=open diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-adhoc b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-adhoc new file mode 100644 index 00000000..9d0bacfc --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-adhoc @@ -0,0 +1,15 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=11 +MODE=Ad-Hoc +RATE=auto +ONBOOT=no +USERCTL=yes +PEERDNS=no +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPV6INIT=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-agent-keys b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-agent-keys new file mode 100644 index 00000000..2bc16b6e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-agent-keys @@ -0,0 +1,18 @@ +ESSID="foobar" +MODE=Managed +TYPE=Wireless +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=yes +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +UUID=9c4637bd-7600-40cc-9c24-13819c5bf5dd +ONBOOT=yes +HWADDR=00:16:BB:AA:CC:DD +WEP_KEY_FLAGS=user +PEERDNS=yes +PEERROUTES=yes +IPV6_PEERDNS=yes +IPV6_PEERROUTES=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-eap-ttls-chap b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-eap-ttls-chap new file mode 100644 index 00000000..bdea6aa3 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-eap-ttls-chap @@ -0,0 +1,20 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +BOOTPROTO=dhcp +ONBOOT=yes +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +NM_CONTROLLED=yes +PEERDNS=yes +ESSID=blahblah +MODE=Managed +RATE=auto +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=TTLS +IEEE_8021X_IDENTITY="David Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_INNER_AUTH_METHODS=CHAP + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-no-keys b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-no-keys new file mode 100644 index 00000000..cb4da43f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-no-keys @@ -0,0 +1,18 @@ +ESSID="foobar" +MODE=Managed +TYPE=Wireless +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=yes +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +UUID=9c4637bd-7600-40cc-9c24-13819c5bf5dd +ONBOOT=yes +HWADDR=00:16:BB:AA:CC:DD +DEFAULTKEY=1 +PEERDNS=yes +PEERROUTES=yes +IPV6_PEERDNS=yes +IPV6_PEERROUTES=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-passphrase b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-passphrase new file mode 100644 index 00000000..250efa13 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wep-passphrase @@ -0,0 +1,14 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +SECURITYMODE=open diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls new file mode 100644 index 00000000..9a74bb4d --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls @@ -0,0 +1,22 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +BOOTPROTO=dhcp +ONBOOT=yes +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +NM_CONTROLLED=yes +PEERDNS=yes +ESSID=blahblah +MODE=Managed +RATE=auto +KEY_MGMT=WPA-EAP-SUITE-B-192 +WPA_ALLOW_WPA=yes +WPA_ALLOW_WPA2=yes +IEEE_8021X_EAP_METHODS=TLS +IEEE_8021X_IDENTITY="Bill Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_CLIENT_CERT=test1_key_and_cert.pem +IEEE_8021X_PRIVATE_KEY=test1_key_and_cert.pem + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-tls b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-tls new file mode 100644 index 00000000..92aaeeab --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-tls @@ -0,0 +1,25 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +BOOTPROTO=dhcp +ONBOOT=yes +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +NM_CONTROLLED=yes +PEERDNS=yes +ESSID=blahblah +MODE=Managed +RATE=auto +CIPHER_PAIRWISE="TKIP CCMP" +CIPHER_GROUP="TKIP CCMP WEP40 WEP104" +KEY_MGMT=WPA-EAP +WPA_ALLOW_WPA=yes +WPA_ALLOW_WPA2=yes +IEEE_8021X_EAP_METHODS=TLS +IEEE_8021X_IDENTITY="Bill Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_CLIENT_CERT=test1_key_and_cert.pem +IEEE_8021X_PRIVATE_KEY=test1_key_and_cert.pem + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls new file mode 100644 index 00000000..42ed1d68 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls @@ -0,0 +1,28 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +BOOTPROTO=dhcp +ONBOOT=yes +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +NM_CONTROLLED=yes +PEERDNS=yes +ESSID=blahblah +MODE=Managed +RATE=auto +CIPHER_PAIRWISE="TKIP CCMP" +CIPHER_GROUP="TKIP CCMP WEP40 WEP104" +KEY_MGMT=WPA-EAP +WPA_ALLOW_WPA=yes +WPA_ALLOW_WPA2=yes +IEEE_8021X_EAP_METHODS=TTLS +IEEE_8021X_IDENTITY="Chuck Shumer" +IEEE_8021X_ANON_IDENTITY="anonymous" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_INNER_AUTH_METHODS=EAP-TLS +IEEE_8021X_INNER_CA_CERT=test_ca_cert.pem +IEEE_8021X_INNER_CLIENT_CERT=test1_key_and_cert.pem +IEEE_8021X_INNER_PRIVATE_KEY=test1_key_and_cert.pem + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk new file mode 100644 index 00000000..4ab45ae3 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk @@ -0,0 +1,19 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +CIPHER_PAIRWISE="TKIP CCMP" +CIPHER_GROUP="TKIP CCMP WEP40 WEP104" +KEY_MGMT=WPA-PSK +WPA_ALLOW_WPA=yes +WPA_ALLOW_WPA2=yes +CTCPROT=no-newline \ No newline at end of file diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-2 new file mode 100644 index 00000000..038b656d --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-2 @@ -0,0 +1,19 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=ipsum +CHANNEL=6 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +CIPHER_PAIRWISE="TKIP CCMP" +CIPHER_GROUP="TKIP CCMP WEP40 WEP104" +KEY_MGMT=WPA-PSK +WPA_ALLOW_WPA=yes +WPA_ALLOW_WPA2=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-adhoc b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-adhoc new file mode 100644 index 00000000..c3cadbb8 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-adhoc @@ -0,0 +1,17 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Ad-Hoc +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +CIPHER_GROUP=CCMP +CIPHER_PAIRWISE=CCMP +KEY_MGMT=WPA-PSK + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-hex b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-hex new file mode 100644 index 00000000..2119ba8e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-hex @@ -0,0 +1,19 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +CIPHER_PAIRWISE="TKIP CCMP" +CIPHER_GROUP="TKIP CCMP WEP40 WEP104" +KEY_MGMT=WPA-PSK +WPA_ALLOW_WPA=yes +WPA_ALLOW_WPA2=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-unquoted b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-unquoted new file mode 100644 index 00000000..2119ba8e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-unquoted @@ -0,0 +1,19 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +CIPHER_PAIRWISE="TKIP CCMP" +CIPHER_GROUP="TKIP CCMP WEP40 WEP104" +KEY_MGMT=WPA-PSK +WPA_ALLOW_WPA=yes +WPA_ALLOW_WPA2=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-unquoted2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-unquoted2 new file mode 100644 index 00000000..2119ba8e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk-unquoted2 @@ -0,0 +1,19 @@ +TYPE=Wireless +DEVICE=eth2 +HWADDR=00:16:41:11:22:33 +NM_CONTROLLED=yes +BOOTPROTO=dhcp +ESSID=blahblah +CHANNEL=1 +MODE=Managed +RATE=auto +ONBOOT=yes +USERCTL=yes +PEERDNS=yes +IPV6INIT=no +CIPHER_PAIRWISE="TKIP CCMP" +CIPHER_GROUP="TKIP CCMP WEP40 WEP104" +KEY_MGMT=WPA-PSK +WPA_ALLOW_WPA=yes +WPA_ALLOW_WPA2=yes + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1X-subj-matches b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1X-subj-matches new file mode 100644 index 00000000..70d69bf5 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1X-subj-matches @@ -0,0 +1,17 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=PEAP +IEEE_8021X_IDENTITY="Jara Cimrman" +IEEE_8021X_PEAP_VERSION=1 +IEEE_8021X_INNER_AUTH_METHODS=GTC +IEEE_8021X_PASSWORD_FLAGS="user ask" +IEEE_8021X_SUBJECT_MATCH=server1.yourdomain.tld +IEEE_8021X_ALTSUBJECT_MATCHES="a.yourdomain.tld b.yourdomain.tld c.yourdomain.tld" +IEEE_8021X_PHASE2_SUBJECT_MATCH=server2.yourdomain.tld +IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES="x.yourdomain.tld y.yourdomain.tld" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-password-raw b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-password-raw new file mode 100644 index 00000000..a5434434 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-password-raw @@ -0,0 +1,15 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=TTLS +IEEE_8021X_IDENTITY="Bill Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_INNER_AUTH_METHODS=EAP-GTC +IEEE_8021X_PASSWORD_RAW=0408151623420001 +#IEEE_8021X_PIN=hallo1 +IEEE_8021X_PIN_FLAGS=0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-ttls-eapgtc b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-ttls-eapgtc new file mode 100644 index 00000000..fad92772 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-802-1x-ttls-eapgtc @@ -0,0 +1,13 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=TTLS +IEEE_8021X_IDENTITY="Bill Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_INNER_AUTH_METHODS=EAP-GTC +IEEE_8021X_PASSWORD_FLAGS=ask diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 new file mode 100644 index 00000000..27bcbbf9 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 @@ -0,0 +1,15 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=PEAP +IEEE_8021X_IDENTITY="David Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_PEAP_VERSION=1 +IEEE_8021X_PEAP_FORCE_NEW_LABEL=yes +IEEE_8021X_INNER_AUTH_METHODS=MSCHAPV2 +IEEE_8021X_ANON_IDENTITY=somebody diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-agent b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-agent new file mode 100644 index 00000000..052ab425 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-agent @@ -0,0 +1,14 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=TLS +IEEE_8021X_IDENTITY="David Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_CLIENT_CERT=test1_key_and_cert.pem +IEEE_8021X_PRIVATE_KEY=test1_key_and_cert.pem +IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS=user diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-always b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-always new file mode 100644 index 00000000..5deee066 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-always @@ -0,0 +1,14 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=TLS +IEEE_8021X_IDENTITY="David Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_CLIENT_CERT=test1_key_and_cert.pem +IEEE_8021X_PRIVATE_KEY=test1_key_and_cert.pem +IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS="user ask" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert new file mode 100644 index 00000000..24397473 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert @@ -0,0 +1,13 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=TLS +IEEE_8021X_IDENTITY="David Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_PRIVATE_KEY=test_client.p12 +IEEE_8021X_PRIVATE_KEY_PASSWORD="test1" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-auto-negotiate-on b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-auto-negotiate-on new file mode 100644 index 00000000..d0b50775 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-auto-negotiate-on @@ -0,0 +1,22 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes +USERCTL=yes +MTU=1492 +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 +IPV6INIT=yes +IPV6_AUTOCONF=no +IPV6ADDR=dead:beaf::1 +IPV6ADDR_SECONDARIES="dead:beaf::2/56" +DNS3=1:2:3:4::a +DNS4=1:2:3:4::b +RES_OPTIONS= +ETHTOOL_OPTS="wol apgs sopass 00:11:22:33:44:55 autoneg on speed 100" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-autoip b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-autoip new file mode 100644 index 00000000..e683db3c --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-autoip @@ -0,0 +1,6 @@ +TYPE=Ethernet +DEVICE=eth0 +BOOTPROTO=autoip +IPV4_FAILURE_FATAL=yes +PEERDNS=no +DEVTIMEOUT=2.6 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ctc-static b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ctc-static new file mode 100644 index 00000000..61f8f423 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ctc-static @@ -0,0 +1,12 @@ +# IBM CTC +DEVICE=ctc0 +TYPE=CTC +BOOTPROTO=static +IPADDR=192.168.70.87 +GATEWAY=192.168.70.136 +NETMASK=255.255.255.0 +ONBOOT=yes +SUBCHANNELS=0.0.1b00,0.0.1b01 +NETTYPE=ctc +CTCPROT=0 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-defroute-no b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-defroute-no new file mode 100644 index 00000000..fe8b15b2 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-defroute-no @@ -0,0 +1,15 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth4 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +USERCTL=yes +NM_CONTROLLED=yes +PEERDNS=yes +DEFROUTE=no + +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=no + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-defroute-no-gatewaydev-yes b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-defroute-no-gatewaydev-yes new file mode 100644 index 00000000..3cf4323d --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-defroute-no-gatewaydev-yes @@ -0,0 +1,15 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +USERCTL=yes +NM_CONTROLLED=yes +PEERDNS=yes +DEFROUTE=no + +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=no + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp new file mode 100644 index 00000000..5d36675a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp @@ -0,0 +1,19 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +NM_CONTROLLED=yes +PEERDNS=no +DHCP_FQDN=foo.bar +DHCP_HOSTNAME_FLAGS=6 +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPV6_AUTOCONF=no +IPV6INIT=yes +DHCPV6C=yes +DHCPV6_HOSTNAME_FLAGS=8 +DHCPV6_HOSTNAME=foo.bar diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp-plus-ip b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp-plus-ip new file mode 100644 index 00000000..af254f9c --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp-plus-ip @@ -0,0 +1,26 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +IPV6INIT=yes +IPV6_AUTOCONF=yes +USERCTL=yes +NM_CONTROLLED=yes +PEERDNS=no +DNS1=4.2.2.1 +DNS2=4.2.2.2 +DNS3=1:2:3:4::a +DNS4=1:2:3:4::b + +# additional IPs +IPADDR=1.2.3.4 +PREFIX=24 +IPADDR1=9.8.7.6 +PREFIX1=16 +GATEWAY=1.1.1.1 + +IPV6ADDR="1001:abba::1234/56" +IPV6ADDR_SECONDARIES="2001:abba::2234/64 3001:abba::3234/96" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp-send-hostname b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp-send-hostname new file mode 100644 index 00000000..cba380db --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp-send-hostname @@ -0,0 +1,12 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +IPV6INIT=yes +IPV6_AUTOCONF=yes +USERCTL=yes +NM_CONTROLLED=yes +PEERDNS=no +DHCP_HOSTNAME="svata-pulec" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp6-only b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp6-only new file mode 100644 index 00000000..de03e044 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcp6-only @@ -0,0 +1,11 @@ +DEVICE="eth0" +ONBOOT=no +TYPE=Ethernet +DEFROUTE=yes +PEERDNS=yes +PEERROUTES=yes +IPV6INIT=yes +IPV6_AUTOCONF=no +DHCPV6C=yes +HWADDR=00:13:20:F5:F5:E4 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcpv6-hostname-fallback b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcpv6-hostname-fallback new file mode 100644 index 00000000..d38e762e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-dhcpv6-hostname-fallback @@ -0,0 +1,12 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +IPV6INIT=yes +IPV6_AUTOCONF=yes +USERCTL=yes +NM_CONTROLLED=yes +PEERDNS=no +DHCP_HOSTNAME="fully.qualified.domain" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-global-gateway b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-global-gateway new file mode 100644 index 00000000..98d91053 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-global-gateway @@ -0,0 +1,14 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +PREFIX=24 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-global-gateway-ignore b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-global-gateway-ignore new file mode 100644 index 00000000..bb81399e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-global-gateway-ignore @@ -0,0 +1,8 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +USERCTL=yes +IPV6INIT=no + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1 new file mode 100644 index 00000000..db09afdb --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1 @@ -0,0 +1,14 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR0=1.2.3.4 +PREFIX0=24 +IPADDR1=9.8.7.6 +PREFIX1=16 +IPADDR2=3.3.3.3 +PREFIX2=8 +GATEWAY=1.1.1.1 +ACD_TIMEOUT=2000 +ARPING_WAIT=1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2 new file mode 100644 index 00000000..6972e279 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2 @@ -0,0 +1,13 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +IPADDR2=9.8.7.6 +PREFIX2=16 +IPADDR3=3.3.3.3 +PREFIX3=8 +ACD_TIMEOUT=2000 +ARPING_WAIT=1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3 new file mode 100644 index 00000000..d9065994 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3 @@ -0,0 +1,13 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR2=1.2.3.4 +PREFIX2=24 +IPADDR3=9.8.7.6 +PREFIX3=16 +IPADDR4=3.3.3.3 +PREFIX4=8 +ACD_TIMEOUT=2000 +ARPING_WAIT=1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4 new file mode 100644 index 00000000..935267f7 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4 @@ -0,0 +1,13 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +IPADDR1=9.8.7.6 +PREFIX1=16 +IPADDR2=3.3.3.3 +PREFIX2=8 +ACD_TIMEOUT=2000 +ARPING_WAIT=1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-manual b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-manual new file mode 100644 index 00000000..45db0e4c --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-manual @@ -0,0 +1,19 @@ +# Intel Corporation 82567LM Gigabit Network Connection +TYPE=Ethernet +DEVICE=eth2 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +USERCTL=yes +NM_CONTROLLED=yes +PEERDNS=yes +DNS1=10.2.0.4 +DNS2=10.2.0.5 +DNS3=1:2:3:4::a +DNS4=1:2:3:4::b +DOMAIN="lorem.com ipsum.org dolor.edu" +IPV6INIT=yes +IPV6_AUTOCONF=no +IPV6ADDR="1001:abba::1234/56" +IPV6ADDR_SECONDARIES="2001:abba::2234/64 3001:abba::3234/96" +IPV6_FAILURE_FATAL=no diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only new file mode 100644 index 00000000..94189064 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only @@ -0,0 +1,14 @@ +# Intel Corporation 82567LM Gigabit Network Connection +TYPE=Ethernet +DEVICE=eth2 +HWADDR=00:11:22:33:44:ee +ONBOOT=yes +USERCTL=yes +NM_CONTROLLED=yes +PEERDNS=yes +DNS1=1:2:3:4::a +IPV6_DOMAIN="lorem.com ipsum.org dolor.edu" +IPV6INIT=yes +IPV6_AUTOCONF=no +IPV6ADDR="1001:abba::1234/56" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only-1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only-1 new file mode 100644 index 00000000..95f8a8ed --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only-1 @@ -0,0 +1,16 @@ +IPADDR= +GATEWAY= +NETMASK= +BOOTPROTO=static +TYPE=Ethernet +DEVICE=eth2 +HWADDR=00:11:22:33:44:ee +ONBOOT=yes +USERCTL=yes +NM_CONTROLLED=yes +PEERDNS=yes +DNS1=1:2:3:4::a +DOMAIN="lorem.com ipsum.org dolor.edu" +IPV6INIT=yes +IPV6_AUTOCONF=no +IPV6ADDR="1001:abba::1234/56" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-never-default b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-never-default new file mode 100644 index 00000000..12d5b5e6 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-never-default @@ -0,0 +1,11 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth4 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +USERCTL=yes +NM_CONTROLLED=yes +PEERDNS=yes +IPV6INIT=yes +IPV6_AUTOCONF=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-obsolete-gateway-n b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-obsolete-gateway-n new file mode 100644 index 00000000..2190a995 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-obsolete-gateway-n @@ -0,0 +1,13 @@ +# GATEWAY<N> is obsolete, but we read it for backwards compatibility + +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes + +# manual IP +IPADDR1=1.2.3.4 +PREFIX1=24 +GATEWAY1=1.1.1.1 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-qeth-static b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-qeth-static new file mode 100644 index 00000000..4719de21 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-qeth-static @@ -0,0 +1,13 @@ +# IBM QETH +DEVICE=eth1 +BOOTPROTO=static +IPADDR=192.168.70.87 +NETMASK=255.255.255.0 +ONBOOT=yes +NETTYPE=qeth +SUBCHANNELS=0.0.0600,0.0.0601,0.0.0602 +TYPE=Ethernet +PORTNAME=OSAPORT +OPTIONS='layer2=1 portno=0' +MACADDR=02:00:00:23:65:1a + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-shared-plus-ip b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-shared-plus-ip new file mode 100644 index 00000000..5098aef7 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-shared-plus-ip @@ -0,0 +1,19 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=shared +ONBOOT=no +IPV6INIT=yes + +# additional IPs +IPADDR=10.20.30.5 +PREFIX=24 +GATEWAY=1.1.1.1 + +# these are ignored for shared method +IPADDR1=6.7.8.9 +PREFIX1=16 +IPADDR2=3.3.3.3 +PREFIX2=24 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static new file mode 100644 index 00000000..34acf9fe --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static @@ -0,0 +1,22 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes +USERCTL=yes +MTU=1492 +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 +IPV6INIT=yes +IPV6_AUTOCONF=no +IPV6ADDR=dead:beaf::1 +IPV6ADDR_SECONDARIES="dead:beaf::2/56" +DNS3=1:2:3:4::a +DNS4=1:2:3:4::b +RES_OPTIONS= +IPV6_RES_OPTIONS= diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-bootproto b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-bootproto new file mode 100644 index 00000000..a01f6558 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-bootproto @@ -0,0 +1,16 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=static +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +MTU=1492 +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 +RES_OPTIONS= diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-no-prefix-16 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-no-prefix-16 new file mode 100644 index 00000000..07999038 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-no-prefix-16 @@ -0,0 +1,14 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes +USERCTL=yes +MTU=1492 +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=172.16.3.4 +GATEWAY=172.16.3.1 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-no-prefix-24 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-no-prefix-24 new file mode 100644 index 00000000..688143cd --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-no-prefix-24 @@ -0,0 +1,14 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes +USERCTL=yes +MTU=1492 +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +GATEWAY=192.168.1.1 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-no-prefix-8 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-no-prefix-8 new file mode 100644 index 00000000..0433c62a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-no-prefix-8 @@ -0,0 +1,14 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes +USERCTL=yes +MTU=1492 +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=10.11.12.13 +GATEWAY=10.0.0.1 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-routes b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-routes new file mode 100644 index 00000000..7faf49bd --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-routes @@ -0,0 +1,15 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +MTU=1492 +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-routes-legacy b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-routes-legacy new file mode 100644 index 00000000..7faf49bd --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static-routes-legacy @@ -0,0 +1,15 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes +USERCTL=yes +IPV6INIT=no +MTU=1492 +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-unknown-ethtool-opt b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-unknown-ethtool-opt new file mode 100644 index 00000000..861dd41f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-unknown-ethtool-opt @@ -0,0 +1,22 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes +USERCTL=yes +MTU=1492 +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 +IPV6INIT=yes +IPV6_AUTOCONF=no +IPV6ADDR=dead:beaf::1 +IPV6ADDR_SECONDARIES="dead:beaf::2/56" +DNS3=1:2:3:4::a +DNS4=1:2:3:4::b +RES_OPTIONS= +ETHTOOL_OPTS="unknown1 wol apgs sopass 00:11:22:33:44:55 unkwnown2 opt2 unknown3" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-wake-on-lan b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-wake-on-lan new file mode 100644 index 00000000..1dfc9a43 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-wake-on-lan @@ -0,0 +1,22 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +ONBOOT=yes +USERCTL=yes +MTU=1492 +NM_CONTROLLED=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 +IPV6INIT=yes +IPV6_AUTOCONF=no +IPV6ADDR=dead:beaf::1 +IPV6ADDR_SECONDARIES="dead:beaf::2/56" +DNS3=1:2:3:4::a +DNS4=1:2:3:4::b +RES_OPTIONS= +ETHTOOL_OPTS="speed 100 duplex full wol apgs sopass 00:11:22:33:44:55 autoneg off" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-1 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-1 new file mode 100644 index 00000000..2af1daf3 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-1 @@ -0,0 +1,8 @@ +FOO='val + bar=3' +wrong line + F2=b + F3='b +XXA=adf' + XXA2=val2 +' diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-1.expected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-1.expected new file mode 100644 index 00000000..7f154073 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-1.expected @@ -0,0 +1,12 @@ +FOO= +#NM: FOO='val +bar= +#NM: bar=3' +#NM: wrong line + F2=b +F3= +#NM: F3='b +XXA= +#NM: XXA=adf' + XXA2=val2 +#NM: ' diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-2 new file mode 100644 index 00000000..850149b2 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-2 @@ -0,0 +1,3 @@ +FOO=' +BAR=a +' diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-2.expected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-2.expected new file mode 100644 index 00000000..6ec53c17 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-2.expected @@ -0,0 +1,4 @@ +FOO= +#NM: FOO=' +BAR=a +#NM: ' diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-3 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-3 new file mode 100644 index 00000000..3d198e00 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-3 @@ -0,0 +1,3 @@ +FOO=' +BAR=" +' diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-3.expected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-3.expected new file mode 100644 index 00000000..d2b27eb3 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-3.expected @@ -0,0 +1,5 @@ +FOO= +#NM: FOO=' +BAR= +#NM: BAR=" +#NM: ' diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4 new file mode 100644 index 00000000..f834464e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4 @@ -0,0 +1,36 @@ +# test what happens with multiple defines of a name. +# +# Note that svGetValue() will return "l4x", which +# isn't correct in terms of shell-parsing. But we +# don't consider only line-by-line, thus this is +# expected. +# +# Also note that setting IPADDR will replace the last +# occurrence, and delete all previous once. + +#L1 +IPADDR=l2 + +#L2 +IPADDR=l3 + +METRIC1='' +METRIC2=$'\U0x' +METRIC3=$'x\U0' + +#L4 +IPADDR=' +IPADDR=l4x +' + +#Lx-1 +IPADDR2=not-visible +#Lx-2 +IPADDR2='invalid +#Lx-3 + +#Ly-1 +IPADDR3='invalid +#Ly-2 +IPADDR3=name3-value +#Ly-3 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected new file mode 100644 index 00000000..040ddc9d --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected @@ -0,0 +1,31 @@ +# test what happens with multiple defines of a name. +# +# Note that svGetValue() will return "l4x", which +# isn't correct in terms of shell-parsing. But we +# don't consider only line-by-line, thus this is +# expected. +# +# Also note that setting IPADDR will replace the last +# occurrence, and delete all previous once. + +#L1 + +#L2 + +METRIC1='' +METRIC2=$'\U0x' +METRIC3=$'x\U0' + +#L4 +IPADDR=set-by-test1 +#NM: ' + +#Lx-1 +#Lx-2 +IPADDR2=set-by-test2 +#Lx-3 + +#Ly-1 +#Ly-2 +IPADDR3=set-by-test3 +#Ly-3 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected new file mode 100644 index 00000000..42608576 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected @@ -0,0 +1,15 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +ETHTOOL_OPTS="autoneg on ; -K net0 rxvlan off tx on" +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test Write Wired Auto-Negotiate" +UUID=${UUID} +ONBOOT=yes diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-dynamic-wep-leap b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-dynamic-wep-leap new file mode 100644 index 00000000..6936f2e0 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-dynamic-wep-leap @@ -0,0 +1,2 @@ +IEEE_8021X_PASSWORD="foobar baz" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-leap b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-leap new file mode 100644 index 00000000..fe78177e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-leap @@ -0,0 +1 @@ +IEEE_8021X_PASSWORD="foobarblah" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-sae b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-sae new file mode 100644 index 00000000..5a9569ed --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-sae @@ -0,0 +1 @@ +WPA_PSK="The king is dead." diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep new file mode 100644 index 00000000..0bd766c2 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep @@ -0,0 +1 @@ +KEY=0123456789abcdef0123456789 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-104-ascii b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-104-ascii new file mode 100644 index 00000000..f5d532b2 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-104-ascii @@ -0,0 +1 @@ +KEY1=s:LoremIpsumSit diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-40-ascii b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-40-ascii new file mode 100644 index 00000000..b0d14708 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-40-ascii @@ -0,0 +1 @@ +KEY1=s:Lorem diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-adhoc b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-adhoc new file mode 100644 index 00000000..0bd766c2 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-adhoc @@ -0,0 +1 @@ +KEY=0123456789abcdef0123456789 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-eap-ttls-chap b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-eap-ttls-chap new file mode 100644 index 00000000..6936f2e0 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-eap-ttls-chap @@ -0,0 +1,2 @@ +IEEE_8021X_PASSWORD="foobar baz" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-passphrase b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-passphrase new file mode 100644 index 00000000..d45c0ea8 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wep-passphrase @@ -0,0 +1 @@ +KEY_PASSPHRASE1="foobar222blahblah" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-eap-tls b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-eap-tls new file mode 100644 index 00000000..0c3bc1e1 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-eap-tls @@ -0,0 +1,2 @@ +IEEE_8021X_PRIVATE_KEY_PASSWORD="test1" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-eap-ttls-tls b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-eap-ttls-tls new file mode 100644 index 00000000..b6256b95 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-eap-ttls-tls @@ -0,0 +1,2 @@ +IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD="test1" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk new file mode 100644 index 00000000..d7813b2f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk @@ -0,0 +1,2 @@ +WPA_PSK="I wonder what the king is doing tonight?" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-2 new file mode 100644 index 00000000..995721dd --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-2 @@ -0,0 +1,2 @@ +WPA_PSK="They're really saying I love you. >>\`<< '" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-adhoc b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-adhoc new file mode 100644 index 00000000..d7813b2f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-adhoc @@ -0,0 +1,2 @@ +WPA_PSK="I wonder what the king is doing tonight?" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-hex b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-hex new file mode 100644 index 00000000..d0576a8d --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-hex @@ -0,0 +1,2 @@ +WPA_PSK=1da190379817bc360dda52e85c388c439a21ea5c7bf819c64e9da051807deae6 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-unquoted b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-unquoted new file mode 100644 index 00000000..9a47196a --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-unquoted @@ -0,0 +1,2 @@ +WPA_PSK=54336845e2f3f321c4c7 + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-unquoted2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-unquoted2 new file mode 100644 index 00000000..347bb85e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wifi-wpa-psk-unquoted2 @@ -0,0 +1,2 @@ +WPA_PSK="a5d4d45e78e1455d8e6124e81ea137f9a5d4d45e78e1455d8e6124e81ea137f9" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wired-802-1x-password-raw b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wired-802-1x-password-raw new file mode 100644 index 00000000..d29a4289 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wired-802-1x-password-raw @@ -0,0 +1 @@ +IEEE_8021X_PIN=hallo2 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wired-8021x-peap-mschapv2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wired-8021x-peap-mschapv2 new file mode 100644 index 00000000..6936f2e0 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/keys-test-wired-8021x-peap-mschapv2 @@ -0,0 +1,2 @@ +IEEE_8021X_PASSWORD="foobar baz" + diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-defroute-no-gatewaydev-yes b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-defroute-no-gatewaydev-yes new file mode 100644 index 00000000..0d6a302f --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-defroute-no-gatewaydev-yes @@ -0,0 +1,2 @@ +GATEWAYDEV=eth0 +IPV6_DEFAULTDEV=eth0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-global-gateway b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-global-gateway new file mode 100644 index 00000000..7987d105 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-global-gateway @@ -0,0 +1 @@ +GATEWAY=192.168.1.2 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-global-gateway-ignore b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-global-gateway-ignore new file mode 100644 index 00000000..7987d105 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-global-gateway-ignore @@ -0,0 +1 @@ +GATEWAY=192.168.1.2 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-never-default b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-never-default new file mode 100644 index 00000000..4347405e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-never-default @@ -0,0 +1,4 @@ +GATEWAYDEV=eth0 +# when devices in IPV6_DEFAULTDEV and IPV6_DEFAULTGW don't match the one in IPV6_DEFAULTGW is preferred +IPV6_DEFAULTDEV=eth4 +IPV6_DEFAULTGW=2001::1234%eth0 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-static-routes-legacy b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-static-routes-legacy new file mode 100644 index 00000000..c9e422e7 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-static-routes-legacy @@ -0,0 +1,4 @@ +1.2.3.0/24 via 222.173.190.239 metric 0 +3.2.1.0/24 via 202.254.171.190 metric 77 +7.7.7.7/32 via 10.0.2.2 metric 11 +8.8.8.8/32 metric 12 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes new file mode 100644 index 00000000..9c05417e --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes @@ -0,0 +1,19 @@ +ADDRESS0=11.22.33.0 +NETMASK0=255.255.255.0 +GATEWAY0=192.168.1.5 + +ADDRESS1=44.55.66.77 +NETMASK1=255.255.255.255 +GATEWAY1=192.168.1.7 +METRIC1=3 +OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 scope 10 initcwnd lock 13 initrwnd 14" + +ADDRESS2=44.55.66.78 +NETMASK2=255.255.255.255 +GATEWAY2=192.168.1.8 +METRIC2=3 +OPTIONS2="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 onlink window 30000 initcwnd lock 13 initrwnd 14 scope link" + +ADDRESS3=1.2.3.4 +NETMASK3=255.255.255.255 +OPTIONS3="local scope host" diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy new file mode 100644 index 00000000..1fef7e97 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy @@ -0,0 +1,9 @@ +# Test route file in legacy format; i.e. lines passed as argumet to "ip route add" + + +21.31.41.0/24 via 9.9.9.9 metric 1 + via 8.8.8.8 to 32.42.52.62 + 43.53.0.0/16 metric 3 via 7.7.7.7 dev eth2 cwnd 14 mtu lock 9000 initrwnd 20 window lock 10000 initcwnd lock 42 src 1.2.3.4 + +7.7.7.8/32 via (null) metric 18 +local 1.2.3.4 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual new file mode 100644 index 00000000..8bdf0acf --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual @@ -0,0 +1,9 @@ +9876::1234/96 via 9876::7777 metric 2 + +# default route is ignored by ifcfg-rh reader, because NM handles it internally +default via dead::beaf + +# routes without "via" are valid +abbe::cafe/64 metric 777 + +aaaa::cccc/64 from 1111::2222/48 via 3333::4444 src 5555::6666 mtu lock 1450 cwnd 13 diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/test1_key_and_cert.pem b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/test1_key_and_cert.pem new file mode 100644 index 00000000..dec9aa1b --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/test1_key_and_cert.pem @@ -0,0 +1,118 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,4DE0615F23D82107 + +QPNCO5Dobvz9dDhN32KkZRoEifW+HDm2PCbRQhKDiscGwB6LgypvVjHNsZiFKwzz +L4R51UqgQeJx7GSGJqE626e9z9J+UNBhop02aOO2X0eSPdvBzr/uJ6Umiyr1xqD7 +zWf7u9l5kXElDJRhK+87GMBewp4Ie9NeXDjhF8hzC5Kiulen4AH3AYnfH3S7DimU +h8GFMg8inrudrTbcjBhCdPeHG2jCygOxw3InRFz7uaN6LIhOaPQvmvpP4Cc1WRnW +ZPq9o+eU3fPWPD5t+Op/VzYLvKwgBy/yK1rQXUm6ZMO7MhhRJ94ZCsJv+nVWpJlv +QyBlxDKxwfkfYbDELdnnDQdHdMbKatLqa0KhSkgpp8LywBtanPz731tyT0r7b3na +eLdra59lRU7ZQLPEdS3lPZd2O/KQvWf8wbg7MjXS9LxQ7R5HOPu6DNJlwXVZBmmo +cAfu2q8ubU2IePvWLD1GOrBi6hE9TiGvFJkw+wBK+t72sz3njv9Xm/zlxruaEk5m +RW/kybU3FP4PtjriBbskz3/VZaaxuRN7OoOYTkmyHmG1ADgcRUV6fea19qqsBlN8 +xb+SRtoH28oT/JVWU5neE2dbNzk5LeVO+w70NNdR5s5xqkBhbGGaJxvXwNP4ltFr +T06SMh8znOLKwWB00aRtwfU7jOwR3mOleQO4ugIHmau3zp1TqzAHW8XtpuV7qVeI +ESZOZuf0vW43BtNzgLXt1+r+bmsMsRwhnyomL9M0TUyyBdVYY9GkzTG9pOESheRo +RSvAZ8qKGUliTpgBcbt2v1+NqkszcHa6FxuvS8YU4uo5/GqsgTxHTNIB232hIrrZ +EIm6QL9TC5oFXMjy6UNqoCm5Nb8DBJ6aErt7pt7aoktqUW3O3QIzQT3IbZ4nAcTt +lVF4d7j29I9t7bcC8GOVU1neilguZUss4ghJg9x4zI5UZdR7hZ8fbFT47TyxB+j5 +r0YdmjbjVTaSyaN2JGh1wvb4TzawGNVx/U2EJE16HigOtPfsfQRJ3x+FROKBdVa4 +aIFYXkRBeIPxX6n9pcw0lBCsnXo6/5iTjQSk2VqO3rHO/wyWiEjNczhL33dY2A8W +GG5ECMO5SqXZHQQzpABqK94dxe3UC8aEESO5NhEqDuV7qQGol0qPKrUA3wb0jb2e +DrejJ9HS2m1SUDmjpvvmEGy6GN7CRibbKt5rNZdJNNvWArOF5d0F6wkixQLl73oE +lq5gLQQk9n7ClleKLhlQpBCorxilBbzmSUekkJLi0eaZiBBFWBX9udqnUZloXTgO +8qwuO8K/GPR9Jy1/UH2Vh1H+wivaqKTVgEb0NotzgzECgTEFKJafl7rUNs1OZRZ3 +VBjevi6+iDpxVFgF71kXfdUC4ph0E1XDl0ja2rrKQGivMkUhWJ57+4EV5+hBkAnt +G0RV45NwHXLrK2bd8F9PlRk2XHW6mIcFRXsW1DjeBhk/sQjvlO9R01GRSgcXtekJ +tmX17FWrMrzXHpvy1IC3fk4RVnSjpzQ8O+17YE8/la9wVaeZZzHyYFmMT7VXjIhW +QozJQ0vJ2jxJRh5GYn3tpJzdaeRfvTBik0pChNdUTnWP+BJ35xoCTs8iwJbmgVZ1 +-----END RSA PRIVATE KEY----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: md5WithRSAEncryption + Issuer: C=US, ST=Berkshire, L=Newbury, O=My Company Ltd, OU=Testing, CN=test/emailAddress=test@test.com + Validity + Not Before: Mar 10 15:13:16 2009 GMT + Not After : Mar 8 15:13:16 2019 GMT + Subject: C=US, ST=Berkshire, O=My Company Ltd, OU=Testing, CN=test1/emailAddress=test@test.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:cd:34:b1:2e:b0:04:c6:f4:2b:a2:c0:a0:39:7a: + 82:ed:96:c4:f7:19:83:91:5c:b4:e7:9c:de:ec:48: + ec:2d:e4:51:08:26:42:ac:d3:98:26:7a:72:f7:49: + c2:9e:66:05:c6:47:29:fe:3b:ac:6b:af:6f:5e:a8: + 03:5a:73:33:ba:19:03:00:35:f5:00:bc:a8:be:14: + ce:46:69:e3:6d:ed:34:37:85:55:87:62:b3:b7:c9: + c0:cc:9a:aa:61:05:5b:cd:a2:17:42:d3:e5:6f:1c: + 60:8d:c2:15:41:46:f8:12:54:d0:38:57:e1:fd:8d: + 44:c8:fb:56:b3:b9:6c:e9:f8:9e:21:11:57:1b:8b: + f9:cf:e3:17:e7:d8:fd:ac:d1:01:c6:92:30:f3:2d: + c9:d6:c1:f0:3d:fd:ca:30:dd:75:74:e7:d1:6b:75: + d8:c5:4d:43:61:fe:f6:ad:7e:4c:63:7c:03:17:a2: + 06:8f:d0:8b:69:d3:7a:07:0f:0b:a2:cf:0c:70:38: + ba:cc:55:35:60:84:58:d8:d2:be:1f:ef:76:a9:ba: + ae:6a:dc:08:97:80:de:42:00:b7:d4:ce:9a:b0:36: + 2a:c7:6f:45:04:7c:ea:41:19:d8:b9:19:04:1f:11: + a9:22:80:bd:69:08:15:0d:3c:de:cd:7e:88:6c:0f: + a3:43 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + CE:03:7E:EF:E7:DE:C9:87:BF:DE:56:F4:C8:A3:40:F6:C8:6F:05:8C + X509v3 Authority Key Identifier: + keyid:B8:35:37:32:BE:CF:4F:79:F5:7B:74:B2:F2:10:5A:BA:80:C5:6A:10 + DirName:/C=US/ST=Berkshire/L=Newbury/O=My Company Ltd/OU=Testing/CN=test/emailAddress=test@test.com + serial:EB:E7:64:FB:79:F7:22:19 + + Signature Algorithm: md5WithRSAEncryption + 7a:20:93:63:40:73:7d:33:01:2e:c0:13:52:a4:a7:e1:4d:82: + f4:fb:b2:7b:d0:2b:5a:3f:0e:3c:28:61:71:ab:01:4d:fe:89: + b5:cd:2f:97:59:93:53:9d:51:86:48:dd:b9:e4:73:5e:22:0b: + 12:0d:25:39:76:16:44:06:0c:40:45:21:6b:a6:b1:e0:bf:76: + 1b:36:f3:1e:41:82:57:d9:59:b7:60:40:43:1c:1d:79:f6:48: + 32:5c:4e:e2:06:89:96:41:d2:54:1f:4a:6f:f6:78:a5:3c:02: + 85:21:e2:65:e1:8a:6d:24:19:95:f8:c0:35:ab:bd:ff:3d:f1: + fb:50:2d:30:1e:67:a6:7c:50:f9:d5:77:66:77:5a:14:0f:5c: + cd:21:09:9b:a3:92:57:19:dd:01:a4:18:c5:f9:70:e4:17:43: + 8d:b1:e6:61:e9:50:89:83:4f:ce:a4:57:68:58:40:70:ae:71: + 1c:47:66:d2:30:54:50:ea:3a:87:32:64:3b:18:42:fe:5a:19: + 07:64:f7:f1:b1:10:07:fd:a7:d2:a7:a8:05:79:5b:25:ba:69: + 7b:1a:3e:b1:3e:e4:17:17:01:ba:eb:54:ae:83:00:ed:66:62: + 8d:c0:3e:8a:b4:27:5f:e9:01:ce:20:c3:34:a9:28:c0:6f:c7: + 3b:65:fe:f9 +-----BEGIN CERTIFICATE----- +MIIEojCCA4qgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMCVVMx +EjAQBgNVBAgTCUJlcmtzaGlyZTEQMA4GA1UEBxMHTmV3YnVyeTEXMBUGA1UEChMO +TXkgQ29tcGFueSBMdGQxEDAOBgNVBAsTB1Rlc3RpbmcxDTALBgNVBAMTBHRlc3Qx +HDAaBgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wHhcNMDkwMzEwMTUxMzE2WhcN +MTkwMzA4MTUxMzE2WjB6MQswCQYDVQQGEwJVUzESMBAGA1UECBMJQmVya3NoaXJl +MRcwFQYDVQQKEw5NeSBDb21wYW55IEx0ZDEQMA4GA1UECxMHVGVzdGluZzEOMAwG +A1UEAxMFdGVzdDExHDAaBgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNNLEusATG9CuiwKA5eoLtlsT3GYOR +XLTnnN7sSOwt5FEIJkKs05gmenL3ScKeZgXGRyn+O6xrr29eqANaczO6GQMANfUA +vKi+FM5GaeNt7TQ3hVWHYrO3ycDMmqphBVvNohdC0+VvHGCNwhVBRvgSVNA4V+H9 +jUTI+1azuWzp+J4hEVcbi/nP4xfn2P2s0QHGkjDzLcnWwfA9/cow3XV059FrddjF +TUNh/vatfkxjfAMXogaP0Itp03oHDwuizwxwOLrMVTVghFjY0r4f73apuq5q3AiX +gN5CALfUzpqwNirHb0UEfOpBGdi5GQQfEakigL1pCBUNPN7NfohsD6NDAgMBAAGj +ggEfMIIBGzAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVy +YXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUzgN+7+feyYe/3lb0yKNA9shvBYww +gcAGA1UdIwSBuDCBtYAUuDU3Mr7PT3n1e3Sy8hBauoDFahChgZGkgY4wgYsxCzAJ +BgNVBAYTAlVTMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAOBgNVBAcTB05ld2J1cnkx +FzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMRAwDgYDVQQLEwdUZXN0aW5nMQ0wCwYD +VQQDEwR0ZXN0MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRlc3QuY29tggkA6+dk+3n3 +IhkwDQYJKoZIhvcNAQEEBQADggEBAHogk2NAc30zAS7AE1Kkp+FNgvT7snvQK1o/ +DjwoYXGrAU3+ibXNL5dZk1OdUYZI3bnkc14iCxINJTl2FkQGDEBFIWumseC/dhs2 +8x5BglfZWbdgQEMcHXn2SDJcTuIGiZZB0lQfSm/2eKU8AoUh4mXhim0kGZX4wDWr +vf898ftQLTAeZ6Z8UPnVd2Z3WhQPXM0hCZujklcZ3QGkGMX5cOQXQ42x5mHpUImD +T86kV2hYQHCucRxHZtIwVFDqOocyZDsYQv5aGQdk9/GxEAf9p9KnqAV5WyW6aXsa +PrE+5BcXAbrrVK6DAO1mYo3APoq0J1/pAc4gwzSpKMBvxztl/vk= +-----END CERTIFICATE----- diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/test_ca_cert.pem b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/test_ca_cert.pem new file mode 100644 index 00000000..ef1be20d --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/test_ca_cert.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEjzCCA3egAwIBAgIJAOvnZPt59yIZMA0GCSqGSIb3DQEBBQUAMIGLMQswCQYD +VQQGEwJVUzESMBAGA1UECBMJQmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcw +FQYDVQQKEw5NeSBDb21wYW55IEx0ZDEQMA4GA1UECxMHVGVzdGluZzENMAsGA1UE +AxMEdGVzdDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTAeFw0wOTAzMTAx +NTEyMTRaFw0xOTAzMDgxNTEyMTRaMIGLMQswCQYDVQQGEwJVUzESMBAGA1UECBMJ +QmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcwFQYDVQQKEw5NeSBDb21wYW55 +IEx0ZDEQMA4GA1UECxMHVGVzdGluZzENMAsGA1UEAxMEdGVzdDEcMBoGCSqGSIb3 +DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKot9j+/+CX1/gZLgJHIXCRgCItKLGnf7qGbgqB9T2ACBqR0jllKWwDKrcWU +xjXNIc+GF9Wnv+lX6G0Okn4Zt3/uRNobL+2b/yOF7M3Td3/9W873zdkQQX930YZc +Rr8uxdRPP5bxiCgtcw632y21sSEbG9mjccAUnV/0jdvfmMNj0i8gN6E0fMBiJ9S3 +FkxX/KFvt9JWE9CtoyL7ki7UIDq+6vj7Gd5N0B3dOa1y+rRHZzKlJPcSXQSEYUS4 +HmKDwiKSVahft8c4tDn7KPi0vex91hlgZVd3usL2E/Vq7o5D9FAZ5kZY0AdFXwdm +J4lO4Mj7ac7GE4vNERNcXVIX59sCAwEAAaOB8zCB8DAdBgNVHQ4EFgQUuDU3Mr7P +T3n1e3Sy8hBauoDFahAwgcAGA1UdIwSBuDCBtYAUuDU3Mr7PT3n1e3Sy8hBauoDF +ahChgZGkgY4wgYsxCzAJBgNVBAYTAlVTMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAO +BgNVBAcTB05ld2J1cnkxFzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMRAwDgYDVQQL +EwdUZXN0aW5nMQ0wCwYDVQQDEwR0ZXN0MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRl +c3QuY29tggkA6+dk+3n3IhkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC +AQEAVRG4aALIvCXCiKfe7K+iJxjBVRDFPEf7JWA9LGgbFOn6pNvbxonrR+0BETdc +JV1ET4ct2xsE7QNFIkp9GKRC+6J32zCo8qtLCD5+v436r8TUG2/t2JRMkb9I2XVT +p7RJoot6M0Ltf8KNQUPYh756xmKZ4USfQUwc58MOSDGY8VWEXJOYij9Pf0e0c52t +qiCEjXH7uXiS8Pgq9TYm7AkWSOrglYhSa83x0f8mtT8Q15nBESIHZ6o8FAS2bBgn +B0BkrKRjtBUkuJG3vTox+bYINh2Gxi1JZHWSV1tN5z3hd4VFcKqanW5OgQwToBqp +3nniskIjbH0xjgZf/nVMyLnjxg== +-----END CERTIFICATE----- diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 new file mode 100644 index 00000000..edc2af75 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 Binary files differdiff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c new file mode 100644 index 00000000..59127d01 --- /dev/null +++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -0,0 +1,11858 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008 - 2011 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> +#include <linux/pkt_sched.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <linux/if_ether.h> +#include <linux/if_infiniband.h> + +#include "nm-glib-aux/nm-json-aux.h" +#include "nm-utils.h" +#include "nm-setting-connection.h" +#include "nm-setting-wired.h" +#include "nm-setting-user.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-8021x.h" +#include "nm-setting-pppoe.h" +#include "nm-setting-ppp.h" +#include "nm-setting-vpn.h" +#include "nm-setting-ethtool.h" +#include "nm-setting-gsm.h" +#include "nm-setting-cdma.h" +#include "nm-setting-serial.h" +#include "nm-setting-vlan.h" +#include "nm-setting-dcb.h" +#include "nm-core-internal.h" +#include "nm-base/nm-ethtool-base.h" +#include "nm-base/nm-ethtool-utils-base.h" + +#include "NetworkManagerUtils.h" + +#include "settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h" +#include "settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h" +#include "settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h" +#include "settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h" + +#include "nm-test-utils-core.h" + +#define TEST_IFCFG_DIR NM_BUILD_SRCDIR "/src/core/settings/plugins/ifcfg-rh/tests/network-scripts" +#define TEST_SCRATCH_DIR \ + NM_BUILD_BUILDDIR "/src/core/settings/plugins/ifcfg-rh/tests/network-scripts" +#define TEST_SCRATCH_DIR_TMP TEST_SCRATCH_DIR "/tmp" + +#define TEST_SCRATCH_ALIAS_BASE TEST_SCRATCH_DIR "/ifcfg-alias0" + +/*****************************************************************************/ + +#define _svOpenFile(testfile) \ + ({ \ + shvarFile * _f; \ + GError * _error = NULL; \ + const char *_testfile = (testfile); \ + \ + g_assert(_testfile); \ + _f = svOpenFile(_testfile, &_error); \ + nmtst_assert_success(_f, _error); \ + _f; \ + }) + +#define _svGetValue_check(f, key, expected_value) \ + G_STMT_START \ + { \ + const char * _val; \ + gs_free char * _to_free = NULL; \ + gs_free char * _val_string = NULL; \ + shvarFile *const _f = (f); \ + const char *const _key = (key); \ + \ + _val_string = svGetValueStr_cp(_f, _key); \ + _val = svGetValue(_f, _key, &_to_free); \ + g_assert_cmpstr(_val, ==, (expected_value)); \ + g_assert((!_val_string && (!_val || !_val[0])) \ + || (_val_string && nm_streq0(_val, _val_string))); \ + } \ + G_STMT_END + +static void +_assert_reread_same(NMConnection *connection, NMConnection *reread) +{ + nmtst_assert_connection_verifies_without_normalization(reread); + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +_assert_reread_same_FIXME(NMConnection *connection, NMConnection *reread) +{ + gs_unref_object NMConnection *connection_normalized = NULL; + gs_unref_hashtable GHashTable *settings = NULL; + + /* FIXME: these assertion failures should not happen as we expect + * that re-reading a connection after write yields the same result. + * + * Needs investigation and fixing. */ + nmtst_assert_connection_verifies_without_normalization(reread); + + connection_normalized = nmtst_connection_duplicate_and_normalize(connection); + + g_assert(!nm_connection_compare(connection_normalized, reread, NM_SETTING_COMPARE_FLAG_EXACT)); + g_assert(!nm_connection_diff(connection_normalized, + reread, + NM_SETTING_COMPARE_FLAG_EXACT, + &settings)); +} + +/* dummy path for an "expected" file, meaning: don't check for expected + * written ifcfg file. */ +static const char NO_EXPECTED[1]; + +static void +_assert_expected_content(NMConnection *connection, const char *filename, const char *expected) +{ + gs_free char *content_expectd = NULL; + gs_free char *content_written = NULL; + GError * error = NULL; + gsize len_expectd = 0; + gsize len_written = 0; + gboolean success; + const char * uuid = NULL; + + g_assert(NM_IS_CONNECTION(connection)); + g_assert(filename); + g_assert(g_file_test(filename, G_FILE_TEST_EXISTS)); + + g_assert(expected); + if (expected == NO_EXPECTED) + return; + + success = g_file_get_contents(filename, &content_written, &len_written, &error); + nmtst_assert_success(success, error); + + success = g_file_get_contents(expected, &content_expectd, &len_expectd, &error); + nmtst_assert_success(success, error); + + { + gsize i, j; + + for (i = 0; i < len_expectd;) { + if (content_expectd[i] != '$') { + i++; + continue; + } + if (g_str_has_prefix(&content_expectd[i], "${UUID}")) { + GString *str; + + if (!uuid) { + uuid = nm_connection_get_uuid(connection); + g_assert(uuid); + } + + j = strlen(uuid); + + str = g_string_new_len(content_expectd, len_expectd); + g_string_erase(str, i, NM_STRLEN("${UUID}")); + g_string_insert_len(str, i, uuid, j); + + g_free(content_expectd); + len_expectd = str->len; + content_expectd = g_string_free(str, FALSE); + i += j; + continue; + } + + /* other '$' is not supported. If need be, support escaping of + * '$' via '$$'. */ + g_assert_not_reached(); + } + } + + if (len_expectd != len_written || memcmp(content_expectd, content_written, len_expectd) != 0) { + if (g_getenv("NMTST_IFCFG_RH_UPDATE_EXPECTED") + || nm_streq0(g_getenv("NM_TEST_REGENERATE"), "1")) { + if (uuid) { + gs_free char *search = g_strdup_printf("UUID=%s\n", uuid); + const char * s; + gsize i; + GString * str; + + s = content_written; + while (TRUE) { + s = strstr(s, search); + g_assert(s); + if (s == content_written || s[-1] == '\n') + break; + s += strlen(search); + } + + i = s - content_written; + + str = g_string_new_len(content_written, len_written); + g_string_erase(str, i, strlen(search)); + g_string_insert(str, i, "UUID=${UUID}\n"); + + len_written = str->len; + content_written = g_string_free(str, FALSE); + } + success = g_file_set_contents(expected, content_written, len_written, &error); + nmtst_assert_success(success, error); + } else { + g_error("The content of \"%s\" (%zu) differs from \"%s\" (%zu). Set " + "NMTST_IFCFG_RH_UPDATE_EXPECTED=yes to update the files " + "inplace\n\n>>>%s<<<\n\n>>>%s<<<\n", + filename, + len_written, + expected, + len_expectd, + content_written, + content_expectd); + } + } +} + +#define _writer_update_connection_reread(connection, \ + ifcfg_dir, \ + filename, \ + expected, \ + out_reread, \ + out_reread_same) \ + G_STMT_START \ + { \ + gs_unref_object NMConnection *_connection = \ + nmtst_connection_duplicate_and_normalize(connection); \ + NMConnection **_out_reread = (out_reread); \ + gboolean * _out_reread_same = (out_reread_same); \ + const char * _ifcfg_dir = (ifcfg_dir); \ + const char * _filename = (filename); \ + const char * _expected = (expected); \ + GError * _error = NULL; \ + gboolean _success; \ + \ + g_assert(_ifcfg_dir &&_ifcfg_dir[0]); \ + g_assert(_filename &&_filename[0]); \ + \ + _success = nms_ifcfg_rh_writer_write_connection(_connection, \ + _ifcfg_dir, \ + _filename, \ + NULL, \ + NULL, \ + NULL, \ + _out_reread, \ + _out_reread_same, \ + &_error); \ + nmtst_assert_success(_success, _error); \ + _assert_expected_content(_connection, _filename, _expected); \ + } \ + G_STMT_END + +#define _writer_update_connection(connection, ifcfg_dir, filename, expected) \ + G_STMT_START \ + { \ + gs_unref_object NMConnection *_reread = NULL; \ + NMConnection * _c = (connection); \ + gboolean _reread_same = FALSE; \ + \ + _writer_update_connection_reread(_c, \ + ifcfg_dir, \ + filename, \ + expected, \ + &_reread, \ + &_reread_same); \ + _assert_reread_same(_c, _reread); \ + g_assert(_reread_same); \ + } \ + G_STMT_END + +static NMConnection * +_connection_from_file(const char *filename, + const char *network_file, + const char *test_type, + char ** out_unhandled) +{ + NMConnection *connection; + GError * error = NULL; + char * unhandled_fallback = NULL; + + g_assert(!out_unhandled || !*out_unhandled); + + connection = nmtst_connection_from_file(filename, + network_file, + test_type, + out_unhandled ?: &unhandled_fallback, + &error); + g_assert_no_error(error); + g_assert(!unhandled_fallback); + + if (out_unhandled && *out_unhandled) + nmtst_assert_connection_verifies(connection); + else + nmtst_assert_connection_verifies_without_normalization(connection); + return connection; +} + +static void +_connection_from_file_fail(const char *filename, + const char *network_file, + const char *test_type, + GError ** error) +{ + NMConnection *connection; + GError * local = NULL; + char * unhandled = NULL; + + connection = nmtst_connection_from_file(filename, network_file, test_type, &unhandled, &local); + + g_assert(!connection); + g_assert(local); + g_assert(!unhandled); + g_propagate_error(error, local); +} + +static void +_writer_new_connection_reread(NMConnection * connection, + const char * ifcfg_dir, + char ** out_filename, + const char * expected, + NMConnection **out_reread, + gboolean * out_reread_same) +{ + gboolean success; + GError * error = NULL; + char * filename = NULL; + gs_unref_object NMConnection *con_verified = NULL; + gs_unref_object NMConnection *reread_copy = NULL; + NMConnection **reread = out_reread ?: ((nmtst_get_rand_uint32() % 2) ? &reread_copy : NULL); + + g_assert(NM_IS_CONNECTION(connection)); + g_assert(ifcfg_dir); + + con_verified = nmtst_connection_duplicate_and_normalize(connection); + + success = nms_ifcfg_rh_writer_write_connection(con_verified, + ifcfg_dir, + NULL, + NULL, + NULL, + &filename, + reread, + out_reread_same, + &error); + nmtst_assert_success(success, error); + g_assert(filename && filename[0]); + + if (reread) + nmtst_assert_connection_verifies_without_normalization(*reread); + + _assert_expected_content(con_verified, filename, expected); + + if (out_filename) + *out_filename = filename; + else + g_free(filename); +} + +static void +_writer_new_connec_exp(NMConnection *connection, + const char * ifcfg_dir, + const char * expected, + char ** out_filename) +{ + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same = FALSE; + + _writer_new_connection_reread(connection, + ifcfg_dir, + out_filename, + expected, + &reread, + &reread_same); + _assert_reread_same(connection, reread); + g_assert(reread_same); +} + +static void +_writer_new_connection(NMConnection *connection, const char *ifcfg_dir, char **out_filename) +{ + _writer_new_connec_exp(connection, ifcfg_dir, NO_EXPECTED, out_filename); +} + +static void +_writer_new_connection_FIXME(NMConnection *connection, const char *ifcfg_dir, char **out_filename) +{ + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same = FALSE; + + /* FIXME: this should not happen. Fix it to use _writer_new_connection() instead. */ + + _writer_new_connection_reread(connection, + ifcfg_dir, + out_filename, + NO_EXPECTED, + &reread, + &reread_same); + _assert_reread_same_FIXME(connection, reread); + g_assert(!reread_same); +} + +static void +_writer_new_connection_fail(NMConnection *connection, const char *ifcfg_dir, GError **error) +{ + gs_unref_object NMConnection *connection_normalized = NULL; + gs_unref_object NMConnection *reread = NULL; + gboolean success; + GError * local = NULL; + char * filename = NULL; + + g_assert(NM_IS_CONNECTION(connection)); + g_assert(ifcfg_dir); + + connection_normalized = nmtst_connection_duplicate_and_normalize(connection); + + success = nms_ifcfg_rh_writer_write_connection(connection_normalized, + ifcfg_dir, + NULL, + NULL, + NULL, + &filename, + &reread, + NULL, + &local); + nmtst_assert_no_success(success, local); + g_assert(!filename); + g_assert(!reread); + + g_propagate_error(error, local); +} + +/*****************************************************************************/ + +static void +test_read_netmask_1(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_free char * content = NULL; + NMSettingConnection * s_con; + NMSettingIPConfig * s_ip4; + NMIPAddress * ip4_addr; + const char * FILENAME = TEST_IFCFG_DIR "/ifcfg-netmask-1"; + + connection = _connection_from_file(FILENAME, NULL, TYPE_ETHERNET, NULL); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System netmask-1"); + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpuint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 1); + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "102.0.2.2"); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 15); + + nmtst_assert_connection_verifies_without_normalization(connection); + + content = nmtst_file_get_contents(FILENAME); + + testfile = g_strdup(TEST_SCRATCH_DIR "/ifcfg-netmask-1.copy"); + + nmtst_file_set_contents(testfile, content); + + _writer_update_connection(connection, + TEST_SCRATCH_DIR, + testfile, + TEST_IFCFG_DIR "/ifcfg-netmask-1.cexpected"); +} + +/*****************************************************************************/ + +static gboolean +verify_cert_or_key(NMSetting8021x *s_compare, + const char * file, + const char * privkey_password, + const char * property) +{ + NMSetting8021x * s_8021x; + GError * error = NULL; + gboolean success = FALSE; + const char * expected = NULL, *setting = NULL; + gboolean phase2 = FALSE; + NMSetting8021xCKScheme scheme = NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + + if (strstr(property, "phase2")) + phase2 = TRUE; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new(); + + /* Load the certificate into an empty setting */ + if (strstr(property, "ca-cert")) { + if (phase2) + success = nm_setting_802_1x_set_phase2_ca_cert(s_8021x, + file, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + else + success = nm_setting_802_1x_set_ca_cert(s_8021x, + file, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + } else if (strstr(property, "client-cert")) { + if (phase2) + success = nm_setting_802_1x_set_phase2_client_cert(s_8021x, + file, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + else + success = nm_setting_802_1x_set_client_cert(s_8021x, + file, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + } else if (strstr(property, "private-key")) { + if (phase2) + success = nm_setting_802_1x_set_phase2_private_key(s_8021x, + file, + privkey_password, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + else + success = nm_setting_802_1x_set_private_key(s_8021x, + file, + privkey_password, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + } + g_assert_no_error(error); + g_assert_cmpint(success, ==, TRUE); + + /* Ensure it was loaded using the PATH scheme */ + if (strstr(property, "ca-cert")) { + if (phase2) + scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme(s_8021x); + else + scheme = nm_setting_802_1x_get_ca_cert_scheme(s_8021x); + } else if (strstr(property, "client-cert")) { + if (phase2) + scheme = nm_setting_802_1x_get_phase2_client_cert_scheme(s_8021x); + else + scheme = nm_setting_802_1x_get_client_cert_scheme(s_8021x); + } else if (strstr(property, "private-key")) { + if (phase2) + scheme = nm_setting_802_1x_get_phase2_private_key_scheme(s_8021x); + else + scheme = nm_setting_802_1x_get_private_key_scheme(s_8021x); + } + g_assert_cmpint(scheme, ==, NM_SETTING_802_1X_CK_SCHEME_PATH); + + /* Grab the path back out */ + if (strstr(property, "ca-cert")) { + if (phase2) + expected = nm_setting_802_1x_get_phase2_ca_cert_path(s_8021x); + else + expected = nm_setting_802_1x_get_ca_cert_path(s_8021x); + } else if (strstr(property, "client-cert")) { + if (phase2) + expected = nm_setting_802_1x_get_phase2_client_cert_path(s_8021x); + else + expected = nm_setting_802_1x_get_client_cert_path(s_8021x); + } else if (strstr(property, "private-key")) { + if (phase2) + expected = nm_setting_802_1x_get_phase2_private_key_path(s_8021x); + else + expected = nm_setting_802_1x_get_private_key_path(s_8021x); + } + g_assert_cmpstr(expected, ==, file); + + /* Compare the path with the expected path from the real setting */ + if (strstr(property, "ca-cert")) { + if (phase2) + setting = nm_setting_802_1x_get_phase2_ca_cert_path(s_compare); + else + setting = nm_setting_802_1x_get_ca_cert_path(s_compare); + } else if (strstr(property, "client-cert")) { + if (phase2) + setting = nm_setting_802_1x_get_phase2_client_cert_path(s_compare); + else + setting = nm_setting_802_1x_get_client_cert_path(s_compare); + } else if (strstr(property, "private-key")) { + if (phase2) + setting = nm_setting_802_1x_get_phase2_private_key_path(s_compare); + else + setting = nm_setting_802_1x_get_private_key_path(s_compare); + } + g_assert_cmpstr(setting, ==, expected); + + g_object_unref(s_8021x); + return TRUE; +} + +static void +test_read_basic(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + const char * mac; + char expected_mac_address[ETH_ALEN] = {0x00, 0x16, 0x41, 0x11, 0x22, 0x33}; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-minimal", NULL, TYPE_ETHERNET, NULL); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-minimal"); + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + g_assert_cmpint(nm_setting_connection_get_autoconnect_retries(s_con), ==, -1); + + /* UUID can't be tested if the ifcfg does not contain the UUID key, because + * the UUID is generated on the full path of the ifcfg file, which can change + * depending on where the tests are run. + */ + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + g_assert_cmpint(nm_setting_wired_get_mtu(s_wired), ==, 0); + + /* MAC address */ + mac = nm_setting_wired_get_mac_address(s_wired); + g_assert(mac); + g_assert(nm_utils_hwaddr_matches(mac, -1, expected_mac_address, ETH_ALEN)); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_DISABLED); + g_assert(nm_setting_ip_config_get_never_default(s_ip4) == FALSE); + + /* ===== IPv6 SETTING ===== */ + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), + ==, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE); + g_assert(nm_setting_ip_config_get_never_default(s_ip6) == FALSE); + + g_object_unref(connection); +} + +static void +test_read_miscellaneous_variables(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + char * expected_mac_blacklist[3] = {"00:16:41:11:22:88", + "00:16:41:11:22:99", + "6a:5d:5a:fa:dd:f0"}; + int mac_blacklist_num, i; + guint64 expected_timestamp = 0; + + NMTST_EXPECT_NM_WARN("*invalid MAC in HWADDR_BLACKLIST 'XX:aa:invalid'*"); + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-misc-variables", + NULL, + TYPE_ETHERNET, + NULL); + g_test_assert_expected_messages(); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, expected_timestamp); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + g_assert_cmpint(nm_setting_connection_get_autoconnect_retries(s_con), ==, 100); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + g_assert_cmpint(nm_setting_wired_get_mtu(s_wired), ==, 0); + + /* MAC blacklist */ + mac_blacklist_num = nm_setting_wired_get_num_mac_blacklist_items(s_wired); + g_assert_cmpint(mac_blacklist_num, ==, 3); + for (i = 0; i < mac_blacklist_num; i++) + g_assert(nm_utils_hwaddr_matches(nm_setting_wired_get_mac_blacklist_item(s_wired, i), + -1, + expected_mac_blacklist[i], + -1)); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_DISABLED); + g_assert(nm_setting_ip_config_get_never_default(s_ip4) == FALSE); + + g_object_unref(connection); +} + +static void +test_read_variables_corner_cases(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + const char * mac; + char expected_mac_address[ETH_ALEN] = {0x00, 0x16, 0x41, 0x11, 0x22, 0x33}; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-variables-corner-cases-1", + NULL, + TYPE_ETHERNET, + NULL); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System test-variables-corner-cases-1"); + g_assert_cmpstr(nm_setting_connection_get_zone(s_con), ==, NULL); + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + g_assert_cmpint(nm_setting_wired_get_mtu(s_wired), ==, 0); + + /* MAC address */ + mac = nm_setting_wired_get_mac_address(s_wired); + g_assert(mac); + g_assert(nm_utils_hwaddr_matches(mac, -1, expected_mac_address, ETH_ALEN)); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_DISABLED); + g_assert(nm_setting_ip_config_get_never_default(s_ip4) == FALSE); + + g_object_unref(connection); +} + +static void +test_read_unmanaged(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + char * unhandled_spec = NULL; + guint64 expected_timestamp = 0; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-nm-controlled", + NULL, + TYPE_ETHERNET, + &unhandled_spec); + g_assert_cmpstr(unhandled_spec, ==, "unmanaged:mac:00:11:22:33:f8:9f"); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-nm-controlled"); + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, expected_timestamp); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + + g_free(unhandled_spec); + g_object_unref(connection); +} + +static void +test_read_unmanaged_unrecognized(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + gs_free char * unhandled_spec = NULL; + guint64 expected_timestamp = 0; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-nm-controlled-unrecognized", + NULL, + NULL, + &unhandled_spec); + g_assert_cmpstr(unhandled_spec, ==, "unmanaged:interface-name:=ipoac0"); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "PigeonNet"); + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, expected_timestamp); + + g_object_unref(connection); +} + +static void +test_read_unrecognized(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + gs_free char * unhandled_spec = NULL; + guint64 expected_timestamp = 0; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-unrecognized", + NULL, + NULL, + &unhandled_spec); + g_assert_cmpstr(unhandled_spec, ==, "unrecognized:mac:00:11:22:33"); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-unrecognized"); + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, expected_timestamp); + + g_object_unref(connection); +} + +static void +test_read_wired_static(gconstpointer test_data) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + char * unmanaged = NULL; + const char * mac; + char expected_mac_address[ETH_ALEN] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xee}; + NMIPAddress * ip4_addr; + NMIPAddress * ip6_addr; + const char * file, *expected_id; + gpointer expect_ip6_p; + + nmtst_test_data_unpack(test_data, &file, &expected_id, &expect_ip6_p); + + g_assert(expected_id); + + connection = _connection_from_file(file, NULL, TYPE_ETHERNET, &unmanaged); + g_assert_cmpstr(unmanaged, ==, NULL); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, expected_id); + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + g_assert_cmpint(nm_setting_wired_get_mtu(s_wired), ==, 1492); + + /* MAC address */ + mac = nm_setting_wired_get_mac_address(s_wired); + g_assert(mac); + g_assert(nm_utils_hwaddr_matches(mac, -1, expected_mac_address, ETH_ALEN)); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + g_assert(nm_setting_ip_config_get_may_fail(s_ip4)); + + g_assert(nm_setting_ip_config_has_dns_options(s_ip4)); + g_assert_cmpint(nm_setting_ip_config_get_num_dns_options(s_ip4), ==, 0); + + /* DNS Addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 0), ==, "4.2.2.1"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 1), ==, "4.2.2.2"); + + /* IP addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 1); + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 24); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "192.168.1.5"); + + /* Gateway */ + g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "192.168.1.1"); + + /* ===== IPv6 SETTING ===== */ + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + if (GPOINTER_TO_INT(expect_ip6_p)) { + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), + ==, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL); + g_assert(nm_setting_ip_config_get_may_fail(s_ip6)); + + g_assert(nm_setting_ip_config_has_dns_options(s_ip6)); + g_assert_cmpint(nm_setting_ip_config_get_num_dns_options(s_ip6), ==, 0); + + /* DNS Addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip6), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 0), ==, "1:2:3:4::a"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 1), ==, "1:2:3:4::b"); + + /* IP addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip6), ==, 2); + + ip6_addr = nm_setting_ip_config_get_address(s_ip6, 0); + g_assert(ip6_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, 64); + g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, "dead:beaf::1"); + + ip6_addr = nm_setting_ip_config_get_address(s_ip6, 1); + g_assert(ip6_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, 56); + g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, "dead:beaf::2"); + } else { + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), + ==, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE); + g_assert(!nm_setting_ip_config_has_dns_options(s_ip6)); + } + + g_object_unref(connection); +} + +static void +test_read_wired_static_no_prefix(gconstpointer user_data) +{ + guint32 expected_prefix = GPOINTER_TO_UINT(user_data); + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingIPConfig * s_ip4; + NMIPAddress * ip4_addr; + char * file, *expected_id; + + file = g_strdup_printf(TEST_IFCFG_DIR "/ifcfg-test-wired-static-no-prefix-%u", expected_prefix); + expected_id = g_strdup_printf("System test-wired-static-no-prefix-%u", expected_prefix); + + NMTST_EXPECT_NM_WARN("*missing PREFIX, assuming*"); + connection = _connection_from_file(file, NULL, TYPE_ETHERNET, NULL); + g_test_assert_expected_messages(); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, expected_id); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + + g_assert(!nm_setting_ip_config_has_dns_options(s_ip4)); + g_assert_cmpint(nm_setting_ip_config_get_num_dns_options(s_ip4), ==, 0); + + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 1); + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, expected_prefix); + + g_free(file); + g_free(expected_id); + g_object_unref(connection); +} + +static void +test_read_wired_dhcp(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + char * unmanaged = NULL; + char expected_mac_address[ETH_ALEN] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xee}; + const char * mac; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-dhcp", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(unmanaged == NULL); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-wired-dhcp"); + g_assert_cmpuint(nm_setting_connection_get_timestamp(s_con), ==, 0); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* MAC address */ + mac = nm_setting_wired_get_mac_address(s_wired); + g_assert(mac); + g_assert(nm_utils_hwaddr_matches(mac, -1, expected_mac_address, sizeof(expected_mac_address))); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + g_assert_cmpstr(nm_setting_ip4_config_get_dhcp_fqdn(NM_SETTING_IP4_CONFIG(s_ip4)), + ==, + "foo.bar"); + g_assert(nm_setting_ip_config_get_ignore_auto_dns(s_ip4)); + g_assert_cmpuint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 0), ==, "4.2.2.1"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 1), ==, "4.2.2.2"); + g_assert_cmpuint(nm_setting_ip_config_get_dhcp_hostname_flags(s_ip4), + ==, + NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED | NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE); + + /* ===== IPv6 SETTING ===== */ + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_DHCP); + g_assert_cmpstr(nm_setting_ip_config_get_dhcp_hostname(s_ip6), ==, "foo.bar"); + g_assert_cmpuint(nm_setting_ip_config_get_dhcp_hostname_flags(s_ip6), + ==, + NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS); + + g_object_unref(connection); +} + +static void +test_read_wired_dhcp_plus_ip(void) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + NMIPAddress * ip4_addr; + NMIPAddress * ip6_addr; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-dhcp-plus-ip", + NULL, + TYPE_ETHERNET, + NULL); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + g_assert(nm_setting_ip_config_get_may_fail(s_ip4)); + + /* DNS Addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 0), ==, "4.2.2.1"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 1), ==, "4.2.2.2"); + + /* IP addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 2); + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 24); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "1.2.3.4"); + + /* Gateway */ + g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "1.1.1.1"); + + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 1); + g_assert(ip4_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 16); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "9.8.7.6"); + + /* ===== IPv6 SETTING ===== */ + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO); + g_assert(nm_setting_ip_config_get_may_fail(s_ip6)); + + /* DNS Addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip6), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 0), ==, "1:2:3:4::a"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 1), ==, "1:2:3:4::b"); + + /* IP addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip6), ==, 3); + ip6_addr = nm_setting_ip_config_get_address(s_ip6, 0); + g_assert(ip6_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, 56); + g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, "1001:abba::1234"); + + ip6_addr = nm_setting_ip_config_get_address(s_ip6, 1); + g_assert(ip6_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, 64); + g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, "2001:abba::2234"); + + ip6_addr = nm_setting_ip_config_get_address(s_ip6, 2); + g_assert(ip6_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, 96); + g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, "3001:abba::3234"); + + g_object_unref(connection); +} + +static void +test_read_wired_shared_plus_ip(void) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip4; + NMIPAddress * ip4_addr; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-shared-plus-ip", + NULL, + TYPE_ETHERNET, + NULL); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_SHARED); + g_assert(nm_setting_ip_config_get_may_fail(s_ip4)); + + /* IP addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 1); + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 24); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "10.20.30.5"); + + /* Gateway */ + g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "1.1.1.1"); + + g_object_unref(connection); +} + +static void +test_read_wired_global_gateway(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMIPAddress * ip4_addr; + char * unmanaged = NULL; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-global-gateway", + TEST_IFCFG_DIR "/network-test-wired-global-gateway", + TYPE_ETHERNET, + &unmanaged); + g_assert(unmanaged == NULL); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-wired-global-gateway"); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + + /* Address #1 */ + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 24); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "192.168.1.5"); + + /* Gateway */ + g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "192.168.1.2"); + + g_object_unref(connection); +} + +/* Ignore GATEWAY from /etc/sysconfig/network for automatic connections */ +static void +test_read_wired_global_gateway_ignore(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + char * unmanaged = NULL; + + NMTST_EXPECT_NM_WARN("*ignoring GATEWAY (/etc/sysconfig/network) for * because the connection " + "has no static addresses"); + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-global-gateway-ignore", + TEST_IFCFG_DIR "/network-test-wired-global-gateway-ignore", + TYPE_ETHERNET, + &unmanaged); + g_test_assert_expected_messages(); + g_assert(unmanaged == NULL); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System test-wired-global-gateway-ignore"); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + /* Addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 0); + + /* Gateway */ + g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, NULL); + + g_object_unref(connection); +} + +static void +test_read_wired_obsolete_gateway_n(void) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip4; + NMIPAddress * ip4_addr; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-obsolete-gateway-n", + NULL, + TYPE_ETHERNET, + NULL); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + + /* IP addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 1); + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 24); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "1.2.3.4"); + + /* Gateway */ + g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "1.1.1.1"); + + g_object_unref(connection); +} + +static void +test_user_1(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingUser * s_user; + + connection = + nmtst_create_minimal_connection("Test User 1", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); + s_user = NM_SETTING_USER(nm_setting_user_new()); + +#define _USER_SET_DATA(s_user, key, val) \ + G_STMT_START \ + { \ + GError * _error = NULL; \ + gboolean _success; \ + \ + _success = nm_setting_user_set_data((s_user), (key), (val), &_error); \ + nmtst_assert_success(_success, _error); \ + } \ + G_STMT_END + +#define _USER_SET_DATA_X(s_user, key) _USER_SET_DATA(s_user, key, "val=" key "") + + _USER_SET_DATA(s_user, "my.val1", ""); + _USER_SET_DATA_X(s_user, "my.val2"); + _USER_SET_DATA_X(s_user, "my.v__al3"); + _USER_SET_DATA_X(s_user, "my._v"); + _USER_SET_DATA_X(s_user, "my.v+"); + _USER_SET_DATA_X(s_user, "my.Av"); + _USER_SET_DATA_X(s_user, "MY.AV"); + _USER_SET_DATA_X(s_user, "MY.8V"); + _USER_SET_DATA_X(s_user, "MY.8-V"); + _USER_SET_DATA_X(s_user, "MY.8_V"); + _USER_SET_DATA_X(s_user, "MY.8+V"); + _USER_SET_DATA_X(s_user, "MY.8/V"); + _USER_SET_DATA_X(s_user, "MY.8=V"); + _USER_SET_DATA_X(s_user, "MY.-"); + _USER_SET_DATA_X(s_user, "MY._"); + _USER_SET_DATA_X(s_user, "MY.+"); + _USER_SET_DATA_X(s_user, "MY./"); + _USER_SET_DATA_X(s_user, "MY.="); + _USER_SET_DATA_X(s_user, "my.keys.1"); + _USER_SET_DATA_X(s_user, "my.other.KEY.42"); + + nm_connection_add_setting(connection, NM_SETTING(s_user)); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_User_1.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_wired_never_default(void) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-never-default", + TEST_IFCFG_DIR "/network-test-wired-never-default", + TYPE_ETHERNET, + NULL); + + /* ===== WIRED SETTING ===== */ + g_assert(nm_connection_get_setting_wired(connection)); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + g_assert(nm_setting_ip_config_get_never_default(s_ip4)); + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 0); + + /* ===== IPv6 SETTING ===== */ + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO); + g_assert(nm_setting_ip_config_get_never_default(s_ip6)); + + g_object_unref(connection); +} + +static void +test_read_wired_defroute_no(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + char * unmanaged = NULL; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-defroute-no", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(unmanaged == NULL); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-wired-defroute-no"); + + g_assert(nm_connection_get_setting_wired(connection)); + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + g_assert(nm_setting_ip_config_get_never_default(s_ip4)); + + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO); + g_assert(nm_setting_ip_config_get_never_default(s_ip6)); + + g_object_unref(connection); +} + +static void +test_read_wired_defroute_no_gatewaydev_yes(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + char * unmanaged = NULL; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-defroute-no-gatewaydev-yes", + TEST_IFCFG_DIR "/network-test-wired-defroute-no-gatewaydev-yes", + TYPE_ETHERNET, + &unmanaged); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System test-wired-defroute-no-gatewaydev-yes"); + + g_assert(nm_connection_get_setting_wired(connection)); + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + g_assert(nm_setting_ip_config_get_never_default(s_ip4) == FALSE); + + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO); + g_assert(nm_setting_ip_config_get_never_default(s_ip6) == FALSE); + + g_object_unref(connection); +} + +static void +test_read_wired_static_routes(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMIPRoute * ip4_route; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-static-routes", + NULL, + TYPE_ETHERNET, + NULL); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-wired-static-routes"); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + + /* Routes */ + g_assert_cmpint(nm_setting_ip_config_get_num_routes(s_ip4), ==, 4); + + ip4_route = nm_setting_ip_config_get_route(s_ip4, 0); + g_assert(ip4_route); + g_assert_cmpstr(nm_ip_route_get_dest(ip4_route), ==, "11.22.33.0"); + g_assert_cmpint(nm_ip_route_get_prefix(ip4_route), ==, 24); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip4_route), ==, "192.168.1.5"); + g_assert_cmpint(nm_ip_route_get_metric(ip4_route), ==, -1); + + ip4_route = nm_setting_ip_config_get_route(s_ip4, 1); + g_assert(ip4_route); + g_assert_cmpstr(nm_ip_route_get_dest(ip4_route), ==, "44.55.66.77"); + g_assert_cmpint(nm_ip_route_get_prefix(ip4_route), ==, 32); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip4_route), ==, "192.168.1.7"); + g_assert_cmpint(nm_ip_route_get_metric(ip4_route), ==, 3); + nmtst_assert_route_attribute_byte(ip4_route, NM_IP_ROUTE_ATTRIBUTE_TOS, 0x28); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 30000); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 12); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 13); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 14); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000); + nmtst_assert_route_attribute_boolean(ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE); + nmtst_assert_route_attribute_boolean(ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, TRUE); + nmtst_assert_route_attribute_string(ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.1.1.1"); + nmtst_assert_route_attribute_byte(ip4_route, NM_IP_ROUTE_ATTRIBUTE_SCOPE, 10); + + ip4_route = nm_setting_ip_config_get_route(s_ip4, 2); + g_assert(ip4_route); + g_assert_cmpstr(nm_ip_route_get_dest(ip4_route), ==, "44.55.66.78"); + g_assert_cmpint(nm_ip_route_get_prefix(ip4_route), ==, 32); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip4_route), ==, "192.168.1.8"); + g_assert_cmpint(nm_ip_route_get_metric(ip4_route), ==, 3); + nmtst_assert_route_attribute_byte(ip4_route, NM_IP_ROUTE_ATTRIBUTE_TOS, 0x28); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 30000); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 12); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 13); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 14); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000); + nmtst_assert_route_attribute_boolean(ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE); + nmtst_assert_route_attribute_boolean(ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, TRUE); + nmtst_assert_route_attribute_string(ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.1.1.1"); + nmtst_assert_route_attribute_boolean(ip4_route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, TRUE); + nmtst_assert_route_attribute_byte(ip4_route, NM_IP_ROUTE_ATTRIBUTE_SCOPE, 253); + + ip4_route = nm_setting_ip_config_get_route(s_ip4, 3); + g_assert(ip4_route); + g_assert_cmpstr(nm_ip_route_get_dest(ip4_route), ==, "1.2.3.4"); + g_assert_cmpint(nm_ip_route_get_prefix(ip4_route), ==, 32); + nmtst_assert_route_attribute_string(ip4_route, NM_IP_ROUTE_ATTRIBUTE_TYPE, "local"); + nmtst_assert_route_attribute_byte(ip4_route, NM_IP_ROUTE_ATTRIBUTE_SCOPE, 254); + + g_object_unref(connection); +} + +static void +test_read_wired_static_routes_legacy(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + char * unmanaged = NULL; + NMIPRoute * ip4_route; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-static-routes-legacy", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(!unmanaged); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System test-wired-static-routes-legacy"); + + /* ===== WIRED SETTING ===== */ + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + + /* Routes */ + g_assert_cmpint(nm_setting_ip_config_get_num_routes(s_ip4), ==, 5); + + /* Route #1 */ + ip4_route = nm_setting_ip_config_get_route(s_ip4, 0); + g_assert(ip4_route != NULL); + g_assert_cmpstr(nm_ip_route_get_dest(ip4_route), ==, "21.31.41.0"); + g_assert_cmpint(nm_ip_route_get_prefix(ip4_route), ==, 24); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip4_route), ==, "9.9.9.9"); + g_assert_cmpint(nm_ip_route_get_metric(ip4_route), ==, 1); + + /* Route #2 */ + ip4_route = nm_setting_ip_config_get_route(s_ip4, 1); + g_assert(ip4_route != NULL); + g_assert_cmpstr(nm_ip_route_get_dest(ip4_route), ==, "32.42.52.62"); + g_assert_cmpint(nm_ip_route_get_prefix(ip4_route), ==, 32); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip4_route), ==, "8.8.8.8"); + g_assert_cmpint(nm_ip_route_get_metric(ip4_route), ==, -1); + + /* Route #3 */ + ip4_route = nm_setting_ip_config_get_route(s_ip4, 2); + g_assert(ip4_route != NULL); + g_assert_cmpstr(nm_ip_route_get_dest(ip4_route), ==, "43.53.0.0"); + g_assert_cmpint(nm_ip_route_get_prefix(ip4_route), ==, 16); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip4_route), ==, "7.7.7.7"); + g_assert_cmpint(nm_ip_route_get_metric(ip4_route), ==, 3); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 10000); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 14); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 42); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 20); + nmtst_assert_route_attribute_uint32(ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000); + nmtst_assert_route_attribute_boolean(ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, TRUE); + nmtst_assert_route_attribute_boolean(ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE); + nmtst_assert_route_attribute_string(ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.2.3.4"); + + ip4_route = nm_setting_ip_config_get_route(s_ip4, 3); + g_assert(ip4_route != NULL); + g_assert_cmpstr(nm_ip_route_get_dest(ip4_route), ==, "7.7.7.8"); + g_assert_cmpint(nm_ip_route_get_prefix(ip4_route), ==, 32); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip4_route), ==, NULL); + g_assert_cmpint(nm_ip_route_get_metric(ip4_route), ==, 18); + + /* Route #5 */ + ip4_route = nm_setting_ip_config_get_route(s_ip4, 4); + g_assert(ip4_route != NULL); + g_assert_cmpstr(nm_ip_route_get_dest(ip4_route), ==, "1.2.3.4"); + nmtst_assert_route_attribute_string(ip4_route, NM_IP_ROUTE_ATTRIBUTE_TYPE, "local"); + + g_object_unref(connection); +} + +static void +test_read_wired_ipv4_manual(gconstpointer data) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + char * unmanaged = NULL; + NMIPAddress * ip4_addr; + const char * file, *expected_id; + + nmtst_test_data_unpack(data, &file, &expected_id); + + g_assert(expected_id); + + connection = _connection_from_file(file, NULL, TYPE_ETHERNET, &unmanaged); + g_assert(!unmanaged); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, expected_id); + + /* ===== WIRED SETTING ===== */ + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + g_assert_cmpint(nm_setting_ip_config_get_dad_timeout(s_ip4), ==, 2000); + + /* IP addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 3); + + /* Address #1 */ + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "1.2.3.4"); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 24); + + /* Address #2 */ + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 1); + g_assert(ip4_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "9.8.7.6"); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 16); + + /* Address #3 */ + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 2); + g_assert(ip4_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "3.3.3.3"); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 8); + + g_object_unref(connection); +} + +static void +test_read_wired_ipv6_manual(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + char * unmanaged = NULL; + NMIPAddress * ip6_addr; + NMIPRoute * ip6_route; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-ipv6-manual", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(!unmanaged); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-wired-ipv6-manual"); + + /* ===== WIRED SETTING ===== */ + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + + /* DNS Addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 2); + + /* DNS search domains */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns_searches(s_ip4), ==, 3); + g_assert_cmpstr(nm_setting_ip_config_get_dns_search(s_ip4, 0), ==, "lorem.com"); + g_assert_cmpstr(nm_setting_ip_config_get_dns_search(s_ip4, 1), ==, "ipsum.org"); + g_assert_cmpstr(nm_setting_ip_config_get_dns_search(s_ip4, 2), ==, "dolor.edu"); + + /* ===== IPv6 SETTING ===== */ + + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), + ==, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL); + g_assert(!nm_setting_ip_config_get_never_default(s_ip6)); + g_assert(nm_setting_ip_config_get_may_fail(s_ip6)); + + /* IP addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip6), ==, 3); + + /* Address #1 */ + ip6_addr = nm_setting_ip_config_get_address(s_ip6, 0); + g_assert(ip6_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, "1001:abba::1234"); + g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, 56); + + /* Address #2 */ + ip6_addr = nm_setting_ip_config_get_address(s_ip6, 1); + g_assert(ip6_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, "2001:abba::2234"); + g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, 64); + + /* Address #3 */ + ip6_addr = nm_setting_ip_config_get_address(s_ip6, 2); + g_assert(ip6_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, "3001:abba::3234"); + g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, 96); + + /* Routes */ + g_assert_cmpint(nm_setting_ip_config_get_num_routes(s_ip6), ==, 4); + /* Route #1 */ + ip6_route = nm_setting_ip_config_get_route(s_ip6, 0); + g_assert(ip6_route); + g_assert_cmpstr(nm_ip_route_get_dest(ip6_route), ==, "9876::1234"); + g_assert_cmpint(nm_ip_route_get_prefix(ip6_route), ==, 96); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip6_route), ==, "9876::7777"); + g_assert_cmpint(nm_ip_route_get_metric(ip6_route), ==, 2); + /* Route #2 */ + ip6_route = nm_setting_ip_config_get_route(s_ip6, 1); + g_assert(ip6_route); + g_assert_cmpstr(nm_ip_route_get_dest(ip6_route), ==, "::"); + g_assert_cmpint(nm_ip_route_get_prefix(ip6_route), ==, 0); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip6_route), ==, "dead::beaf"); + g_assert_cmpint(nm_ip_route_get_metric(ip6_route), ==, -1); + /* Route #3 */ + ip6_route = nm_setting_ip_config_get_route(s_ip6, 2); + g_assert(ip6_route); + g_assert_cmpstr(nm_ip_route_get_dest(ip6_route), ==, "abbe::cafe"); + g_assert_cmpint(nm_ip_route_get_prefix(ip6_route), ==, 64); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip6_route), ==, NULL); + g_assert_cmpint(nm_ip_route_get_metric(ip6_route), ==, 777); + /* Route #4 */ + ip6_route = nm_setting_ip_config_get_route(s_ip6, 3); + g_assert(ip6_route); + g_assert_cmpstr(nm_ip_route_get_dest(ip6_route), ==, "aaaa::cccc"); + g_assert_cmpint(nm_ip_route_get_prefix(ip6_route), ==, 64); + g_assert_cmpstr(nm_ip_route_get_next_hop(ip6_route), ==, "3333::4444"); + nmtst_assert_route_attribute_uint32(ip6_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 13); + nmtst_assert_route_attribute_uint32(ip6_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 1450); + nmtst_assert_route_attribute_boolean(ip6_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE); + nmtst_assert_route_attribute_string(ip6_route, NM_IP_ROUTE_ATTRIBUTE_FROM, "1111::2222/48"); + nmtst_assert_route_attribute_string(ip6_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "5555::6666"); + + /* DNS Addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip6), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 0), ==, "1:2:3:4::a"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 1), ==, "1:2:3:4::b"); + + /* DNS domains - none as domains are stuffed to 'ipv4' setting */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns_searches(s_ip6), ==, 0); + + g_object_unref(connection); +} + +static void +test_read_wired_ipv6_only(gconstpointer test_data) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + char * unmanaged = NULL; + NMIPAddress * ip6_addr; + const char * method; + const char * file, *expected_id; + + nmtst_test_data_unpack(test_data, &file, &expected_id); + + g_assert(expected_id); + + connection = _connection_from_file(file, NULL, TYPE_ETHERNET, &unmanaged); + g_assert(!unmanaged); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, expected_id); + + /* ===== WIRED SETTING ===== */ + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + + method = nm_setting_ip_config_get_method(s_ip4); + g_assert_cmpstr(method, ==, NM_SETTING_IP4_CONFIG_METHOD_DISABLED); + + /* ===== IPv6 SETTING ===== */ + + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), + ==, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL); + + /* IP addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip6), ==, 1); + + /* Address #1 */ + ip6_addr = nm_setting_ip_config_get_address(s_ip6, 0); + g_assert(ip6_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, "1001:abba::1234"); + g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, 56); + + /* DNS Addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip6), ==, 1); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip6, 0), ==, "1:2:3:4::a"); + + /* DNS domains should be in IPv6, because IPv4 is disabled */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns_searches(s_ip6), ==, 3); + g_assert_cmpstr(nm_setting_ip_config_get_dns_search(s_ip6, 0), ==, "lorem.com"); + g_assert_cmpstr(nm_setting_ip_config_get_dns_search(s_ip6, 1), ==, "ipsum.org"); + g_assert_cmpstr(nm_setting_ip_config_get_dns_search(s_ip6, 2), ==, "dolor.edu"); + + g_object_unref(connection); +} + +static void +test_read_wired_dhcp6_only(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + char * unmanaged = NULL; + const char * method; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-dhcp6-only", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(!unmanaged); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-wired-dhcp6-only"); + + /* ===== WIRED SETTING ===== */ + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + + method = nm_setting_ip_config_get_method(s_ip4); + g_assert_cmpstr(method, ==, NM_SETTING_IP4_CONFIG_METHOD_DISABLED); + + /* ===== IPv6 SETTING ===== */ + + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_DHCP); + + g_object_unref(connection); +} + +static void +test_read_wired_autoip(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + NMSettingIPConfig * s_ip4; + char * unmanaged = NULL; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-autoip", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(unmanaged == NULL); + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL); + g_assert(!nm_setting_ip_config_get_may_fail(s_ip4)); + g_assert(nm_setting_ip_config_get_ignore_auto_dns(s_ip4)); + + s_con = nm_connection_get_setting_connection(connection); + g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, 2600); +} + +static void +test_read_onboot_no(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + char * unmanaged = NULL; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-onboot-no", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(!unmanaged); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + + g_assert(!nm_setting_connection_get_autoconnect(s_con)); + + g_object_unref(connection); +} + +static void +test_read_noip(void) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-noip", NULL, TYPE_ETHERNET, NULL); + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_DISABLED); + g_assert(nm_setting_ip_config_get_never_default(s_ip4) == FALSE); + + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), + ==, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE); + g_assert(nm_setting_ip_config_get_never_default(s_ip6) == FALSE); + + g_object_unref(connection); +} + +#define TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2_CA_CERT TEST_IFCFG_DIR "/test_ca_cert.pem" + +static void +test_read_wired_8021x_peap_mschapv2(void) +{ + NMConnection * connection; + NMSettingWired * s_wired; + NMSettingIPConfig *s_ip4; + NMSetting8021x * s_8021x; + NMSetting8021x * tmp_8021x; + char * unmanaged = NULL; + GError * error = NULL; + gboolean success = FALSE; + const char * expected_ca_cert_path; + const char * read_ca_cert_path; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-8021x-peap-mschapv2", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(!unmanaged); + + /* ===== WIRED SETTING ===== */ + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x(connection); + g_assert(s_8021x); + + g_assert_cmpint(nm_setting_802_1x_get_num_eap_methods(s_8021x), ==, 1); + g_assert_cmpstr(nm_setting_802_1x_get_eap_method(s_8021x, 0), ==, "peap"); + g_assert_cmpstr(nm_setting_802_1x_get_identity(s_8021x), ==, "David Smith"); + g_assert_cmpstr(nm_setting_802_1x_get_anonymous_identity(s_8021x), ==, "somebody"); + g_assert_cmpstr(nm_setting_802_1x_get_password(s_8021x), ==, "foobar baz"); + g_assert_cmpstr(nm_setting_802_1x_get_phase1_peapver(s_8021x), ==, "1"); + g_assert_cmpstr(nm_setting_802_1x_get_phase1_peaplabel(s_8021x), ==, "1"); + + /* CA Cert */ + tmp_8021x = (NMSetting8021x *) nm_setting_802_1x_new(); + + success = nm_setting_802_1x_set_ca_cert(tmp_8021x, + TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + g_assert_no_error(error); + g_assert(success == TRUE); + + expected_ca_cert_path = nm_setting_802_1x_get_ca_cert_path(tmp_8021x); + g_assert(expected_ca_cert_path); + + read_ca_cert_path = nm_setting_802_1x_get_ca_cert_path(s_8021x); + g_assert(read_ca_cert_path); + + g_assert_cmpstr(read_ca_cert_path, ==, expected_ca_cert_path); + + g_object_unref(tmp_8021x); + + g_object_unref(connection); +} + +static void +test_read_wired_8021x_tls_secret_flags(gconstpointer test_data) +{ + NMConnection * connection; + NMSettingWired *s_wired; + NMSetting8021x *s_8021x; + char * dirname, *tmp; + const char * ifcfg; + gpointer expected_flags_p; + + nmtst_test_data_unpack(test_data, &ifcfg, &expected_flags_p); + + connection = _connection_from_file(ifcfg, NULL, TYPE_ETHERNET, NULL); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x(connection); + g_assert(s_8021x); + g_assert_cmpint(nm_setting_802_1x_get_num_eap_methods(s_8021x), ==, 1); + g_assert_cmpstr(nm_setting_802_1x_get_eap_method(s_8021x, 0), ==, "tls"); + g_assert_cmpstr(nm_setting_802_1x_get_identity(s_8021x), ==, "David Smith"); + g_assert_cmpint(nm_setting_802_1x_get_private_key_password_flags(s_8021x), + ==, + GPOINTER_TO_INT(expected_flags_p)); + + dirname = g_path_get_dirname(ifcfg); + tmp = g_build_path("/", dirname, "test_ca_cert.pem", NULL); + g_assert_cmpstr(nm_setting_802_1x_get_ca_cert_path(s_8021x), ==, tmp); + g_free(tmp); + + tmp = g_build_path("/", dirname, "test1_key_and_cert.pem", NULL); + g_assert_cmpstr(nm_setting_802_1x_get_client_cert_path(s_8021x), ==, tmp); + g_assert_cmpstr(nm_setting_802_1x_get_private_key_path(s_8021x), ==, tmp); + g_free(tmp); + + g_free(dirname); + + g_object_unref(connection); +} + +static void +test_read_write_802_1X_subj_matches(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSetting8021x * s_8021x; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-802-1X-subj-matches", + NULL, + TYPE_ETHERNET, + NULL); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x(connection); + g_assert(s_8021x); + g_assert_cmpint(nm_setting_802_1x_get_num_eap_methods(s_8021x), ==, 1); + g_assert_cmpstr(nm_setting_802_1x_get_eap_method(s_8021x, 0), ==, "peap"); + g_assert_cmpstr(nm_setting_802_1x_get_identity(s_8021x), ==, "Jara Cimrman"); + g_assert_cmpstr(nm_setting_802_1x_get_subject_match(s_8021x), ==, "server1.yourdomain.tld"); + g_assert_cmpstr(nm_setting_802_1x_get_phase2_subject_match(s_8021x), + ==, + "server2.yourdomain.tld"); + g_assert_cmpint(nm_setting_802_1x_get_num_altsubject_matches(s_8021x), ==, 3); + g_assert_cmpstr(nm_setting_802_1x_get_altsubject_match(s_8021x, 0), ==, "a.yourdomain.tld"); + g_assert_cmpstr(nm_setting_802_1x_get_altsubject_match(s_8021x, 1), ==, "b.yourdomain.tld"); + g_assert_cmpstr(nm_setting_802_1x_get_altsubject_match(s_8021x, 2), ==, "c.yourdomain.tld"); + g_assert_cmpint(nm_setting_802_1x_get_num_phase2_altsubject_matches(s_8021x), ==, 2); + g_assert_cmpstr(nm_setting_802_1x_get_phase2_altsubject_match(s_8021x, 0), + ==, + "x.yourdomain.tld"); + g_assert_cmpstr(nm_setting_802_1x_get_phase2_altsubject_match(s_8021x, 1), + ==, + "y.yourdomain.tld"); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-System_test-wired-802-1X-subj-matches.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + /* Check 802.1X stuff of the re-read connection. */ + s_8021x = nm_connection_get_setting_802_1x(reread); + g_assert(s_8021x); + g_assert_cmpint(nm_setting_802_1x_get_num_eap_methods(s_8021x), ==, 1); + g_assert_cmpstr(nm_setting_802_1x_get_eap_method(s_8021x, 0), ==, "peap"); + g_assert_cmpstr(nm_setting_802_1x_get_identity(s_8021x), ==, "Jara Cimrman"); + g_assert_cmpstr(nm_setting_802_1x_get_subject_match(s_8021x), ==, "server1.yourdomain.tld"); + g_assert_cmpstr(nm_setting_802_1x_get_phase2_subject_match(s_8021x), + ==, + "server2.yourdomain.tld"); + g_assert_cmpint(nm_setting_802_1x_get_num_altsubject_matches(s_8021x), ==, 3); + g_assert_cmpstr(nm_setting_802_1x_get_altsubject_match(s_8021x, 0), ==, "a.yourdomain.tld"); + g_assert_cmpstr(nm_setting_802_1x_get_altsubject_match(s_8021x, 1), ==, "b.yourdomain.tld"); + g_assert_cmpstr(nm_setting_802_1x_get_altsubject_match(s_8021x, 2), ==, "c.yourdomain.tld"); + g_assert_cmpint(nm_setting_802_1x_get_num_phase2_altsubject_matches(s_8021x), ==, 2); + g_assert_cmpstr(nm_setting_802_1x_get_phase2_altsubject_match(s_8021x, 0), + ==, + "x.yourdomain.tld"); + g_assert_cmpstr(nm_setting_802_1x_get_phase2_altsubject_match(s_8021x, 1), + ==, + "y.yourdomain.tld"); +} + +static void +test_read_802_1x_ttls_eapgtc(void) +{ + NMConnection * connection; + NMSetting8021x *s_8021x; + + /* Test that EAP-* inner methods are correctly read into the + * NMSetting8021x::autheap property. + */ + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-802-1x-ttls-eapgtc", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x(connection); + g_assert(s_8021x); + + /* EAP methods */ + g_assert_cmpint(nm_setting_802_1x_get_num_eap_methods(s_8021x), ==, 1); + g_assert_cmpstr(nm_setting_802_1x_get_eap_method(s_8021x, 0), ==, "ttls"); + + /* Auth methods */ + g_assert_cmpstr(nm_setting_802_1x_get_phase2_auth(s_8021x), ==, NULL); + g_assert_cmpstr(nm_setting_802_1x_get_phase2_autheap(s_8021x), ==, "gtc"); + + g_object_unref(connection); +} + +static void +test_read_802_1x_tls_p12_no_client_cert(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSetting8021x * s_8021x; + const char * path; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-8021x-tls-p12-no-client-cert", + NULL, + TYPE_ETHERNET, + NULL); + + s_8021x = nm_connection_get_setting_802_1x(connection); + g_assert(s_8021x); + + g_assert_cmpint(nm_setting_802_1x_get_private_key_scheme(s_8021x), + ==, + NM_SETTING_802_1X_CK_SCHEME_PATH); + path = nm_setting_802_1x_get_private_key_path(s_8021x); + g_assert(path); + + g_assert_cmpint(nm_setting_802_1x_get_client_cert_scheme(s_8021x), + ==, + NM_SETTING_802_1X_CK_SCHEME_PATH); + g_assert_cmpstr(path, ==, nm_setting_802_1x_get_client_cert_path(s_8021x)); +} + +static void +test_read_write_802_1x_password_raw(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSetting8021x * s_8021x; + GBytes * bytes; + gconstpointer data; + gsize size; + + /* Test that the 802-1x.password-raw is correctly read and written. */ + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-802-1x-password-raw", + NULL, + TYPE_ETHERNET, + NULL); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x(connection); + g_assert(s_8021x); + + bytes = nm_setting_802_1x_get_password_raw(s_8021x); + g_assert(bytes); + data = g_bytes_get_data(bytes, &size); + g_assert_cmpmem(data, size, "\x04\x08\x15\x16\x23\x42\x00\x01", 8); + + g_assert_cmpint(nm_setting_802_1x_get_password_raw_flags(s_8021x), + ==, + NM_SETTING_SECRET_FLAG_NONE); + + g_assert_cmpstr(nm_setting_802_1x_get_pin(s_8021x), ==, "hallo2"); + g_assert_cmpint(nm_setting_802_1x_get_pin_flags(s_8021x), ==, NM_SETTING_SECRET_FLAG_NONE); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + keyfile = utils_get_keys_path(testfile); + g_assert(g_file_test(keyfile, G_FILE_TEST_EXISTS)); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_wired_aliases_good(gconstpointer test_data) +{ + const int N = GPOINTER_TO_INT(test_data); + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingIPConfig * s_ip4; + int expected_num_addresses; + const char * expected_address_0[] = {"192.168.1.5", + "192.168.1.6", + "192.168.1.9", + "192.168.1.99", + NULL}; + const char * expected_address_3[] = {"192.168.1.5", "192.168.1.6", NULL}; + const char * expected_label_0[] = { + NULL, + "aliasem0:1", + "aliasem0:2", + "aliasem0:99", + NULL, + }; + const char *expected_label_3[] = { + NULL, + "aliasem3:1", + NULL, + }; + const char **expected_address; + const char **expected_label; + int i, j; + char path[256]; + + expected_address = N == 0 ? expected_address_0 : expected_address_3; + expected_label = N == 0 ? expected_label_0 : expected_label_3; + expected_num_addresses = g_strv_length((char **) expected_address); + + nm_sprintf_buf(path, TEST_IFCFG_DIR "/ifcfg-aliasem%d", N); + + connection = _connection_from_file(path, NULL, TYPE_ETHERNET, NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + if (N == 0) + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System aliasem0"); + else + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System aliasem3"); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, expected_num_addresses); + + /* Addresses */ + for (i = 0; i < expected_num_addresses; i++) { + NMIPAddress *ip4_addr; + const char * addr; + GVariant * label; + + ip4_addr = nm_setting_ip_config_get_address(s_ip4, i); + g_assert(ip4_addr != NULL); + + addr = nm_ip_address_get_address(ip4_addr); + g_assert(nm_utils_ipaddr_is_valid(AF_INET, addr)); + + for (j = 0; j < expected_num_addresses; j++) { + if (!g_strcmp0(addr, expected_address[j])) + break; + } + g_assert(j < expected_num_addresses); + + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 24); + label = nm_ip_address_get_attribute(ip4_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL); + if (expected_label[j]) + g_assert_cmpstr(g_variant_get_string(label, NULL), ==, expected_label[j]); + else + g_assert(label == NULL); + + expected_address[j] = NULL; + expected_label[j] = NULL; + } + + /* Gateway */ + g_assert(!nm_setting_ip_config_get_never_default(s_ip4)); + g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "192.168.1.1"); + + for (i = 0; i < expected_num_addresses; i++) + g_assert(!expected_address[i]); + + g_object_unref(connection); +} + +static void +test_read_wired_aliases_bad(const char *base, const char *expected_id) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingIPConfig * s_ip4; + NMIPAddress * ip4_addr; + + g_assert(expected_id); + + connection = _connection_from_file(base, NULL, TYPE_ETHERNET, NULL); + g_test_assert_expected_messages(); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, expected_id); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 1); + + /* Addresses */ + ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0); + g_assert(ip4_addr != NULL); + g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, "192.168.1.5"); + g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, 24); + g_assert(nm_ip_address_get_attribute(ip4_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL) == NULL); + + /* Gateway */ + g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "192.168.1.1"); + + g_object_unref(connection); +} + +static void +test_read_wired_aliases_bad_1(void) +{ + NMTST_EXPECT_NM_WARN("*aliasem1:1*has no DEVICE*"); + test_read_wired_aliases_bad(TEST_IFCFG_DIR "/ifcfg-aliasem1", "System aliasem1"); +} + +static void +test_read_wired_aliases_bad_2(void) +{ + NMTST_EXPECT_NM_WARN("*aliasem2:1*has invalid DEVICE*"); + test_read_wired_aliases_bad(TEST_IFCFG_DIR "/ifcfg-aliasem2", "System aliasem2"); +} + +static void +test_read_dns_options(void) +{ + NMConnection * connection; + NMSettingIPConfig *s_ip4, *s_ip6; + char * unmanaged = NULL; + const char * option; + const char * options4[] = {"ndots:3", "single-request-reopen"}; + const char * options6[] = {"inet6"}; + guint32 i, num; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-dns-options", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert_cmpstr(unmanaged, ==, NULL); + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + + num = nm_setting_ip_config_get_num_dns_options(s_ip4); + g_assert_cmpint(num, ==, G_N_ELEMENTS(options4)); + + for (i = 0; i < num; i++) { + option = nm_setting_ip_config_get_dns_option(s_ip4, i); + g_assert_cmpstr(options4[i], ==, option); + } + + num = nm_setting_ip_config_get_num_dns_options(s_ip6); + g_assert_cmpint(num, ==, G_N_ELEMENTS(options6)); + + for (i = 0; i < num; i++) { + option = nm_setting_ip_config_get_dns_option(s_ip6, i); + g_assert_cmpstr(options6[i], ==, option); + } + + g_object_unref(connection); +} + +static void +test_clear_master(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_free char * keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + char * unmanaged = NULL; + shvarFile * f; + + /* 1. load the bridge slave connection from disk */ + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bridge-component", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert_cmpstr(unmanaged, ==, NULL); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + + g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "br0"); + g_assert_cmpstr(nm_setting_connection_get_slave_type(s_con), ==, "bridge"); + + /* 2. write the connection to a new file */ + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-System_test-bridge-component-a.cexpected", + &testfile); + + /* 3. clear master and slave-type */ + g_object_set(s_con, + NM_SETTING_CONNECTION_MASTER, + NULL, + NM_SETTING_CONNECTION_SLAVE_TYPE, + NULL, + NULL); + + g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, NULL); + g_assert_cmpstr(nm_setting_connection_get_slave_type(s_con), ==, NULL); + + nmtst_assert_connection_verifies_after_normalization(connection, 0, 0); + + /* 4. update the connection on disk */ + _writer_update_connection(connection, + TEST_SCRATCH_DIR, + testfile, + TEST_IFCFG_DIR "/ifcfg-System_test-bridge-component-b.cexpected"); + keyfile = utils_get_keys_path(testfile); + g_assert(!g_file_test(keyfile, G_FILE_TEST_EXISTS)); + + /* 5. check that BRIDGE variable has been removed */ + f = _svOpenFile(testfile); + _svGetValue_check(f, "BRIDGE", NULL); + svCloseFile(f); +} + +static void +test_write_dns_options(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + static const char * mac = "31:33:33:37:be:cd"; + guint32 mtu = 1492; + NMIPAddress * addr; + NMIPAddress * addr6; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test DNS options", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NM_SETTING_WIRED_MTU, mtu, NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NM_SETTING_IP_CONFIG_ROUTE_METRIC, + (gint64) 204, + NULL); + + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + nm_setting_ip_config_add_dns_option(s_ip4, "debug"); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NM_SETTING_IP_CONFIG_ROUTE_METRIC, + (gint64) 206, + NULL); + + /* Add addresses */ + addr6 = nm_ip_address_new(AF_INET6, "1003:1234:abcd::1", 11, &error); + nm_setting_ip_config_add_address(s_ip6, addr6); + nm_ip_address_unref(addr6); + + nm_setting_ip_config_add_dns_option(s_ip6, "timeout:3"); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_wifi_open(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NMSettingIPConfig * s_ip4, *s_ip6; + GBytes * ssid; + const char * mac; + char expected_mac_address[ETH_ALEN] = {0x00, 0x16, 0x41, 0x11, 0x22, 0x33}; + const char *expected_ssid = "blahblah"; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-open", NULL, TYPE_WIRELESS, NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System blahblah (test-wifi-open)"); + + /* UUID can't be tested if the ifcfg does not contain the UUID key, because + * the UUID is generated on the full path of the ifcfg file, which can change + * depending on where the tests are run. + */ + + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + g_assert_cmpint(nm_setting_connection_get_autoconnect_priority(s_con), ==, -1); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* MAC address */ + mac = nm_setting_wireless_get_mac_address(s_wireless); + g_assert(mac); + g_assert(nm_utils_hwaddr_matches(mac, -1, expected_mac_address, sizeof(expected_mac_address))); + + g_assert_cmpint(nm_setting_wireless_get_mtu(s_wireless), ==, 0); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + g_assert(ssid); + g_assert_cmpmem(g_bytes_get_data(ssid, NULL), + g_bytes_get_size(ssid), + expected_ssid, + strlen(expected_ssid)); + + g_assert(!nm_setting_wireless_get_bssid(s_wireless)); + g_assert_cmpstr(nm_setting_wireless_get_mode(s_wireless), ==, "infrastructure"); + g_assert_cmpint(nm_setting_wireless_get_channel(s_wireless), ==, 1); + + /* ===== Wi-Fi SECURITY SETTING ===== */ + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec == NULL); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpint(nm_setting_ip_config_get_route_metric(s_ip4), ==, 104); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert_cmpint(nm_setting_ip_config_get_route_metric(s_ip6), ==, 106); + + g_object_unref(connection); +} + +static void +test_read_wifi_open_auto(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWireless * s_wireless; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-open-auto", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System blahblah (test-wifi-open-auto)"); + + /* ===== WIRELESS SETTING ===== */ + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + g_assert_cmpstr(nm_setting_wireless_get_mode(s_wireless), ==, "infrastructure"); + + g_object_unref(connection); +} + +static void +test_read_wifi_open_ssid_hex(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWireless * s_wireless; + GBytes * ssid; + const char * expected_ssid = "blahblah"; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-open-ssid-hex", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System blahblah (test-wifi-open-ssid-hex)"); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + g_assert(ssid); + g_assert_cmpmem(g_bytes_get_data(ssid, NULL), + g_bytes_get_size(ssid), + expected_ssid, + strlen(expected_ssid)); + + g_object_unref(connection); +} + +static void +test_read_wifi_open_ssid_hex_bad(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + GBytes * ssid; + const char * expected_ssid = "0x626cxx"; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-open-ssid-bad-hex", + NULL, + TYPE_WIRELESS, + NULL); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System 0x626cxx (test-wifi-open-ssid-bad-hex)"); + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + g_assert(ssid); + g_assert_cmpmem(g_bytes_get_data(ssid, NULL), + g_bytes_get_size(ssid), + expected_ssid, + strlen(expected_ssid)); +} + +static void +test_read_wifi_open_ssid_bad(gconstpointer data) +{ + _connection_from_file_fail((const char *) data, NULL, TYPE_WIRELESS, NULL); +} + +static void +test_read_wifi_open_ssid_quoted(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWireless * s_wireless; + GBytes * ssid; + const char * expected_ssid = "foo\"bar\\"; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-open-ssid-quoted", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System foo\"bar\\ (test-wifi-open-ssid-quoted)"); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + g_assert(ssid); + g_assert_cmpmem(g_bytes_get_data(ssid, NULL), + g_bytes_get_size(ssid), + expected_ssid, + strlen(expected_ssid)); + + g_object_unref(connection); +} + +static void +test_read_wifi_wep(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NMSettingIPConfig * s_ip4; + GBytes * ssid; + const char * mac; + char expected_mac_address[ETH_ALEN] = {0x00, 0x16, 0x41, 0x11, 0x22, 0x33}; + const char * expected_ssid = "blahblah"; + NMWepKeyType key_type; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wep", NULL, TYPE_WIRELESS, NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System blahblah (test-wifi-wep)"); + + /* UUID can't be tested if the ifcfg does not contain the UUID key, because + * the UUID is generated on the full path of the ifcfg file, which can change + * depending on where the tests are run. + */ + + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* MAC address */ + mac = nm_setting_wireless_get_mac_address(s_wireless); + g_assert(mac); + g_assert(nm_utils_hwaddr_matches(mac, -1, expected_mac_address, sizeof(expected_mac_address))); + + /* MTU */ + g_assert_cmpint(nm_setting_wireless_get_mtu(s_wireless), ==, 0); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + g_assert(ssid); + g_assert_cmpmem(g_bytes_get_data(ssid, NULL), + g_bytes_get_size(ssid), + expected_ssid, + strlen(expected_ssid)); + + g_assert(!nm_setting_wireless_get_bssid(s_wireless)); + g_assert_cmpstr(nm_setting_wireless_get_mode(s_wireless), ==, "infrastructure"); + g_assert_cmpint(nm_setting_wireless_get_channel(s_wireless), ==, 1); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "none"); + g_assert_cmpstr(nm_setting_wireless_security_get_auth_alg(s_wsec), ==, "shared"); + g_assert_cmpint(nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec), ==, 0); + + key_type = nm_setting_wireless_security_get_wep_key_type(s_wsec); + g_assert(key_type == NM_WEP_KEY_TYPE_UNKNOWN || key_type == NM_WEP_KEY_TYPE_KEY); + + g_assert_cmpstr(nm_setting_wireless_security_get_wep_key(s_wsec, 0), + ==, + "0123456789abcdef0123456789"); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 1)); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 2)); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 3)); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + g_object_unref(connection); +} + +static void +test_read_wifi_wep_adhoc(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NMSettingIPConfig * s_ip4; + GBytes * ssid; + const char * expected_ssid = "blahblah"; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wep-adhoc", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System blahblah (test-wifi-wep-adhoc)"); + + /* UUID can't be tested if the ifcfg does not contain the UUID key, because + * the UUID is generated on the full path of the ifcfg file, which can change + * depending on where the tests are run. + */ + + g_assert(!nm_setting_connection_get_autoconnect(s_con)); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + g_assert(ssid); + g_assert_cmpmem(g_bytes_get_data(ssid, NULL), + g_bytes_get_size(ssid), + expected_ssid, + strlen(expected_ssid)); + + g_assert(!nm_setting_wireless_get_bssid(s_wireless)); + g_assert_cmpstr(nm_setting_wireless_get_mode(s_wireless), ==, "adhoc"); + g_assert_cmpint(nm_setting_wireless_get_channel(s_wireless), ==, 11); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "none"); + g_assert(!nm_setting_wireless_security_get_auth_alg(s_wsec)); + g_assert_cmpint(nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec), ==, 0); + + g_assert_cmpstr(nm_setting_wireless_security_get_wep_key(s_wsec, 0), + ==, + "0123456789abcdef0123456789"); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 1)); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 2)); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 3)); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + /* Ignore auto DNS */ + g_assert(nm_setting_ip_config_get_ignore_auto_dns(s_ip4)); + + /* DNS Addresses */ + g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 2); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 0), ==, "4.2.2.1"); + g_assert_cmpstr(nm_setting_ip_config_get_dns(s_ip4, 1), ==, "4.2.2.2"); + + g_object_unref(connection); +} + +static void +test_read_wifi_wep_passphrase(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wep-passphrase", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "none"); + g_assert_cmpint(nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec), ==, 0); + g_assert_cmpint(nm_setting_wireless_security_get_wep_key_type(s_wsec), + ==, + NM_WEP_KEY_TYPE_UNKNOWN); + g_assert_cmpstr(nm_setting_wireless_security_get_wep_key(s_wsec, 0), ==, "foobar222blahblah"); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 1)); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 2)); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 3)); + + g_object_unref(connection); +} + +static void +test_read_wifi_wep_40_ascii(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NMWepKeyType key_type; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wep-40-ascii", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "none"); + g_assert_cmpint(nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec), ==, 0); + + key_type = nm_setting_wireless_security_get_wep_key_type(s_wsec); + g_assert(key_type == NM_WEP_KEY_TYPE_UNKNOWN || key_type == NM_WEP_KEY_TYPE_KEY); + + g_assert_cmpstr(nm_setting_wireless_security_get_wep_key(s_wsec, 0), ==, "Lorem"); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 1)); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 2)); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 3)); + + g_object_unref(connection); +} + +static void +test_read_wifi_wep_104_ascii(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NMWepKeyType key_type; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wep-104-ascii", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "none"); + g_assert_cmpint(nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec), ==, 0); + + key_type = nm_setting_wireless_security_get_wep_key_type(s_wsec); + g_assert(key_type == NM_WEP_KEY_TYPE_UNKNOWN || key_type == NM_WEP_KEY_TYPE_KEY); + + g_assert_cmpstr(nm_setting_wireless_security_get_wep_key(s_wsec, 0), ==, "LoremIpsumSit"); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 1)); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 2)); + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 3)); + + g_object_unref(connection); +} + +static void +test_read_wifi_leap(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-leap", NULL, TYPE_WIRELESS, NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System blahblah (test-wifi-leap)"); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "ieee8021x"); + g_assert_cmpstr(nm_setting_wireless_security_get_auth_alg(s_wsec), ==, "leap"); + g_assert_cmpstr(nm_setting_wireless_security_get_leap_username(s_wsec), ==, "Bill Smith"); + g_assert_cmpstr(nm_setting_wireless_security_get_leap_password(s_wsec), ==, "foobarblah"); + + g_object_unref(connection); +} + +static void +test_read_wifi_leap_secret_flags(gconstpointer test_data) +{ + NMConnection * connection; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity *s_wsec; + const char * file; + gpointer expected_flags_p; + + nmtst_test_data_unpack(test_data, &file, &expected_flags_p); + + connection = _connection_from_file(file, NULL, TYPE_WIRELESS, NULL); + + /* ===== WIRELESS SETTING ===== */ + s_wifi = nm_connection_get_setting_wireless(connection); + g_assert(s_wifi); + + /* ===== WIRELESS SECURITY SETTING ===== */ + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + + g_assert(g_strcmp0(nm_setting_wireless_security_get_key_mgmt(s_wsec), "ieee8021x") == 0); + g_assert(g_strcmp0(nm_setting_wireless_security_get_auth_alg(s_wsec), "leap") == 0); + g_assert(g_strcmp0(nm_setting_wireless_security_get_leap_username(s_wsec), "Bill Smith") == 0); + /* password blank as it's not system-owned */ + g_assert(nm_setting_wireless_security_get_leap_password_flags(s_wsec) + == GPOINTER_TO_INT(expected_flags_p)); + g_assert(nm_setting_wireless_security_get_leap_password(s_wsec) == NULL); + + g_object_unref(connection); +} + +static void +test_ifcfg_no_trailing_newline(void) +{ + shvarFile *sv; + + sv = _svOpenFile(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-psk"); + _svGetValue_check(sv, "CTCPROT", "no-newline"); + svCloseFile(sv); +} + +static void +test_read_wifi_wpa_psk(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NMSettingIPConfig * s_ip4; + GBytes * ssid; + const char * mac; + char expected_mac_address[ETH_ALEN] = {0x00, 0x16, 0x41, 0x11, 0x22, 0x33}; + const char *expected_ssid = "blahblah"; + guint32 n, i; + gboolean found_pair_tkip = FALSE; + gboolean found_pair_ccmp = FALSE; + gboolean found_group_tkip = FALSE; + gboolean found_group_ccmp = FALSE; + gboolean found_group_wep40 = FALSE; + gboolean found_group_wep104 = FALSE; + gboolean found_proto_wpa = FALSE; + gboolean found_proto_rsn = FALSE; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-psk", NULL, TYPE_WIRELESS, NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System blahblah (test-wifi-wpa-psk)"); + + /* UUID can't be tested if the ifcfg does not contain the UUID key, because + * the UUID is generated on the full path of the ifcfg file, which can change + * depending on where the tests are run. + */ + + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + mac = nm_setting_wireless_get_mac_address(s_wireless); + g_assert(mac); + g_assert(nm_utils_hwaddr_matches(mac, -1, expected_mac_address, sizeof(expected_mac_address))); + + g_assert_cmpint(nm_setting_wireless_get_mtu(s_wireless), ==, 0); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + g_assert(ssid); + g_assert_cmpmem(g_bytes_get_data(ssid, NULL), + g_bytes_get_size(ssid), + expected_ssid, + strlen(expected_ssid)); + + g_assert(!nm_setting_wireless_get_bssid(s_wireless)); + g_assert_cmpstr(nm_setting_wireless_get_mode(s_wireless), ==, "infrastructure"); + g_assert_cmpint(nm_setting_wireless_get_channel(s_wireless), ==, 1); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "wpa-psk"); + g_assert_cmpstr(nm_setting_wireless_security_get_psk(s_wsec), + ==, + "I wonder what the king is doing tonight?"); + g_assert(!nm_setting_wireless_security_get_auth_alg(s_wsec)); + + /* Pairwise ciphers */ + n = nm_setting_wireless_security_get_num_pairwise(s_wsec); + g_assert_cmpint(n, ==, 2); + for (i = 0; i < n; i++) { + const char *tmp = nm_setting_wireless_security_get_pairwise(s_wsec, i); + g_assert(tmp); + if (strcmp(tmp, "tkip") == 0) + found_pair_tkip = TRUE; + else if (strcmp(tmp, "ccmp") == 0) + found_pair_ccmp = TRUE; + } + g_assert(found_pair_tkip); + g_assert(found_pair_ccmp); + + /* Group ciphers */ + n = nm_setting_wireless_security_get_num_groups(s_wsec); + g_assert_cmpint(n, ==, 4); + for (i = 0; i < n; i++) { + const char *tmp = nm_setting_wireless_security_get_group(s_wsec, i); + g_assert(tmp); + if (strcmp(tmp, "tkip") == 0) + found_group_tkip = TRUE; + else if (strcmp(tmp, "ccmp") == 0) + found_group_ccmp = TRUE; + else if (strcmp(tmp, "wep40") == 0) + found_group_wep40 = TRUE; + else if (strcmp(tmp, "wep104") == 0) + found_group_wep104 = TRUE; + } + g_assert(found_group_tkip); + g_assert(found_group_ccmp); + g_assert(found_group_wep40); + g_assert(found_group_wep104); + + /* Protocols */ + n = nm_setting_wireless_security_get_num_protos(s_wsec); + g_assert_cmpint(n, ==, 2); + for (i = 0; i < n; i++) { + const char *tmp = nm_setting_wireless_security_get_proto(s_wsec, i); + g_assert(tmp); + if (strcmp(tmp, "wpa") == 0) + found_proto_wpa = TRUE; + else if (strcmp(tmp, "rsn") == 0) + found_proto_rsn = TRUE; + } + g_assert(found_proto_wpa); + g_assert(found_proto_rsn); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + g_object_unref(connection); +} + +static void +test_read_wifi_sae(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity * s_wsec; + GBytes * ssid; + const char * expected_ssid = "blahblah"; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-sae", NULL, TYPE_WIRELESS, NULL); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System blahblah (test-wifi-sae)"); + + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + g_assert_cmpint(nm_setting_wireless_get_mtu(s_wireless), ==, 0); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + g_assert(ssid); + g_assert_cmpmem(g_bytes_get_data(ssid, NULL), + g_bytes_get_size(ssid), + expected_ssid, + strlen(expected_ssid)); + + g_assert(!nm_setting_wireless_get_bssid(s_wireless)); + g_assert_cmpstr(nm_setting_wireless_get_mode(s_wireless), ==, "infrastructure"); + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "sae"); + g_assert_cmpstr(nm_setting_wireless_security_get_psk(s_wsec), ==, "The king is dead."); + g_assert(!nm_setting_wireless_security_get_auth_alg(s_wsec)); +} + +static void +test_read_wifi_owe(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity * s_wsec; + GBytes * ssid; + const char * expected_ssid = "blahblah_owe"; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-owe", NULL, TYPE_WIRELESS, NULL); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System blahblah_owe (test-wifi-owe)"); + + g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0); + g_assert(nm_setting_connection_get_autoconnect(s_con)); + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + g_assert_cmpint(nm_setting_wireless_get_mtu(s_wireless), ==, 0); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + g_assert(ssid); + g_assert_cmpmem(g_bytes_get_data(ssid, NULL), + g_bytes_get_size(ssid), + expected_ssid, + strlen(expected_ssid)); + + g_assert(!nm_setting_wireless_get_bssid(s_wireless)); + g_assert_cmpstr(nm_setting_wireless_get_mode(s_wireless), ==, "infrastructure"); + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "owe"); + g_assert(!nm_setting_wireless_security_get_psk(s_wsec)); + g_assert(!nm_setting_wireless_security_get_auth_alg(s_wsec)); +} + +static void +test_read_wifi_wpa_psk_2(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-psk-2", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System ipsum (test-wifi-wpa-psk-2)"); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_psk(s_wsec), + ==, + "They're really saying I love you. >>`<< '"); + + g_object_unref(connection); +} + +static void +test_read_wifi_wpa_psk_unquoted(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-psk-unquoted", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System blahblah (test-wifi-wpa-psk-unquoted)"); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_psk(s_wsec), ==, "54336845e2f3f321c4c7"); + + g_object_unref(connection); +} + +static void +test_read_wifi_wpa_psk_unquoted2(void) +{ + gs_unref_object NMConnection *connection = NULL; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-psk-unquoted2", + NULL, + TYPE_WIRELESS, + NULL); +} + +static void +test_read_wifi_wpa_psk_adhoc(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NMSettingIPConfig * s_ip4; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-psk-adhoc", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System blahblah (test-wifi-wpa-psk-adhoc)"); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + g_assert_cmpstr(nm_setting_wireless_get_mode(s_wireless), ==, "adhoc"); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "wpa-psk"); + g_assert_cmpstr(nm_setting_wireless_security_get_psk(s_wsec), + ==, + "I wonder what the king is doing tonight?"); + + g_assert_cmpint(nm_setting_wireless_security_get_num_pairwise(s_wsec), ==, 1); + g_assert_cmpstr(nm_setting_wireless_security_get_pairwise(s_wsec, 0), ==, "ccmp"); + + g_assert_cmpint(nm_setting_wireless_security_get_num_groups(s_wsec), ==, 1); + g_assert_cmpstr(nm_setting_wireless_security_get_group(s_wsec, 0), ==, "ccmp"); + + g_assert_cmpint(nm_setting_wireless_security_get_num_protos(s_wsec), ==, 1); + g_assert_cmpstr(nm_setting_wireless_security_get_proto(s_wsec, 0), ==, "rsn"); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + g_object_unref(connection); +} + +static void +test_read_wifi_wpa_psk_hex(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NMSettingIPConfig * s_ip4; + GBytes * ssid; + const char * expected_ssid = "blahblah"; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-psk-hex", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System blahblah (test-wifi-wpa-psk-hex)"); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + ssid = nm_setting_wireless_get_ssid(s_wireless); + g_assert(ssid); + g_assert(nm_utils_gbytes_equal_mem(ssid, expected_ssid, strlen(expected_ssid))); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "wpa-psk"); + g_assert_cmpstr(nm_setting_wireless_security_get_psk(s_wsec), + ==, + "1da190379817bc360dda52e85c388c439a21ea5c7bf819c64e9da051807deae6"); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + g_object_unref(connection); +} + +#define TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT TEST_IFCFG_DIR "/test_ca_cert.pem" +#define TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT TEST_IFCFG_DIR "/test1_key_and_cert.pem" +#define TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY TEST_IFCFG_DIR "/test1_key_and_cert.pem" + +static void +test_read_wifi_wpa_eap_tls(void) +{ + NMConnection * connection; + NMSettingWireless *s_wireless; + NMSettingIPConfig *s_ip4; + NMSetting8021x * s_8021x; + char * unmanaged = NULL; + const char * expected_privkey_password = "test1"; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-eap-tls", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(!unmanaged); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x(connection); + g_assert(s_8021x); + g_assert_cmpint(nm_setting_802_1x_get_num_eap_methods(s_8021x), ==, 1); + g_assert_cmpstr(nm_setting_802_1x_get_eap_method(s_8021x, 0), ==, "tls"); + g_assert_cmpstr(nm_setting_802_1x_get_identity(s_8021x), ==, "Bill Smith"); + + /* CA Cert */ + verify_cert_or_key(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, + NULL, + NM_SETTING_802_1X_CA_CERT); + + /* Client Cert */ + verify_cert_or_key(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT, + NULL, + NM_SETTING_802_1X_CLIENT_CERT); + + /* Private Key Password */ + g_assert_cmpstr(nm_setting_802_1x_get_private_key_password(s_8021x), + ==, + expected_privkey_password); + + /* Private key */ + verify_cert_or_key(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY, + expected_privkey_password, + NM_SETTING_802_1X_PRIVATE_KEY); + + g_object_unref(connection); +} + +/* Also use TLS defines from the previous test */ + +static void +test_read_wifi_wpa_eap_ttls_tls(void) +{ + NMConnection * connection; + NMSettingWireless *s_wireless; + NMSettingIPConfig *s_ip4; + NMSetting8021x * s_8021x; + char * unmanaged = NULL; + const char * expected_privkey_password = "test1"; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-eap-ttls-tls", + NULL, + TYPE_WIRELESS, + &unmanaged); + g_assert(!unmanaged); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x(connection); + g_assert(s_8021x); + g_assert_cmpint(nm_setting_802_1x_get_num_eap_methods(s_8021x), ==, 1); + g_assert_cmpstr(nm_setting_802_1x_get_eap_method(s_8021x, 0), ==, "ttls"); + g_assert_cmpstr(nm_setting_802_1x_get_identity(s_8021x), ==, "Chuck Shumer"); + + /* CA Cert */ + verify_cert_or_key(s_8021x, + TEST_IFCFG_DIR "/test_ca_cert.pem", + NULL, + NM_SETTING_802_1X_CA_CERT); + + /* Inner auth method */ + g_assert_cmpstr(nm_setting_802_1x_get_phase2_autheap(s_8021x), ==, "tls"); + + /* Inner CA Cert */ + verify_cert_or_key(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, + NULL, + NM_SETTING_802_1X_PHASE2_CA_CERT); + + /* Inner Client Cert */ + verify_cert_or_key(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT, + NULL, + NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + + /* Inner Private Key Password */ + g_assert_cmpstr(nm_setting_802_1x_get_phase2_private_key_password(s_8021x), + ==, + expected_privkey_password); + + /* Inner private key */ + verify_cert_or_key(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY, + expected_privkey_password, + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + + g_object_unref(connection); +} + +static void +test_read_wifi_wpa_eap_suite_b_192_tls(void) +{ + NMConnection * connection; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-eap-suite-b-192-tls", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wireless_sec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wireless_sec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wireless_sec), + ==, + "wpa-eap-suite-b-192"); + + g_object_unref(connection); +} + +static void +test_read_wifi_dynamic_wep_leap(void) +{ + NMConnection * connection; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSetting8021x * s_8021x; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-dynamic-wep-leap", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== WIRELESS SETTING ===== */ + + s_wifi = nm_connection_get_setting_wireless(connection); + g_assert(s_wifi); + + /* ===== Wi-Fi SECURITY SETTING ===== */ + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + + /* Key management */ + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "ieee8021x"); + + /* Auth alg should be NULL (open) for dynamic WEP with LEAP as the EAP method; + * only "old-school" LEAP uses 'leap' for the auth alg. + */ + g_assert_cmpstr(nm_setting_wireless_security_get_auth_alg(s_wsec), ==, NULL); + + /* Expect no old-school LEAP username/password, that'll be in the 802.1x setting */ + g_assert_cmpstr(nm_setting_wireless_security_get_leap_username(s_wsec), ==, NULL); + g_assert_cmpstr(nm_setting_wireless_security_get_leap_password(s_wsec), ==, NULL); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x(connection); + g_assert(s_8021x); + + /* EAP method should be "leap" */ + g_assert_cmpint(nm_setting_802_1x_get_num_eap_methods(s_8021x), ==, 1); + g_assert_cmpstr(nm_setting_802_1x_get_eap_method(s_8021x, 0), ==, "leap"); + + /* username & password */ + g_assert_cmpstr(nm_setting_802_1x_get_identity(s_8021x), ==, "bill smith"); + g_assert_cmpstr(nm_setting_802_1x_get_password(s_8021x), ==, "foobar baz"); + + g_object_unref(connection); +} + +static void +test_read_wifi_wep_eap_ttls_chap(void) +{ + NMConnection * connection; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NMSettingIPConfig * s_ip4; + NMSetting8021x * s_8021x; + char * unmanaged = NULL; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wep-eap-ttls-chap", + NULL, + TYPE_WIRELESS, + &unmanaged); + g_assert(!unmanaged); + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + /* ===== 802.1x SETTING ===== */ + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "ieee8021x"); + + /* ===== 802.1x SETTING ===== */ + s_8021x = nm_connection_get_setting_802_1x(connection); + g_assert(s_8021x); + + /* EAP methods */ + g_assert_cmpint(nm_setting_802_1x_get_num_eap_methods(s_8021x), ==, 1); + g_assert_cmpstr(nm_setting_802_1x_get_eap_method(s_8021x, 0), ==, "ttls"); + + /* CA Cert */ + verify_cert_or_key(s_8021x, + TEST_IFCFG_DIR "/test_ca_cert.pem", + NULL, + NM_SETTING_802_1X_CA_CERT); + + g_assert_cmpstr(nm_setting_802_1x_get_phase2_auth(s_8021x), ==, "chap"); + g_assert_cmpstr(nm_setting_802_1x_get_identity(s_8021x), ==, "David Smith"); + g_assert_cmpstr(nm_setting_802_1x_get_password(s_8021x), ==, "foobar baz"); + + g_object_unref(connection); +} + +static void +test_read_wired_wake_on_lan(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-wake-on-lan", + NULL, + TYPE_WIRELESS, + NULL); + + 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); + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + g_assert_cmpint(nm_setting_wired_get_wake_on_lan(s_wired), + ==, + NM_SETTING_WIRED_WAKE_ON_LAN_ARP | NM_SETTING_WIRED_WAKE_ON_LAN_PHY + | NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC); + + g_assert_cmpstr(nm_setting_wired_get_wake_on_lan_password(s_wired), ==, "00:11:22:33:44:55"); + + g_object_unref(connection); +} + +static void +test_read_wired_auto_negotiate_off(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-wake-on-lan", + NULL, + TYPE_ETHERNET, + NULL); + + 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); + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + g_assert(!nm_setting_wired_get_auto_negotiate(s_wired)); + g_assert_cmpint(nm_setting_wired_get_speed(s_wired), ==, 100); + g_assert_cmpstr(nm_setting_wired_get_duplex(s_wired), ==, "full"); +} + +static void +test_read_wired_auto_negotiate_on(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-auto-negotiate-on", + NULL, + TYPE_ETHERNET, + NULL); + + 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); + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + g_assert(nm_setting_wired_get_auto_negotiate(s_wired)); + g_assert_cmpint(nm_setting_wired_get_speed(s_wired), ==, 0); + g_assert_cmpstr(nm_setting_wired_get_duplex(s_wired), ==, NULL); +} + +static void +test_read_wired_unknown_ethtool_opt(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-unknown-ethtool-opt", + NULL, + TYPE_ETHERNET, + NULL); + + 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); + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + g_assert(!nm_setting_wired_get_auto_negotiate(s_wired)); + g_assert(!nm_setting_wired_get_speed(s_wired)); + g_assert(!nm_setting_wired_get_duplex(s_wired)); + + g_assert_cmpint(nm_setting_wired_get_wake_on_lan(s_wired), + ==, + NM_SETTING_WIRED_WAKE_ON_LAN_ARP | NM_SETTING_WIRED_WAKE_ON_LAN_PHY + | NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC); + g_assert_cmpstr(nm_setting_wired_get_wake_on_lan_password(s_wired), ==, "00:11:22:33:44:55"); +} + +static void +test_read_wifi_hidden(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWireless * s_wifi; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-hidden", NULL, TYPE_WIRELESS, NULL); + + 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_WIRELESS_SETTING_NAME); + + s_wifi = nm_connection_get_setting_wireless(connection); + g_assert(s_wifi); + g_assert(nm_setting_wireless_get_hidden(s_wifi) == TRUE); + + g_object_unref(connection); +} + +static void +test_write_wifi_hidden(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + shvarFile * f; + GBytes * ssid; + const unsigned char ssid_data[] = {0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44}; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wi-Fi Hidden", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, sizeof(ssid_data)); + + g_object_set(s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL); + + g_bytes_unref(ssid); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_WiFi_Hidden.cexpected", + &testfile); + + f = _svOpenFile(testfile); + _svGetValue_check(f, "SSID_HIDDEN", "yes"); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_wifi_mac_random(gconstpointer user_data) +{ + gs_unref_object NMConnection *connection = NULL; + gs_free char * path = NULL; + NMSettingWireless * s_wifi; + const char * name; + gpointer value_p; + NMSettingMacRandomization value; + + nmtst_test_data_unpack(user_data, &name, &value_p); + value = GPOINTER_TO_INT(value_p); + + path = g_strdup_printf(TEST_IFCFG_DIR "/ifcfg-test-wifi-mac-random-%s", name); + connection = _connection_from_file(path, NULL, TYPE_WIRELESS, NULL); + + s_wifi = nm_connection_get_setting_wireless(connection); + g_assert(s_wifi); + g_assert_cmpint(nm_setting_wireless_get_mac_address_randomization(s_wifi), ==, value); +} + +static void +test_write_wifi_mac_random(gconstpointer user_data) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + char * val; + shvarFile * f; + GBytes * ssid; + const unsigned char ssid_data[] = {0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44}; + const char * name, *write_expected; + gpointer value_p; + NMSettingMacRandomization value; + char cexpected[NM_STRLEN(TEST_IFCFG_DIR) + 100]; + + nmtst_test_data_unpack(user_data, &name, &value_p, &write_expected); + value = GPOINTER_TO_INT(value_p); + + g_assert(write_expected); + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + val = g_strdup_printf("Test Write Wi-Fi MAC %s", name); + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + val, + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + g_free(val); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, sizeof(ssid_data)); + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, + value, + NULL); + g_bytes_unref(ssid); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp( + connection, + TEST_SCRATCH_DIR, + nm_sprintf_buf(cexpected, TEST_IFCFG_DIR "/ifcfg-Test_Write_WiFi_MAC_%s.cexpected", name), + &testfile); + + f = _svOpenFile(testfile); + _svGetValue_check(f, "MAC_ADDRESS_RANDOMIZATION", write_expected); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wired_wake_on_lan(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingWiredWakeOnLan wol; + char * val; + shvarFile * f; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired Wake-on-LAN", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + wol = NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST | NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST + | NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC; + + g_object_set(s_wired, + NM_SETTING_WIRED_WAKE_ON_LAN, + wol, + NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, + "00:00:00:11:22:33", + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected", + &testfile); + + f = _svOpenFile(testfile); + val = svGetValueStr_cp(f, "ETHTOOL_OPTS"); + g_assert(val); + g_assert(strstr(val, "wol")); + g_assert(strstr(val, "sopass 00:00:00:11:22:33")); + g_free(val); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wired_auto_negotiate_off(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingWired * s_wired; + char * val; + shvarFile * f; + + connection = nmtst_create_minimal_connection("Test Write Wired Auto-Negotiate", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + s_wired = nm_connection_get_setting_wired(connection); + g_object_set(s_wired, + NM_SETTING_WIRED_AUTO_NEGOTIATE, + FALSE, + NM_SETTING_WIRED_DUPLEX, + "half", + NM_SETTING_WIRED_SPEED, + 10, + NULL); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected", + &testfile); + + f = _svOpenFile(testfile); + val = svGetValueStr_cp(f, "ETHTOOL_OPTS"); + g_assert(val); + g_assert(strstr(val, "autoneg off")); + g_assert(strstr(val, "speed 10")); + g_assert(strstr(val, "duplex half")); + g_free(val); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wired_auto_negotiate_on(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingWired * s_wired; + NMSettingEthtool * s_ethtool; + char * val; + shvarFile * f; + + connection = nmtst_create_minimal_connection("Test Write Wired Auto-Negotiate", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + s_wired = nm_connection_get_setting_wired(connection); + g_object_set(s_wired, NM_SETTING_WIRED_AUTO_NEGOTIATE, TRUE, NULL); + + s_ethtool = NM_SETTING_ETHTOOL(nm_setting_ethtool_new()); + nm_setting_ethtool_set_feature(s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_TX, NM_TERNARY_TRUE); + nm_setting_ethtool_set_feature(s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN, NM_TERNARY_FALSE); + nm_connection_add_setting(connection, NM_SETTING(s_ethtool)); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-test_write_wired_auto_negotiate_on.cexpected", + &testfile); + + f = _svOpenFile(testfile); + val = svGetValueStr_cp(f, "ETHTOOL_OPTS"); + g_assert(val); + g_assert(strstr(val, "autoneg on")); + g_assert(!strstr(val, "speed")); + g_assert(!strstr(val, "duplex")); + g_free(val); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_verifies_without_normalization(reread); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + s_ethtool = NM_SETTING_ETHTOOL(nm_connection_get_setting(reread, NM_TYPE_SETTING_ETHTOOL)); + g_assert(s_ethtool); + g_assert_cmpint(nm_setting_ethtool_get_feature(s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_TX), + ==, + NM_TERNARY_TRUE); + g_assert_cmpint(nm_setting_ethtool_get_feature(s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN), + ==, + NM_TERNARY_FALSE); + g_assert_cmpint(nm_setting_ethtool_get_feature(s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_TXVLAN), + ==, + NM_TERNARY_DEFAULT); +} + +static void +test_read_wifi_band_a(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWireless * s_wifi; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-band-a", NULL, TYPE_WIRELESS, NULL); + + 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_WIRELESS_SETTING_NAME); + + s_wifi = nm_connection_get_setting_wireless(connection); + g_assert(s_wifi); + g_assert_cmpstr(nm_setting_wireless_get_band(s_wifi), ==, "a"); + + g_object_unref(connection); +} + +static void +test_write_wifi_band_a(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + shvarFile * f; + GBytes * ssid; + const unsigned char ssid_data[] = {0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44}; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wi-Fi Band A", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, sizeof(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NM_SETTING_WIRELESS_BAND, + "a", + NULL); + + g_bytes_unref(ssid); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_WiFi_Band_A.cexpected", + &testfile); + + f = _svOpenFile(testfile); + _svGetValue_check(f, "BAND", "a"); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_ap_mode(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + gs_unref_bytes GBytes *ssid = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wi-Fi AP Mode", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new("MySSID", NM_STRLEN("MySSID")); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "ap", + NM_SETTING_WIRELESS_BAND, + "a", + NM_SETTING_WIRELESS_CHANNEL, + (guint) 196, + NM_SETTING_WIRELESS_AP_ISOLATION, + NM_TERNARY_TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_WiFi_AP_Mode.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_wifi_band_a_channel_mismatch(void) +{ + gs_free_error GError *error = NULL; + + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-wifi-band-a-channel-mismatch", + NULL, + TYPE_WIRELESS, + &error); + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); +} + +static void +test_read_wifi_band_bg_channel_mismatch(void) +{ + gs_free_error GError *error = NULL; + + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-wifi-band-bg-channel-mismatch", + NULL, + TYPE_WIRELESS, + &error); + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); +} + +static void +test_read_wired_qeth_static(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + char * unmanaged = NULL; + const char *const * subchannels; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-qeth-static", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(!unmanaged); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-wired-qeth-static"); + + /* ===== WIRED SETTING ===== */ + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + g_assert(!nm_setting_wired_get_mac_address(s_wired)); + + /* Subchannels */ + subchannels = nm_setting_wired_get_s390_subchannels(s_wired); + g_assert(subchannels); + g_assert(subchannels[0] && subchannels[1] && subchannels[2] && !subchannels[3]); + + g_assert_cmpstr(subchannels[0], ==, "0.0.0600"); + g_assert_cmpstr(subchannels[1], ==, "0.0.0601"); + g_assert_cmpstr(subchannels[2], ==, "0.0.0602"); + + g_assert_cmpstr(nm_setting_wired_get_s390_nettype(s_wired), ==, "qeth"); + g_assert_cmpstr(nm_setting_wired_get_s390_option_by_key(s_wired, "portname"), ==, "OSAPORT"); + g_assert_cmpstr(nm_setting_wired_get_s390_option_by_key(s_wired, "portno"), ==, "0"); + g_assert_cmpstr(nm_setting_wired_get_s390_option_by_key(s_wired, "layer2"), ==, "1"); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), + ==, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + + g_object_unref(connection); +} + +static void +test_read_wired_ctc_static(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + char * unmanaged = NULL; + const char *const * subchannels; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-ctc-static", + NULL, + TYPE_ETHERNET, + &unmanaged); + g_assert(unmanaged == NULL); + + /* ===== CONNECTION SETTING ===== */ + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con != NULL); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "System test-wired-ctc-static"); + + /* ===== WIRED SETTING ===== */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired != NULL); + + g_assert(nm_setting_wired_get_mac_address(s_wired) == NULL); + + /* Subchannels */ + subchannels = nm_setting_wired_get_s390_subchannels(s_wired); + g_assert(subchannels != NULL); + g_assert(subchannels[0] && subchannels[1] && !subchannels[2]); + + g_assert_cmpstr(subchannels[0], ==, "0.0.1b00"); + g_assert_cmpstr(subchannels[1], ==, "0.0.1b01"); + + g_assert_cmpstr(nm_setting_wired_get_s390_nettype(s_wired), ==, "ctc"); + g_assert_cmpstr(nm_setting_wired_get_s390_option_by_key(s_wired, "ctcprot"), ==, "0"); + + g_object_unref(connection); +} + +static void +test_read_wifi_wep_no_keys(void) +{ + NMConnection * connection; + NMSettingConnection * s_con; + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wsec; + NMWepKeyType key_type; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wep-no-keys", + NULL, + TYPE_WIRELESS, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_id(s_con), + ==, + "System foobar (test-wifi-wep-no-keys)"); + + /* UUID can't be tested if the ifcfg does not contain the UUID key, because + * the UUID is generated on the full path of the ifcfg file, which can change + * depending on where the tests are run. + */ + + /* ===== WIRELESS SETTING ===== */ + + s_wireless = nm_connection_get_setting_wireless(connection); + g_assert(s_wireless); + + /* ===== WIRELESS SECURITY SETTING ===== */ + + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + + /* Key management */ + g_assert_cmpstr(nm_setting_wireless_security_get_key_mgmt(s_wsec), ==, "none"); + + /* WEP key index */ + g_assert_cmpint(nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec), ==, 0); + + /* WEP key type */ + key_type = nm_setting_wireless_security_get_wep_key_type(s_wsec); + g_assert(key_type == NM_WEP_KEY_TYPE_UNKNOWN || key_type == NM_WEP_KEY_TYPE_KEY); + + /* WEP key index 0; we don't expect it to be filled */ + g_assert(!nm_setting_wireless_security_get_wep_key(s_wsec, 0)); + + g_object_unref(connection); +} + +static void +test_read_permissions(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + gboolean success; + guint32 num; + const char * tmp; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-permissions", NULL, TYPE_ETHERNET, NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + + num = nm_setting_connection_get_num_permissions(s_con); + g_assert_cmpint(num, ==, 3); + + /* verify each permission */ + tmp = NULL; + success = nm_setting_connection_get_permission(s_con, 0, NULL, &tmp, NULL); + g_assert(success); + g_assert_cmpstr(tmp, ==, "dcbw"); + + tmp = NULL; + success = nm_setting_connection_get_permission(s_con, 1, NULL, &tmp, NULL); + g_assert(success); + g_assert_cmpstr(tmp, ==, "ssmith"); + + tmp = NULL; + success = nm_setting_connection_get_permission(s_con, 2, NULL, &tmp, NULL); + g_assert(success); + g_assert_cmpstr(tmp, ==, "johnny5"); + + g_object_unref(connection); +} + +static void +test_read_wifi_wep_agent_keys(void) +{ + NMConnection * connection; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMWepKeyType key_type; + NMSettingSecretFlags flags; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wep-agent-keys", + NULL, + TYPE_WIRELESS, + NULL); + + /* Ensure the connection is still marked for wifi security even though + * we don't have any WEP keys because they are agent owned. + */ + + /* ===== WIRELESS SETTING ===== */ + s_wifi = nm_connection_get_setting_wireless(connection); + g_assert(s_wifi); + + /* ===== WIRELESS SECURITY SETTING ===== */ + s_wsec = nm_connection_get_setting_wireless_security(connection); + g_assert(s_wsec); + + g_assert(strcmp(nm_setting_wireless_security_get_key_mgmt(s_wsec), "none") == 0); + g_assert(nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec) == 0); + + key_type = nm_setting_wireless_security_get_wep_key_type(s_wsec); + g_assert(key_type == NM_WEP_KEY_TYPE_UNKNOWN || key_type == NM_WEP_KEY_TYPE_KEY); + + /* We don't expect WEP key0 to be filled */ + g_assert(nm_setting_wireless_security_get_wep_key(s_wsec, 0) == NULL); + + flags = nm_setting_wireless_security_get_wep_key_flags(s_wsec); + g_assert(flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED); + + g_object_unref(connection); +} + +static void +test_write_wired_static(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *route6file = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4, *reread_s_ip4; + NMSettingIPConfig * s_ip6, *reread_s_ip6; + NMIPAddress * addr; + NMIPAddress * addr6; + NMIPRoute * route6; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired Static", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES, + 1, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + g_object_set(s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, + "31:33:33:37:be:cd", + NM_SETTING_WIRED_MTU, + (guint32) 1492, + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NM_SETTING_IP_CONFIG_ROUTE_METRIC, + (gint64) 204, + NULL); + + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + addr = nm_ip_address_new(AF_INET, "1.1.1.5", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + nm_setting_ip_config_add_dns(s_ip4, "4.2.2.1"); + nm_setting_ip_config_add_dns(s_ip4, "4.2.2.2"); + + nm_setting_ip_config_add_dns_search(s_ip4, "foobar.com"); + nm_setting_ip_config_add_dns_search(s_ip4, "lab.foobar.com"); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NM_SETTING_IP_CONFIG_ROUTE_METRIC, + (gint64) 206, + NULL); + + /* Add addresses */ + addr6 = nm_ip_address_new(AF_INET6, "1003:1234:abcd::1", 11, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip6, addr6); + nm_ip_address_unref(addr6); + + addr6 = nm_ip_address_new(AF_INET6, "2003:1234:abcd::2", 22, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip6, addr6); + nm_ip_address_unref(addr6); + + addr6 = nm_ip_address_new(AF_INET6, "3003:1234:abcd::3", 33, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip6, addr6); + nm_ip_address_unref(addr6); + + /* Add routes */ + route6 = nm_ip_route_new(AF_INET6, + "2222:aaaa:bbbb:cccc::", + 64, + "2222:aaaa:bbbb:cccc:dddd:eeee:5555:6666", + 99, + &error); + g_assert_no_error(error); + nm_setting_ip_config_add_route(s_ip6, route6); + nm_ip_route_unref(route6); + + route6 = nm_ip_route_new(AF_INET6, "::", 128, "2222:aaaa::9999", 1, &error); + g_assert_no_error(error); + nm_ip_route_set_attribute(route6, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32(100)); + nm_ip_route_set_attribute(route6, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32(1280)); + nm_ip_route_set_attribute(route6, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, g_variant_new_boolean(TRUE)); + nm_ip_route_set_attribute(route6, + NM_IP_ROUTE_ATTRIBUTE_FROM, + g_variant_new_string("2222::bbbb/32")); + nm_ip_route_set_attribute(route6, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string("::42")); + nm_setting_ip_config_add_route(s_ip6, route6); + nm_ip_route_unref(route6); + + /* DNS servers */ + nm_setting_ip_config_add_dns(s_ip6, "fade:0102:0103::face"); + nm_setting_ip_config_add_dns(s_ip6, "cafe:ffff:eeee:dddd:cccc:bbbb:aaaa:feed"); + + /* DNS domains */ + nm_setting_ip_config_add_dns_search(s_ip6, "foobar6.com"); + nm_setting_ip_config_add_dns_search(s_ip6, "lab6.foobar.com"); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + route6file = utils_get_route6_path(testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + reread_s_ip4 = nm_connection_get_setting_ip4_config(reread); + reread_s_ip6 = nm_connection_get_setting_ip6_config(reread); + + g_assert_cmpint(nm_setting_ip_config_get_route_metric(reread_s_ip4), ==, 204); + g_assert_cmpint(nm_setting_ip_config_get_route_metric(reread_s_ip6), ==, 206); + + nm_connection_add_setting(connection, nm_setting_proxy_new()); + + nmtst_assert_connection_equals(connection, FALSE, reread, FALSE); +} + +static void +test_write_wired_static_with_generic(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *route6file = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4, *reread_s_ip4; + NMSettingIPConfig * s_ip6, *reread_s_ip6; + NMIPAddress * addr; + NMIPAddress * addr6; + NMIPRoute * route6; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired Static", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES, + 1, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + g_object_set(s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, + "31:33:33:37:be:cd", + NM_SETTING_WIRED_MTU, + (guint32) 1492, + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NM_SETTING_IP_CONFIG_ROUTE_METRIC, + (gint64) 204, + NULL); + + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + addr = nm_ip_address_new(AF_INET, "1.1.1.5", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + nm_setting_ip_config_add_dns(s_ip4, "4.2.2.1"); + nm_setting_ip_config_add_dns(s_ip4, "4.2.2.2"); + + nm_setting_ip_config_add_dns_search(s_ip4, "foobar.com"); + nm_setting_ip_config_add_dns_search(s_ip4, "lab.foobar.com"); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NM_SETTING_IP_CONFIG_ROUTE_METRIC, + (gint64) 206, + NULL); + + /* Add addresses */ + addr6 = nm_ip_address_new(AF_INET6, "1003:1234:abcd::1", 11, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip6, addr6); + nm_ip_address_unref(addr6); + + addr6 = nm_ip_address_new(AF_INET6, "2003:1234:abcd::2", 22, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip6, addr6); + nm_ip_address_unref(addr6); + + addr6 = nm_ip_address_new(AF_INET6, "3003:1234:abcd::3", 33, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip6, addr6); + nm_ip_address_unref(addr6); + + /* Add routes */ + route6 = nm_ip_route_new(AF_INET6, + "2222:aaaa:bbbb:cccc::", + 64, + "2222:aaaa:bbbb:cccc:dddd:eeee:5555:6666", + 99, + &error); + g_assert_no_error(error); + nm_setting_ip_config_add_route(s_ip6, route6); + nm_ip_route_unref(route6); + + route6 = nm_ip_route_new(AF_INET6, "::", 128, "2222:aaaa::9999", 1, &error); + g_assert_no_error(error); + nm_ip_route_set_attribute(route6, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32(100)); + nm_ip_route_set_attribute(route6, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32(1280)); + nm_ip_route_set_attribute(route6, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, g_variant_new_boolean(TRUE)); + nm_ip_route_set_attribute(route6, + NM_IP_ROUTE_ATTRIBUTE_FROM, + g_variant_new_string("2222::bbbb/32")); + nm_ip_route_set_attribute(route6, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string("::42")); + nm_setting_ip_config_add_route(s_ip6, route6); + nm_ip_route_unref(route6); + + /* DNS servers */ + nm_setting_ip_config_add_dns(s_ip6, "fade:0102:0103::face"); + nm_setting_ip_config_add_dns(s_ip6, "cafe:ffff:eeee:dddd:cccc:bbbb:aaaa:feed"); + + /* DNS domains */ + nm_setting_ip_config_add_dns_search(s_ip6, "foobar6.com"); + nm_setting_ip_config_add_dns_search(s_ip6, "lab6.foobar.com"); + + nm_connection_add_setting(connection, nm_setting_generic_new()); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection_FIXME(connection, TEST_SCRATCH_DIR, &testfile); + route6file = utils_get_route6_path(testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + reread_s_ip4 = nm_connection_get_setting_ip4_config(reread); + reread_s_ip6 = nm_connection_get_setting_ip6_config(reread); + + g_assert_cmpint(nm_setting_ip_config_get_route_metric(reread_s_ip4), ==, 204); + g_assert_cmpint(nm_setting_ip_config_get_route_metric(reread_s_ip6), ==, 206); + + nm_connection_add_setting(connection, nm_setting_proxy_new()); + + { + gs_unref_hashtable GHashTable *diffs = NULL; + + g_assert(!nm_connection_diff(connection, reread, NM_SETTING_COMPARE_FLAG_EXACT, &diffs)); + g_assert(diffs); + g_assert(g_hash_table_size(diffs) == 1); + g_assert(g_hash_table_lookup(diffs, "generic")); + g_assert(!nm_connection_compare(connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); + } + g_assert(!nm_connection_get_setting(reread, NM_TYPE_SETTING_GENERIC)); + nm_connection_add_setting(reread, nm_setting_generic_new()); + { + gs_unref_hashtable GHashTable *diffs = NULL; + + g_assert(nm_connection_diff(connection, reread, NM_SETTING_COMPARE_FLAG_EXACT, &diffs)); + g_assert(!diffs); + g_assert(nm_connection_compare(connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); + } +} + +static void +test_write_wired_dhcp(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired DHCP", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, + "random-client-id-00:22:33", + NM_SETTING_IP4_CONFIG_DHCP_FQDN, + "awesome.hostname", + NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS, + (guint) NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, + TRUE, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, + TRUE, + NM_SETTING_IP_CONFIG_DHCP_IAID, + "2864434397", + NULL); + + nmtst_assert_connection_verifies(connection); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_DHCP, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, + "awesome.hostname", + NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS, + (guint) NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE, + NULL); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static NMIPRoutingRule * +_ip_routing_rule_new(int addr_family, const char *str) +{ + NMIPRoutingRuleAsStringFlags flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE; + gs_free_error GError *local = NULL; + NMIPRoutingRule * rule; + + if (addr_family != AF_UNSPEC) { + if (addr_family == AF_INET) + flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET; + else { + g_assert(addr_family == AF_INET6); + flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6; + } + } + + rule = nm_ip_routing_rule_from_string(str, + NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE | flags, + NULL, + nmtst_get_rand_bool() ? &local : NULL); + nmtst_assert_success(rule, local); + + if (addr_family != AF_UNSPEC) + g_assert_cmpint(nm_ip_routing_rule_get_addr_family(rule), ==, addr_family); + return rule; +} + +static void +_ip_routing_rule_add_to_setting(NMSettingIPConfig *s_ip, const char *str) +{ + nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL; + + rule = _ip_routing_rule_new(nm_setting_ip_config_get_addr_family(s_ip), str); + nm_setting_ip_config_add_routing_rule(s_ip, rule); +} + +static void +test_write_routing_rules(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + + connection = nm_simple_connection_new(); + + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Routing Rules", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); + + _ip_routing_rule_add_to_setting(s_ip4, "pref 10 from 0.0.0.0/0 table 1"); + _ip_routing_rule_add_to_setting(s_ip4, "priority 10 to 192.167.8.0/24 table 2"); + _ip_routing_rule_add_to_setting(s_ip6, "pref 10 from ::/0 table 10"); + _ip_routing_rule_add_to_setting(s_ip6, "pref 10 from ::/0 to 1:2:3::5/24 table 22"); + _ip_routing_rule_add_to_setting(s_ip6, "pref 10 from ::/0 to 1:3:3::5/128 table 55"); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Routing_Rules.cexpected", + &testfile); + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wired_match(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingMatch * s_match; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired with Match setting", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + /* Match setting */ + s_match = (NMSettingMatch *) nm_setting_match_new(); + nm_setting_match_add_interface_name(s_match, "ens*"); + nm_setting_match_add_interface_name(s_match, "eth 1?"); + nm_setting_match_add_interface_name(s_match, "!veth*"); + nm_setting_match_add_driver(s_match, "!virtio"); + nm_setting_match_add_driver(s_match, "e1000e"); + nm_setting_match_add_kernel_command_line(s_match, "!ip="); + nm_connection_add_setting(connection, NM_SETTING(s_match)); + + nmtst_assert_connection_verifies(connection); + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Wired_match.cexpected", + &testfile); + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wired_dhcp_plus_ip(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-dhcp-plus-ip", + NULL, + TYPE_ETHERNET, + NULL); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_write_wired_dhcp_send_hostname(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + const char * dhcp_hostname = "kamil-patka"; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-dhcp-send-hostname", + NULL, + TYPE_ETHERNET, + NULL); + + /* Check dhcp-hostname and dhcp-send-hostname */ + s_ip4 = nm_connection_get_setting_ip4_config(connection); + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip4); + g_assert(s_ip6); + g_assert(nm_setting_ip_config_get_dhcp_send_hostname(s_ip4) == TRUE); + g_assert_cmpstr(nm_setting_ip_config_get_dhcp_hostname(s_ip4), ==, "svata-pulec"); + g_assert(!nm_setting_ip_config_get_dhcp_hostname(s_ip6)); + + /* Set dhcp-send-hostname=false dhcp-hostname="kamil-patka" and write the connection. */ + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, FALSE, NULL); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, dhcp_hostname, NULL); + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, dhcp_hostname, NULL); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + /* Check dhcp-hostname and dhcp-send-hostname from the re-read connection. */ + s_ip4 = nm_connection_get_setting_ip4_config(reread); + s_ip6 = nm_connection_get_setting_ip6_config(reread); + g_assert(s_ip4); + g_assert(s_ip6); + g_assert(nm_setting_ip_config_get_dhcp_send_hostname(s_ip4) == FALSE); + g_assert_cmpstr(nm_setting_ip_config_get_dhcp_hostname(s_ip4), ==, dhcp_hostname); + g_assert_cmpstr(nm_setting_ip_config_get_dhcp_hostname(s_ip6), ==, dhcp_hostname); +} + +static void +test_read_wired_dhcpv6_hostname_fallback(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingIPConfig * s_ip6; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-dhcpv6-hostname-fallback", + NULL, + TYPE_ETHERNET, + NULL); + + s_ip6 = nm_connection_get_setting_ip6_config(connection); + g_assert(s_ip6); + g_assert(nm_setting_ip_config_get_dhcp_send_hostname(s_ip6) == TRUE); + g_assert_cmpstr(nm_setting_ip_config_get_dhcp_hostname(s_ip6), ==, "fully.qualified.domain"); +} + +static void +test_write_wired_static_ip6_only(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + NMIPAddress * addr6; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired Static IP6 Only", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, "31:33:33:37:be:cd", NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, NULL); + + /* Add addresses */ + addr6 = nm_ip_address_new(AF_INET6, "1003:1234:abcd::1", 11, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip6, addr6); + nm_ip_address_unref(addr6); + + /* DNS server */ + nm_setting_ip_config_add_dns(s_ip6, "fade:0102:0103::face"); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_ip6_disabled(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + + connection = nmtst_create_minimal_connection("Test Write Wired Disabled IP6", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DISABLED, NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR_TMP, + TEST_IFCFG_DIR "/ifcfg-test-ip6-disabled.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +/* Test writing an IPv6 config with varying gateway address. + * For missing gateway (::), we expect no IPV6_DEFAULTGW to be written + * to ifcfg-rh. + * + * As user_data pass the IPv6 address of the gateway as string. NULL means + * not to explicitly set the gateway in the configuration before writing it. + * That way, the gateway actually defaults to "::". + */ +static void +test_write_wired_static_ip6_only_gw(gconstpointer user_data) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + NMIPAddress * addr6; + GError * error = NULL; + char * id = NULL; + gs_free char * written_ifcfg_gateway = NULL; + const char * gateway6 = user_data; + shvarFile * ifcfg; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + id = g_strdup_printf("Test Write Wired Static IP6 Only With Gateway %s", gateway6 ?: "NULL"); + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + id, + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + g_free(id); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, "31:33:33:37:be:cd", NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + gateway6, + NULL); + + /* Add addresses */ + addr6 = nm_ip_address_new(AF_INET6, "1003:1234:abcd::1", 11, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip6, addr6); + nm_ip_address_unref(addr6); + + /* DNS server */ + nm_setting_ip_config_add_dns(s_ip6, "fade:0102:0103::face"); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + ifcfg = _svOpenFile(testfile); + written_ifcfg_gateway = svGetValueStr_cp(ifcfg, "IPV6_DEFAULTGW"); + svCloseFile(ifcfg); + + /* access the gateway from the loaded connection. */ + s_ip6 = nm_connection_get_setting_ip6_config(reread); + g_assert(s_ip6 && nm_setting_ip_config_get_num_addresses(s_ip6) == 1); + addr6 = nm_setting_ip_config_get_address(s_ip6, 0); + g_assert(addr6); + + /* assert that the gateway was written and reloaded as expected */ + if (!gateway6 || !strcmp(gateway6, "::")) { + g_assert(nm_setting_ip_config_get_gateway(s_ip6) == NULL); + g_assert(written_ifcfg_gateway == NULL); + } else { + g_assert(nm_setting_ip_config_get_gateway(s_ip6) != NULL); + g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip6), ==, gateway6); + g_assert_cmpstr(written_ifcfg_gateway, ==, gateway6); + } +} + +static void +test_read_write_static_routes_legacy(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *routefile = NULL; + gs_free char * route6file = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + const char * tmp; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-static-routes-legacy", + NULL, + TYPE_ETHERNET, + NULL); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + + /* ID */ + tmp = nm_setting_connection_get_id(s_con); + g_assert(tmp); + + /* Autoconnect */ + g_assert(nm_setting_connection_get_autoconnect(s_con)); + + /* ===== WIRED SETTING ===== */ + + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config(connection); + g_assert(s_ip4); + g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + g_assert(!nm_setting_ip_config_get_never_default(s_ip4)); + + /* Save the ifcfg; use a special different scratch dir to ensure that + * we can clean up after the written connection in both the original + * source tree and for 'make distcheck'. + */ + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR_TMP, + TEST_IFCFG_DIR "/ifcfg-test-static-routes-legacy.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + routefile = utils_get_route_path(testfile); + route6file = utils_get_route6_path(testfile); + g_assert(!g_file_test(route6file, G_FILE_TEST_EXISTS)); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wired_static_routes(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *routefile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + NMIPAddress * addr; + NMIPRoute * route; + GError * error = NULL; + gboolean reread_same = FALSE; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired Static Routes", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + g_object_set(s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, + "31:33:33:37:be:cd", + NM_SETTING_WIRED_MTU, + (guint32) 1492, + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NM_SETTING_IP_CONFIG_DAD_TIMEOUT, + 400, + NULL); + + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + addr = nm_ip_address_new(AF_INET, "1.1.1.5", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + /* Write out routes */ + route = nm_ip_route_new(AF_INET, "1.2.3.0", 24, "222.173.190.239", 0, &error); + nm_ip_route_set_attribute(route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, g_variant_new_uint32(3455)); + nm_ip_route_set_attribute(route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean(TRUE)); + g_assert_no_error(error); + nm_setting_ip_config_add_route(s_ip4, route); + nm_ip_route_unref(route); + + route = nm_ip_route_new(AF_INET, "3.2.1.0", 24, "202.254.186.190", 77, &error); + nm_ip_route_set_attribute(route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, g_variant_new_uint32(30000)); + nm_ip_route_set_attribute(route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean(FALSE)); + g_assert_no_error(error); + nm_setting_ip_config_add_route(s_ip4, route); + nm_ip_route_unref(route); + + nm_setting_ip_config_add_dns(s_ip4, "4.2.2.1"); + nm_setting_ip_config_add_dns(s_ip4, "4.2.2.2"); + + nm_setting_ip_config_add_dns_search(s_ip4, "foobar.com"); + nm_setting_ip_config_add_dns_search(s_ip4, "lab.foobar.com"); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection_reread(connection, + TEST_SCRATCH_DIR, + &testfile, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Wired_Static_Routes.cexpected", + &reread, + &reread_same); + /* ifcfg does not support setting onlink=0. It gets lost during write+re-read. + * Assert that it's missing, and patch it to check whether the rest of the + * connection equals. */ + g_assert(!reread_same); + nmtst_assert_connection_verifies_without_normalization(reread); + s_ip4 = nm_connection_get_setting_ip4_config(reread); + g_assert(s_ip4); + g_assert_cmpint(nm_setting_ip_config_get_num_routes(s_ip4), ==, 2); + route = nm_setting_ip_config_get_route(s_ip4, 1); + g_assert(route); + g_assert(!nm_ip_route_get_attribute(route, NM_IP_ROUTE_ATTRIBUTE_ONLINK)); + nm_ip_route_set_attribute(route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean(FALSE)); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + routefile = utils_get_route_path(testfile); +} + +static void +test_write_wired_dhcp_8021x_peap_mschapv2(void) +{ + nmtst_auto_unlinkfile char *keyfile = NULL; + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + NMSetting8021x * s_8021x; + gboolean success; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired DHCP 802.1x PEAP MSCHAPv2", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + /* 802.1x setting */ + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new(); + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + g_object_set(s_8021x, + NM_SETTING_802_1X_IDENTITY, + "Bob Saget", + NM_SETTING_802_1X_ANONYMOUS_IDENTITY, + "barney", + NM_SETTING_802_1X_PASSWORD, + "Kids, it was back in October 2008...", + NM_SETTING_802_1X_PHASE1_PEAPVER, + "1", + NM_SETTING_802_1X_PHASE1_PEAPLABEL, + "1", + NM_SETTING_802_1X_PHASE2_AUTH, + "mschapv2", + NULL); + + nm_setting_802_1x_add_eap_method(s_8021x, "peap"); + + success = nm_setting_802_1x_set_ca_cert(s_8021x, + TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + nmtst_assert_success(success, error); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + keyfile = utils_get_keys_path(testfile); +} + +static void +test_write_wired_8021x_tls(gconstpointer test_data) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_free char * keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + NMSetting8021x * s_8021x; + gboolean success; + GError * error = NULL; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + const char * pw; + char * tmp; + gpointer scheme_p, flags_p; + NMSetting8021xCKScheme scheme; + NMSettingSecretFlags flags; + + nmtst_test_data_unpack(test_data, &scheme_p, &flags_p); + scheme = GPOINTER_TO_INT(scheme_p); + flags = GPOINTER_TO_INT(flags_p); + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired 802.1x TLS Blobs", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + /* 802.1x setting */ + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new(); + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + g_object_set(s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL); + nm_setting_802_1x_add_eap_method(s_8021x, "tls"); + + /* CA cert */ + success = nm_setting_802_1x_set_ca_cert(s_8021x, + TEST_IFCFG_DIR "/test_ca_cert.pem", + scheme, + &format, + &error); + g_assert_no_error(error); + g_assert(success); + g_assert(format == NM_SETTING_802_1X_CK_FORMAT_X509); + + /* Client cert */ + format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + success = nm_setting_802_1x_set_client_cert(s_8021x, + TEST_IFCFG_DIR "/test1_key_and_cert.pem", + scheme, + &format, + &error); + g_assert_no_error(error); + g_assert(success); + g_assert(format == NM_SETTING_802_1X_CK_FORMAT_X509); + + /* Private key */ + format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + success = nm_setting_802_1x_set_private_key(s_8021x, + TEST_IFCFG_DIR "/test1_key_and_cert.pem", + "test1", + scheme, + &format, + &error); + g_assert_no_error(error); + g_assert(success); + g_assert(format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY); + + /* Set secret flags */ + g_object_set(s_8021x, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, flags, NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection_FIXME(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + nmtst_file_unlink_if_exists(keyfile); + + /* Ensure the reread connection's certificates and private key are paths; no + * matter what scheme was used in the original connection they will be read + * back in as paths. + */ + s_8021x = nm_connection_get_setting_802_1x(reread); + g_assert(s_8021x); + g_assert_cmpint(nm_setting_802_1x_get_ca_cert_scheme(s_8021x), + ==, + NM_SETTING_802_1X_CK_SCHEME_PATH); + g_assert_cmpint(nm_setting_802_1x_get_client_cert_scheme(s_8021x), + ==, + NM_SETTING_802_1X_CK_SCHEME_PATH); + g_assert_cmpint(nm_setting_802_1x_get_private_key_scheme(s_8021x), + ==, + NM_SETTING_802_1X_CK_SCHEME_PATH); + + g_assert_cmpint(nm_setting_802_1x_get_private_key_password_flags(s_8021x), ==, flags); + pw = nm_setting_802_1x_get_private_key_password(s_8021x); + if (flags == NM_SETTING_SECRET_FLAG_NONE) { + /* Ensure the private key password is still set */ + g_assert(pw != NULL); + g_assert_cmpstr(pw, ==, "test1"); + } else { + /* If the secret isn't owned by system settings, make sure its no longer there */ + g_assert(pw == NULL); + } + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + /* Do a direct compare if using the path scheme since then the + * certificate and key properties should be the same. If using blob + * scheme the original connection cert/key properties will be blobs + * but the re-read connection is always path scheme, so we wouldn't + * expect it to compare successfully. + */ + if (flags != NM_SETTING_SECRET_FLAG_NONE) { + /* Clear original connection's private key password because flags + * say it's not system-owned, and therefore it should not show up + * in the re-read connection. + */ + s_8021x = nm_connection_get_setting_802_1x(connection); + g_object_set(s_8021x, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, NULL, NULL); + } + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + } + + /* Clean up created certs and keys */ + tmp = utils_cert_path(testfile, "ca-cert", "der"); + nmtst_file_unlink_if_exists(tmp); + g_free(tmp); + + tmp = utils_cert_path(testfile, "client-cert", "der"); + nmtst_file_unlink_if_exists(tmp); + g_free(tmp); + + tmp = utils_cert_path(testfile, "private-key", "pem"); + nmtst_file_unlink_if_exists(tmp); + g_free(tmp); +} + +static void +test_write_wired_aliases(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + int num_addresses = 4; + const char * ip[] = {"1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4"}; + const char * label[] = {NULL, "alias0:2", NULL, "alias0:3"}; + NMIPAddress * addr; + GError * error = NULL; + shvarFile * ifcfg; + int i, j; + + nmtst_file_unlink_if_exists(TEST_SCRATCH_ALIAS_BASE ":2"); + nmtst_file_unlink_if_exists(TEST_SCRATCH_ALIAS_BASE ":3"); + nmtst_file_unlink_if_exists(TEST_SCRATCH_ALIAS_BASE ":5"); + + connection = nm_simple_connection_new(); + g_assert(connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "alias0", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + for (i = 0; i < num_addresses; i++) { + addr = nm_ip_address_new(AF_INET, ip[i], 24, &error); + g_assert_no_error(error); + if (label[i]) + nm_ip_address_set_attribute(addr, + NM_IP_ADDRESS_ATTRIBUTE_LABEL, + g_variant_new_string(label[i])); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + } + + nmtst_assert_connection_verifies(connection); + + /* Create some pre-existing alias files, to make sure they get overwritten / deleted. */ + ifcfg = svCreateFile(TEST_SCRATCH_ALIAS_BASE ":2"); + svSetValueStr(ifcfg, "DEVICE", "alias0:2"); + svSetValueStr(ifcfg, "IPADDR", "192.168.1.2"); + svWriteFile(ifcfg, 0644, NULL); + svCloseFile(ifcfg); + g_assert(g_file_test(TEST_SCRATCH_ALIAS_BASE ":2", G_FILE_TEST_EXISTS)); + + ifcfg = svCreateFile(TEST_SCRATCH_ALIAS_BASE ":5"); + svSetValueStr(ifcfg, "DEVICE", "alias0:5"); + svSetValueStr(ifcfg, "IPADDR", "192.168.1.5"); + svWriteFile(ifcfg, 0644, NULL); + svCloseFile(ifcfg); + g_assert(g_file_test(TEST_SCRATCH_ALIAS_BASE ":5", G_FILE_TEST_EXISTS)); + + _writer_new_connection_FIXME(connection, TEST_SCRATCH_DIR, &testfile); + + /* Re-check the alias files */ + g_assert(g_file_test(TEST_SCRATCH_ALIAS_BASE ":2", G_FILE_TEST_EXISTS)); + g_assert(g_file_test(TEST_SCRATCH_ALIAS_BASE ":3", G_FILE_TEST_EXISTS)); + g_assert(!g_file_test(TEST_SCRATCH_ALIAS_BASE ":5", G_FILE_TEST_EXISTS)); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + nmtst_file_unlink(TEST_SCRATCH_ALIAS_BASE ":2"); + nmtst_file_unlink(TEST_SCRATCH_ALIAS_BASE ":3"); + + /* 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); + g_assert(nm_setting_ip_config_get_num_addresses(s_ip4) == num_addresses); + + /* Addresses */ + for (i = 0; i < num_addresses; i++) { + const char *addrstr; + + addr = nm_setting_ip_config_get_address(s_ip4, i); + g_assert(addr != NULL); + + addrstr = nm_ip_address_get_address(addr); + for (j = 0; j < num_addresses; j++) { + if (!g_strcmp0(addrstr, ip[j])) + break; + } + if (j >= num_addresses) + g_assert_not_reached(); + else { + g_assert_cmpint(nm_ip_address_get_prefix(addr), ==, 24); + if (label[j]) + g_assert_cmpstr( + g_variant_get_string( + nm_ip_address_get_attribute(addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL), + NULL), + ==, + label[j]); + else + g_assert(nm_ip_address_get_attribute(addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL) == NULL); + ip[j] = NULL; + } + } + + for (i = 0; i < num_addresses; i++) + g_assert(!ip[i]); + + /* Gateway */ + g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, "1.1.1.1"); +} + +static void +test_write_gateway(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + GError * error = NULL; + shvarFile * f; + NMIPAddress * addr; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Static Addresses Gateway", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.254", + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + addr = nm_ip_address_new(AF_INET, "2.2.2.5", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + f = _svOpenFile(testfile); + _svGetValue_check(f, "IPADDR", "1.1.1.3"); + _svGetValue_check(f, "IPADDR1", "2.2.2.5"); + _svGetValue_check(f, "IPADDR0", NULL); + _svGetValue_check(f, "PREFIX", "24"); + _svGetValue_check(f, "PREFIX1", "24"); + _svGetValue_check(f, "PREFIX0", NULL); + _svGetValue_check(f, "GATEWAY", "1.1.1.254"); + _svGetValue_check(f, "GATEWAY0", NULL); + _svGetValue_check(f, "GATEWAY1", NULL); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_open(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const unsigned char ssid_data[] = {0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44}; + shvarFile * ifcfg; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi Open", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, sizeof(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_BSSID, + "11:22:33:44:55:66", + NM_SETTING_WIRELESS_MAC_ADDRESS, + "aa:bb:cc:dd:ee:ff", + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NM_SETTING_WIRELESS_BAND, + "bg", + NM_SETTING_WIRELESS_CHANNEL, + (guint32) 9, + NM_SETTING_WIRELESS_MTU, + (guint32) 1345, + NULL); + + g_bytes_unref(ssid); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + ifcfg = _svOpenFile(testfile); + _svGetValue_check(ifcfg, "ESSID", "Test SSID"); + svCloseFile(ifcfg); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_open_hex_ssid(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const unsigned char ssid_data[] = + {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd}; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi Open Hex SSID", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, sizeof(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wep(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const char * ssid_data = "blahblah"; + struct stat statbuf; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi WEP", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "none", + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, + 2, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "shared", + NULL); + nm_setting_wireless_security_set_wep_key(s_wsec, 0, "0123456789abcdef0123456789"); + nm_setting_wireless_security_set_wep_key(s_wsec, 1, "11111111111111111111111111"); + nm_setting_wireless_security_set_wep_key(s_wsec, 2, "aaaaaaaaaaaaaaaaaaaaaaaaaa"); + nm_setting_wireless_security_set_wep_key(s_wsec, 3, "BBBBBBBBBBBBBBBBBBBBBBBBBB"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + g_assert_cmpint(stat(keyfile, &statbuf), ==, 0); + g_assert(S_ISREG(statbuf.st_mode)); + g_assert_cmpint((statbuf.st_mode & 0077), ==, 0); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wep_adhoc(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GError * error = NULL; + GBytes * ssid; + const char * ssid_data = "blahblah"; + struct stat statbuf; + NMIPAddress * addr; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi WEP AdHoc", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NM_SETTING_WIRELESS_MODE, "adhoc", NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NULL); + nm_setting_wireless_security_set_wep_key(s_wsec, 0, "0123456789abcdef0123456789"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NULL); + + /* IP Address */ + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + nm_setting_ip_config_add_dns(s_ip4, "4.2.2.1"); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + g_assert_cmpint(stat(keyfile, &statbuf), ==, 0); + g_assert(S_ISREG(statbuf.st_mode)); + g_assert_cmpint((statbuf.st_mode & 0077), ==, 0); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wep_passphrase(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const char * ssid_data = "blahblah"; + struct stat statbuf; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi WEP Passphrase", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "none", + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, + 0, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "shared", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, + NM_WEP_KEY_TYPE_PASSPHRASE, + NULL); + nm_setting_wireless_security_set_wep_key(s_wsec, 0, "asdfdjaslfjasd;flasjdfl;aksdf"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + g_assert_cmpint(stat(keyfile, &statbuf), ==, 0); + g_assert(S_ISREG(statbuf.st_mode)); + g_assert_cmpint((statbuf.st_mode & 0077), ==, 0); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wep_40_ascii(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const char * ssid_data = "blahblah40"; + struct stat statbuf; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi WEP 40 ASCII", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "none", + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, + 2, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, + NM_WEP_KEY_TYPE_KEY, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "shared", + NULL); + nm_setting_wireless_security_set_wep_key(s_wsec, 0, "lorem"); + nm_setting_wireless_security_set_wep_key(s_wsec, 1, "ipsum"); + nm_setting_wireless_security_set_wep_key(s_wsec, 2, "dolor"); + nm_setting_wireless_security_set_wep_key(s_wsec, 3, "donec"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + g_assert_cmpint(stat(keyfile, &statbuf), ==, 0); + g_assert(S_ISREG(statbuf.st_mode)); + g_assert_cmpint((statbuf.st_mode & 0077), ==, 0); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wep_104_ascii(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const char * ssid_data = "blahblah104"; + struct stat statbuf; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi WEP 104 ASCII", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "none", + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, + 0, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, + NM_WEP_KEY_TYPE_UNKNOWN, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open", + NULL); + nm_setting_wireless_security_set_wep_key(s_wsec, 0, "LoremIpsumSit"); + nm_setting_wireless_security_set_wep_key(s_wsec, 1, "AlfaBetaGamma"); + nm_setting_wireless_security_set_wep_key(s_wsec, 2, "WEP-104 ASCII"); + nm_setting_wireless_security_set_wep_key(s_wsec, 3, "thisismyascii"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + g_assert_cmpint(stat(keyfile, &statbuf), ==, 0); + g_assert(S_ISREG(statbuf.st_mode)); + g_assert_cmpint((statbuf.st_mode & 0077), ==, 0); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_leap(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const char * ssid_data = "blahblah"; + struct stat statbuf; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi LEAP", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "leap", + NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, + "Bill Smith", + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + "foobar22", + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Wifi_LEAP.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + g_assert_cmpint(stat(keyfile, &statbuf), ==, 0); + g_assert(S_ISREG(statbuf.st_mode)); + g_assert_cmpint((statbuf.st_mode & 0077), ==, 0); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_leap_secret_flags(gconstpointer data) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_free char * keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const char * ssid_data = "blahblah"; + NMSettingSecretFlags flags = GPOINTER_TO_UINT(data); + + connection = nm_simple_connection_new(); + g_assert(connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi LEAP Secret Flags", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "leap", + NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, + "Bill Smith", + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + "foobar22", + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, + flags, + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection_FIXME(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + /* No key should be written out since the secret is not system owned */ + keyfile = utils_get_keys_path(testfile); + g_assert(g_file_test(keyfile, G_FILE_TEST_EXISTS) == FALSE); + + /* Remove the LEAP password from the original connection since it wont' be + * in the reread connection, as the password is not system owned. + */ + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, NULL, NULL); + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wpa_psk(gconstpointer test_data) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const char * ssid_data = "blahblah"; + struct { + const char *name, *psk; + gpointer wep_group_p, wpa_p, wpa2_p; + } args; + + nmtst_test_data_unpack(test_data, + &args.name, + &args.wep_group_p, + &args.wpa_p, + &args.wpa2_p, + &args.psk); + + g_assert(args.psk); + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + args.name, + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_PSK, + args.psk, + NM_SETTING_WIRELESS_SECURITY_PMF, + (int) NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED, + NULL); + + if (GPOINTER_TO_INT(args.wep_group_p)) { + nm_setting_wireless_security_add_group(s_wsec, "wep40"); + nm_setting_wireless_security_add_group(s_wsec, "wep104"); + } + if (GPOINTER_TO_INT(args.wpa_p)) { + nm_setting_wireless_security_add_proto(s_wsec, "wpa"); + nm_setting_wireless_security_add_pairwise(s_wsec, "tkip"); + nm_setting_wireless_security_add_group(s_wsec, "tkip"); + } + if (GPOINTER_TO_INT(args.wpa2_p)) { + nm_setting_wireless_security_add_proto(s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise(s_wsec, "ccmp"); + nm_setting_wireless_security_add_group(s_wsec, "ccmp"); + } + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wpa_psk_adhoc(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GError * error = NULL; + GBytes * ssid; + const char * ssid_data = "blahblah"; + NMIPAddress * addr; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi WPA PSK", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "adhoc", + NM_SETTING_WIRELESS_CHANNEL, + 11, + NM_SETTING_WIRELESS_BAND, + "bg", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_PSK, + "7d308b11df1b4243b0f78e5f3fc68cdbb9a264ed0edf4c188edf329ff5b467f0", + NULL); + + nm_setting_wireless_security_add_proto(s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise(s_wsec, "ccmp"); + nm_setting_wireless_security_add_group(s_wsec, "ccmp"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NULL); + + /* IP Address */ + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 25, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + nm_setting_ip_config_add_dns(s_ip4, "4.2.2.1"); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wpa_eap_tls(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSetting8021x * s_8021x; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + gboolean success; + GError * error = NULL; + GBytes * ssid; + const char * ssid_data = "blahblah"; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi WPA EAP-TLS", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-eap", + NM_SETTING_WIRELESS_SECURITY_FILS, + (int) NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED, + NULL); + nm_setting_wireless_security_add_proto(s_wsec, "wpa"); + nm_setting_wireless_security_add_pairwise(s_wsec, "tkip"); + nm_setting_wireless_security_add_group(s_wsec, "tkip"); + + /* Wireless security setting */ + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new(); + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + g_object_set(s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL); + g_object_set(s_8021x, + NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, + (guint)(NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE + | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE), + NULL); + + nm_setting_802_1x_add_eap_method(s_8021x, "tls"); + + success = nm_setting_802_1x_set_ca_cert(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + nmtst_assert_success(success, error); + + success = nm_setting_802_1x_set_client_cert(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + nmtst_assert_success(success, error); + + success = nm_setting_802_1x_set_private_key(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY, + "test1", + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + nmtst_assert_success(success, error); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wpa_eap_ttls_tls(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSetting8021x * s_8021x; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + gboolean success; + GError * error = NULL; + GBytes * ssid; + const char * ssid_data = "blahblah"; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi WPA EAP-TTLS (TLS)", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", NULL); + nm_setting_wireless_security_add_proto(s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise(s_wsec, "ccmp"); + nm_setting_wireless_security_add_group(s_wsec, "ccmp"); + + /* Wireless security setting */ + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new(); + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + nm_setting_802_1x_add_eap_method(s_8021x, "ttls"); + + g_object_set(s_8021x, + NM_SETTING_802_1X_IDENTITY, + "Bill Smith", + NM_SETTING_802_1X_ANONYMOUS_IDENTITY, + "foobar22", + NM_SETTING_802_1X_PHASE2_AUTHEAP, + "tls", + NULL); + + success = nm_setting_802_1x_set_ca_cert(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + nmtst_assert_success(success, error); + + /* Phase 2 TLS stuff */ + + /* phase2 CA cert */ + success = nm_setting_802_1x_set_phase2_ca_cert(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + nmtst_assert_success(success, error); + + /* phase2 client cert */ + success = nm_setting_802_1x_set_phase2_client_cert(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + nmtst_assert_success(success, error); + + /* phase2 private key */ + success = nm_setting_802_1x_set_phase2_private_key(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY, + "test1", + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + nmtst_assert_success(success, error); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wpa_eap_ttls_mschapv2(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSetting8021x * s_8021x; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + gboolean success; + GError * error = NULL; + GBytes * ssid; + const char * ssid_data = "blahblah"; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi WPA EAP-TTLS (MSCHAPv2)", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", NULL); + nm_setting_wireless_security_add_proto(s_wsec, "wpa"); + nm_setting_wireless_security_add_proto(s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise(s_wsec, "tkip"); + nm_setting_wireless_security_add_pairwise(s_wsec, "ccmp"); + nm_setting_wireless_security_add_group(s_wsec, "tkip"); + nm_setting_wireless_security_add_group(s_wsec, "ccmp"); + + /* Wireless security setting */ + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new(); + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + nm_setting_802_1x_add_eap_method(s_8021x, "ttls"); + + g_object_set(s_8021x, + NM_SETTING_802_1X_IDENTITY, + "Bill Smith", + NM_SETTING_802_1X_PASSWORD, + ";alkdfja;dslkfjsad;lkfjsadf", + NM_SETTING_802_1X_ANONYMOUS_IDENTITY, + "foobar22", + NM_SETTING_802_1X_PHASE2_AUTHEAP, + "mschapv2", + NULL); + + success = nm_setting_802_1x_set_ca_cert(s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); + nmtst_assert_success(success, error); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + keyfile = utils_get_keys_path(testfile); +} + +static void +test_write_wifi_wpa_then_open(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_free char * keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const char * ssid_data = "blahblah"; + + /* Test that writing out a WPA config then changing that to an open + * config doesn't leave various WPA-related keys lying around in the ifcfg. + */ + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "random wifi connection", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_PSK, + "some cool PSK", + NULL); + + nm_setting_wireless_security_add_proto(s_wsec, "wpa"); + nm_setting_wireless_security_add_pairwise(s_wsec, "tkip"); + nm_setting_wireless_security_add_group(s_wsec, "tkip"); + + nm_setting_wireless_security_add_proto(s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise(s_wsec, "ccmp"); + nm_setting_wireless_security_add_group(s_wsec, "ccmp"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + g_object_unref(reread); + + nmtst_connection_normalize(connection); + + /* Now change the connection to open and recheck */ + nm_connection_remove_setting(connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + + /* Write it back out */ + _writer_update_connection(connection, + TEST_SCRATCH_DIR, + testfile, + TEST_IFCFG_DIR "/ifcfg-random_wifi_connection.cexpected"); + keyfile = utils_get_keys_path(testfile); + g_assert(!g_file_test(keyfile, G_FILE_TEST_EXISTS)); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wpa_then_wep_with_perms(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + gboolean success; + GBytes * ssid; + char ** perms; + const char * ssid_data = "SomeSSID"; + + /* Test that writing out a WPA config then changing that to a WEP + * config works and doesn't cause infinite loop or other issues. + */ + + connection = nm_simple_connection_new(); + g_assert(connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + perms = g_strsplit("user:superman:", ",", -1); + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "random wifi connection 2", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_PERMISSIONS, + perms, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + g_strfreev(perms); + g_assert_cmpint(nm_setting_connection_get_num_permissions(s_con), ==, 1); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_PSK, + "My cool PSK", + NULL); + + nm_setting_wireless_security_add_proto(s_wsec, "wpa"); + nm_setting_wireless_security_add_pairwise(s_wsec, "tkip"); + nm_setting_wireless_security_add_group(s_wsec, "tkip"); + + nm_setting_wireless_security_add_proto(s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise(s_wsec, "ccmp"); + nm_setting_wireless_security_add_group(s_wsec, "ccmp"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + g_object_unref(reread); + + nmtst_connection_normalize(connection); + + /* Now change the connection to WEP and recheck */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NULL); + nm_setting_wireless_security_set_wep_key(s_wsec, 0, "abraka dabra"); + + /* Write it back out */ + _writer_update_connection(connection, + TEST_SCRATCH_DIR, + testfile, + TEST_IFCFG_DIR "/ifcfg-random_wifi_connection_2.cexpected"); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + nmtst_connection_normalize(connection); + success = nm_connection_compare(connection, + reread, + NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS + | NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS); + g_assert(success); + + keyfile = utils_get_keys_path(testfile); +} + +static void +test_write_wifi_dynamic_wep_leap(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + nmtst_auto_unlinkfile char *keyfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSetting8021x * s_8021x; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + GBytes * ssid; + const char * ssid_data = "blahblah"; + shvarFile * ifcfg; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi Dynamic WEP LEAP", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(ssid_data, strlen(ssid_data)); + + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + + g_bytes_unref(ssid); + + /* Wireless security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", NULL); + + /* Wireless security setting */ + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new(); + nm_connection_add_setting(connection, NM_SETTING(s_8021x)); + + nm_setting_802_1x_add_eap_method(s_8021x, "leap"); + + g_object_set(s_8021x, + NM_SETTING_802_1X_IDENTITY, + "Bill Smith", + NM_SETTING_802_1X_PASSWORD, + ";alkdfja;dslkfjsad;lkfjsadf", + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + keyfile = utils_get_keys_path(testfile); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); + + /* Check and make sure that an "old-school" LEAP (Network EAP) connection + * did not get written. Check first that the auth alg is not set to "LEAP" + * and next that the only IEEE 802.1x EAP method is "LEAP". + */ + ifcfg = _svOpenFile(testfile); + _svGetValue_check(ifcfg, "SECURITYMODE", NULL); + _svGetValue_check(ifcfg, "IEEE_8021X_EAP_METHODS", "LEAP"); + svCloseFile(ifcfg); +} + +static void +test_write_wired_qeth_dhcp(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + char ** subchans; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired qeth Static", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + subchans = g_strsplit("0.0.600,0.0.601,0.0.602", ",", -1); + g_object_set(s_wired, + NM_SETTING_WIRED_S390_SUBCHANNELS, + subchans, + NM_SETTING_WIRED_S390_NETTYPE, + "qeth", + NULL); + g_strfreev(subchans); + + nm_setting_wired_add_s390_option(s_wired, "portname", "FOOBAR"); + nm_setting_wired_add_s390_option(s_wired, "portno", "1"); + nm_setting_wired_add_s390_option(s_wired, "layer2", "0"); + nm_setting_wired_add_s390_option(s_wired, "protocol", "blahbalh"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wired_ctc_dhcp(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + char ** subchans; + shvarFile * ifcfg; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired ctc Static", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + subchans = g_strsplit("0.0.600,0.0.601", ",", -1); + g_object_set(s_wired, + NM_SETTING_WIRED_S390_SUBCHANNELS, + subchans, + NM_SETTING_WIRED_S390_NETTYPE, + "ctc", + NULL); + g_strfreev(subchans); + nm_setting_wired_add_s390_option(s_wired, "ctcprot", "0"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + /* Ensure the CTCPROT item gets written out as its own option */ + ifcfg = _svOpenFile(testfile); + + _svGetValue_check(ifcfg, "CTCPROT", "0"); + + /* And that it's not in the generic OPTIONS string */ + _svGetValue_check(ifcfg, "OPTIONS", NULL); + + svCloseFile(ifcfg); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_permissions(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Permissions", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + nm_setting_connection_add_permission(s_con, "user", "blahblah", NULL); + nm_setting_connection_add_permission(s_con, "user", "foobar", NULL); + nm_setting_connection_add_permission(s_con, "user", "asdfasdf", NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Permissions.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wifi_wep_agent_keys(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity * s_wsec; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + const char * str_ssid = "foobarbaz"; + GBytes * ssid; + + connection = nm_simple_connection_new(); + g_assert(connection != NULL); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wifi WEP Agent Owned", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + /* Wifi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wifi)); + + ssid = g_bytes_new(str_ssid, strlen(str_ssid)); + g_object_set(s_wifi, + NM_SETTING_WIRELESS_SSID, + ssid, + NM_SETTING_WIRELESS_MODE, + "infrastructure", + NULL); + g_bytes_unref(ssid); + + /* Wifi security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "none", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, + NM_WEP_KEY_TYPE_PASSPHRASE, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, + NM_SETTING_SECRET_FLAG_AGENT_OWNED, + NULL); + nm_setting_wireless_security_set_wep_key(s_wsec, 0, "asdfdjaslfjasd;flasjdfl;aksdf"); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection_FIXME(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL); + + /* Remove the WEP key from the original, because it should not have been + * written out to disk as it was agent-owned. The new connection should + * not have any WEP keys set. + * Also the new connection should not have WEP key type set. + */ + nm_setting_wireless_security_set_wep_key(s_wsec, 0, NULL); + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_wired_pppoe(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingPppoe * s_pppoe; + NMSettingPpp * s_ppp; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Wired PPPoE", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* PPPoE setting */ + s_pppoe = (NMSettingPppoe *) nm_setting_pppoe_new(); + nm_connection_add_setting(connection, NM_SETTING(s_pppoe)); + + g_object_set(G_OBJECT(s_pppoe), + NM_SETTING_PPPOE_SERVICE, + "stupid-service", + NM_SETTING_PPPOE_USERNAME, + "Bill Smith", + NM_SETTING_PPPOE_PASSWORD, + "test1", + NULL); + + /* PPP setting */ + s_ppp = (NMSettingPpp *) nm_setting_ppp_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ppp)); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection_fail(connection, TEST_SCRATCH_DIR, NULL); + + g_object_unref(connection); + g_clear_error(&error); +} + +static void +test_write_vpn(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingIPConfig * s_ip4; + NMSettingVpn * s_vpn; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write VPN", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_VPN_SETTING_NAME, + NULL); + + /* VPN setting */ + s_vpn = (NMSettingVpn *) nm_setting_vpn_new(); + nm_connection_add_setting(connection, NM_SETTING(s_vpn)); + + g_object_set(s_vpn, + NM_SETTING_VPN_SERVICE_TYPE, + "awesomevpn", + NM_SETTING_VPN_USER_NAME, + "Bill Smith", + NULL); + + nm_setting_vpn_add_data_item(s_vpn, "server", "vpn.somewhere.com"); + nm_setting_vpn_add_secret(s_vpn, "password", "sup3rs3cr3t"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection_fail(connection, TEST_SCRATCH_DIR, NULL); + + g_object_unref(connection); + g_clear_error(&error); +} + +static void +test_write_mobile_broadband(gconstpointer data) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingIPConfig * s_ip4; + NMSettingGsm * s_gsm; + NMSettingCdma * s_cdma; + NMSettingPpp * s_ppp; + NMSettingSerial * s_serial; + GError * error = NULL; + gboolean gsm = GPOINTER_TO_UINT(data); + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + gsm ? "Test Write GSM" : "Test Write CDMA", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + gsm ? NM_SETTING_GSM_SETTING_NAME : NM_SETTING_CDMA_SETTING_NAME, + NULL); + + if (gsm) { + /* GSM setting */ + s_gsm = (NMSettingGsm *) nm_setting_gsm_new(); + nm_connection_add_setting(connection, NM_SETTING(s_gsm)); + } else { + /* CDMA setting */ + s_cdma = (NMSettingCdma *) nm_setting_cdma_new(); + nm_connection_add_setting(connection, NM_SETTING(s_cdma)); + + g_object_set(s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL); + } + + /* Serial setting */ + s_serial = (NMSettingSerial *) nm_setting_serial_new(); + nm_connection_add_setting(connection, NM_SETTING(s_serial)); + + g_object_set(s_serial, + NM_SETTING_SERIAL_BAUD, + 115200, + NM_SETTING_SERIAL_BITS, + 8, + NM_SETTING_SERIAL_PARITY, + NM_SETTING_SERIAL_PARITY_NONE, + NM_SETTING_SERIAL_STOPBITS, + 1, + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* PPP setting */ + s_ppp = (NMSettingPpp *) nm_setting_ppp_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ppp)); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection_fail(connection, TEST_SCRATCH_DIR, NULL); + + g_object_unref(connection); + g_clear_error(&error); +} + +static void +test_read_bridge_main(void) +{ + NMConnection * connection; + NMSettingBridge *s_bridge; + NMSettingWired * s_wired; + const char * mac; + char expected_mac_address[ETH_ALEN] = {0x00, 0x16, 0x41, 0x11, 0x22, 0x33}; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bridge-main", NULL, TYPE_ETHERNET, NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "br0"); + + /* ===== Bridging SETTING ===== */ + + s_bridge = nm_connection_get_setting_bridge(connection); + g_assert(s_bridge); + g_assert_cmpuint(nm_setting_bridge_get_forward_delay(s_bridge), ==, 2); + g_assert(nm_setting_bridge_get_stp(s_bridge)); + g_assert_cmpuint(nm_setting_bridge_get_priority(s_bridge), ==, 32744); + g_assert_cmpuint(nm_setting_bridge_get_hello_time(s_bridge), ==, 7); + g_assert_cmpuint(nm_setting_bridge_get_max_age(s_bridge), ==, 39); + g_assert_cmpuint(nm_setting_bridge_get_ageing_time(s_bridge), ==, 235352); + g_assert_cmpuint(nm_setting_bridge_get_group_forward_mask(s_bridge), ==, 24); + g_assert(!nm_setting_bridge_get_multicast_snooping(s_bridge)); + g_assert_cmpint(nm_setting_bridge_get_vlan_filtering(s_bridge), ==, TRUE); + g_assert_cmpint(nm_setting_bridge_get_vlan_default_pvid(s_bridge), ==, 99); + + /* MAC address */ + s_wired = nm_connection_get_setting_wired(connection); + g_assert(s_wired); + mac = nm_setting_wired_get_cloned_mac_address(s_wired); + g_assert(mac); + g_assert(nm_utils_hwaddr_matches(mac, -1, expected_mac_address, ETH_ALEN)); + + g_object_unref(connection); +} + +static void +test_write_bridge_main(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingBridge * s_bridge; + NMSettingIPConfig * s_ip4, *s_ip6; + NMSettingWired * s_wired; + NMIPAddress * addr; + static const char * mac = "31:33:33:37:be:cd"; + GError * error = NULL; + gs_unref_ptrarray GPtrArray *vlans = NULL; + NMBridgeVlan * vlan; + + connection = nm_simple_connection_new(); + g_assert(connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Bridge Main", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_INTERFACE_NAME, + "br0", + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_BRIDGE_SETTING_NAME, + NULL); + + /* bridge setting */ + s_bridge = (NMSettingBridge *) nm_setting_bridge_new(); + nm_connection_add_setting(connection, NM_SETTING(s_bridge)); + + vlans = g_ptr_array_new_with_free_func((GDestroyNotify) nm_bridge_vlan_unref); + vlan = nm_bridge_vlan_new(10, 16); + nm_bridge_vlan_set_untagged(vlan, TRUE); + g_ptr_array_add(vlans, vlan); + vlan = nm_bridge_vlan_new(22, 22); + nm_bridge_vlan_set_pvid(vlan, TRUE); + nm_bridge_vlan_set_untagged(vlan, TRUE); + g_ptr_array_add(vlans, vlan); + vlan = nm_bridge_vlan_new(44, 0); + g_ptr_array_add(vlans, vlan); + + g_object_set(s_bridge, + NM_SETTING_BRIDGE_MAC_ADDRESS, + mac, + NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, + 19008, + NM_SETTING_BRIDGE_VLAN_FILTERING, + TRUE, + NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID, + 4000, + NM_SETTING_BRIDGE_VLANS, + vlans, + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + nm_connection_add_setting(connection, nm_setting_proxy_new()); + + nmtst_assert_connection_verifies_without_normalization(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_BRIDGE, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_bridge_component(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingBridgePort *s_port; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bridge-component", + NULL, + TYPE_ETHERNET, + NULL); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "br0"); + g_assert_cmpstr(nm_setting_connection_get_slave_type(s_con), + ==, + NM_SETTING_BRIDGE_SETTING_NAME); + + s_port = nm_connection_get_setting_bridge_port(connection); + g_assert(s_port); + g_assert(nm_setting_bridge_port_get_hairpin_mode(s_port)); + g_assert_cmpuint(nm_setting_bridge_port_get_priority(s_port), ==, 28); + g_assert_cmpuint(nm_setting_bridge_port_get_path_cost(s_port), ==, 100); + + g_object_unref(connection); +} + +static void +test_write_bridge_component(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSetting * s_port; + static const char * mac = "31:33:33:37:be:cd"; + guint32 mtu = 1492; + gs_unref_ptrarray GPtrArray *vlans = NULL; + NMBridgeVlan * vlan; + + connection = nm_simple_connection_new(); + g_assert(connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Bridge Component", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, + "br0", + NM_SETTING_CONNECTION_SLAVE_TYPE, + NM_SETTING_BRIDGE_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NM_SETTING_WIRED_MTU, mtu, NULL); + + /* Bridge port */ + vlans = g_ptr_array_new_with_free_func((GDestroyNotify) nm_bridge_vlan_unref); + vlan = nm_bridge_vlan_new(1, 0); + nm_bridge_vlan_set_untagged(vlan, TRUE); + g_ptr_array_add(vlans, vlan); + vlan = nm_bridge_vlan_new(4, 4094); + nm_bridge_vlan_set_untagged(vlan, TRUE); + g_ptr_array_add(vlans, vlan); + vlan = nm_bridge_vlan_new(2, 2); + nm_bridge_vlan_set_pvid(vlan, TRUE); + g_ptr_array_add(vlans, vlan); + + s_port = nm_setting_bridge_port_new(); + nm_connection_add_setting(connection, s_port); + g_object_set(s_port, + NM_SETTING_BRIDGE_PORT_PRIORITY, + 50, + NM_SETTING_BRIDGE_PORT_PATH_COST, + 33, + NM_SETTING_BRIDGE_PORT_VLANS, + vlans, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Bridge_Component.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_bridge_missing_stp(void) +{ + NMConnection * connection; + NMSettingBridge *s_bridge; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bridge-missing-stp", + NULL, + TYPE_BRIDGE, + NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "br0"); + + /* ===== Bridging SETTING ===== */ + + s_bridge = nm_connection_get_setting_bridge(connection); + g_assert(s_bridge); + g_assert(nm_setting_bridge_get_stp(s_bridge) == FALSE); + + g_object_unref(connection); +} + +#define TEST_IFCFG_VLAN_INTERFACE TEST_IFCFG_DIR "/ifcfg-test-vlan-interface" + +static void +test_read_vlan_interface(void) +{ + NMConnection * connection; + NMSettingVlan *s_vlan; + guint32 from = 0, to = 0; + + connection = _connection_from_file(TEST_IFCFG_VLAN_INTERFACE, NULL, TYPE_ETHERNET, NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "vlan43"); + + s_vlan = nm_connection_get_setting_vlan(connection); + g_assert(s_vlan); + + g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth9"); + g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 43); + g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), + ==, + NM_VLAN_FLAG_GVRP | NM_VLAN_FLAG_LOOSE_BINDING | NM_VLAN_FLAG_REORDER_HEADERS); + + /* Ingress map */ + g_assert_cmpint(nm_setting_vlan_get_num_priorities(s_vlan, NM_VLAN_INGRESS_MAP), ==, 2); + + g_assert(nm_setting_vlan_get_priority(s_vlan, NM_VLAN_INGRESS_MAP, 0, &from, &to)); + g_assert_cmpint(from, ==, 0); + g_assert_cmpint(to, ==, 1); + + g_assert(nm_setting_vlan_get_priority(s_vlan, NM_VLAN_INGRESS_MAP, 1, &from, &to)); + g_assert_cmpint(from, ==, 2); + g_assert_cmpint(to, ==, 5); + + /* Egress map */ + g_assert_cmpint(nm_setting_vlan_get_num_priorities(s_vlan, NM_VLAN_EGRESS_MAP), ==, 3); + + g_assert(nm_setting_vlan_get_priority(s_vlan, NM_VLAN_EGRESS_MAP, 0, &from, &to)); + g_assert_cmpint(from, ==, 3); + g_assert_cmpint(to, ==, 1); + + g_assert(nm_setting_vlan_get_priority(s_vlan, NM_VLAN_EGRESS_MAP, 1, &from, &to)); + g_assert_cmpint(from, ==, 12); + g_assert_cmpint(to, ==, 3); + + g_assert(nm_setting_vlan_get_priority(s_vlan, NM_VLAN_EGRESS_MAP, 2, &from, &to)); + g_assert_cmpint(from, ==, 14); + g_assert_cmpint(to, ==, 7); + + g_object_unref(connection); +} + +#define TEST_IFCFG_VLAN_ONLY_VLANID TEST_IFCFG_DIR "/ifcfg-test-vlan-only-vlanid" + +static void +test_read_vlan_only_vlan_id(void) +{ + NMConnection * connection; + NMSettingVlan *s_vlan; + + connection = _connection_from_file(TEST_IFCFG_VLAN_ONLY_VLANID, NULL, TYPE_ETHERNET, NULL); + + g_assert(nm_connection_get_interface_name(connection) == NULL); + + s_vlan = nm_connection_get_setting_vlan(connection); + g_assert(s_vlan); + + g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth9"); + g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 43); + g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), ==, NM_VLAN_FLAG_REORDER_HEADERS); + + g_object_unref(connection); +} + +static void +test_read_vlan_only_device(void) +{ + NMConnection * connection; + NMSettingVlan *s_vlan; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-vlan-only-device", + NULL, + TYPE_ETHERNET, + NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "eth0.9"); + + s_vlan = nm_connection_get_setting_vlan(connection); + g_assert(s_vlan); + + g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth0"); + g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 9); + + g_object_unref(connection); +} + +static void +test_read_vlan_physdev(void) +{ + NMConnection * connection; + NMSettingVlan *s_vlan; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-vlan-physdev", NULL, TYPE_ETHERNET, NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "vlan0.3"); + + s_vlan = nm_connection_get_setting_vlan(connection); + g_assert(s_vlan); + + g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth0"); + g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 3); + + g_object_unref(connection); +} + +static void +test_read_vlan_reorder_hdr_1(void) +{ + NMConnection * connection; + NMSettingVlan *s_vlan; + + NMTST_EXPECT_NM_WARN("*REORDER_HDR key is deprecated, use VLAN_FLAGS*"); + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-vlan-reorder-hdr-1", + NULL, + TYPE_ETHERNET, + NULL); + g_test_assert_expected_messages(); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "vlan0.3"); + + s_vlan = nm_connection_get_setting_vlan(connection); + g_assert(s_vlan); + + g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth0"); + g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 3); + /* Check that REORDER_HDR=0 is ignored */ + g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), ==, NM_VLAN_FLAG_REORDER_HEADERS); + + g_object_unref(connection); +} + +static void +test_read_vlan_reorder_hdr_2(void) +{ + NMConnection * connection; + NMSettingVlan *s_vlan; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-vlan-reorder-hdr-2", + NULL, + TYPE_ETHERNET, + NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "vlan0.3"); + + s_vlan = nm_connection_get_setting_vlan(connection); + g_assert(s_vlan); + + g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth0"); + g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 3); + /* Check that VLAN_FLAGS=NO_REORDER_HDR works */ + g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), ==, NM_VLAN_FLAG_LOOSE_BINDING); + + g_object_unref(connection); +} + +static void +test_read_vlan_flags_1(void) +{ + NMConnection * connection; + NMSettingVlan *s_vlan; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-vlan-flags-1", NULL, TYPE_ETHERNET, NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "super-vlan"); + + s_vlan = nm_connection_get_setting_vlan(connection); + g_assert(s_vlan); + + g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth9"); + g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 44); + g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), + ==, + NM_VLAN_FLAG_LOOSE_BINDING | NM_VLAN_FLAG_REORDER_HEADERS); + + g_object_unref(connection); +} + +static void +test_read_vlan_flags_2(void) +{ + NMConnection * connection; + NMSettingVlan *s_vlan; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-vlan-flags-2", NULL, TYPE_ETHERNET, NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "super-vlan"); + + s_vlan = nm_connection_get_setting_vlan(connection); + g_assert(s_vlan); + + g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth9"); + g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 44); + g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), + ==, + NM_VLAN_FLAG_GVRP | NM_VLAN_FLAG_LOOSE_BINDING | NM_VLAN_FLAG_REORDER_HEADERS); + + g_object_unref(connection); +} + +static void +test_write_vlan(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + + connection = _connection_from_file(TEST_IFCFG_VLAN_INTERFACE, NULL, TYPE_VLAN, NULL); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Vlan_test-vlan-interface.cexpected", + &testfile); +} + +static void +test_write_vlan_flags(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-vlan-flags-2", NULL, TYPE_VLAN, NULL); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_vlan_only_vlanid(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + + connection = _connection_from_file(TEST_IFCFG_VLAN_ONLY_VLANID, NULL, TYPE_VLAN, NULL); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_vlan_reorder_hdr(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingVlan * s_vlan; + NMSettingWired * s_wired; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write VLAN reorder_hdr", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + FALSE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_VLAN_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* VLAN setting */ + s_vlan = (NMSettingVlan *) nm_setting_vlan_new(); + nm_connection_add_setting(connection, NM_SETTING(s_vlan)); + + g_object_set(s_vlan, + NM_SETTING_VLAN_PARENT, + "eth0", + NM_SETTING_VLAN_ID, + 444, + NM_SETTING_VLAN_FLAGS, + 1, + NULL); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_ethernet_missing_ipv6(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + + connection = nm_simple_connection_new(); + g_assert(connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Ethernet Without IPv6 Setting", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, + "random-client-id-00:22:33", + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, + TRUE, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, + TRUE, + NULL); + + /* IP6 setting */ + /* + * We intentionally don't add IPv6 setting here. ifcfg-rh plugin should regard + * missing IPv6 as IPv6 with NM_SETTING_IP6_CONFIG_METHOD_AUTO method. + */ + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_ibft_ignored(void) +{ + gs_free_error GError *error = NULL; + + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-ibft", NULL, TYPE_ETHERNET, &error); + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); +} + +static void +test_read_bond_main(void) +{ + NMConnection * connection; + NMSettingBond *s_bond; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bond-main", NULL, TYPE_ETHERNET, NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "bond0"); + + /* ===== Bonding SETTING ===== */ + + s_bond = nm_connection_get_setting_bond(connection); + g_assert(s_bond); + + g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_MIIMON), + ==, + "100"); + + g_object_unref(connection); +} + +static void +test_read_bond_eth_type(void) +{ + NMConnection * connection; + NMSettingBond *s_bond; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bond-eth-type", + NULL, + TYPE_ETHERNET, + NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "bond0"); + + /* ===== Bonding SETTING ===== */ + + s_bond = nm_connection_get_setting_bond(connection); + g_assert(s_bond); + + g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_MIIMON), + ==, + "213"); + g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_LACP_RATE), + ==, + "1"); + + g_object_unref(connection); +} + +static void +test_write_bond_main(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingBond * s_bond; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + NMSettingWired * s_wired; + NMIPAddress * addr; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Bond Main", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_INTERFACE_NAME, + "bond0", + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_BOND_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* bond setting */ + s_bond = (NMSettingBond *) nm_setting_bond_new(); + nm_connection_add_setting(connection, NM_SETTING(s_bond)); + + nm_setting_bond_add_option(s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, "5"); + nm_setting_bond_add_option(s_bond, NM_SETTING_BOND_OPTION_UPDELAY, "10"); + nm_setting_bond_add_option(s_bond, NM_SETTING_BOND_OPTION_MIIMON, "100"); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + nm_connection_add_setting(connection, nm_setting_proxy_new()); + + nmtst_assert_connection_verifies_without_normalization(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Bond_Main.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_BOND, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_bond_slave(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bond-slave", NULL, TYPE_ETHERNET, NULL); + g_test_assert_expected_messages(); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + + g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "bond0"); + + g_assert_cmpstr(nm_setting_connection_get_slave_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME); + + g_object_unref(connection); +} + +static void +test_write_bond_slave(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + static const char * mac = "31:33:33:37:be:cd"; + guint32 mtu = 1492; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Bond Slave", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, + "bond0", + NM_SETTING_CONNECTION_SLAVE_TYPE, + NM_SETTING_BOND_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + g_object_set(s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NM_SETTING_WIRED_MTU, mtu, NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_infiniband(void) +{ + NMConnection * connection; + NMSettingInfiniband *s_infiniband; + char * unmanaged = NULL; + const char * mac; + char expected_mac_address[INFINIBAND_ALEN] = {0x80, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, + 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, + 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22}; + const char *transport_mode; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-infiniband", + NULL, + TYPE_INFINIBAND, + &unmanaged); + g_assert(!unmanaged); + + /* ===== INFINIBAND SETTING ===== */ + + s_infiniband = nm_connection_get_setting_infiniband(connection); + g_assert(s_infiniband); + + /* MAC address */ + mac = nm_setting_infiniband_get_mac_address(s_infiniband); + g_assert(mac); + g_assert(nm_utils_hwaddr_matches(mac, -1, expected_mac_address, sizeof(expected_mac_address))); + + /* Transport mode */ + transport_mode = nm_setting_infiniband_get_transport_mode(s_infiniband); + g_assert(transport_mode); + g_assert_cmpstr(transport_mode, ==, "connected"); + + g_object_unref(connection); +} + +static void +test_write_infiniband(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingInfiniband * s_infiniband; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + const char * mac = "80:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22"; + guint32 mtu = 65520; + NMIPAddress *addr; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write InfiniBand", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_INFINIBAND_SETTING_NAME, + NULL); + + /* InfiniBand setting */ + s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new(); + nm_connection_add_setting(connection, NM_SETTING(s_infiniband)); + + g_object_set(s_infiniband, + NM_SETTING_INFINIBAND_MAC_ADDRESS, + mac, + NM_SETTING_INFINIBAND_MTU, + mtu, + NM_SETTING_INFINIBAND_TRANSPORT_MODE, + "connected", + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_INFINIBAND, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_bond_slave_ib(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bond-slave-ib", NULL, NULL, NULL); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(s_con); + + g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "bond0"); + g_assert_cmpstr(nm_setting_connection_get_slave_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME); +} + +static void +test_write_bond_slave_ib(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingInfiniband * s_infiniband; + static const char *mac = "80:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22"; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Bond Slave InfiniBand", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_INFINIBAND_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, + "bond0", + NM_SETTING_CONNECTION_SLAVE_TYPE, + NM_SETTING_BOND_SETTING_NAME, + NULL); + + /* InfiniBand setting */ + s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new(); + nm_connection_add_setting(connection, NM_SETTING(s_infiniband)); + + g_object_set(s_infiniband, + NM_SETTING_INFINIBAND_MAC_ADDRESS, + mac, + NM_SETTING_INFINIBAND_MTU, + 2044, + NM_SETTING_INFINIBAND_TRANSPORT_MODE, + "datagram", + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + reread = _connection_from_file(testfile, NULL, NULL, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_bond_opts_mode_numeric(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingBond * s_bond; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bond-mode-numeric", + NULL, + TYPE_ETHERNET, + NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "bond0"); + + 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_BOND_SETTING_NAME); + + s_bond = nm_connection_get_setting_bond(connection); + g_assert(s_bond); + g_assert_cmpstr(nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_MODE), + ==, + "802.3ad"); + + g_object_unref(connection); +} + +#define DCB_ALL_FLAGS \ + (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + +static void +test_read_dcb_basic(void) +{ + NMConnection *connection; + NMSettingDcb *s_dcb; + guint i; + guint expected_group_ids[8] = {0, 0, 0, 0, 1, 1, 1, 0xF}; + guint expected_group_bandwidths[8] = {25, 0, 0, 75, 0, 0, 0, 0}; + guint expected_bandwidths[8] = {5, 10, 30, 25, 10, 50, 5, 0}; + gboolean expected_strict[8] = {FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE}; + guint expected_traffic_classes[8] = {7, 6, 5, 4, 3, 2, 1, 0}; + gboolean expected_pfcs[8] = {TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE}; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-dcb", NULL, TYPE_ETHERNET, NULL); + + s_dcb = nm_connection_get_setting_dcb(connection); + g_assert(s_dcb); + + g_assert_cmpint(nm_setting_dcb_get_app_fcoe_flags(s_dcb), ==, DCB_ALL_FLAGS); + g_assert_cmpint(nm_setting_dcb_get_app_fcoe_priority(s_dcb), ==, 7); + + g_assert_cmpint(nm_setting_dcb_get_app_iscsi_flags(s_dcb), ==, DCB_ALL_FLAGS); + g_assert_cmpint(nm_setting_dcb_get_app_iscsi_priority(s_dcb), ==, 6); + + g_assert_cmpint(nm_setting_dcb_get_app_fip_flags(s_dcb), ==, DCB_ALL_FLAGS); + g_assert_cmpint(nm_setting_dcb_get_app_fip_priority(s_dcb), ==, 2); + + g_assert_cmpint(nm_setting_dcb_get_priority_flow_control_flags(s_dcb), + ==, + (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE)); + for (i = 0; i < 8; i++) + g_assert_cmpint(nm_setting_dcb_get_priority_flow_control(s_dcb, i), ==, expected_pfcs[i]); + + g_assert_cmpint(nm_setting_dcb_get_priority_group_flags(s_dcb), ==, DCB_ALL_FLAGS); + + /* Group IDs */ + for (i = 0; i < 8; i++) + g_assert_cmpint(nm_setting_dcb_get_priority_group_id(s_dcb, i), ==, expected_group_ids[i]); + + /* Group bandwidth */ + for (i = 0; i < 8; i++) + g_assert_cmpint(nm_setting_dcb_get_priority_group_bandwidth(s_dcb, i), + ==, + expected_group_bandwidths[i]); + + /* User priority bandwidth */ + for (i = 0; i < 8; i++) + g_assert_cmpint(nm_setting_dcb_get_priority_bandwidth(s_dcb, i), + ==, + expected_bandwidths[i]); + + /* Strict bandwidth */ + for (i = 0; i < 8; i++) + g_assert_cmpint(nm_setting_dcb_get_priority_strict_bandwidth(s_dcb, i), + ==, + expected_strict[i]); + + /* Traffic class */ + for (i = 0; i < 8; i++) + g_assert_cmpint(nm_setting_dcb_get_priority_traffic_class(s_dcb, i), + ==, + expected_traffic_classes[i]); + + g_object_unref(connection); +} + +static void +test_write_dcb_basic(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingDcb * s_dcb; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + guint i; + const guint group_ids[8] = {4, 0xF, 6, 0xF, 1, 7, 3, 0xF}; + const guint group_bandwidths[8] = {10, 20, 15, 10, 2, 3, 35, 5}; + const guint bandwidths[8] = {10, 20, 30, 40, 50, 10, 0, 25}; + const gboolean strict[8] = {TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE}; + const guint traffic_classes[8] = {3, 4, 7, 2, 1, 0, 5, 6}; + const gboolean pfcs[8] = {TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE}; + + connection = nm_simple_connection_new(); + + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + g_object_set(G_OBJECT(s_con), + NM_SETTING_CONNECTION_ID, + "dcb-test", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_INTERFACE_NAME, + "eth0", + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP stuff */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + /* DCB */ + s_dcb = (NMSettingDcb *) nm_setting_dcb_new(); + nm_connection_add_setting(connection, NM_SETTING(s_dcb)); + + g_object_set(G_OBJECT(s_dcb), + NM_SETTING_DCB_APP_FCOE_FLAGS, + DCB_ALL_FLAGS, + NM_SETTING_DCB_APP_FCOE_PRIORITY, + 5, + NM_SETTING_DCB_APP_ISCSI_FLAGS, + DCB_ALL_FLAGS, + NM_SETTING_DCB_APP_ISCSI_PRIORITY, + 1, + NM_SETTING_DCB_APP_FIP_FLAGS, + DCB_ALL_FLAGS, + NM_SETTING_DCB_APP_FIP_PRIORITY, + 3, + NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, + DCB_ALL_FLAGS, + NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, + DCB_ALL_FLAGS, + NULL); + + for (i = 0; i < 8; i++) { + nm_setting_dcb_set_priority_flow_control(s_dcb, i, pfcs[i]); + nm_setting_dcb_set_priority_group_id(s_dcb, i, group_ids[i]); + nm_setting_dcb_set_priority_group_bandwidth(s_dcb, i, group_bandwidths[i]); + nm_setting_dcb_set_priority_bandwidth(s_dcb, i, bandwidths[i]); + nm_setting_dcb_set_priority_strict_bandwidth(s_dcb, i, strict[i]); + nm_setting_dcb_set_priority_traffic_class(s_dcb, i, traffic_classes[i]); + } + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "//ifcfg-dcb-test.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_dcb_default_app_priorities(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingDcb * s_dcb; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-dcb-default-app-priorities", + NULL, + TYPE_ETHERNET, + NULL); + + s_dcb = nm_connection_get_setting_dcb(connection); + g_assert(s_dcb); + + g_assert_cmpint(nm_setting_dcb_get_app_fcoe_flags(s_dcb), ==, NM_SETTING_DCB_FLAG_ENABLE); + g_assert_cmpint(nm_setting_dcb_get_app_fcoe_priority(s_dcb), ==, -1); + + g_assert_cmpint(nm_setting_dcb_get_app_iscsi_flags(s_dcb), ==, NM_SETTING_DCB_FLAG_ENABLE); + g_assert_cmpint(nm_setting_dcb_get_app_iscsi_priority(s_dcb), ==, -1); + + g_assert_cmpint(nm_setting_dcb_get_app_fip_flags(s_dcb), ==, NM_SETTING_DCB_FLAG_ENABLE); + g_assert_cmpint(nm_setting_dcb_get_app_fip_priority(s_dcb), ==, -1); +} + +static void +test_read_dcb_bad_booleans(void) +{ + gs_free_error GError *error = NULL; + + NMTST_EXPECT_NM_WARN("*invalid DCB_PG_STRICT value*not all 0s and 1s*"); + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-dcb-bad-booleans", + NULL, + TYPE_ETHERNET, + &error); + g_test_assert_expected_messages(); + + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); + g_assert(strstr(error->message, "invalid boolean digit")); +} + +static void +test_read_dcb_short_booleans(void) +{ + gs_free_error GError *error = NULL; + + NMTST_EXPECT_NM_WARN("*DCB_PG_STRICT value*8 characters*"); + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-dcb-short-booleans", + NULL, + TYPE_ETHERNET, + &error); + g_test_assert_expected_messages(); + + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); + g_assert(strstr(error->message, "boolean array must be 8 characters")); +} + +static void +test_read_dcb_bad_uints(void) +{ + gs_free_error GError *error = NULL; + + NMTST_EXPECT_NM_WARN("*invalid DCB_PG_UP2TC value*not 0 - 7*"); + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-dcb-bad-uints", + NULL, + TYPE_ETHERNET, + &error); + g_test_assert_expected_messages(); + + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); + g_assert(strstr(error->message, "invalid uint digit")); +} + +static void +test_read_dcb_short_uints(void) +{ + gs_free_error GError *error = NULL; + + NMTST_EXPECT_NM_WARN("*DCB_PG_UP2TC value*8 characters*"); + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-dcb-short-uints", + NULL, + TYPE_ETHERNET, + &error); + g_test_assert_expected_messages(); + + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); + g_assert(strstr(error->message, "uint array must be 8 characters")); +} + +static void +test_read_dcb_bad_percent(void) +{ + gs_free_error GError *error = NULL; + + NMTST_EXPECT_NM_WARN("*invalid DCB_PG_PCT percentage value*"); + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-dcb-bad-percent", + NULL, + TYPE_ETHERNET, + &error); + g_test_assert_expected_messages(); + + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); + g_assert(strstr(error->message, "invalid percent element")); +} + +static void +test_read_dcb_short_percent(void) +{ + gs_free_error GError *error = NULL; + + NMTST_EXPECT_NM_WARN("*invalid DCB_PG_PCT percentage list value*"); + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-dcb-short-percent", + NULL, + TYPE_ETHERNET, + &error); + g_test_assert_expected_messages(); + + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); + g_assert(strstr(error->message, "percent array must be 8 elements")); +} + +static void +test_read_dcb_pgpct_not_100(void) +{ + gs_free_error GError *error = NULL; + + NMTST_EXPECT_NM_WARN("*DCB_PG_PCT percentages do not equal 100*"); + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-dcb-pgpct-not-100", + NULL, + TYPE_ETHERNET, + &error); + g_test_assert_expected_messages(); + + g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); + g_assert(strstr(error->message, "invalid percentage sum")); +} + +static void +test_read_fcoe_mode(gconstpointer user_data) +{ + gs_unref_object NMConnection *connection = NULL; + gs_free char * file = NULL; + const char * expected_mode = user_data; + NMSettingDcb * s_dcb; + + file = g_strdup_printf(TEST_IFCFG_DIR "/ifcfg-test-fcoe-%s", expected_mode); + connection = _connection_from_file(file, NULL, TYPE_ETHERNET, NULL); + + s_dcb = nm_connection_get_setting_dcb(connection); + g_assert(s_dcb); + + g_assert_cmpint(nm_setting_dcb_get_app_fcoe_flags(s_dcb), ==, NM_SETTING_DCB_FLAG_ENABLE); + g_assert_cmpstr(nm_setting_dcb_get_app_fcoe_mode(s_dcb), ==, expected_mode); +} + +static void +test_write_fcoe_mode(gconstpointer user_data) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + const char * expected_mode = user_data; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingDcb * s_dcb; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + shvarFile * ifcfg; + + connection = nm_simple_connection_new(); + + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + g_object_set(G_OBJECT(s_con), + NM_SETTING_CONNECTION_ID, + "fcoe-test", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_INTERFACE_NAME, + "eth0", + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP stuff */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + /* DCB */ + s_dcb = (NMSettingDcb *) nm_setting_dcb_new(); + nm_connection_add_setting(connection, NM_SETTING(s_dcb)); + + g_object_set(G_OBJECT(s_dcb), + NM_SETTING_DCB_APP_FCOE_FLAGS, + NM_SETTING_DCB_FLAG_ENABLE, + NM_SETTING_DCB_APP_FCOE_MODE, + expected_mode, + NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + ifcfg = _svOpenFile(testfile); + _svGetValue_check(ifcfg, "DCB_APP_FCOE_MODE", expected_mode); + svCloseFile(ifcfg); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_team_master(gconstpointer user_data) +{ + const char *const PATH_NAME = user_data; + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingTeam * s_team; + const char * expected_config = + "{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}"; + + connection = _connection_from_file(PATH_NAME, NULL, TYPE_ETHERNET, NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "team0"); + + 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_TEAM_SETTING_NAME); + + s_team = nm_connection_get_setting_team(connection); + g_assert(s_team); + g_assert_cmpstr(nm_setting_team_get_config(s_team), ==, expected_config); + + g_object_unref(connection); +} + +static void +test_read_team_master_invalid(gconstpointer user_data) +{ + const char *const PATH_NAME = user_data; + gs_free_error GError *error = NULL; + gs_unref_object NMConnection *connection = NULL; + + if (nm_json_vt()) { + _connection_from_file_fail(PATH_NAME, NULL, TYPE_ETHERNET, &error); + + g_assert_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + g_assert(strstr(error->message, _("invalid json"))); + } else + connection = _connection_from_file(PATH_NAME, NULL, TYPE_ETHERNET, NULL); +} + +static void +test_write_team_master(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingTeam * s_team; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + const char * expected_config = + "{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}"; + shvarFile *f; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Team Master", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_INTERFACE_NAME, + "team0", + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_TEAM_SETTING_NAME, + NULL); + + /* Team setting */ + s_team = (NMSettingTeam *) nm_setting_team_new(); + nm_connection_add_setting(connection, NM_SETTING(s_team)); + + g_object_set(s_team, NM_SETTING_TEAM_CONFIG, expected_config, NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); + + nm_connection_add_setting(connection, nm_setting_proxy_new()); + + nmtst_assert_connection_verifies_without_normalization(connection); + + _writer_new_connection(connection, TEST_SCRATCH_DIR, &testfile); + + f = _svOpenFile(testfile); + _svGetValue_check(f, "DEVICETYPE", "Team"); + _svGetValue_check(f, "TEAM_CONFIG", expected_config); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_team_port(gconstpointer user_data) +{ + const char *const PATH_NAME = user_data; + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingTeamPort * s_team_port; + const char * expected_config = "{\"p4p1\": {\"prio\": -10, \"sticky\": true}}"; + + connection = _connection_from_file(PATH_NAME, NULL, TYPE_ETHERNET, NULL); + + 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_master(s_con), ==, "team0"); + + s_team_port = nm_connection_get_setting_team_port(connection); + g_assert(s_team_port); + g_assert_cmpstr(nm_setting_team_port_get_config(s_team_port), ==, expected_config); + + g_object_unref(connection); +} + +static void +test_write_team_port(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingTeamPort * s_team_port; + NMSettingWired * s_wired; + const char * expected_config = "{\"p4p1\": {\"prio\": -10, \"sticky\": true}}"; + shvarFile * f; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Team Port", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, + "team0", + NM_SETTING_CONNECTION_SLAVE_TYPE, + NM_SETTING_TEAM_SETTING_NAME, + NULL); + + /* Team setting */ + s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new(); + nm_connection_add_setting(connection, NM_SETTING(s_team_port)); + g_object_set(s_team_port, NM_SETTING_TEAM_PORT_CONFIG, expected_config, NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Team_Port.cexpected", + &testfile); + + f = _svOpenFile(testfile); + _svGetValue_check(f, "TYPE", NULL); + _svGetValue_check(f, "DEVICETYPE", "TeamPort"); + _svGetValue_check(f, "TEAM_PORT_CONFIG", expected_config); + _svGetValue_check(f, "TEAM_MASTER", "team0"); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_write_team_infiniband_port(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingTeamPort * s_team_port; + NMSettingInfiniband * s_inf; + const char * expected_config = "{\"inf1\": {\"prio\": -10, \"sticky\": true}}"; + shvarFile * f; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Team Infiniband Port", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_INFINIBAND_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, + "team0", + NM_SETTING_CONNECTION_SLAVE_TYPE, + NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_CONNECTION_INTERFACE_NAME, + "inf1", + NULL); + + /* Team setting */ + s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new(); + nm_connection_add_setting(connection, NM_SETTING(s_team_port)); + g_object_set(s_team_port, NM_SETTING_TEAM_PORT_CONFIG, expected_config, NULL); + + /* Infiniband setting */ + s_inf = (NMSettingInfiniband *) nm_setting_infiniband_new(); + nm_connection_add_setting(connection, NM_SETTING(s_inf)); + g_object_set(s_inf, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Team_Infiniband_Port.cexpected", + &testfile); + + f = _svOpenFile(testfile); + _svGetValue_check(f, "TYPE", "InfiniBand"); + _svGetValue_check(f, "DEVICETYPE", "TeamPort"); + _svGetValue_check(f, "TEAM_PORT_CONFIG", expected_config); + _svGetValue_check(f, "TEAM_MASTER", "team0"); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +static void +test_read_team_port_empty_config(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-team-port-empty-config", + NULL, + TYPE_ETHERNET, + NULL); + + 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_master(s_con), ==, "team0"); + + /* Normalization adds a team-port setting */ + g_assert(nm_connection_get_setting_team_port(connection)); + + /* empty/missing config */ + g_assert(!nm_setting_team_port_get_config(nm_connection_get_setting_team_port(connection))); + + g_object_unref(connection); +} + +static void +test_team_reread_slave(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection_1 = NULL; + gs_unref_object NMConnection *connection_2 = NULL; + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same = FALSE; + NMSettingConnection * s_con; + + connection_1 = + nmtst_create_connection_from_keyfile("[connection]\n" + "id=team-slave-enp31s0f1-142\n" + "uuid=74f435bb-ede4-415a-9d48-f580b60eba04\n" + "type=vlan\n" + "autoconnect=false\n" + "interface-name=enp31s0f1-142\n" + "master=team142\n" + "permissions=\n" + "slave-type=team\n" + "\n" + "[vlan]\n" + "egress-priority-map=\n" + "flags=1\n" + "id=142\n" + "ingress-priority-map=\n" + "parent=enp31s0f1\n", + "/test_team_reread_slave"); + + /* to double-check keyfile syntax, re-create the connection by hand. */ + connection_2 = nmtst_create_minimal_connection("team-slave-enp31s0f1-142", + "74f435bb-ede4-415a-9d48-f580b60eba04", + NM_SETTING_VLAN_SETTING_NAME, + &s_con); + g_object_set(s_con, + NM_SETTING_CONNECTION_AUTOCONNECT, + FALSE, + NM_SETTING_CONNECTION_INTERFACE_NAME, + "enp31s0f1-142", + NM_SETTING_CONNECTION_MASTER, + "team142", + NM_SETTING_CONNECTION_SLAVE_TYPE, + "team", + NULL); + g_object_set(nm_connection_get_setting_vlan(connection_2), + NM_SETTING_VLAN_FLAGS, + 1, + NM_SETTING_VLAN_ID, + 142, + NM_SETTING_VLAN_PARENT, + "enp31s0f1", + NULL); + nm_connection_add_setting(connection_2, nm_setting_team_port_new()); + nmtst_connection_normalize(connection_2); + + nmtst_assert_connection_equals(connection_1, FALSE, connection_2, FALSE); + + _writer_new_connection_reread((nmtst_get_rand_uint32() % 2) ? connection_1 : connection_2, + TEST_SCRATCH_DIR, + &testfile, + TEST_IFCFG_DIR "/ifcfg-team-slave-enp31s0f1-142.cexpected", + &reread, + &reread_same); + _assert_reread_same((nmtst_get_rand_uint32() % 2) ? connection_1 : connection_2, reread); + g_assert(reread_same); + g_clear_object(&reread); + + reread = _connection_from_file(testfile, NULL, TYPE_VLAN, NULL); + nmtst_assert_connection_equals((nmtst_get_rand_uint32() % 2) ? connection_1 : connection_2, + FALSE, + reread, + FALSE); +} + +static void +test_read_proxy_basic(void) +{ + NMConnection * connection; + NMSettingProxy *s_proxy; + + /* Test basic proxy configuration */ + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-read-proxy-basic", + NULL, + TYPE_ETHERNET, + NULL); + + /* ===== Proxy setting ===== */ + s_proxy = nm_connection_get_setting_proxy(connection); + g_assert(s_proxy); + + /* Proxy method */ + g_assert_cmpint(nm_setting_proxy_get_method(s_proxy), ==, NM_SETTING_PROXY_METHOD_AUTO); + g_assert(nm_setting_proxy_get_browser_only(s_proxy)); + g_assert_cmpstr(nm_setting_proxy_get_pac_url(s_proxy), + ==, + "http://wpad.mycompany.com/wpad.dat"); + + g_object_unref(connection); +} + +static void +test_write_proxy_basic(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingWired * s_wired; + NMSettingProxy * s_proxy; + const char * expected_url = "https://wpad.neverland.org/wpad.dat"; + shvarFile * f; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write Proxy Basic", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Proxy setting */ + s_proxy = (NMSettingProxy *) nm_setting_proxy_new(); + nm_connection_add_setting(connection, NM_SETTING(s_proxy)); + g_object_set(s_proxy, NM_SETTING_PROXY_METHOD, NM_SETTING_PROXY_METHOD_AUTO, NULL); + g_object_set(s_proxy, NM_SETTING_PROXY_PAC_URL, expected_url, NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + nmtst_assert_connection_verifies(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-Test_Write_Proxy_Basic.cexpected", + &testfile); + + f = _svOpenFile(testfile); + _svGetValue_check(f, "TYPE", "Ethernet"); + _svGetValue_check(f, "PROXY_METHOD", "auto"); + _svGetValue_check(f, "PAC_URL", expected_url); + svCloseFile(f); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +/*****************************************************************************/ + +static const char * +_svUnescape(const char *str, char **to_free) +{ + const char * s; + gs_free char *str_free = NULL; + + g_assert(str); + g_assert(to_free); + + if (str[0] == '\0') { + /* avoid static string "" */ + str = (str_free = g_strdup(str)); + } + + s = svUnescape(str, to_free); + if (*to_free) { + g_assert(s == *to_free); + g_assert(s[0]); + } else { + g_assert(s == NULL || (!s[0] && (s < str || s > strchr(str, '\0'))) + || (s[0] && s >= str && s <= strchr(str, '\0'))); + } + return s; +} + +typedef struct { + const char *val; + const char *exp; + bool can_concat : 1; + bool needs_ascii_separator : 1; +} UnescapeTestData; + +static void +do_svUnescape_assert(const char *str, const char *expected) +{ + gs_free char *to_free = NULL; + const char * s; + + s = _svUnescape(str, &to_free); + g_assert_cmpstr(s, ==, expected); + + /* check we can make a round-trip */ + if (expected) { + gs_free char *s1_free = NULL; + gs_free char *s2_free = NULL; + const char * s1, *s2; + + s1 = svEscape(expected, &s1_free); + g_assert(s1); + + s2 = _svUnescape(s1, &s2_free); + g_assert(s2); + + g_assert_cmpstr(s2, ==, expected); + } +} + +static void +do_svUnescape_combine_ansi_append(GString * str_val, + GString * str_exp, + const UnescapeTestData *data, + gboolean honor_needs_ascii_separator) +{ + g_string_append(str_val, data->val); + g_string_append(str_exp, data->exp); + if (honor_needs_ascii_separator && data->needs_ascii_separator) { + /* the string has an open escape sequence. We must ensure that when + * combining it with another sequence, that they don't merge into + * something different. for example "\xa" + "a" must not result in + * "\xaa". Instead, we add a space in between to get "\xa a". */ + g_string_append(str_val, " "); + g_string_append(str_exp, " "); + } +} + +static void +do_svUnescape_combine_ansi(GString * str_val, + GString * str_exp, + const UnescapeTestData *data_ansi, + gsize data_len, + gssize idx) +{ + gsize i, j; + + g_string_set_size(str_val, 0); + g_string_set_size(str_exp, 0); + g_string_append(str_val, "$'"); + if (idx < 0) { + for (i = -idx; i > 0; i--) { + j = nmtst_get_rand_uint32() % data_len; + if (!data_ansi[j].can_concat) { + i++; + continue; + } + do_svUnescape_combine_ansi_append(str_val, str_exp, &data_ansi[j], i > 1); + } + } else { + g_assert_cmpint(idx, <, data_len); + do_svUnescape_combine_ansi_append(str_val, str_exp, &data_ansi[idx], FALSE); + } + g_string_append(str_val, "'"); +} + +static void +test_svUnescape(void) +{ +#define V0(v_value, v_expected) \ + { \ + .val = "" v_value "", .exp = v_expected, .can_concat = FALSE, \ + } +#define V1(v_value, v_expected) \ + { \ + .val = "" v_value "", .exp = v_expected, .can_concat = !!v_expected, \ + } +#define V2(v_value, v_expected) \ + { \ + .val = "" v_value "", .exp = v_expected, .can_concat = TRUE, \ + .needs_ascii_separator = TRUE, \ + } + const UnescapeTestData data_full[] = { + V1("", ""), + V0("'", NULL), + V1("'x'", "x"), + V1("' '", " "), + V1("'x'", "x"), + V0("\"", NULL), + V0("\\", NULL), + V0(" ", ""), + V0(" ", ""), + V0("a; #", "a"), + V0(" ; #", ""), + V0("; ", ""), + V0("; ;", NULL), + V0(" ; a #", NULL), + V0(" ; a;; #", NULL), + V0("a; ; #", NULL), + V0("\t # ", ""), + V0("\t #a", ""), + V0("\t #a\r", ""), + V0("\r", ""), + V1("\\\"", "\""), + V1("\\`", "`"), + V1("\\$", "$"), + V1("\\\\", "\\"), + V1("\\a", "a"), + V1("\\b", "b"), + V1("\\'", "'"), + V1("\\~", "~"), + V1("\\\t", "\t"), + V1("\"\\\"\"", "\""), + V1("\"\\`\"", "`"), + V1("\"\\$\"", "$"), + V1("\"\\\\\"", "\\"), + V1("\"\\a\"", "\\a"), + V1("\"\\b\"", "\\b"), + V1("\"\\\t\"", "\\\t"), + V0("ab\r", "ab"), + V0("a'b'\r ", "ab"), + V0("a'b' \r", "ab"), + V0("a#b", "a#b"), + V0("#b", "#b"), + V1("\'some string\'", "some string"), + V0("Bob outside LAN", NULL), + V1("x", "x"), + V1("'{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }'", + "{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }"), + V1("'{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}'", + "{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}"), + V1("x\"\"b", "xb"), + V1("x\"c\"b", "xcb"), + V1("\"c\"b", "cb"), + V1("\"c\"\\'b", "c'b"), + V1("$''", ""), + V1("$'\\n'", "\n"), + V0("$'\\'", NULL), + V1("$'\\x'", "\\x"), + V1("$'\\xa'", "\xa"), + V0("$'\\x0'", ""), + V1("$'\\x12'", "\x12"), + V1("$'\\x12A'", + "\x12" + "A"), + V1("$'\\x12t'", "\x12t"), + V1("\"aa\\\"\"", "aa\""), + V1("\"aa\\\"b\"c", "aa\"bc"), + V1("\"aa\\\"\"b", "aa\"b"), + + /* the following is not shell behavior, but kept for backward compatibility + * with old svEscape(). */ + V0("\"\\'\"", "'"), + V0("\"\\~\"", "~"), + V0("\"b\\~b\"", "b~b"), + V0("\"\\~\\~\"", "~~"), + V0("\"\\~\\'\"", "~'"), + + /* the following is shell-behavior, because it doesn't look like written + * by old svEscape(). */ + V1("\"\\~~\"", "\\~~"), + V1("\"\\a\\'\"", "\\a\\'"), + V1("x\"\\~\"", "x\\~"), + V1("\"\\'\"''", "\\'"), + V0("\"b\\~b\" ", "b\\~b"), + V1("\"b\\~b\"x", "b\\~bx"), + }; + const UnescapeTestData data_ansi[] = { + /* strings inside $''. They cannot be compared directly, but must + * be wrapped by do_svUnescape_combine_ansi(). */ + V1("", ""), + V1("a", "a"), + V1("b", "b"), + V1("x", "x"), + V1(" ", " "), + V1("\\a", "\a"), + V1("\\b", "\b"), + V1("\\e", "\e"), + V1("\\E", "\E"), + V1("\\f", "\f"), + V1("\\n", "\n"), + V1("\\r", "\r"), + V1("\\t", "\t"), + V1("\\v", "\v"), + V1("\\\\", "\\"), + V1("\\'", "'"), + V1("\\\"", "\""), + V1("\\?", "\?"), + V1("\\?", "?"), + V2("\\8", "\\8"), + V2("\\1", "\1"), + V1("\\1A", "\1A"), + V1("\\18", "\18"), + V2("\\01", "\1"), + V1("\\001", "\1"), + V0("\\008", ""), + V1("\\018", "\0018"), + V0("\\08", ""), + V1("\\18", "\0018"), + V1("\\x", "\\x"), + V2("\\xa", "\xa"), + V1("\\x12", "\x12"), + V1("\\x12A", + "\x12" + "A"), + V1("\\x12a", + "\x12" + "a"), + V1("\\x12t", "\x12t"), + V1("\\x1a", "\x1a"), + V1("\\x1A", "\x1A"), + V1("\\ut", "\\ut"), + V2("\\ua", "\xa"), + V1("\\uat", "\xat"), + V2("\\uab", "\xc2\xab"), + V1("\\uabt", "\xc2\xabt"), + V2("\\uabc", "\xe0\xaa\xbc"), + V1("\\uabct", "\xe0\xaa\xbct"), + V2("\\uabcd", "\xea\xaf\x8d"), + V1("\\uabcdt", "\xea\xaf\x8dt"), + V2("\\uabcde", + "\xea\xaf\x8d" + "e"), + V1("\\uabcdet", + "\xea\xaf\x8d" + "et"), + V1("\\Ut", "\\Ut"), + V2("\\Ua", "\xa"), + V1("\\Uat", "\xat"), + V2("\\Uab", "\xc2\xab"), + V1("\\Uabt", "\xc2\xabt"), + V2("\\Uabc", "\xe0\xaa\xbc"), + V1("\\Uabct", "\xe0\xaa\xbct"), + V2("\\Uabcd", "\xea\xaf\x8d"), + V1("\\Uabcdt", "\xea\xaf\x8dt"), + V2("\\Uabcde", "\xf2\xab\xb3\x9e"), + V1("\\Uabcdet", "\xf2\xab\xb3\x9et"), + V2("\\Uabcde0", "\xf8\xaa\xbc\xb7\xa0"), + V1("\\Uabcde0t", "\xf8\xaa\xbc\xb7\xa0t"), + V2("\\Uabcde01", "\xfc\x8a\xaf\x8d\xb8\x81"), + V1("\\Uabcde01t", "\xfc\x8a\xaf\x8d\xb8\x81t"), + V2("\\U0abcde01", "\xfc\x8a\xaf\x8d\xb8\x81"), + V1("\\U0abcde01t", "\xfc\x8a\xaf\x8d\xb8\x81t"), + V1("\\U00abcde01", + "\xf8\xaa\xbc\xb7\xa0" + "1"), + V1("\\U00abcde01t", + "\xf8\xaa\xbc\xb7\xa0" + "1t"), + + /* control-x sequence is not supported */ + V1("\\c", "\\c"), + V1("\\c1", "\\c1"), + }; +#undef V0 +#undef V1 +#undef V2 + gsize i; + nm_auto_free_gstring GString *str_val = g_string_new(NULL); + nm_auto_free_gstring GString *str_val2 = g_string_new(NULL); + nm_auto_free_gstring GString *str_exp = g_string_new(NULL); + nm_auto_free_gstring GString *str_exp2 = g_string_new(NULL); + + do_svUnescape_assert("' '' '", " "); + + for (i = 0; i < G_N_ELEMENTS(data_full); i++) + do_svUnescape_assert(data_full[i].val, data_full[i].exp); + + for (i = 0; i < G_N_ELEMENTS(data_ansi); i++) { + do_svUnescape_combine_ansi(str_val, str_exp, data_ansi, G_N_ELEMENTS(data_ansi), i); + do_svUnescape_assert(str_val->str, str_exp->str); + } + + /* different values can be just concatenated... */ + for (i = 0; i < 200; i++) { + gsize num_concat = (nmtst_get_rand_uint32() % 5) + 2; + + g_string_set_size(str_val, 0); + g_string_set_size(str_exp, 0); + + while (num_concat > 0) { + gsize idx; + + if ((nmtst_get_rand_uint32() % 3 == 0)) { + do_svUnescape_combine_ansi(str_val2, + str_exp2, + data_ansi, + G_N_ELEMENTS(data_ansi), + -((int) ((nmtst_get_rand_uint32() % 5) + 1))); + continue; + } + + idx = nmtst_get_rand_uint32() % G_N_ELEMENTS(data_full); + if (!data_full[idx].can_concat) + continue; + g_string_append(str_val, data_full[idx].val); + g_string_append(str_exp, data_full[idx].exp); + num_concat--; + } + + switch (nmtst_get_rand_uint32() % 3) { + case 0: + g_string_append(str_val, " "); + break; + case 1: + g_string_append(str_val, " "); + break; + } + switch (nmtst_get_rand_uint32() % 3) { + case 0: + g_string_append(str_val, " #"); + break; + case 1: + g_string_append(str_val, " #foo"); + break; + } + do_svUnescape_assert(str_val->str, str_exp->str); + } +} + +/*****************************************************************************/ + +static void +test_write_unknown(gconstpointer test_data) +{ + nmtst_auto_unlinkfile char *filename_tmp_1 = g_strdup(TEST_SCRATCH_DIR_TMP "/tmp-1"); + const char * testfile = test_data; + gs_free char * testfile_expected = g_strconcat(testfile, ".expected", NULL); + shvarFile * sv; + gs_free_error GError *error = NULL; + gboolean success; + gs_free char * file_contents_out = NULL; + gs_free char * file_contents_exp = NULL; + + sv = _svOpenFile(testfile); + + _nmtst_svFileSetName(sv, filename_tmp_1); + _nmtst_svFileSetModified(sv); + + if (g_str_has_suffix(testfile, "ifcfg-test-write-unknown-4")) { + _svGetValue_check(sv, "IPADDR", "l4x"); + _svGetValue_check(sv, "IPADDR2", ""); + _svGetValue_check(sv, "IPADDR3", "name3-value"); + + svSetValue(sv, "IPADDR", "set-by-test1"); + svSetValue(sv, "IPADDR2", NULL); + svSetValue(sv, "IPADDR2", "set-by-test2"); + svSetValue(sv, "IPADDR3", "set-by-test3"); + + _svGetValue_check(sv, "METRIC", NULL); + _svGetValue_check(sv, "METRIC1", ""); + _svGetValue_check(sv, "METRIC2", ""); + _svGetValue_check(sv, "METRIC3", "x"); + + _svGetValue_check(sv, "IPADDR", "set-by-test1"); + _svGetValue_check(sv, "IPADDR2", "set-by-test2"); + _svGetValue_check(sv, "IPADDR3", "set-by-test3"); + } + + success = svWriteFile(sv, 0644, &error); + nmtst_assert_success(success, error); + + file_contents_out = nmtst_file_get_contents(filename_tmp_1); + file_contents_exp = nmtst_file_get_contents(testfile_expected); + + g_assert_cmpstr(file_contents_out, ==, file_contents_exp); + + svCloseFile(sv); +} + +/*****************************************************************************/ + +static void +test_read_vlan_trailing_spaces(void) +{ + const char * testfile = TEST_IFCFG_DIR "/ifcfg-test-vlan-trailing-spaces"; + NMConnection * connection; + gboolean success; + GError * error = NULL; + NMSettingVlan *s_vlan; + char * contents = NULL; + + /* Ensure there is whitespace at the end of the VLAN interface name, + * to prevent the whitespace getting stripped off and committed mistakenly + * by something in the future. + */ + success = g_file_get_contents(testfile, &contents, NULL, &error); + g_assert_no_error(error); + g_assert(success); + g_assert(contents && contents[0]); + g_assert(strstr(contents, "DEVICE=\"vlan201\" \n")); + g_free(contents); + + connection = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + s_vlan = nm_connection_get_setting_vlan(connection); + g_assert(s_vlan); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "vlan201"); + g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "enccw0.0.fb00"); + g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 201); + g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), ==, NM_VLAN_FLAG_REORDER_HEADERS); + + g_object_unref(connection); +} + +/*****************************************************************************/ + +static void +test_sit_read_ignore(void) +{ + gs_free_error GError *error = NULL; + + _connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-sit-ignore", + NULL, + TYPE_ETHERNET, + &error); + nmtst_assert_error(error, 0, 0, "*Ignoring unsupported connection due to IPV6TUNNELIPV4*"); +} + +/*****************************************************************************/ + +static void +do_test_utils_name(const char *desc, const char *path, gboolean only_ifcfg, const char *expected) +{ + const char *result; + + result = utils_get_ifcfg_name(path, only_ifcfg); + g_assert_cmpstr(result, ==, expected); +} + +static void +test_utils_name(void) +{ + do_test_utils_name("get-ifcfg-name-bad", "/foo/bar/adfasdfadf", FALSE, NULL); + do_test_utils_name("get-ifcfg-name-good", "/foo/bar/ifcfg-FooBar", FALSE, "FooBar"); + do_test_utils_name("get-ifcfg-name-keys", "/foo/bar/keys-BlahLbah", FALSE, "BlahLbah"); + do_test_utils_name("get-ifcfg-name-route", "/foo/bar/route-Lalalala", FALSE, "Lalalala"); + do_test_utils_name("get-ifcfg-name-only-ifcfg-route", "/foo/bar/route-Lalalala", TRUE, NULL); + do_test_utils_name("get-ifcfg-name-only-ifcfg-keys", "/foo/bar/keys-Lalalala", TRUE, NULL); + do_test_utils_name("get-ifcfg-name-no-path-ifcfg", "ifcfg-Lalalala", FALSE, "Lalalala"); + do_test_utils_name("get-ifcfg-name-no-path-keys", "keys-Lalalala", FALSE, "Lalalala"); + do_test_utils_name("get-ifcfg-name-no-path-route", "route-Lalalala", FALSE, "Lalalala"); + + do_test_utils_name("get-ifcfg-name-bad2-ifcfg", "/foo/bar/asdfasifcfg-Foobar", FALSE, NULL); + do_test_utils_name("get-ifcfg-name-bad2-keys", "/foo/bar/asdfaskeys-Foobar", FALSE, NULL); + do_test_utils_name("get-ifcfg-name-bad2-route", "/foo/bar/asdfasroute-Foobar", FALSE, NULL); +} + +/*****************************************************************************/ + +static void +do_test_utils_path_ifcfg(const char *desc, const char *path, const char *expected) +{ + char *result; + + result = utils_get_ifcfg_path(path); + g_assert_cmpstr(result, ==, expected); + g_free(result); +} + +static void +do_test_utils_path_keys(const char *desc, const char *path, const char *expected) +{ + char *result; + + result = utils_get_keys_path(path); + g_assert_cmpstr(result, ==, expected); + g_free(result); +} + +static void +do_test_utils_path_route(const char *desc, const char *path, const char *expected) +{ + char *result; + + result = utils_get_route_path(path); + g_assert_cmpstr(result, ==, expected); + g_free(result); +} + +static void +test_utils_path(void) +{ + do_test_utils_path_ifcfg("ifcfg-path-bad", "/foo/bar/adfasdfasdf", NULL); + do_test_utils_path_ifcfg("ifcfg-path-from-keys-no-path", "keys-BlahBlah", "ifcfg-BlahBlah"); + do_test_utils_path_ifcfg("ifcfg-path-from-keys", + "/foo/bar/keys-BlahBlah", + "/foo/bar/ifcfg-BlahBlah"); + do_test_utils_path_ifcfg("ifcfg-path-from-route", + "/foo/bar/route-BlahBlah", + "/foo/bar/ifcfg-BlahBlah"); + + do_test_utils_path_keys("keys-path-bad", "/foo/bar/asdfasdfasdfasdf", NULL); + do_test_utils_path_keys("keys-path-from-ifcfg-no-path", "ifcfg-FooBar", "keys-FooBar"); + do_test_utils_path_keys("keys-path-from-ifcfg", + "/foo/bar/ifcfg-FooBar", + "/foo/bar/keys-FooBar"); + do_test_utils_path_keys("keys-path-from-route", + "/foo/bar/route-FooBar", + "/foo/bar/keys-FooBar"); + + do_test_utils_path_route("route-path-bad", "/foo/bar/asdfasdfasdfasdf", NULL); + do_test_utils_path_route("route-path-from-ifcfg-no-path", "ifcfg-FooBar", "route-FooBar"); + do_test_utils_path_route("route-path-from-ifcfg", + "/foo/bar/ifcfg-FooBar", + "/foo/bar/route-FooBar"); + do_test_utils_path_route("route-path-from-keys", + "/foo/bar/keys-FooBar", + "/foo/bar/route-FooBar"); +} + +/*****************************************************************************/ + +static void +do_test_utils_ignored(const char *desc, const char *path, gboolean expected_ignored) +{ + gboolean result; + + result = utils_should_ignore_file(path, FALSE); + g_assert(result == expected_ignored); +} + +static void +test_utils_ignore(void) +{ + do_test_utils_ignored("ignored-ifcfg", "ifcfg-FooBar", FALSE); + do_test_utils_ignored("ignored-keys", "keys-FooBar", FALSE); + do_test_utils_ignored("ignored-route", "route-FooBar", FALSE); + do_test_utils_ignored("ignored-bak", "ifcfg-FooBar" BAK_TAG, TRUE); + do_test_utils_ignored("ignored-tilde", "ifcfg-FooBar" TILDE_TAG, TRUE); + do_test_utils_ignored("ignored-orig", "ifcfg-FooBar" ORIG_TAG, TRUE); + do_test_utils_ignored("ignored-rej", "ifcfg-FooBar" REJ_TAG, TRUE); + do_test_utils_ignored("ignored-rpmnew", "ifcfg-FooBar" RPMNEW_TAG, TRUE); + do_test_utils_ignored("ignored-augnew", "ifcfg-FooBar" AUGNEW_TAG, TRUE); + do_test_utils_ignored("ignored-augtmp", "ifcfg-FooBar" AUGTMP_TAG, TRUE); +} + +/*****************************************************************************/ + +static void +test_sriov_read(void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingSriov * s_sriov; + NMSriovVF * vf; + GVariant * variant; + GError * error = NULL; + char * str; + + connection = + _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-sriov", NULL, TYPE_ETHERNET, NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "eth0"); + + s_sriov = NM_SETTING_SRIOV(nm_connection_get_setting(connection, NM_TYPE_SETTING_SRIOV)); + g_assert(s_sriov); + + g_assert_cmpint(nm_setting_sriov_get_total_vfs(s_sriov), ==, 16); + g_assert_cmpint(nm_setting_sriov_get_num_vfs(s_sriov), ==, 3); + g_assert_cmpint(nm_setting_sriov_get_autoprobe_drivers(s_sriov), ==, NM_TERNARY_FALSE); + + /* VF 3 */ + vf = nm_setting_sriov_get_vf(s_sriov, 0); + g_assert(vf); + g_assert_cmpint(nm_sriov_vf_get_index(vf), ==, 3); + + variant = nm_sriov_vf_get_attribute(vf, NM_SRIOV_VF_ATTRIBUTE_MAC); + g_assert(variant); + g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr(g_variant_get_string(variant, NULL), ==, "55:44:33:22:11:00"); + + variant = nm_sriov_vf_get_attribute(vf, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK); + g_assert(variant); + g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)); + g_assert_cmpint(g_variant_get_boolean(variant), ==, TRUE); + + /* VF 12 */ + vf = nm_setting_sriov_get_vf(s_sriov, 1); + str = nm_utils_sriov_vf_to_str(vf, FALSE, &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "12 min-tx-rate=100 trust=false vlans=1.200.ad"); + g_free(str); + + /* VF 15 */ + vf = nm_setting_sriov_get_vf(s_sriov, 2); + str = nm_utils_sriov_vf_to_str(vf, FALSE, &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "15 mac=01:23:45:67:89:ab max-tx-rate=200 vlans=2"); + g_free(str); +} + +static void +test_sriov_write(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + NMSettingWired * s_wired; + NMSettingSriov * s_sriov; + NMSriovVF * vf; + gs_unref_ptrarray GPtrArray *vfs = NULL; + NMIPAddress * addr; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write SR-IOV config", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_INTERFACE_NAME, + "eth0", + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + /* SRIOV setting */ + s_sriov = (NMSettingSriov *) nm_setting_sriov_new(); + nm_connection_add_setting(connection, NM_SETTING(s_sriov)); + + vfs = g_ptr_array_new_with_free_func((GDestroyNotify) nm_sriov_vf_unref); + + vf = nm_utils_sriov_vf_from_str("2 mac=55:55:55:55:55:55 vlans=3.10.ad;10", &error); + nmtst_assert_success(vf, error); + g_ptr_array_add(vfs, vf); + + vf = nm_utils_sriov_vf_from_str("19 spoof-check=true", &error); + nmtst_assert_success(vf, error); + g_ptr_array_add(vfs, vf); + + g_object_set(s_sriov, + NM_SETTING_SRIOV_TOTAL_VFS, + 64, + NM_SETTING_SRIOV_VFS, + vfs, + NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, + NM_TERNARY_TRUE, + NULL); + + nm_connection_add_setting(connection, nm_setting_proxy_new()); + + nmtst_assert_connection_verifies_without_normalization(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-test-sriov-write.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +/*****************************************************************************/ + +static void +test_tc_read(void) +{ + NMConnection * connection; + NMSettingTCConfig *s_tc; + NMTCQdisc * qdisc; + NMTCTfilter * filter; + char * str; + + connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-tc", NULL, TYPE_ETHERNET, NULL); + + g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "eth0"); + + s_tc = nm_connection_get_setting_tc_config(connection); + g_assert(s_tc); + + g_assert_cmpint(nm_setting_tc_config_get_num_qdiscs(s_tc), ==, 1); + qdisc = nm_setting_tc_config_get_qdisc(s_tc, 0); + g_assert(qdisc); + g_assert_cmpint(nm_tc_qdisc_get_parent(qdisc), ==, TC_H_ROOT); + g_assert_cmpint(nm_tc_qdisc_get_handle(qdisc), ==, TC_H_UNSPEC); + g_assert_cmpstr(nm_tc_qdisc_get_kind(qdisc), ==, "fq_codel"); + + g_assert_cmpint(nm_setting_tc_config_get_num_tfilters(s_tc), ==, 1); + filter = nm_setting_tc_config_get_tfilter(s_tc, 0); + g_assert(filter); + str = nm_utils_tc_tfilter_to_str(filter, NULL); + g_assert_cmpstr(str, ==, "parent 1234: matchall action simple sdata Hello"); + g_free(str); + + g_object_unref(connection); +} + +static void +test_tc_write(void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection * s_con; + NMSettingIPConfig * s_ip4; + NMSettingIPConfig * s_ip6; + NMSettingWired * s_wired; + NMSettingTCConfig * s_tc; + NMTCQdisc * qdisc; + NMTCTfilter * tfilter; + NMIPAddress * addr; + GError * error = NULL; + + connection = nm_simple_connection_new(); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + g_object_set(s_con, + NM_SETTING_CONNECTION_ID, + "Test Write TC config", + NM_SETTING_CONNECTION_UUID, + nm_utils_uuid_generate_a(), + NM_SETTING_CONNECTION_AUTOCONNECT, + TRUE, + NM_SETTING_CONNECTION_INTERFACE_NAME, + "eth0", + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip4)); + + g_object_set(s_ip4, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.1", + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + addr = nm_ip_address_new(AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, addr); + nm_ip_address_unref(addr); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_ip6)); + + g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + /* TC setting */ + s_tc = (NMSettingTCConfig *) nm_setting_tc_config_new(); + nm_connection_add_setting(connection, NM_SETTING(s_tc)); + + qdisc = nm_tc_qdisc_new("pfifo_fast", TC_H_MAKE(0x2468 << 16, 0x2), &error); + g_assert_no_error(error); + nm_setting_tc_config_add_qdisc(s_tc, qdisc); + nm_tc_qdisc_unref(qdisc); + + tfilter = + nm_utils_tc_tfilter_from_str("parent 1234: matchall action simple sdata Hello", &error); + g_assert_no_error(error); + nm_setting_tc_config_add_tfilter(s_tc, tfilter); + nm_tc_tfilter_unref(tfilter); + + nm_connection_add_setting(connection, nm_setting_proxy_new()); + + nmtst_assert_connection_verifies_without_normalization(connection); + + _writer_new_connec_exp(connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-test-tc-write.cexpected", + &testfile); + + reread = _connection_from_file(testfile, NULL, TYPE_BOND, NULL); + + nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); +} + +/*****************************************************************************/ + +static void +test_well_known_keys(void) +{ + gsize i; + + for (i = 0; i < G_N_ELEMENTS(nms_ifcfg_well_known_keys); i++) { + const NMSIfcfgKeyTypeInfo *ti = &nms_ifcfg_well_known_keys[i]; + + g_assert(ti->key_name); + g_assert(ti->key_name[0]); + g_assert(NM_FLAGS_HAS(ti->key_flags, NMS_IFCFG_KEY_TYPE_WELL_KNOWN)); + g_assert( + nm_utils_is_power_of_two(ti->key_flags + & (NMS_IFCFG_KEY_TYPE_IS_PLAIN | NMS_IFCFG_KEY_TYPE_IS_NUMBERED + | NMS_IFCFG_KEY_TYPE_IS_PREFIX))); + } + + for (i = 1; i < G_N_ELEMENTS(nms_ifcfg_well_known_keys); i++) { + const NMSIfcfgKeyTypeInfo *ti_prev = &nms_ifcfg_well_known_keys[i - 1]; + const NMSIfcfgKeyTypeInfo *ti = &nms_ifcfg_well_known_keys[i]; + + g_assert_cmpstr(ti_prev->key_name, <, ti->key_name); + } + + for (i = 0; i < G_N_ELEMENTS(nms_ifcfg_well_known_keys); i++) { + const NMSIfcfgKeyTypeInfo *ti = &nms_ifcfg_well_known_keys[i]; + gs_free char * key_name = NULL; + gssize idx; + + g_assert(ti == nms_ifcfg_well_known_key_find_info(ti->key_name, &idx)); + g_assert_cmpint(i, ==, idx); + + key_name = g_strdup(ti->key_name); + g_assert(ti == nms_ifcfg_well_known_key_find_info(key_name, &idx)); + g_assert_cmpint(i, ==, idx); + } + +#define _test_well_known(key, expected) \ + G_STMT_START \ + { \ + const NMSIfcfgKeyTypeInfo *_ti; \ + const char * _expected = (expected); \ + \ + _ti = nms_ifcfg_rh_utils_is_well_known_key("" key ""); \ + if (!_expected) { \ + g_assert(!_ti); \ + } else { \ + g_assert(_ti); \ + g_assert_cmpstr(_ti->key_name, ==, _expected); \ + } \ + } \ + G_STMT_END + +#define _test_well_known_plain(key) _test_well_known("" key "", "" key "") + + _test_well_known_plain("ONBOOT"); + _test_well_known("NM_USER_", NULL); + _test_well_known("NM_USER_x", "NM_USER_"); + _test_well_known("IPADDR", "IPADDR"); + _test_well_known("IPADDR1", "IPADDR"); + _test_well_known("IPADDRx", NULL); +} + +/*****************************************************************************/ + +static void +_do_utils_has_route_file_new_syntax_size(gboolean has_new_syntax, + const char *content, + gssize content_len) +{ + nmtst_auto_unlinkfile char *testfile = + g_strdup(TEST_SCRATCH_DIR "/utils-has-route-file-new-syntax-test.txt"); + gboolean val; + + nmtst_file_set_contents_size(testfile, content, content_len); + + val = utils_has_route_file_new_syntax(testfile); + + g_assert_cmpint(val, ==, has_new_syntax); +} +#define _do_utils_has_route_file_new_syntax(has_new_syntax, content) \ + _do_utils_has_route_file_new_syntax_size(has_new_syntax, (content), NM_STRLEN(content)) + +static void +test_utils_has_route_file_new_syntax(void) +{ + _do_utils_has_route_file_new_syntax(TRUE, ""); + _do_utils_has_route_file_new_syntax(FALSE, "\0"); + _do_utils_has_route_file_new_syntax(FALSE, "\n"); + _do_utils_has_route_file_new_syntax(FALSE, "ADDRESS=bogus"); + _do_utils_has_route_file_new_syntax(FALSE, "ADDRESS=bogus\0"); + _do_utils_has_route_file_new_syntax(TRUE, "ADDRESS1=b\0ogus\0"); + _do_utils_has_route_file_new_syntax(TRUE, "ADDRESS1=bogus\0"); + _do_utils_has_route_file_new_syntax(TRUE, "\n\n\tADDRESS1=bogus\0"); + _do_utils_has_route_file_new_syntax(FALSE, "\n\n\tADDRESS=bogus\n"); + _do_utils_has_route_file_new_syntax(TRUE, "\n\n\tADDRESS=bogus\n ADDRESS000=\n"); + _do_utils_has_route_file_new_syntax(FALSE, "\n\n\tROUTE1=bogus\n ADDRES=\n"); + _do_utils_has_route_file_new_syntax(FALSE, "\n\n\tADDRESS=bogus\n ADDRESS\000000=\n"); +} + +/*****************************************************************************/ + +static void +test_ethtool_names(void) +{ + static const struct { + NMEthtoolID ethtool_id; + const char *kernel_name; + } kernel_names[] = { + {NM_ETHTOOL_ID_FEATURE_GRO, "rx-gro"}, + {NM_ETHTOOL_ID_FEATURE_GSO, "tx-generic-segmentation"}, + {NM_ETHTOOL_ID_FEATURE_LRO, "rx-lro"}, + {NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter"}, + {NM_ETHTOOL_ID_FEATURE_RX, "rx-checksum"}, + {NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing"}, + {NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse"}, + {NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert"}, + }; + const struct { + guint nm_ethtool_id_first; + guint nm_ethtool_id_last; + } s_idxs[] = { + {_NM_ETHTOOL_ID_FEATURE_FIRST, _NM_ETHTOOL_ID_FEATURE_LAST}, + {_NM_ETHTOOL_ID_COALESCE_FIRST, _NM_ETHTOOL_ID_COALESCE_LAST}, + {_NM_ETHTOOL_ID_RING_FIRST, _NM_ETHTOOL_ID_RING_LAST}, + }; + const NMEthtoolData *data; + NMEthtoolID id; + guint i, k; + + for (k = 0; k < sizeof(s_idxs) / sizeof(*s_idxs); ++k) { + for (id = s_idxs[k].nm_ethtool_id_first; id <= s_idxs[k].nm_ethtool_id_last; id++) { + const char *ifcfg_rh_name; + + g_assert(id >= 0); + g_assert(id < G_N_ELEMENTS(_nm_ethtool_ifcfg_names)); + ifcfg_rh_name = _nm_ethtool_ifcfg_names[id]; + g_assert(ifcfg_rh_name && ifcfg_rh_name[0]); + + for (i = s_idxs[k].nm_ethtool_id_first; i < s_idxs[k].nm_ethtool_id_last; i++) { + if (i != id) + g_assert_cmpstr(ifcfg_rh_name, !=, _nm_ethtool_ifcfg_names[i]); + } + + g_assert_cmpstr(nms_ifcfg_rh_utils_get_ethtool_name(id), ==, ifcfg_rh_name); + + data = nms_ifcfg_rh_utils_get_ethtool_by_name(ifcfg_rh_name, nm_ethtool_id_to_type(id)); + + g_assert(data); + g_assert(data->id == id); + } + } + + for (i = 0; i < G_N_ELEMENTS(kernel_names); i++) { + const char *name = kernel_names[i].kernel_name; + + id = kernel_names[i].ethtool_id; + + data = nms_ifcfg_rh_utils_get_ethtool_by_name(name, nm_ethtool_id_to_type(id)); + + g_assert(data); + g_assert(data->id == id); + g_assert_cmpstr(nms_ifcfg_rh_utils_get_ethtool_name(id), !=, name); + } +} + +/*****************************************************************************/ + +#define TPATH "/settings/plugins/ifcfg-rh/" + +#define TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED \ + TEST_IFCFG_DIR "/ifcfg-test-wifi-open-ssid-long-quoted" +#define TEST_IFCFG_WIFI_OPEN_SSID_LONG_HEX TEST_IFCFG_DIR "/ifcfg-test-wifi-open-ssid-long-hex" + +#define DEFAULT_HEX_PSK "7d308b11df1b4243b0f78e5f3fc68cdbb9a264ed0edf4c188edf329ff5b467f0" + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + int errsv; + + nmtst_init_assert_logging(&argc, &argv, "INFO", "DEFAULT"); + + if (g_mkdir_with_parents(TEST_SCRATCH_DIR_TMP, 0755) != 0) { + errsv = errno; + g_error("failure to create test directory \"%s\": %s", + TEST_SCRATCH_DIR_TMP, + nm_strerror_native(errsv)); + } + + g_test_add_func(TPATH "svUnescape", test_svUnescape); + + g_test_add_data_func(TPATH "write-unknown/1", + TEST_IFCFG_DIR "/ifcfg-test-write-unknown-1", + test_write_unknown); + g_test_add_data_func(TPATH "write-unknown/2", + TEST_IFCFG_DIR "/ifcfg-test-write-unknown-2", + test_write_unknown); + g_test_add_data_func(TPATH "write-unknown/3", + TEST_IFCFG_DIR "/ifcfg-test-write-unknown-3", + test_write_unknown); + g_test_add_data_func(TPATH "write-unknown/4", + TEST_IFCFG_DIR "/ifcfg-test-write-unknown-4", + test_write_unknown); + + g_test_add_func(TPATH "vlan-trailing-spaces", test_read_vlan_trailing_spaces); + + g_test_add_func(TPATH "unmanaged", test_read_unmanaged); + g_test_add_func(TPATH "unmanaged-unrecognized", test_read_unmanaged_unrecognized); + g_test_add_func(TPATH "unrecognized", test_read_unrecognized); + g_test_add_func(TPATH "basic", test_read_basic); + g_test_add_func(TPATH "miscellaneous-variables", test_read_miscellaneous_variables); + g_test_add_func(TPATH "variables-corner-cases", test_read_variables_corner_cases); + g_test_add_data_func(TPATH "no-prefix/8", + GUINT_TO_POINTER(8), + test_read_wired_static_no_prefix); + g_test_add_data_func(TPATH "no-prefix/16", + GUINT_TO_POINTER(16), + test_read_wired_static_no_prefix); + g_test_add_data_func(TPATH "no-prefix/24", + GUINT_TO_POINTER(24), + test_read_wired_static_no_prefix); + g_test_add_data_func(TPATH "static-ip6-only-gw/_NULL_", + NULL, + test_write_wired_static_ip6_only_gw); + g_test_add_data_func(TPATH "static-ip6-only-gw/::", "::", test_write_wired_static_ip6_only_gw); + g_test_add_data_func(TPATH "static-ip6-only-gw/2001:db8:8:4::2", + "2001:db8:8:4::2", + test_write_wired_static_ip6_only_gw); + g_test_add_data_func(TPATH "static-ip6-only-gw/::ffff:255.255.255.255", + "::ffff:255.255.255.255", + test_write_wired_static_ip6_only_gw); + g_test_add_func(TPATH "ip6/disabled", test_write_ip6_disabled); + g_test_add_func(TPATH "read-dns-options", test_read_dns_options); + g_test_add_func(TPATH "clear-master", test_clear_master); + + nmtst_add_test_func(TPATH "read-static", + test_read_wired_static, + TEST_IFCFG_DIR "/ifcfg-test-wired-static", + "System test-wired-static", + GINT_TO_POINTER(TRUE)); + nmtst_add_test_func(TPATH "read-static-bootproto", + test_read_wired_static, + TEST_IFCFG_DIR "/ifcfg-test-wired-static-bootproto", + "System test-wired-static-bootproto", + GINT_TO_POINTER(FALSE)); + + g_test_add_func(TPATH "read-netmask-1", test_read_netmask_1); + + g_test_add_func(TPATH "read-dhcp", test_read_wired_dhcp); + g_test_add_func(TPATH "read-dhcp-plus-ip", test_read_wired_dhcp_plus_ip); + g_test_add_func(TPATH "read-shared-plus-ip", test_read_wired_shared_plus_ip); + g_test_add_func(TPATH "read-dhcp-send-hostname", test_read_write_wired_dhcp_send_hostname); + g_test_add_func(TPATH "read-dhcpv6-hostname-fallback", + test_read_wired_dhcpv6_hostname_fallback); + g_test_add_func(TPATH "read-global-gateway", test_read_wired_global_gateway); + g_test_add_func(TPATH "read-global-gateway-ignore", test_read_wired_global_gateway_ignore); + g_test_add_func(TPATH "read-obsolete-gateway-n", test_read_wired_obsolete_gateway_n); + g_test_add_func(TPATH "read-never-default", test_read_wired_never_default); + g_test_add_func(TPATH "read-defroute-no", test_read_wired_defroute_no); + g_test_add_func(TPATH "read-defroute-no-gatewaydev-yes", + test_read_wired_defroute_no_gatewaydev_yes); + g_test_add_func(TPATH "routes/read-static", test_read_wired_static_routes); + g_test_add_func(TPATH "routes/read-static-legacy", test_read_wired_static_routes_legacy); + + nmtst_add_test_func(TPATH "wired/read/manual/1", + test_read_wired_ipv4_manual, + TEST_IFCFG_DIR "/ifcfg-test-wired-ipv4-manual-1", + "System test-wired-ipv4-manual-1"); + nmtst_add_test_func(TPATH "wired/read/manual/2", + test_read_wired_ipv4_manual, + TEST_IFCFG_DIR "/ifcfg-test-wired-ipv4-manual-2", + "System test-wired-ipv4-manual-2"); + nmtst_add_test_func(TPATH "wired/read/manual/3", + test_read_wired_ipv4_manual, + TEST_IFCFG_DIR "/ifcfg-test-wired-ipv4-manual-3", + "System test-wired-ipv4-manual-3"); + nmtst_add_test_func(TPATH "wired/read/manual/4", + test_read_wired_ipv4_manual, + TEST_IFCFG_DIR "/ifcfg-test-wired-ipv4-manual-4", + "System test-wired-ipv4-manual-4"); + + g_test_add_func(TPATH "user/1", test_user_1); + + g_test_add_func(TPATH "wired/ipv6-manual", test_read_wired_ipv6_manual); + + nmtst_add_test_func(TPATH "wired-ipv6-only/0", + test_read_wired_ipv6_only, + TEST_IFCFG_DIR "/ifcfg-test-wired-ipv6-only", + "System test-wired-ipv6-only"); + nmtst_add_test_func(TPATH "wired-ipv6-only/1", + test_read_wired_ipv6_only, + TEST_IFCFG_DIR "/ifcfg-test-wired-ipv6-only-1", + "System test-wired-ipv6-only-1"); + + g_test_add_func(TPATH "wired/dhcpv6-only", test_read_wired_dhcp6_only); + g_test_add_func(TPATH "wired/autoip", test_read_wired_autoip); + g_test_add_func(TPATH "wired/onboot/no", test_read_onboot_no); + g_test_add_func(TPATH "wired/no-ip", test_read_noip); + g_test_add_func(TPATH "802-1x/peap/mschapv2", test_read_wired_8021x_peap_mschapv2); + + nmtst_add_test_func(TPATH "test-wired-8021x-tls/agent", + test_read_wired_8021x_tls_secret_flags, + TEST_IFCFG_DIR "/ifcfg-test-wired-8021x-tls-agent", + GINT_TO_POINTER(NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + nmtst_add_test_func( + TPATH "test-wired-8021x-tls/always", + test_read_wired_8021x_tls_secret_flags, + TEST_IFCFG_DIR "/ifcfg-test-wired-8021x-tls-always", + GINT_TO_POINTER(NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED)); + + g_test_add_func(TPATH "802-1x/subj-matches", test_read_write_802_1X_subj_matches); + g_test_add_func(TPATH "802-1x/ttls-eapgtc", test_read_802_1x_ttls_eapgtc); + g_test_add_func(TPATH "802-1x/password_raw", test_read_write_802_1x_password_raw); + g_test_add_func(TPATH "802-1x/tls-p12-no-client-cert", test_read_802_1x_tls_p12_no_client_cert); + + g_test_add_data_func(TPATH "wired/read/aliases/good/0", + GINT_TO_POINTER(0), + test_read_wired_aliases_good); + g_test_add_data_func(TPATH "wired/read/aliases/good/3", + GINT_TO_POINTER(3), + test_read_wired_aliases_good); + g_test_add_func(TPATH "wired/read/aliases/bad1", test_read_wired_aliases_bad_1); + g_test_add_func(TPATH "wired/read/aliases/bad2", test_read_wired_aliases_bad_2); + g_test_add_func(TPATH "wifi/read/open", test_read_wifi_open); + g_test_add_func(TPATH "wifi/read/open/auto", test_read_wifi_open_auto); + g_test_add_func(TPATH "wifi/read/open/hex-ssid", test_read_wifi_open_ssid_hex); + g_test_add_func(TPATH "wifi/read/open-ssid/bad-hex", test_read_wifi_open_ssid_hex_bad); + g_test_add_data_func(TPATH "wifi/read/open-ssid/long-hex", + TEST_IFCFG_WIFI_OPEN_SSID_LONG_HEX, + test_read_wifi_open_ssid_bad); + g_test_add_data_func(TPATH "wifi/read/open-ssid/long-quoted", + TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED, + test_read_wifi_open_ssid_bad); + g_test_add_func(TPATH "wifi/read/open/quoted-ssid", test_read_wifi_open_ssid_quoted); + g_test_add_func(TPATH "wifi/read/wep", test_read_wifi_wep); + g_test_add_func(TPATH "wifi/read/wep/adhoc", test_read_wifi_wep_adhoc); + g_test_add_func(TPATH "wifi/read/wep/passphrase", test_read_wifi_wep_passphrase); + g_test_add_func(TPATH "wifi/read/wep/40-ascii", test_read_wifi_wep_40_ascii); + g_test_add_func(TPATH "wifi/read/wep/104-ascii", test_read_wifi_wep_104_ascii); + g_test_add_func(TPATH "wifi/read/leap", test_read_wifi_leap); + + nmtst_add_test_func(TPATH "wifi-leap-secret-flags/agent", + test_read_wifi_leap_secret_flags, + TEST_IFCFG_DIR "/ifcfg-test-wifi-leap-agent", + GINT_TO_POINTER(NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + nmtst_add_test_func( + TPATH "wifi-leap-secret-flags/ask", + test_read_wifi_leap_secret_flags, + TEST_IFCFG_DIR "/ifcfg-test-wifi-leap-always-ask", + GINT_TO_POINTER(NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED)); + + g_test_add_func(TPATH "wifi/read/wpa-psk", test_read_wifi_wpa_psk); + g_test_add_func(TPATH "wifi/read/wpa-psk/2", test_read_wifi_wpa_psk_2); + g_test_add_func(TPATH "wifi/read/wpa-psk/unquoted", test_read_wifi_wpa_psk_unquoted); + g_test_add_func(TPATH "wifi/read/wpa-psk/unquoted2", test_read_wifi_wpa_psk_unquoted2); + g_test_add_func(TPATH "wifi/read/wpa-psk/adhoc", test_read_wifi_wpa_psk_adhoc); + g_test_add_func(TPATH "wifi/read/wpa-psk/hex", test_read_wifi_wpa_psk_hex); + g_test_add_func(TPATH "wifi/read/sae", test_read_wifi_sae); + g_test_add_func(TPATH "wifi/read/owe", test_read_wifi_owe); + g_test_add_func(TPATH "wifi/read/dynamic-wep/leap", test_read_wifi_dynamic_wep_leap); + g_test_add_func(TPATH "wifi/read/wpa/eap/tls", test_read_wifi_wpa_eap_tls); + g_test_add_func(TPATH "wifi/read/wpa/eap/ttls/tls", test_read_wifi_wpa_eap_ttls_tls); + g_test_add_func(TPATH "wifi/read/wpa/eap-suite-b-192/tls", + test_read_wifi_wpa_eap_suite_b_192_tls); + g_test_add_func(TPATH "wifi/read/dynamic-wep/eap/ttls/chap", test_read_wifi_wep_eap_ttls_chap); + g_test_add_func(TPATH "wifi/read-band-a", test_read_wifi_band_a); + g_test_add_func(TPATH "wifi/read-band-a-channel-mismatch", + test_read_wifi_band_a_channel_mismatch); + g_test_add_func(TPATH "wifi/read-band-bg-channel-mismatch", + test_read_wifi_band_bg_channel_mismatch); + g_test_add_func(TPATH "wifi/read-hidden", test_read_wifi_hidden); + + nmtst_add_test_func(TPATH "wifi/read-mac-random-always", + test_read_wifi_mac_random, + "always", + GINT_TO_POINTER(NM_SETTING_MAC_RANDOMIZATION_ALWAYS)); + nmtst_add_test_func(TPATH "wifi/read-mac-random-never", + test_read_wifi_mac_random, + "never", + GINT_TO_POINTER(NM_SETTING_MAC_RANDOMIZATION_NEVER)); + nmtst_add_test_func(TPATH "wifi/read-mac-random-default", + test_read_wifi_mac_random, + "default", + GINT_TO_POINTER(NM_SETTING_MAC_RANDOMIZATION_DEFAULT)); + nmtst_add_test_func(TPATH "wifi/read-mac-random-missing", + test_read_wifi_mac_random, + "missing", + GINT_TO_POINTER(NM_SETTING_MAC_RANDOMIZATION_DEFAULT)); + + nmtst_add_test_func(TPATH "wifi/write-mac-random-always", + test_write_wifi_mac_random, + "always", + GINT_TO_POINTER(NM_SETTING_MAC_RANDOMIZATION_ALWAYS), + "always"); + nmtst_add_test_func(TPATH "wifi/write-mac-random-never", + test_write_wifi_mac_random, + "never", + GINT_TO_POINTER(NM_SETTING_MAC_RANDOMIZATION_NEVER), + "never"); + nmtst_add_test_func(TPATH "wifi/write-mac-random-default", + test_write_wifi_mac_random, + "default", + GINT_TO_POINTER(NM_SETTING_MAC_RANDOMIZATION_DEFAULT), + "default"); + nmtst_add_test_func(TPATH "wifi/write-mac-random-missing", + test_write_wifi_mac_random, + "missing", + GINT_TO_POINTER(NM_SETTING_MAC_RANDOMIZATION_NEVER), + "never"); + + g_test_add_func(TPATH "wifi/read/wep-no-keys", test_read_wifi_wep_no_keys); + g_test_add_func(TPATH "wifi/read/wep-agent-keys", test_read_wifi_wep_agent_keys); + g_test_add_func(TPATH "infiniband/read", test_read_infiniband); + g_test_add_func(TPATH "vlan/read", test_read_vlan_interface); + g_test_add_func(TPATH "vlan/read-flags-1", test_read_vlan_flags_1); + g_test_add_func(TPATH "vlan/read-flags-2", test_read_vlan_flags_2); + g_test_add_func(TPATH "vlan/read/only-vlanid", test_read_vlan_only_vlan_id); + g_test_add_func(TPATH "vlan/read/only-device", test_read_vlan_only_device); + g_test_add_func(TPATH "vlan/read/physdev", test_read_vlan_physdev); + g_test_add_func(TPATH "vlan/read/reorder-hdr-1", test_read_vlan_reorder_hdr_1); + g_test_add_func(TPATH "vlan/read/reorder-hdr-2", test_read_vlan_reorder_hdr_2); + g_test_add_func(TPATH "wired/read/read-wake-on-lan", test_read_wired_wake_on_lan); + g_test_add_func(TPATH "wired/read/read-auto-negotiate-off", test_read_wired_auto_negotiate_off); + g_test_add_func(TPATH "wired/read/read-auto-negotiate-on", test_read_wired_auto_negotiate_on); + g_test_add_func(TPATH "wired/read/unkwnown-ethtool-opt", test_read_wired_unknown_ethtool_opt); + + g_test_add_func(TPATH "wired/write/static", test_write_wired_static); + g_test_add_func(TPATH "wired/write/static-with-generic", test_write_wired_static_with_generic); + g_test_add_func(TPATH "wired/write/static-ip6-only", test_write_wired_static_ip6_only); + g_test_add_func(TPATH "wired/write-static-routes", test_write_wired_static_routes); + g_test_add_func(TPATH "wired/read-write-static-routes-legacy", + test_read_write_static_routes_legacy); + g_test_add_func(TPATH "wired/write/dhcp", test_write_wired_dhcp); + g_test_add_func(TPATH "wired/write-dhcp-plus-ip", test_write_wired_dhcp_plus_ip); + g_test_add_func(TPATH "wired/write/dhcp-8021x-peap-mschapv2", + test_write_wired_dhcp_8021x_peap_mschapv2); + g_test_add_func(TPATH "wired/write/match", test_write_wired_match); + g_test_add_func(TPATH "wired/write/routing-rules", test_write_routing_rules); + +#define _add_test_write_wired_8021x_tls(testpath, scheme, flags) \ + nmtst_add_test_func(testpath, \ + test_write_wired_8021x_tls, \ + GINT_TO_POINTER(scheme), \ + GINT_TO_POINTER(flags)) + _add_test_write_wired_8021x_tls(TPATH "wired-8021x-tls/1", + NM_SETTING_802_1X_CK_SCHEME_PATH, + NM_SETTING_SECRET_FLAG_AGENT_OWNED); + _add_test_write_wired_8021x_tls(TPATH "wired-8021x-tls/2", + NM_SETTING_802_1X_CK_SCHEME_PATH, + NM_SETTING_SECRET_FLAG_NOT_SAVED); + _add_test_write_wired_8021x_tls(TPATH "wired-8021x-tls/3", + NM_SETTING_802_1X_CK_SCHEME_PATH, + NM_SETTING_SECRET_FLAG_AGENT_OWNED + | NM_SETTING_SECRET_FLAG_NOT_SAVED); + _add_test_write_wired_8021x_tls(TPATH "wired-8021x-tls/4", + NM_SETTING_802_1X_CK_SCHEME_BLOB, + NM_SETTING_SECRET_FLAG_NONE); + + g_test_add_func(TPATH "wired/write-aliases", test_write_wired_aliases); + g_test_add_func(TPATH "ipv4/write-static-addresses-GATEWAY", test_write_gateway); + g_test_add_func(TPATH "wired/write-wake-on-lan", test_write_wired_wake_on_lan); + g_test_add_func(TPATH "wired/write-auto-negotiate-off", test_write_wired_auto_negotiate_off); + g_test_add_func(TPATH "wired/write-auto-negotiate-on", test_write_wired_auto_negotiate_on); + g_test_add_func(TPATH "wifi/write/open", test_write_wifi_open); + g_test_add_func(TPATH "wifi/write/open/hex-ssid", test_write_wifi_open_hex_ssid); + g_test_add_func(TPATH "wifi/write/wep", test_write_wifi_wep); + g_test_add_func(TPATH "wifi/write/wep/adhoc", test_write_wifi_wep_adhoc); + g_test_add_func(TPATH "wifi/write/wep/passphrase", test_write_wifi_wep_passphrase); + g_test_add_func(TPATH "wifi/write/wep/40-ascii", test_write_wifi_wep_40_ascii); + g_test_add_func(TPATH "wifi/write/wep/104-ascii", test_write_wifi_wep_104_ascii); + g_test_add_func(TPATH "wifi/write/leap", test_write_wifi_leap); + g_test_add_data_func(TPATH "wifi/write/leap/flags/agent", + GUINT_TO_POINTER(NM_SETTING_SECRET_FLAG_AGENT_OWNED), + test_write_wifi_leap_secret_flags); + g_test_add_data_func(TPATH "wifi/write/leap/flags/not-saved", + GUINT_TO_POINTER(NM_SETTING_SECRET_FLAG_NOT_SAVED), + test_write_wifi_leap_secret_flags); + g_test_add_data_func( + TPATH "wifi/write/leap/flags/agent-and-not-saved", + GUINT_TO_POINTER(NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED), + test_write_wifi_leap_secret_flags); + +#define _add_test_write_wifi_wpa_psk(testpath, name, wep_group, wpa, wpa2, psk) \ + nmtst_add_test_func(testpath, \ + test_write_wifi_wpa_psk, \ + name, \ + GPOINTER_TO_INT(wep_group), \ + GPOINTER_TO_INT(wpa), \ + GPOINTER_TO_INT(wpa2), \ + psk) + _add_test_write_wifi_wpa_psk(TPATH "wifi-wpa-psk/wpa-psk-write", + "Test Write Wifi WPA PSK", + FALSE, + TRUE, + FALSE, + DEFAULT_HEX_PSK); + _add_test_write_wifi_wpa_psk(TPATH "wifi-wpa-psk/wpa2-psk-write", + "Test Write Wifi WPA2 PSK", + FALSE, + FALSE, + TRUE, + DEFAULT_HEX_PSK); + _add_test_write_wifi_wpa_psk(TPATH "wifi-wpa-psk/wpa-wpa2-psk-write", + "Test Write Wifi WPA WPA2 PSK", + FALSE, + TRUE, + TRUE, + DEFAULT_HEX_PSK); + _add_test_write_wifi_wpa_psk(TPATH "wifi-wpa-psk/wep-wpa-wpa2-psk-write", + "Test Write Wifi WEP WPA WPA2 PSK", + TRUE, + TRUE, + TRUE, + DEFAULT_HEX_PSK); + _add_test_write_wifi_wpa_psk(TPATH "wifi-wpa-psk/wpa-wpa2-psk-passphrase-write", + "Test Write Wifi WPA WPA2 PSK Passphrase", + FALSE, + TRUE, + TRUE, + "really insecure passphrase04!"); + _add_test_write_wifi_wpa_psk(TPATH "wifi-wpa-psk/wpa-wpa2-psk-passphrase-write-spec-chars", + "Test Write Wifi WPA WPA2 PSK Passphrase Special Chars", + FALSE, + TRUE, + TRUE, + "blah`oops\"grr'$*@~!%\\"); + + g_test_add_func(TPATH "wifi/write/wpa/psk/adhoc", test_write_wifi_wpa_psk_adhoc); + g_test_add_func(TPATH "wifi/write/wpa/eap/tls", test_write_wifi_wpa_eap_tls); + g_test_add_func(TPATH "wifi/write/wpa/eap/ttls/tls", test_write_wifi_wpa_eap_ttls_tls); + g_test_add_func(TPATH "wifi/write/wpa/eap/ttls/mschapv2", + test_write_wifi_wpa_eap_ttls_mschapv2); + g_test_add_func(TPATH "wifi/write/dynamic-wep/leap", test_write_wifi_dynamic_wep_leap); + g_test_add_func(TPATH "wifi/write-wpa-then-open", test_write_wifi_wpa_then_open); + g_test_add_func(TPATH "wifi/write-wpa-then-wep-with-perms", + test_write_wifi_wpa_then_wep_with_perms); + g_test_add_func(TPATH "wifi/write-hidden", test_write_wifi_hidden); + g_test_add_func(TPATH "wifi/write-band-a", test_write_wifi_band_a); + g_test_add_func(TPATH "wifi/write-ap-mode", test_write_wifi_ap_mode); + + g_test_add_func(TPATH "s390/read-qeth-static", test_read_wired_qeth_static); + g_test_add_func(TPATH "s390/write-qeth-dhcp", test_write_wired_qeth_dhcp); + g_test_add_func(TPATH "s390/read-ctc-static", test_read_wired_ctc_static); + g_test_add_func(TPATH "s390/write-ctc-dhcp", test_write_wired_ctc_dhcp); + + g_test_add_func(TPATH "permissions/read", test_read_permissions); + g_test_add_func(TPATH "permissions/write", test_write_permissions); + g_test_add_func(TPATH "wifi/write-wep-agent-keys", test_write_wifi_wep_agent_keys); + g_test_add_func(TPATH "infiniband/write", test_write_infiniband); + g_test_add_func(TPATH "vlan/write", test_write_vlan); + g_test_add_func(TPATH "vlan/write-flags", test_write_vlan_flags); + g_test_add_func(TPATH "vlan/write-only-vlanid", test_write_vlan_only_vlanid); + g_test_add_func(TPATH "vlan/write-vlan-reorder-hdr", test_write_vlan_reorder_hdr); + g_test_add_func(TPATH "wired/write-missing-ipv6", test_write_ethernet_missing_ipv6); + g_test_add_func(TPATH "write-dns-options", test_write_dns_options); + + g_test_add_func(TPATH "ibft/ignored", test_read_ibft_ignored); + + g_test_add_func(TPATH "dcb/read-basic", test_read_dcb_basic); + g_test_add_func(TPATH "dcb/write-basic", test_write_dcb_basic); + g_test_add_func(TPATH "dcb/default-app-priorities", test_read_dcb_default_app_priorities); + g_test_add_func(TPATH "dcb/bad-booleans", test_read_dcb_bad_booleans); + g_test_add_func(TPATH "dcb/short-booleans", test_read_dcb_short_booleans); + g_test_add_func(TPATH "dcb/bad-uints", test_read_dcb_bad_uints); + g_test_add_func(TPATH "dcb/short-uints", test_read_dcb_short_uints); + g_test_add_func(TPATH "dcb/bad-percent", test_read_dcb_bad_percent); + g_test_add_func(TPATH "dcb/short-percent", test_read_dcb_short_percent); + g_test_add_func(TPATH "dcb/pgpct-not-100", test_read_dcb_pgpct_not_100); + g_test_add_data_func(TPATH "fcoe/fabric", + (gpointer) NM_SETTING_DCB_FCOE_MODE_FABRIC, + test_read_fcoe_mode); + g_test_add_data_func(TPATH "fcoe/vn2vn", + (gpointer) NM_SETTING_DCB_FCOE_MODE_VN2VN, + test_read_fcoe_mode); + g_test_add_data_func(TPATH "fcoe/write-fabric", + (gpointer) NM_SETTING_DCB_FCOE_MODE_FABRIC, + test_write_fcoe_mode); + g_test_add_data_func(TPATH "fcoe/write-vn2vn", + (gpointer) NM_SETTING_DCB_FCOE_MODE_VN2VN, + test_write_fcoe_mode); + + g_test_add_func(TPATH "bond/read-master", test_read_bond_main); + g_test_add_func(TPATH "bond/read-master-eth-type", test_read_bond_eth_type); + g_test_add_func(TPATH "bond/read-slave", test_read_bond_slave); + g_test_add_func(TPATH "bond/read-slave-ib", test_read_bond_slave_ib); + g_test_add_func(TPATH "bond/write-master", test_write_bond_main); + g_test_add_func(TPATH "bond/write-slave", test_write_bond_slave); + g_test_add_func(TPATH "bond/write-slave-ib", test_write_bond_slave_ib); + g_test_add_func(TPATH "bond/bonding-opts-numeric-mode", test_read_bond_opts_mode_numeric); + + g_test_add_func(TPATH "bridge/read-master", test_read_bridge_main); + g_test_add_func(TPATH "bridge/write-master", test_write_bridge_main); + g_test_add_func(TPATH "bridge/read-component", test_read_bridge_component); + g_test_add_func(TPATH "bridge/write-component", test_write_bridge_component); + g_test_add_func(TPATH "bridge/read-missing-stp", test_read_bridge_missing_stp); + + g_test_add_data_func(TPATH "team/read-master-1", + TEST_IFCFG_DIR "/ifcfg-test-team-master-1", + test_read_team_master); + g_test_add_data_func(TPATH "team/read-master-2", + TEST_IFCFG_DIR "/ifcfg-test-team-master-2", + test_read_team_master); + g_test_add_data_func(TPATH "team/read-master-invalid", + TEST_IFCFG_DIR "/ifcfg-test-team-master-invalid", + test_read_team_master_invalid); + g_test_add_func(TPATH "team/write-master", test_write_team_master); + g_test_add_data_func(TPATH "team/read-port-1", + TEST_IFCFG_DIR "/ifcfg-test-team-port-1", + test_read_team_port); + g_test_add_data_func(TPATH "team/read-port-2", + TEST_IFCFG_DIR "/ifcfg-test-team-port-2", + test_read_team_port); + g_test_add_func(TPATH "team/write-port", test_write_team_port); + g_test_add_func(TPATH "team/write-infiniband-port", test_write_team_infiniband_port); + g_test_add_func(TPATH "team/read-port-empty-config", test_read_team_port_empty_config); + g_test_add_func(TPATH "team/reread-slave", test_team_reread_slave); + + g_test_add_func(TPATH "proxy/read-proxy-basic", test_read_proxy_basic); + g_test_add_func(TPATH "proxy/write-proxy-basic", test_write_proxy_basic); + + g_test_add_func(TPATH "sit/read/ignore", test_sit_read_ignore); + + /* Stuff we expect to fail for now */ + g_test_add_func(TPATH "pppoe/write-wired", test_write_wired_pppoe); + g_test_add_func(TPATH "vpn/write", test_write_vpn); + g_test_add_data_func(TPATH "wwan/write-gsm", + GUINT_TO_POINTER(TRUE), + test_write_mobile_broadband); + g_test_add_data_func(TPATH "wwan/write-cdma", + GUINT_TO_POINTER(FALSE), + test_write_mobile_broadband); + + g_test_add_func(TPATH "no-trailing-newline", test_ifcfg_no_trailing_newline); + + g_test_add_func(TPATH "utils/name", test_utils_name); + g_test_add_func(TPATH "utils/path", test_utils_path); + g_test_add_func(TPATH "utils/ignore", test_utils_ignore); + + g_test_add_func(TPATH "sriov/read", test_sriov_read); + g_test_add_func(TPATH "sriov/write", test_sriov_write); + + g_test_add_func(TPATH "tc/read", test_tc_read); + g_test_add_func(TPATH "tc/write", test_tc_write); + g_test_add_func(TPATH "utils/test_well_known_keys", test_well_known_keys); + g_test_add_func(TPATH "utils/test_utils_has_route_file_new_syntax", + test_utils_has_route_file_new_syntax); + + g_test_add_func(TPATH "utils/test_ethtool_names", test_ethtool_names); + + return g_test_run(); +} |