Merge ~hellsworth/snappy-hwe-snaps/+git/network-manager:add-netplan into ~snappy-hwe-team/snappy-hwe-snaps/+git/network-manager:snap-20

Proposed by Heather Ellsworth
Status: Merged
Approved by: Alfonso Sanchez-Beato
Approved revision: 9f4ffcffb20516c4648cfcb5b47ba4edecd0ef7e
Merged at revision: 54fd7daf463370dcc7a3529e1eacae16420c90b2
Proposed branch: ~hellsworth/snappy-hwe-snaps/+git/network-manager:add-netplan
Merge into: ~snappy-hwe-team/snappy-hwe-snaps/+git/network-manager:snap-20
Diff against target: 66425 lines (+66390/-0)
2 files modified
snap-patch/networkmanager/0002-nm-netplan.patch (+66384/-0)
snap/snapcraft.yaml (+6/-0)
Reviewer Review Type Date Requested Status
Lukas Märdian Approve
Alfonso Sanchez-Beato Approve
System Enablement Bot continuous-integration Approve
Review via email: mp+391222@code.launchpad.net

Commit message

Add netplan patch

To post a comment you must log in.
Revision history for this message
System Enablement Bot (system-enablement-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
System Enablement Bot (system-enablement-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
System Enablement Bot (system-enablement-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
System Enablement Bot (system-enablement-ci-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote :

snapcraft.yaml changes LGTM

review: Approve
Revision history for this message
Lukas Märdian (slyon) wrote :

+1.

The netplan patch itself has been checked by Lukasz and myself. Also, it contains an automatic test-suite in src/settings/plugins/netplan/tests/test-netplan.c, which is run during build ('make check').

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/snap-patch/networkmanager/0002-nm-netplan.patch b/snap-patch/networkmanager/0002-nm-netplan.patch
0new file mode 1006440new file mode 100644
index 0000000..8ffcb4f
--- /dev/null
+++ b/snap-patch/networkmanager/0002-nm-netplan.patch
@@ -0,0 +1,66384 @@
1From 2c8ce0635167a447df9c1cee1c3fbd8bc40b3e48 Mon Sep 17 00:00:00 2001
2From: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
3Date: Mon, 16 Sep 2019 11:12:57 +0100
4Subject: [PATCH 01/80] Import structure based on ifcfg-rh
5
6---
7 configure.ac | 7 +
8 meson.build | 8 +
9 src/settings/plugins/meson.build | 4 +
10 src/settings/plugins/netplan/meson.build | 48 +
11 .../plugins/netplan/nms-netplan-plugin.c | 1259 ++
12 .../plugins/netplan/nms-netplan-plugin.h | 25 +
13 .../plugins/netplan/nms-netplan-reader.c | 5991 +++++++++
14 .../plugins/netplan/nms-netplan-reader.h | 23 +
15 .../plugins/netplan/nms-netplan-utils.c | 589 +
16 .../plugins/netplan/nms-netplan-utils.h | 90 +
17 .../plugins/netplan/nms-netplan-writer.c | 3464 +++++
18 .../plugins/netplan/nms-netplan-writer.h | 29 +
19 .../plugins/netplan/tests/meson.build | 17 +
20 .../plugins/netplan/tests/test-netplan.c | 10485 ++++++++++++++++
21 14 files changed, 22039 insertions(+)
22 create mode 100644 src/settings/plugins/netplan/meson.build
23 create mode 100644 src/settings/plugins/netplan/nms-netplan-plugin.c
24 create mode 100644 src/settings/plugins/netplan/nms-netplan-plugin.h
25 create mode 100644 src/settings/plugins/netplan/nms-netplan-reader.c
26 create mode 100644 src/settings/plugins/netplan/nms-netplan-reader.h
27 create mode 100644 src/settings/plugins/netplan/nms-netplan-utils.c
28 create mode 100644 src/settings/plugins/netplan/nms-netplan-utils.h
29 create mode 100644 src/settings/plugins/netplan/nms-netplan-writer.c
30 create mode 100644 src/settings/plugins/netplan/nms-netplan-writer.h
31 create mode 100644 src/settings/plugins/netplan/tests/meson.build
32 create mode 100644 src/settings/plugins/netplan/tests/test-netplan.c
33
34diff --git a/configure.ac b/configure.ac
35index 4f36e8825fa2f0a08b5bcabe26b11ad8564af27c..66986e52a64a308c41ead80678c80e688ca7fa5b 100644
36--- a/configure.ac
37+++ b/configure.ac
38@@ -143,15 +143,19 @@ AC_CHECK_FUNCS([__secure_getenv secure_getenv])
39
40 # Alternative configuration plugins
41 AC_ARG_ENABLE(ifcfg-rh, AS_HELP_STRING([--enable-ifcfg-rh], [enable ifcfg-rh configuration plugin (Fedora/RHEL)]))
42+AC_ARG_ENABLE(netplan, AS_HELP_STRING([--enable-netplan], [enable netplan configuration plugin (Ubuntu)]))
43 AC_ARG_ENABLE(ifupdown, AS_HELP_STRING([--enable-ifupdown], [enable ifupdown configuration plugin (Debian/Ubuntu)]))
44 # Default alternative plugins by distribution
45 AS_IF([test -z "$enable_ifcfg_rh" -a -d /etc/sysconfig/network-scripts], enable_ifcfg_rh=yes)
46+AS_IF([test -z "$enable_netplan" -a -d /etc/netplan], enable_netplan=yes)
47 AS_IF([test -z "$enable_ifupdown" -a -f /etc/debian_version], enable_ifupdown=yes)
48 # Otherwise plugins default to "no"
49 AS_IF([test -z "$enable_ifcfg_rh"], enable_ifcfg_rh=no)
50+AS_IF([test -z "$enable_netplan"], enable_netplan=no)
51 AS_IF([test -z "$enable_ifupdown"], enable_ifupdown=no)
52 # Create automake conditionals
53 AM_CONDITIONAL(CONFIG_PLUGIN_IFCFG_RH, test "$enable_ifcfg_rh" = "yes")
54+AM_CONDITIONAL(CONFIG_PLUGIN_NETPLAN, test "$enable_netplan" = "yes")
55 AM_CONDITIONAL(CONFIG_PLUGIN_IFUPDOWN, test "$enable_ifupdown" = "yes")
56
57 AC_ARG_WITH(config-plugins-default,
58@@ -161,11 +165,13 @@ AC_ARG_WITH(config-plugins-default,
59 if test -z "$config_plugins_default" -o "$config_plugins_default" = no; then
60 config_plugins_default=''
61 test "$enable_ifcfg_rh" = "yes" && config_plugins_default="$config_plugins_default,ifcfg-rh"
62+ test "$enable_netplan" = "yes" && config_plugins_default="$config_plugins_default,netplan"
63 test "$enable_ifupdown" = "yes" && config_plugins_default="$config_plugins_default,ifupdown"
64 config_plugins_default="${config_plugins_default#,}"
65 fi
66
67 test "$enable_ifcfg_rh" = "yes" && distro_plugins="$distro_plugins,ifcfg-rh"
68+test "$enable_netplan" = "yes" && distro_plugins="$distro_plugins,netplan"
69 test "$enable_ifupdown" = "yes" && distro_plugins="$distro_plugins,ifupdown"
70 distro_plugins="${distro_plugins#,}"
71
72@@ -1320,6 +1326,7 @@ echo
73
74 echo "Configuration plugins (main.plugins=${config_plugins_default})"
75 echo " ifcfg-rh: ${enable_ifcfg_rh}"
76+echo " netplan: ${enable_netplan}"
77 echo " ifupdown: ${enable_ifupdown}"
78 echo
79
80diff --git a/meson.build b/meson.build
81index 5bd55e6a0884b52a69d7295b3e0d2bbf5f8ae1fe..1d59d95104ac03d33b7c90f384260f394f0225d1 100644
82--- a/meson.build
83+++ b/meson.build
84@@ -279,6 +279,8 @@ glib_dep = declare_dependency(
85
86 if run_command('test', '-e', '/etc/sysconfig/network-scripts').returncode() == 0
87 distro = 'redhat'
88+elif run_command('test', '-e', '/etc/netplan').returncode() == 0
89+ distro = 'ubuntu'
90 elif run_command('test', '-e', '/etc/SuSE-release').returncode() == 0
91 distro = 'suse'
92 elif run_command('test', '-e', '/etc/debian_version').returncode() == 0
93@@ -290,6 +292,7 @@ else
94 endif
95
96 enable_ifcfg_rh = get_option('ifcfg_rh') or (distro == 'redhat')
97+enable_netplan = get_option('netplan') or (distro == 'ubuntu')
98 enable_ifupdown = get_option('ifupdown') or (distro == 'debian')
99
100 config_plugins_default = get_option('config_plugins_default')
101@@ -300,6 +303,10 @@ if config_plugins_default == ''
102 config_plugins += ['ifcfg-rh']
103 endif
104
105+ if enable_netplan
106+ config_plugins += ['netplan']
107+ endif
108+
109 if enable_ifupdown
110 config_plugins += ['ifupdown']
111 endif
112@@ -1006,6 +1013,7 @@ output += ' nmtui: ' + enable_nmtui.to_string() + '\n'
113 output += ' nm-cloud-setup: ' + enable_nm_cloud_setup.to_string() + '\n'
114 output += '\nConfiguration_plugins (main.plugins=' + config_plugins_default + ')\n'
115 output += ' ifcfg-rh: ' + enable_ifcfg_rh.to_string() + '\n'
116+output += ' netplan: ' + enable_netplan.to_string() + '\n'
117 output += ' ifupdown: ' + enable_ifupdown.to_string() + '\n'
118 output += '\nHandlers for /etc/resolv.conf:\n' + resolv_conf_summary
119 output += '\n'
120diff --git a/src/settings/plugins/meson.build b/src/settings/plugins/meson.build
121index 83981aaba77a2fb869191fcc58235664bd729120..c36a69cd35ba342554d4b4924ce9bd4fd8835d55 100644
122--- a/src/settings/plugins/meson.build
123+++ b/src/settings/plugins/meson.build
124@@ -6,6 +6,10 @@ if enable_ifupdown
125 subdir('ifupdown')
126 endif
127
128+if enable_netplan
129+ subdir('netplan')
130+endif
131+
132 if enable_tests
133 subdir('keyfile/tests')
134 endif
135diff --git a/src/settings/plugins/netplan/meson.build b/src/settings/plugins/netplan/meson.build
136new file mode 100644
137index 0000000000000000000000000000000000000000..365ae1a9c50980c1d14d56db9d76dedfb8e9aec7
138--- /dev/null
139+++ b/src/settings/plugins/netplan/meson.build
140@@ -0,0 +1,48 @@
141+sources = files(
142+ 'nms-ifupdown-interface-parser.c',
143+ 'nms-ifupdown-parser.c',
144+)
145+
146+deps = [
147+ libudev_dep,
148+ nm_dep,
149+]
150+
151+libnms_ifupdown_core = static_library(
152+ 'nms-ifupdown-core',
153+ sources: sources,
154+ dependencies: deps,
155+)
156+
157+sources = files(
158+ 'nms-ifupdown-plugin.c',
159+)
160+
161+libnm_settings_plugin_ifupdown = shared_module(
162+ 'nm-settings-plugin-ifupdown',
163+ sources: sources,
164+ dependencies: deps,
165+ link_with: libnms_ifupdown_core,
166+ link_args: ldflags_linker_script_settings,
167+ link_depends: linker_script_settings,
168+ install: true,
169+ install_dir: nm_plugindir,
170+)
171+
172+core_plugins += libnm_settings_plugin_ifupdown
173+
174+# FIXME: check_so_symbols replacement
175+'''
176+run_target(
177+ 'check-local-symbols-settings-ifupdown',
178+ command: [check_so_symbols, libnm_settings_plugin_ifupdown.full_path()],
179+ depends: libnm_settings_plugin_ifupdown,
180+)
181+
182+check-local-symbols-settings-ifupdown: src/settings/plugins/ifupdown/libnm-settings-plugin-ifupdown.la
183+ $(call check_so_symbols,$(builddir)/src/settings/plugins/ifupdown/.libs/libnm-settings-plugin-ifupdown.so)
184+'''
185+
186+if enable_tests
187+ subdir('tests')
188+endif
189diff --git a/src/settings/plugins/netplan/nms-netplan-plugin.c b/src/settings/plugins/netplan/nms-netplan-plugin.c
190new file mode 100644
191index 0000000000000000000000000000000000000000..f438755d98958623d6d0dc06086a83610c0f3072
192--- /dev/null
193+++ b/src/settings/plugins/netplan/nms-netplan-plugin.c
194@@ -0,0 +1,1259 @@
195+// SPDX-License-Identifier: GPL-2.0+
196+/* NetworkManager system settings service
197+ *
198+ * Dan Williams <dcbw@redhat.com>
199+ * Søren Sandmann <sandmann@daimi.au.dk>
200+ *
201+ * Copyright (C) 2007 - 2011 Red Hat, Inc.
202+ */
203+
204+#include "nm-default.h"
205+
206+#include "nms-ifcfg-rh-plugin.h"
207+
208+#include <sys/types.h>
209+#include <sys/stat.h>
210+#include <unistd.h>
211+
212+#include "nm-std-aux/c-list-util.h"
213+#include "nm-glib-aux/nm-c-list.h"
214+#include "nm-glib-aux/nm-io-utils.h"
215+#include "nm-std-aux/nm-dbus-compat.h"
216+#include "nm-utils.h"
217+#include "nm-core-internal.h"
218+#include "nm-config.h"
219+#include "settings/nm-settings-plugin.h"
220+#include "settings/nm-settings-utils.h"
221+#include "NetworkManagerUtils.h"
222+
223+#include "nms-ifcfg-rh-storage.h"
224+#include "nms-ifcfg-rh-common.h"
225+#include "nms-ifcfg-rh-utils.h"
226+#include "nms-ifcfg-rh-reader.h"
227+#include "nms-ifcfg-rh-writer.h"
228+
229+#define IFCFGRH1_BUS_NAME "com.redhat.ifcfgrh1"
230+#define IFCFGRH1_OBJECT_PATH "/com/redhat/ifcfgrh1"
231+#define IFCFGRH1_IFACE1_NAME "com.redhat.ifcfgrh1"
232+#define IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS "GetIfcfgDetails"
233+
234+/*****************************************************************************/
235+
236+typedef struct {
237+ NMConfig *config;
238+
239+ struct {
240+ GDBusConnection *connection;
241+ GCancellable *cancellable;
242+ gulong signal_id;
243+ guint regist_id;
244+ } dbus;
245+
246+ NMSettUtilStorages storages;
247+
248+ GHashTable *unmanaged_specs;
249+ GHashTable *unrecognized_specs;
250+
251+} NMSIfcfgRHPluginPrivate;
252+
253+struct _NMSIfcfgRHPlugin {
254+ NMSettingsPlugin parent;
255+ NMSIfcfgRHPluginPrivate _priv;
256+};
257+
258+struct _NMSIfcfgRHPluginClass {
259+ NMSettingsPluginClass parent;
260+};
261+
262+G_DEFINE_TYPE (NMSIfcfgRHPlugin, nms_ifcfg_rh_plugin, NM_TYPE_SETTINGS_PLUGIN)
263+
264+#define NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfcfgRHPlugin, NMS_IS_IFCFG_RH_PLUGIN, NMSettingsPlugin)
265+
266+/*****************************************************************************/
267+
268+#define _NMLOG_DOMAIN LOGD_SETTINGS
269+#define _NMLOG(level, ...) \
270+ G_STMT_START { \
271+ nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \
272+ "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
273+ "ifcfg-rh: " \
274+ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
275+ } G_STMT_END
276+
277+/*****************************************************************************/
278+
279+static void _unhandled_specs_reset (NMSIfcfgRHPlugin *self);
280+
281+static void _unhandled_specs_merge_storages (NMSIfcfgRHPlugin *self,
282+ NMSettUtilStorages *storages);
283+
284+/*****************************************************************************/
285+
286+static void
287+nm_assert_self (NMSIfcfgRHPlugin *self, gboolean unhandled_specs_consistent)
288+{
289+ nm_assert (NMS_IS_IFCFG_RH_PLUGIN (self));
290+
291+#if NM_MORE_ASSERTS > 5
292+ {
293+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
294+ NMSIfcfgRHStorage *storage;
295+ gsize n_uuid;
296+ gs_unref_hashtable GHashTable *h_unmanaged = NULL;
297+ gs_unref_hashtable GHashTable *h_unrecognized = NULL;
298+
299+ nm_assert (g_hash_table_size (priv->storages.idx_by_filename) == c_list_length (&priv->storages._storage_lst_head));
300+
301+ h_unmanaged = g_hash_table_new (nm_str_hash, g_str_equal);
302+ h_unrecognized = g_hash_table_new (nm_str_hash, g_str_equal);
303+
304+ n_uuid = 0;
305+
306+ c_list_for_each_entry (storage, &priv->storages._storage_lst_head, parent._storage_lst) {
307+ const char *uuid;
308+ const char *filename;
309+
310+ filename = nms_ifcfg_rh_storage_get_filename (storage);
311+
312+ nm_assert (filename && NM_STR_HAS_PREFIX (filename, IFCFG_DIR"/"));
313+
314+ uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage);
315+
316+ nm_assert ((!!uuid) + (!!storage->unmanaged_spec) + (!!storage->unrecognized_spec) == 1);
317+
318+ nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, filename));
319+
320+ if (uuid) {
321+ NMSettUtilStorageByUuidHead *sbuh;
322+ NMSettUtilStorageByUuidHead *sbuh2;
323+
324+ if (storage->connection)
325+ nm_assert (nm_streq0 (nm_connection_get_uuid (storage->connection), uuid));
326+
327+ if (!g_hash_table_lookup_extended (priv->storages.idx_by_uuid, &uuid, (gpointer *) &sbuh, (gpointer *) &sbuh2))
328+ nm_assert_not_reached ();
329+
330+ nm_assert (sbuh);
331+ nm_assert (nm_streq (uuid, sbuh->uuid));
332+ nm_assert (sbuh == sbuh2);
333+ nm_assert (c_list_contains (&sbuh->_storage_by_uuid_lst_head, &storage->parent._storage_by_uuid_lst));
334+
335+ if (c_list_first (&sbuh->_storage_by_uuid_lst_head) == &storage->parent._storage_by_uuid_lst)
336+ n_uuid++;
337+ } else if (storage->unmanaged_spec) {
338+ nm_assert (strlen (storage->unmanaged_spec) > 0);
339+ g_hash_table_add (h_unmanaged, storage->unmanaged_spec);
340+ } else if (storage->unrecognized_spec) {
341+ nm_assert (strlen (storage->unrecognized_spec) > 0);
342+ g_hash_table_add (h_unrecognized, storage->unrecognized_spec);
343+ } else
344+ nm_assert_not_reached ();
345+
346+ nm_assert (!storage->connection);
347+ }
348+
349+ nm_assert (g_hash_table_size (priv->storages.idx_by_uuid) == n_uuid);
350+
351+ if (unhandled_specs_consistent) {
352+ nm_assert (nm_utils_hashtable_same_keys (h_unmanaged, priv->unmanaged_specs));
353+ nm_assert (nm_utils_hashtable_same_keys (h_unrecognized, priv->unrecognized_specs));
354+ }
355+ }
356+#endif
357+}
358+
359+/*****************************************************************************/
360+
361+static NMSIfcfgRHStorage *
362+_load_file (NMSIfcfgRHPlugin *self,
363+ const char *filename,
364+ GError **error)
365+{
366+ gs_unref_object NMConnection *connection = NULL;
367+ gs_free_error GError *load_error = NULL;
368+ gs_free char *unhandled_spec = NULL;
369+ gboolean load_error_ignore;
370+ struct stat st;
371+
372+ if (stat (filename, &st) != 0) {
373+ int errsv = errno;
374+
375+ if (error) {
376+ nm_utils_error_set_errno (error, errsv,
377+ "failure to stat file \%s\": %s",
378+ filename);
379+ } else
380+ _LOGT ("load[%s]: failure to stat file: %s", filename, nm_strerror_native (errsv));
381+ return NULL;
382+ }
383+
384+ connection = connection_from_file (filename,
385+ &unhandled_spec,
386+ &load_error,
387+ &load_error_ignore);
388+ if (load_error) {
389+ if (error) {
390+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
391+ "failure to read file \"%s\": %s",
392+ filename, load_error->message);
393+ } else {
394+ _NMLOG (load_error_ignore ? LOGL_TRACE : LOGL_WARN,
395+ "load[%s]: failure to read file: %s", filename, load_error->message);
396+ }
397+ return NULL;
398+ }
399+
400+ if (unhandled_spec) {
401+ const char *unmanaged_spec;
402+ const char *unrecognized_spec;
403+
404+ if (!nms_ifcfg_rh_util_parse_unhandled_spec (unhandled_spec,
405+ &unmanaged_spec,
406+ &unrecognized_spec)) {
407+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
408+ "invalid unhandled spec \"%s\"",
409+ unhandled_spec);
410+ nm_assert_not_reached ();
411+ return NULL;
412+ }
413+ return nms_ifcfg_rh_storage_new_unhandled (self,
414+ filename,
415+ unmanaged_spec,
416+ unrecognized_spec);
417+ }
418+
419+ return nms_ifcfg_rh_storage_new_connection (self,
420+ filename,
421+ g_steal_pointer (&connection),
422+ &st.st_mtim);
423+}
424+
425+static void
426+_load_dir (NMSIfcfgRHPlugin *self,
427+ NMSettUtilStorages *storages)
428+{
429+ gs_unref_hashtable GHashTable *dupl_filenames = NULL;
430+ gs_free_error GError *local = NULL;
431+ const char *f_filename;
432+ GDir *dir;
433+
434+ dir = g_dir_open (IFCFG_DIR, 0, &local);
435+ if (!dir) {
436+ _LOGT ("Could not read directory '%s': %s", IFCFG_DIR, local->message);
437+ return;
438+ }
439+
440+ dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free);
441+
442+ while ((f_filename = g_dir_read_name (dir))) {
443+ gs_free char *full_path = NULL;
444+ NMSIfcfgRHStorage *storage;
445+ char *full_filename;
446+
447+ full_path = g_build_filename (IFCFG_DIR, f_filename, NULL);
448+ full_filename = utils_detect_ifcfg_path (full_path, TRUE);
449+ if (!full_filename)
450+ continue;
451+
452+ if (!g_hash_table_add (dupl_filenames, full_filename))
453+ continue;
454+
455+ nm_assert (!nm_sett_util_storages_lookup_by_filename (storages, full_filename));
456+
457+ storage = _load_file (self,
458+ full_filename,
459+ NULL);
460+ if (storage)
461+ nm_sett_util_storages_add_take (storages, storage);
462+ }
463+ g_dir_close (dir);
464+}
465+
466+static void
467+_storages_consolidate (NMSIfcfgRHPlugin *self,
468+ NMSettUtilStorages *storages_new,
469+ gboolean replace_all,
470+ GHashTable *storages_replaced,
471+ NMSettingsPluginConnectionLoadCallback callback,
472+ gpointer user_data)
473+{
474+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
475+ CList lst_conn_info_deleted = C_LIST_INIT (lst_conn_info_deleted);
476+ gs_unref_ptrarray GPtrArray *storages_modified = NULL;
477+ CList storages_deleted;
478+ NMSIfcfgRHStorage *storage_safe;
479+ NMSIfcfgRHStorage *storage_new;
480+ NMSIfcfgRHStorage *storage_old;
481+ NMSIfcfgRHStorage *storage;
482+ guint i;
483+
484+ /* when we reload all files, we must signal add/update/modify of profiles one-by-one.
485+ * NMSettings then goes ahead and emits further signals and a lot of things happen.
486+ *
487+ * So, first, emit an update of the unmanaged/unrecognized specs that contains *all*
488+ * the unmanaged/unrecognized devices from before and after. Since both unmanaged/unrecognized
489+ * specs have the meaning of "not doing something", it makes sense that we temporarily
490+ * disable that action for the sum of before and after. */
491+ _unhandled_specs_merge_storages (self, storages_new);
492+
493+ storages_modified = g_ptr_array_new_with_free_func (g_object_unref);
494+ c_list_init (&storages_deleted);
495+
496+ c_list_for_each_entry (storage_old, &priv->storages._storage_lst_head, parent._storage_lst)
497+ storage_old->dirty = TRUE;
498+
499+ c_list_for_each_entry_safe (storage_new, storage_safe, &storages_new->_storage_lst_head, parent._storage_lst) {
500+ storage_old = nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_ifcfg_rh_storage_get_filename (storage_new));
501+
502+ nm_sett_util_storages_steal (storages_new, storage_new);
503+
504+ if ( !storage_old
505+ || !nms_ifcfg_rh_storage_equal_type (storage_new, storage_old)) {
506+ if (storage_old) {
507+ nm_sett_util_storages_steal (&priv->storages, storage_old);
508+ if (nms_ifcfg_rh_storage_get_uuid_opt (storage_old))
509+ c_list_link_tail (&storages_deleted, &storage_old->parent._storage_lst);
510+ else
511+ nms_ifcfg_rh_storage_destroy (storage_old);
512+ }
513+ storage_new->dirty = FALSE;
514+ nm_sett_util_storages_add_take (&priv->storages, storage_new);
515+ g_ptr_array_add (storages_modified, g_object_ref (storage_new));
516+ continue;
517+ }
518+
519+ storage_old->dirty = FALSE;
520+ nms_ifcfg_rh_storage_copy_content (storage_old, storage_new);
521+ nms_ifcfg_rh_storage_destroy (storage_new);
522+ g_ptr_array_add (storages_modified, g_object_ref (storage_old));
523+ }
524+
525+ c_list_for_each_entry_safe (storage_old, storage_safe, &priv->storages._storage_lst_head, parent._storage_lst) {
526+ if (!storage_old->dirty)
527+ continue;
528+ if ( replace_all
529+ || ( storages_replaced
530+ && g_hash_table_contains (storages_replaced, storage_old))) {
531+ nm_sett_util_storages_steal (&priv->storages, storage_old);
532+ if (nms_ifcfg_rh_storage_get_uuid_opt (storage_old))
533+ c_list_link_tail (&storages_deleted, &storage_old->parent._storage_lst);
534+ else
535+ nms_ifcfg_rh_storage_destroy (storage_old);
536+ }
537+ }
538+
539+ /* raise events. */
540+
541+ for (i = 0; i < storages_modified->len; i++) {
542+ storage = storages_modified->pdata[i];
543+ storage->dirty = TRUE;
544+ }
545+
546+ for (i = 0; i < storages_modified->len; i++) {
547+ gs_unref_object NMConnection *connection = NULL;
548+ storage = storages_modified->pdata[i];
549+
550+ if (!storage->dirty) {
551+ /* the entry is no longer dirty. In the meantime we already emited
552+ * another signal for it. */
553+ continue;
554+ }
555+ storage->dirty = FALSE;
556+ if (storage != nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_ifcfg_rh_storage_get_filename (storage))) {
557+ /* hm? The profile was deleted in the meantime? That is only possible
558+ * if the signal handler called again into the plugin. In any case, the event
559+ * was already emitted. Skip. */
560+ continue;
561+ }
562+
563+ connection = nms_ifcfg_rh_storage_steal_connection (storage);
564+ if (!connection) {
565+ nm_assert (!nms_ifcfg_rh_storage_get_uuid_opt (storage));
566+ continue;
567+ }
568+
569+ nm_assert (NM_IS_CONNECTION (connection));
570+ nm_assert (nms_ifcfg_rh_storage_get_uuid_opt (storage));
571+ callback (NM_SETTINGS_PLUGIN (self),
572+ NM_SETTINGS_STORAGE (storage),
573+ connection,
574+ user_data);
575+ }
576+
577+ while ((storage = c_list_first_entry (&storages_deleted, NMSIfcfgRHStorage, parent._storage_lst))) {
578+ c_list_unlink (&storage->parent._storage_lst);
579+ callback (NM_SETTINGS_PLUGIN (self),
580+ NM_SETTINGS_STORAGE (storage),
581+ NULL,
582+ user_data);
583+ nms_ifcfg_rh_storage_destroy (storage);
584+ }
585+}
586+
587+/*****************************************************************************/
588+
589+static void
590+load_connections (NMSettingsPlugin *plugin,
591+ NMSettingsPluginConnectionLoadEntry *entries,
592+ gsize n_entries,
593+ NMSettingsPluginConnectionLoadCallback callback,
594+ gpointer user_data)
595+{
596+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
597+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
598+ nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_ifcfg_rh_storage_destroy);
599+ gs_unref_hashtable GHashTable *dupl_filenames = NULL;
600+ gs_unref_hashtable GHashTable *storages_replaced = NULL;
601+ gs_unref_hashtable GHashTable *loaded_uuids = NULL;
602+ const char *loaded_uuid;
603+ GHashTableIter h_iter;
604+ gsize i;
605+
606+ if (n_entries == 0)
607+ return;
608+
609+ dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
610+
611+ loaded_uuids = g_hash_table_new (nm_str_hash, g_str_equal);
612+
613+ storages_replaced = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL);
614+
615+ for (i = 0; i < n_entries; i++) {
616+ NMSettingsPluginConnectionLoadEntry *const entry = &entries[i];
617+ gs_free_error GError *local = NULL;
618+ const char *full_filename;
619+ const char *uuid;
620+ gs_free char *full_filename_keep = NULL;
621+ NMSettingsPluginConnectionLoadEntry *dupl_content_entry;
622+ gs_unref_object NMSIfcfgRHStorage *storage = NULL;
623+
624+ if (entry->handled)
625+ continue;
626+
627+ if (entry->filename[0] != '/')
628+ continue;
629+
630+ full_filename_keep = utils_detect_ifcfg_path (entry->filename, FALSE);
631+
632+ if (!full_filename_keep) {
633+ if (nm_utils_file_is_in_path (entry->filename, IFCFG_DIR)) {
634+ nm_utils_error_set (&entry->error,
635+ NM_UTILS_ERROR_UNKNOWN,
636+ ("path is not a valid name for an ifcfg-rh file"));
637+ entry->handled = TRUE;
638+ }
639+ continue;
640+ }
641+
642+ if ((dupl_content_entry = g_hash_table_lookup (dupl_filenames, full_filename_keep))) {
643+ /* we already visited this file. */
644+ entry->handled = dupl_content_entry->handled;
645+ if (dupl_content_entry->error) {
646+ g_set_error_literal (&entry->error,
647+ dupl_content_entry->error->domain,
648+ dupl_content_entry->error->code,
649+ dupl_content_entry->error->message);
650+ }
651+ continue;
652+ }
653+
654+ entry->handled = TRUE;
655+
656+ full_filename = full_filename_keep;
657+ if (!g_hash_table_insert (dupl_filenames, g_steal_pointer (&full_filename_keep), entry))
658+ nm_assert_not_reached ();
659+
660+ storage = _load_file (self,
661+ full_filename,
662+ &local);
663+ if (!storage) {
664+ if (nm_utils_file_stat (full_filename, NULL) == -ENOENT) {
665+ NMSIfcfgRHStorage *storage2;
666+
667+ /* the file does not exist. We take that as indication to unload the file
668+ * that was previously loaded... */
669+ storage2 = nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename);
670+ if (storage2)
671+ g_hash_table_add (storages_replaced, g_object_ref (storage2));
672+ continue;
673+ }
674+ g_propagate_error (&entry->error, g_steal_pointer (&local));
675+ continue;
676+ }
677+
678+ uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage);
679+ if (uuid)
680+ g_hash_table_add (loaded_uuids, (char *) uuid);
681+
682+ nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage));
683+ }
684+
685+ /* now we visit all UUIDs that are about to change... */
686+ g_hash_table_iter_init (&h_iter, loaded_uuids);
687+ while (g_hash_table_iter_next (&h_iter, (gpointer *) &loaded_uuid, NULL)) {
688+ NMSIfcfgRHStorage *storage;
689+ NMSettUtilStorageByUuidHead *sbuh;
690+
691+ sbuh = nm_sett_util_storages_lookup_by_uuid (&priv->storages, loaded_uuid);
692+ if (!sbuh)
693+ continue;
694+
695+ c_list_for_each_entry (storage, &sbuh->_storage_by_uuid_lst_head, parent._storage_by_uuid_lst) {
696+ const char *full_filename = nms_ifcfg_rh_storage_get_filename (storage);
697+ gs_unref_object NMSIfcfgRHStorage *storage_new = NULL;
698+ gs_free_error GError *local = NULL;
699+
700+ if (g_hash_table_contains (dupl_filenames, full_filename)) {
701+ /* already re-loaded. */
702+ continue;
703+ }
704+
705+ /* @storage has a UUID that was just loaded from disk, but we have an entry in cache.
706+ * Reload that file too despite not being told to do so. The reason is to get
707+ * the latest file timestamp so that we get the priorities right. */
708+
709+ storage_new = _load_file (self,
710+ full_filename,
711+ &local);
712+ if ( storage_new
713+ && !nm_streq0 (loaded_uuid, nms_ifcfg_rh_storage_get_uuid_opt (storage_new))) {
714+ /* the file now references a different UUID. We are not told to reload
715+ * that file, so this means the existing storage (with the previous
716+ * filename and UUID tuple) is no longer valid. */
717+ g_clear_object (&storage_new);
718+ }
719+
720+ g_hash_table_add (storages_replaced, g_object_ref (storage));
721+ if (storage_new)
722+ nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage_new));
723+ }
724+ }
725+
726+ nm_clear_pointer (&loaded_uuids, g_hash_table_destroy);
727+ nm_clear_pointer (&dupl_filenames, g_hash_table_destroy);
728+
729+ _storages_consolidate (self,
730+ &storages_new,
731+ FALSE,
732+ storages_replaced,
733+ callback,
734+ user_data);
735+}
736+
737+static void
738+reload_connections (NMSettingsPlugin *plugin,
739+ NMSettingsPluginConnectionLoadCallback callback,
740+ gpointer user_data)
741+{
742+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
743+ nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_ifcfg_rh_storage_destroy);
744+
745+ nm_assert_self (self, TRUE);
746+
747+ _load_dir (self, &storages_new);
748+
749+ _storages_consolidate (self,
750+ &storages_new,
751+ TRUE,
752+ NULL,
753+ callback,
754+ user_data);
755+
756+ nm_assert_self (self, FALSE);
757+}
758+
759+static void
760+load_connections_done (NMSettingsPlugin *plugin)
761+{
762+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
763+
764+ /* at the beginning of a load, we emit a change signal for unmanaged/unrecognized
765+ * specs that contain the sum of before and after (_unhandled_specs_merge_storages()).
766+ *
767+ * The idea is that while we emit signals about changes to connection, we have
768+ * the sum of all unmanaged/unrecognized devices from before and after.
769+ *
770+ * This if triggered at the end, to reset the specs. */
771+ _unhandled_specs_reset (self);
772+
773+ nm_assert_self (self, TRUE);
774+}
775+
776+/*****************************************************************************/
777+
778+static gboolean
779+add_connection (NMSettingsPlugin *plugin,
780+ NMConnection *connection,
781+ NMSettingsStorage **out_storage,
782+ NMConnection **out_connection,
783+ GError **error)
784+{
785+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
786+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
787+ gs_unref_object NMSIfcfgRHStorage *storage = NULL;
788+ gs_unref_object NMConnection *reread = NULL;
789+ gs_free char *full_filename = NULL;
790+ GError *local = NULL;
791+ gboolean reread_same;
792+ struct timespec mtime;
793+
794+ nm_assert_self (self, TRUE);
795+ nm_assert (NM_IS_CONNECTION (connection));
796+ nm_assert (out_storage && !*out_storage);
797+ nm_assert (out_connection && !*out_connection);
798+
799+ if (!nms_ifcfg_rh_writer_write_connection (connection,
800+ IFCFG_DIR,
801+ NULL,
802+ nm_sett_util_allow_filename_cb,
803+ NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, NULL),
804+ &full_filename,
805+ &reread,
806+ &reread_same,
807+ &local)) {
808+ _LOGT ("commit: %s (%s): failed to add: %s",
809+ nm_connection_get_uuid (connection),
810+ nm_connection_get_id (connection),
811+ local->message);
812+ g_propagate_error (error, local);
813+ return FALSE;
814+ }
815+
816+ if ( !reread
817+ || reread_same)
818+ nm_g_object_ref_set (&reread, connection);
819+
820+ nm_assert (full_filename && full_filename[0] == '/');
821+
822+ _LOGT ("commit: %s (%s) added as \"%s\"",
823+ nm_connection_get_uuid (reread),
824+ nm_connection_get_id (reread),
825+ full_filename);
826+
827+ storage = nms_ifcfg_rh_storage_new_connection (self,
828+ full_filename,
829+ g_steal_pointer (&reread),
830+ nm_sett_util_stat_mtime (full_filename, FALSE, &mtime));
831+
832+ nm_sett_util_storages_add_take (&priv->storages, g_object_ref (storage));
833+
834+ *out_connection = nms_ifcfg_rh_storage_steal_connection (storage);
835+ *out_storage = NM_SETTINGS_STORAGE (g_steal_pointer (&storage));
836+
837+ nm_assert_self (self, TRUE);
838+
839+ return TRUE;
840+}
841+
842+static gboolean
843+update_connection (NMSettingsPlugin *plugin,
844+ NMSettingsStorage *storage_x,
845+ NMConnection *connection,
846+ NMSettingsStorage **out_storage,
847+ NMConnection **out_connection,
848+ GError **error)
849+{
850+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
851+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
852+ NMSIfcfgRHStorage *storage = NMS_IFCFG_RH_STORAGE (storage_x);
853+ const char *full_filename;
854+ const char *uuid;
855+ GError *local = NULL;
856+ gs_unref_object NMConnection *reread = NULL;
857+ gboolean reread_same;
858+ struct timespec mtime;
859+
860+ nm_assert_self (self, TRUE);
861+ nm_assert (NM_IS_CONNECTION (connection));
862+ nm_assert (NMS_IS_IFCFG_RH_STORAGE (storage));
863+ nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS);
864+ nm_assert (!error || !*error);
865+
866+ uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage);
867+
868+ nm_assert (uuid && nm_streq0 (uuid, nm_connection_get_uuid (connection)));
869+
870+ full_filename = nms_ifcfg_rh_storage_get_filename (storage);
871+
872+ nm_assert (full_filename);
873+ nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename));
874+
875+ if (!nms_ifcfg_rh_writer_write_connection (connection,
876+ IFCFG_DIR,
877+ full_filename,
878+ nm_sett_util_allow_filename_cb,
879+ NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, full_filename),
880+ NULL,
881+ &reread,
882+ &reread_same,
883+ &local)) {
884+ _LOGT ("commit: failure to write %s (%s) to \"%s\": %s",
885+ nm_connection_get_uuid (connection),
886+ nm_connection_get_id (connection),
887+ full_filename,
888+ local->message);
889+ g_propagate_error (error, local);
890+ return FALSE;
891+ }
892+
893+ if ( !reread
894+ || reread_same)
895+ nm_g_object_ref_set (&reread, connection);
896+
897+ _LOGT ("commit: \"%s\": profile %s (%s) written",
898+ full_filename,
899+ uuid,
900+ nm_connection_get_id (connection));
901+
902+ storage->stat_mtime = *nm_sett_util_stat_mtime (full_filename, FALSE, &mtime);
903+
904+ *out_storage = NM_SETTINGS_STORAGE (g_object_ref (storage));
905+ *out_connection = g_steal_pointer (&reread);
906+
907+ nm_assert_self (self, TRUE);
908+
909+ return TRUE;
910+}
911+
912+static gboolean
913+delete_connection (NMSettingsPlugin *plugin,
914+ NMSettingsStorage *storage_x,
915+ GError **error)
916+{
917+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin);
918+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
919+ NMSIfcfgRHStorage *storage = NMS_IFCFG_RH_STORAGE (storage_x);
920+ const char *operation_message;
921+ const char *full_filename;
922+
923+ nm_assert_self (self, TRUE);
924+ nm_assert (!error || !*error);
925+ nm_assert (NMS_IS_IFCFG_RH_STORAGE (storage));
926+
927+ full_filename = nms_ifcfg_rh_storage_get_filename (storage);
928+ nm_assert (full_filename);
929+
930+ nm_assert (nms_ifcfg_rh_storage_get_uuid_opt (storage));
931+
932+ nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename));
933+
934+ {
935+ gs_free char *keyfile = utils_get_keys_path (full_filename);
936+ gs_free char *routefile = utils_get_route_path (full_filename);
937+ gs_free char *route6file = utils_get_route6_path (full_filename);
938+ const char *const files[] = { full_filename, keyfile, routefile, route6file };
939+ gboolean any_deleted = FALSE;
940+ gboolean any_failure = FALSE;
941+ int i;
942+
943+ for (i = 0; i < G_N_ELEMENTS (files); i++) {
944+ int errsv;
945+
946+ if (unlink (files[i]) == 0) {
947+ any_deleted = TRUE;
948+ continue;
949+ }
950+ errsv = errno;
951+ if (errsv == ENOENT)
952+ continue;
953+
954+ _LOGW ("commit: failure to delete file \"%s\": %s",
955+ files[i],
956+ nm_strerror_native (errsv));
957+ any_failure = TRUE;
958+ }
959+ if (any_failure)
960+ operation_message = "failed to delete files from disk";
961+ else if (any_deleted)
962+ operation_message = "deleted from disk";
963+ else
964+ operation_message = "does not exist on disk";
965+ }
966+
967+ _LOGT ("commit: deleted \"%s\", profile %s (%s)",
968+ full_filename,
969+ nms_ifcfg_rh_storage_get_uuid_opt (storage),
970+ operation_message);
971+
972+ nm_sett_util_storages_steal (&priv->storages, storage);
973+ nms_ifcfg_rh_storage_destroy (storage);
974+
975+ nm_assert_self (self, TRUE);
976+
977+ return TRUE;
978+}
979+
980+/*****************************************************************************/
981+
982+static void
983+_unhandled_specs_reset (NMSIfcfgRHPlugin *self)
984+{
985+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
986+ gs_unref_hashtable GHashTable *unmanaged_specs = NULL;
987+ gs_unref_hashtable GHashTable *unrecognized_specs = NULL;
988+ NMSIfcfgRHStorage *storage;
989+
990+ unmanaged_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
991+ unrecognized_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
992+
993+ c_list_for_each_entry (storage, &priv->storages._storage_lst_head, parent._storage_lst) {
994+ if (storage->unmanaged_spec)
995+ g_hash_table_add (unmanaged_specs, g_strdup (storage->unmanaged_spec));
996+ if (storage->unrecognized_spec)
997+ g_hash_table_add (unrecognized_specs, g_strdup (storage->unrecognized_spec));
998+ }
999+
1000+ if (!nm_utils_hashtable_same_keys (unmanaged_specs, priv->unmanaged_specs)) {
1001+ g_hash_table_unref (priv->unmanaged_specs);
1002+ priv->unmanaged_specs = g_steal_pointer (&unmanaged_specs);
1003+ }
1004+ if (!nm_utils_hashtable_same_keys (unrecognized_specs, priv->unrecognized_specs)) {
1005+ g_hash_table_unref (priv->unrecognized_specs);
1006+ priv->unrecognized_specs = g_steal_pointer (&unrecognized_specs);
1007+ }
1008+
1009+ if (!unmanaged_specs)
1010+ _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
1011+ if (!unrecognized_specs)
1012+ _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
1013+}
1014+
1015+static void
1016+_unhandled_specs_merge_storages (NMSIfcfgRHPlugin *self,
1017+ NMSettUtilStorages *storages)
1018+{
1019+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
1020+ gboolean unmanaged_changed = FALSE;
1021+ gboolean unrecognized_changed = FALSE;
1022+ NMSIfcfgRHStorage *storage;
1023+
1024+ c_list_for_each_entry (storage, &storages->_storage_lst_head, parent._storage_lst) {
1025+ if ( storage->unmanaged_spec
1026+ && !g_hash_table_contains (priv->unmanaged_specs, storage->unmanaged_spec)) {
1027+ unmanaged_changed = TRUE;
1028+ g_hash_table_add (priv->unmanaged_specs, g_strdup (storage->unmanaged_spec));
1029+ }
1030+ if ( storage->unrecognized_spec
1031+ && !g_hash_table_contains (priv->unrecognized_specs, storage->unrecognized_spec)) {
1032+ unrecognized_changed = TRUE;
1033+ g_hash_table_add (priv->unrecognized_specs, g_strdup (storage->unrecognized_spec));
1034+ }
1035+ }
1036+
1037+ if (unmanaged_changed)
1038+ _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
1039+ if (unrecognized_changed)
1040+ _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
1041+}
1042+
1043+static GSList *
1044+_unhandled_specs_from_hashtable (GHashTable *hash)
1045+{
1046+ gs_free const char **keys = NULL;
1047+ GSList *list = NULL;
1048+ guint i, l;
1049+
1050+ keys = nm_utils_strdict_get_keys (hash, TRUE, &l);
1051+ for (i = l; i > 0; ) {
1052+ i--;
1053+ list = g_slist_prepend (list, g_strdup (keys[i]));
1054+ }
1055+ return list;
1056+}
1057+
1058+static GSList *
1059+get_unmanaged_specs (NMSettingsPlugin *plugin)
1060+{
1061+ return _unhandled_specs_from_hashtable (NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (plugin)->unmanaged_specs);
1062+}
1063+
1064+static GSList *
1065+get_unrecognized_specs (NMSettingsPlugin *plugin)
1066+{
1067+ return _unhandled_specs_from_hashtable (NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (plugin)->unrecognized_specs);
1068+}
1069+
1070+/*****************************************************************************/
1071+
1072+static void
1073+impl_ifcfgrh_get_ifcfg_details (NMSIfcfgRHPlugin *self,
1074+ GDBusMethodInvocation *context,
1075+ const char *in_ifcfg)
1076+{
1077+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
1078+ gs_free char *ifcfg_path = NULL;
1079+ NMSIfcfgRHStorage *storage;
1080+ const char *uuid;
1081+ const char *path;
1082+
1083+ if (in_ifcfg[0] != '/') {
1084+ g_dbus_method_invocation_return_error (context,
1085+ NM_SETTINGS_ERROR,
1086+ NM_SETTINGS_ERROR_INVALID_CONNECTION,
1087+ "ifcfg path '%s' is not absolute", in_ifcfg);
1088+ return;
1089+ }
1090+
1091+ ifcfg_path = utils_detect_ifcfg_path (in_ifcfg, TRUE);
1092+ if (!ifcfg_path) {
1093+ g_dbus_method_invocation_return_error (context,
1094+ NM_SETTINGS_ERROR,
1095+ NM_SETTINGS_ERROR_INVALID_CONNECTION,
1096+ "ifcfg path '%s' is not an ifcfg base file", in_ifcfg);
1097+ return;
1098+ }
1099+
1100+ storage = nm_sett_util_storages_lookup_by_filename (&priv->storages, ifcfg_path);
1101+ if (!storage) {
1102+ g_dbus_method_invocation_return_error (context,
1103+ NM_SETTINGS_ERROR,
1104+ NM_SETTINGS_ERROR_INVALID_CONNECTION,
1105+ "ifcfg file '%s' unknown", in_ifcfg);
1106+ return;
1107+ }
1108+
1109+ uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage);
1110+ if (!uuid) {
1111+ g_dbus_method_invocation_return_error (context,
1112+ NM_SETTINGS_ERROR,
1113+ NM_SETTINGS_ERROR_INVALID_CONNECTION,
1114+ "ifcfg file '%s' not managed by NetworkManager", in_ifcfg);
1115+ return;
1116+ }
1117+
1118+ /* It is ugly that the ifcfg-rh plugin needs to call back into NMSettings this
1119+ * way.
1120+ * There are alternatives (like invoking a signal), but they are all significant
1121+ * extra code (and performance overhead). So the quick and dirty solution here
1122+ * is likely to be simpler than getting this right (also from point of readability!).
1123+ */
1124+ path = nm_settings_get_dbus_path_for_uuid (nm_settings_get (), uuid);
1125+
1126+ if (!path) {
1127+ g_dbus_method_invocation_return_error (context,
1128+ NM_SETTINGS_ERROR,
1129+ NM_SETTINGS_ERROR_FAILED,
1130+ "unable to get the connection D-Bus path");
1131+ return;
1132+ }
1133+
1134+ g_dbus_method_invocation_return_value (context,
1135+ g_variant_new ("(so)", uuid, path));
1136+}
1137+
1138+/*****************************************************************************/
1139+
1140+static void
1141+_dbus_clear (NMSIfcfgRHPlugin *self)
1142+{
1143+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
1144+ guint id;
1145+
1146+ nm_clear_g_signal_handler (priv->dbus.connection, &priv->dbus.signal_id);
1147+
1148+ nm_clear_g_cancellable (&priv->dbus.cancellable);
1149+
1150+ if ((id = nm_steal_int (&priv->dbus.regist_id))) {
1151+ if (!g_dbus_connection_unregister_object (priv->dbus.connection, id))
1152+ _LOGW ("dbus: unexpected failure to unregister object");
1153+ }
1154+
1155+ g_clear_object (&priv->dbus.connection);
1156+}
1157+
1158+static void
1159+_dbus_connection_closed (GDBusConnection *connection,
1160+ gboolean remote_peer_vanished,
1161+ GError *error,
1162+ gpointer user_data)
1163+{
1164+ _LOGW ("dbus: %s bus closed", IFCFGRH1_BUS_NAME);
1165+ _dbus_clear (NMS_IFCFG_RH_PLUGIN (user_data));
1166+
1167+ /* Retry or recover? */
1168+}
1169+
1170+static void
1171+_method_call (GDBusConnection *connection,
1172+ const char *sender,
1173+ const char *object_path,
1174+ const char *interface_name,
1175+ const char *method_name,
1176+ GVariant *parameters,
1177+ GDBusMethodInvocation *invocation,
1178+ gpointer user_data)
1179+{
1180+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (user_data);
1181+
1182+ if (nm_streq (interface_name, IFCFGRH1_IFACE1_NAME)) {
1183+ if (nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) {
1184+ const char *ifcfg;
1185+
1186+ g_variant_get (parameters, "(&s)", &ifcfg);
1187+ impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg);
1188+ return;
1189+ }
1190+ }
1191+
1192+ g_dbus_method_invocation_return_error (invocation,
1193+ G_DBUS_ERROR,
1194+ G_DBUS_ERROR_UNKNOWN_METHOD,
1195+ "Unknown method %s",
1196+ method_name);
1197+}
1198+
1199+static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO (
1200+ IFCFGRH1_IFACE1_NAME,
1201+ .methods = NM_DEFINE_GDBUS_METHOD_INFOS (
1202+ NM_DEFINE_GDBUS_METHOD_INFO (
1203+ IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS,
1204+ .in_args = NM_DEFINE_GDBUS_ARG_INFOS (
1205+ NM_DEFINE_GDBUS_ARG_INFO ("ifcfg", "s"),
1206+ ),
1207+ .out_args = NM_DEFINE_GDBUS_ARG_INFOS (
1208+ NM_DEFINE_GDBUS_ARG_INFO ("uuid", "s"),
1209+ NM_DEFINE_GDBUS_ARG_INFO ("path", "o"),
1210+ ),
1211+ ),
1212+ ),
1213+);
1214+
1215+static void
1216+_dbus_request_name_done (GObject *source_object,
1217+ GAsyncResult *res,
1218+ gpointer user_data)
1219+{
1220+ GDBusConnection *connection = G_DBUS_CONNECTION (source_object);
1221+ NMSIfcfgRHPlugin *self;
1222+ NMSIfcfgRHPluginPrivate *priv;
1223+ gs_free_error GError *error = NULL;
1224+ gs_unref_variant GVariant *ret = NULL;
1225+ guint32 result;
1226+
1227+ ret = g_dbus_connection_call_finish (connection, res, &error);
1228+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1229+ return;
1230+
1231+ self = NMS_IFCFG_RH_PLUGIN (user_data);
1232+ priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
1233+
1234+ g_clear_object (&priv->dbus.cancellable);
1235+
1236+ if (!ret) {
1237+ _LOGW ("dbus: couldn't acquire D-Bus service: %s", error->message);
1238+ _dbus_clear (self);
1239+ return;
1240+ }
1241+
1242+ g_variant_get (ret, "(u)", &result);
1243+
1244+ if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1245+ _LOGW ("dbus: couldn't acquire ifcfgrh1 D-Bus service (already taken)");
1246+ _dbus_clear (self);
1247+ return;
1248+ }
1249+
1250+ {
1251+ static const GDBusInterfaceVTable interface_vtable = {
1252+ .method_call = _method_call,
1253+ };
1254+
1255+ priv->dbus.regist_id = g_dbus_connection_register_object (connection,
1256+ IFCFGRH1_OBJECT_PATH,
1257+ interface_info,
1258+ NM_UNCONST_PTR (GDBusInterfaceVTable, &interface_vtable),
1259+ self,
1260+ NULL,
1261+ &error);
1262+ if (!priv->dbus.regist_id) {
1263+ _LOGW ("dbus: couldn't register D-Bus service: %s", error->message);
1264+ _dbus_clear (self);
1265+ return;
1266+ }
1267+ }
1268+
1269+ _LOGD ("dbus: acquired D-Bus service %s and exported %s object",
1270+ IFCFGRH1_BUS_NAME,
1271+ IFCFGRH1_OBJECT_PATH);
1272+}
1273+
1274+static void
1275+_dbus_create_done (GObject *source_object,
1276+ GAsyncResult *res,
1277+ gpointer user_data)
1278+{
1279+ NMSIfcfgRHPlugin *self;
1280+ NMSIfcfgRHPluginPrivate *priv;
1281+ gs_free_error GError *error = NULL;
1282+ GDBusConnection *connection;
1283+
1284+ connection = g_dbus_connection_new_for_address_finish (res, &error);
1285+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1286+ return;
1287+
1288+ self = NMS_IFCFG_RH_PLUGIN (user_data);
1289+ priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
1290+
1291+ g_clear_object (&priv->dbus.cancellable);
1292+
1293+ if (!connection) {
1294+ _LOGW ("dbus: couldn't initialize system bus: %s", error->message);
1295+ return;
1296+ }
1297+
1298+ priv->dbus.connection = connection;
1299+ priv->dbus.cancellable = g_cancellable_new ();
1300+
1301+ priv->dbus.signal_id = g_signal_connect (priv->dbus.connection,
1302+ "closed",
1303+ G_CALLBACK (_dbus_connection_closed),
1304+ self);
1305+
1306+ g_dbus_connection_call (priv->dbus.connection,
1307+ DBUS_SERVICE_DBUS,
1308+ DBUS_PATH_DBUS,
1309+ DBUS_INTERFACE_DBUS,
1310+ "RequestName",
1311+ g_variant_new ("(su)",
1312+ IFCFGRH1_BUS_NAME,
1313+ DBUS_NAME_FLAG_DO_NOT_QUEUE),
1314+ G_VARIANT_TYPE ("(u)"),
1315+ G_DBUS_CALL_FLAGS_NONE,
1316+ -1,
1317+ priv->dbus.cancellable,
1318+ _dbus_request_name_done,
1319+ self);
1320+}
1321+
1322+static void
1323+_dbus_setup (NMSIfcfgRHPlugin *self)
1324+{
1325+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
1326+ gs_free char *address = NULL;
1327+ gs_free_error GError *error = NULL;
1328+
1329+ _dbus_clear (self);
1330+
1331+ address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
1332+ if (address == NULL) {
1333+ _LOGW ("dbus: failed getting address for system bus: %s", error->message);
1334+ return;
1335+ }
1336+
1337+ priv->dbus.cancellable = g_cancellable_new ();
1338+
1339+ g_dbus_connection_new_for_address (address,
1340+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT
1341+ | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
1342+ NULL,
1343+ priv->dbus.cancellable,
1344+ _dbus_create_done,
1345+ self);
1346+}
1347+
1348+static void
1349+config_changed_cb (NMConfig *config,
1350+ NMConfigData *config_data,
1351+ NMConfigChangeFlags changes,
1352+ NMConfigData *old_data,
1353+ NMSIfcfgRHPlugin *self)
1354+{
1355+ NMSIfcfgRHPluginPrivate *priv;
1356+
1357+ /* If the dbus connection for some reason is borked the D-Bus service
1358+ * won't be offered.
1359+ *
1360+ * On SIGHUP and SIGUSR1 try to re-connect to D-Bus. So in the unlikely
1361+ * event that the D-Bus connection is broken, that allows for recovery
1362+ * without need for restarting NetworkManager. */
1363+ if (!NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_CAUSE_SIGHUP
1364+ | NM_CONFIG_CHANGE_CAUSE_SIGUSR1))
1365+ return;
1366+
1367+ priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
1368+ if ( !priv->dbus.connection
1369+ && !priv->dbus.cancellable)
1370+ _dbus_setup (self);
1371+}
1372+
1373+/*****************************************************************************/
1374+
1375+static void
1376+nms_ifcfg_rh_plugin_init (NMSIfcfgRHPlugin *self)
1377+{
1378+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
1379+
1380+ priv->config = g_object_ref (nm_config_get ());
1381+
1382+ priv->unmanaged_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
1383+ priv->unrecognized_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
1384+
1385+ priv->storages = (NMSettUtilStorages) NM_SETT_UTIL_STORAGES_INIT (priv->storages, nms_ifcfg_rh_storage_destroy);
1386+}
1387+
1388+static void
1389+constructed (GObject *object)
1390+{
1391+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (object);
1392+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
1393+
1394+ G_OBJECT_CLASS (nms_ifcfg_rh_plugin_parent_class)->constructed (object);
1395+
1396+ g_signal_connect (priv->config,
1397+ NM_CONFIG_SIGNAL_CONFIG_CHANGED,
1398+ G_CALLBACK (config_changed_cb),
1399+ self);
1400+
1401+ _dbus_setup (self);
1402+}
1403+
1404+static void
1405+dispose (GObject *object)
1406+{
1407+ NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (object);
1408+ NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self);
1409+
1410+ if (priv->config)
1411+ g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self);
1412+
1413+ /* FIXME(shutdown) we need a stop method so that we can unregistering the D-Bus service
1414+ * when NMSettings is shutting down, and not when the instance gets destroyed. */
1415+ _dbus_clear (self);
1416+
1417+ nm_sett_util_storages_clear (&priv->storages);
1418+
1419+ g_clear_object (&priv->config);
1420+
1421+ G_OBJECT_CLASS (nms_ifcfg_rh_plugin_parent_class)->dispose (object);
1422+
1423+ nm_clear_pointer (&priv->unmanaged_specs, g_hash_table_destroy);
1424+ nm_clear_pointer (&priv->unrecognized_specs, g_hash_table_destroy);
1425+}
1426+
1427+static void
1428+nms_ifcfg_rh_plugin_class_init (NMSIfcfgRHPluginClass *klass)
1429+{
1430+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
1431+ NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass);
1432+
1433+ object_class->constructed = constructed;
1434+ object_class->dispose = dispose;
1435+
1436+ plugin_class->plugin_name = "ifcfg-rh";
1437+ plugin_class->get_unmanaged_specs = get_unmanaged_specs;
1438+ plugin_class->get_unrecognized_specs = get_unrecognized_specs;
1439+ plugin_class->reload_connections = reload_connections;
1440+ plugin_class->load_connections = load_connections;
1441+ plugin_class->load_connections_done = load_connections_done;
1442+ plugin_class->add_connection = add_connection;
1443+ plugin_class->update_connection = update_connection;
1444+ plugin_class->delete_connection = delete_connection;
1445+}
1446+
1447+/*****************************************************************************/
1448+
1449+G_MODULE_EXPORT NMSettingsPlugin *
1450+nm_settings_plugin_factory (void)
1451+{
1452+ return g_object_new (NMS_TYPE_IFCFG_RH_PLUGIN, NULL);
1453+}
1454diff --git a/src/settings/plugins/netplan/nms-netplan-plugin.h b/src/settings/plugins/netplan/nms-netplan-plugin.h
1455new file mode 100644
1456index 0000000000000000000000000000000000000000..a226b323049f42da29a4a001d28874bb6e687a55
1457--- /dev/null
1458+++ b/src/settings/plugins/netplan/nms-netplan-plugin.h
1459@@ -0,0 +1,25 @@
1460+// SPDX-License-Identifier: GPL-2.0+
1461+/* NetworkManager system settings service
1462+ *
1463+ * Dan Williams <dcbw@redhat.com>
1464+ * Søren Sandmann <sandmann@daimi.au.dk>
1465+ *
1466+ * Copyright (C) 2007 - 2008 Red Hat, Inc.
1467+ */
1468+
1469+#ifndef __NMS_IFCFG_RH_PLUGIN_H__
1470+#define __NMS_IFCFG_RH_PLUGIN_H__
1471+
1472+#define NMS_TYPE_IFCFG_RH_PLUGIN (nms_ifcfg_rh_plugin_get_type ())
1473+#define NMS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPlugin))
1474+#define NMS_IFCFG_RH_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass))
1475+#define NMS_IS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFCFG_RH_PLUGIN))
1476+#define NMS_IS_IFCFG_RH_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFCFG_RH_PLUGIN))
1477+#define NMS_IFCFG_RH_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass))
1478+
1479+typedef struct _NMSIfcfgRHPlugin NMSIfcfgRHPlugin;
1480+typedef struct _NMSIfcfgRHPluginClass NMSIfcfgRHPluginClass;
1481+
1482+GType nms_ifcfg_rh_plugin_get_type (void);
1483+
1484+#endif /* __NMS_IFCFG_RH_PLUGIN_H__ */
1485diff --git a/src/settings/plugins/netplan/nms-netplan-reader.c b/src/settings/plugins/netplan/nms-netplan-reader.c
1486new file mode 100644
1487index 0000000000000000000000000000000000000000..f348ca473a2102dc3231d1485dc52250384783c1
1488--- /dev/null
1489+++ b/src/settings/plugins/netplan/nms-netplan-reader.c
1490@@ -0,0 +1,5991 @@
1491+// SPDX-License-Identifier: GPL-2.0+
1492+/* NetworkManager system settings service
1493+ *
1494+ * Copyright 2008 - 2017 Red Hat, Inc.
1495+ */
1496+
1497+#include "nm-default.h"
1498+
1499+#include "nms-ifcfg-rh-reader.h"
1500+
1501+#include <stdlib.h>
1502+#include <sys/types.h>
1503+#include <sys/socket.h>
1504+#include <arpa/inet.h>
1505+#include <sys/wait.h>
1506+#include <sys/inotify.h>
1507+#include <sys/ioctl.h>
1508+#include <unistd.h>
1509+
1510+#include "nm-glib-aux/nm-secret-utils.h"
1511+#include "nm-connection.h"
1512+#include "nm-dbus-interface.h"
1513+#include "nm-setting-connection.h"
1514+#include "nm-setting-ip4-config.h"
1515+#include "nm-setting-vlan.h"
1516+#include "nm-setting-ip6-config.h"
1517+#include "nm-setting-wired.h"
1518+#include "nm-setting-wireless.h"
1519+#include "nm-setting-ethtool.h"
1520+#include "nm-setting-8021x.h"
1521+#include "nm-setting-bond.h"
1522+#include "nm-setting-team.h"
1523+#include "nm-setting-team-port.h"
1524+#include "nm-setting-bridge.h"
1525+#include "nm-setting-bridge-port.h"
1526+#include "nm-setting-dcb.h"
1527+#include "nm-setting-user.h"
1528+#include "nm-setting-proxy.h"
1529+#include "nm-setting-generic.h"
1530+#include "nm-core-internal.h"
1531+#include "nm-utils.h"
1532+#include "nm-libnm-core-intern/nm-ethtool-utils.h"
1533+
1534+#include "platform/nm-platform.h"
1535+#include "NetworkManagerUtils.h"
1536+
1537+#include "nms-ifcfg-rh-common.h"
1538+#include "nms-ifcfg-rh-utils.h"
1539+#include "shvar.h"
1540+
1541+/*****************************************************************************/
1542+
1543+#define _NMLOG_DOMAIN LOGD_SETTINGS
1544+#define _NMLOG_PREFIX_NAME "ifcfg-rh"
1545+#define _NMLOG(level, ...) \
1546+ G_STMT_START { \
1547+ nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \
1548+ "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
1549+ _NMLOG_PREFIX_NAME": " \
1550+ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
1551+ } G_STMT_END
1552+
1553+#define PARSE_WARNING(...) _LOGW ("%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), " " _NM_UTILS_MACRO_REST(__VA_ARGS__))
1554+
1555+/*****************************************************************************/
1556+
1557+static char *
1558+get_full_file_path (const char *ifcfg_path, const char *file_path)
1559+{
1560+ const char *base = file_path;
1561+ gs_free char *dirname = NULL;
1562+ char *p;
1563+
1564+ g_return_val_if_fail (ifcfg_path != NULL, NULL);
1565+ g_return_val_if_fail (file_path != NULL, NULL);
1566+
1567+ if (file_path[0] == '/')
1568+ return g_strdup (file_path);
1569+
1570+ p = strrchr (file_path, '/');
1571+ if (p)
1572+ base = p + 1;
1573+
1574+ dirname = g_path_get_dirname (ifcfg_path);
1575+ return g_build_path ("/", dirname, base, NULL);
1576+}
1577+
1578+/*****************************************************************************/
1579+
1580+static NMSettingSecretFlags
1581+_secret_read_ifcfg_flags (shvarFile *ifcfg, const char *flags_key)
1582+{
1583+ NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
1584+ gs_free char *val_free = NULL;
1585+ const char *val;
1586+
1587+ nm_assert (flags_key);
1588+ nm_assert (g_str_has_suffix (flags_key, "_FLAGS"));
1589+
1590+ val = svGetValueStr (ifcfg, flags_key, &val_free);
1591+ if (val) {
1592+ if (strstr (val, SECRET_FLAG_AGENT))
1593+ flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
1594+ if (strstr (val, SECRET_FLAG_NOT_SAVED))
1595+ flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED;
1596+ if (strstr (val, SECRET_FLAG_NOT_REQUIRED))
1597+ flags |= NM_SETTING_SECRET_FLAG_NOT_REQUIRED;
1598+ }
1599+ return flags;
1600+}
1601+
1602+static void
1603+_secret_read_ifcfg (shvarFile *ifcfg,
1604+ shvarFile *keys_ifcfg,
1605+ const char *name,
1606+ char **value,
1607+ NMSettingSecretFlags *flags)
1608+{
1609+ char flags_key[250];
1610+
1611+ nm_sprintf_buf (flags_key, "%s_FLAGS", name);
1612+
1613+ *flags = _secret_read_ifcfg_flags (ifcfg, flags_key);
1614+
1615+ if (*flags != NM_SETTING_SECRET_FLAG_NONE)
1616+ *value = NULL;
1617+ else {
1618+ *value = svGetValue_cp (ifcfg, name);
1619+ if (!*value && keys_ifcfg)
1620+ *value = svGetValue_cp (keys_ifcfg, name);
1621+ }
1622+}
1623+
1624+static void
1625+_secret_set_from_ifcfg (gpointer setting,
1626+ shvarFile *ifcfg,
1627+ shvarFile *keys_ifcfg,
1628+ const char *ifcfg_key,
1629+ const char *property_name)
1630+{
1631+ nm_auto_free_secret char *secret = NULL;
1632+ NMSettingSecretFlags flags;
1633+ char flags_key[250];
1634+
1635+ nm_assert (NM_IS_SETTING (setting));
1636+
1637+ _secret_read_ifcfg (ifcfg, keys_ifcfg, ifcfg_key, &secret, &flags);
1638+
1639+ g_object_set (setting,
1640+ property_name,
1641+ secret,
1642+ nm_sprintf_buf (flags_key, "%s-flags", property_name),
1643+ flags,
1644+ NULL);
1645+}
1646+
1647+static gboolean
1648+_secret_password_raw_to_bytes (const char *ifcfg_key,
1649+ const char *password_raw,
1650+ GBytes **out_bytes,
1651+ GError **error)
1652+{
1653+ nm_auto_free_secret_buf NMSecretBuf *secret = NULL;
1654+ gsize len;
1655+
1656+ if (!password_raw) {
1657+ NM_SET_OUT (out_bytes, NULL);
1658+ return TRUE;
1659+ }
1660+
1661+ if (password_raw[0] == '0' && password_raw[1] == 'x')
1662+ password_raw += 2;
1663+
1664+ secret = nm_secret_buf_new (strlen (password_raw) / 2 + 3);
1665+ if (!nm_utils_hexstr2bin_full (password_raw, FALSE, FALSE, ":", 0, secret->bin, secret->len, &len)) {
1666+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
1667+ "Invalid hex password in %s",
1668+ ifcfg_key);
1669+ return FALSE;
1670+ }
1671+
1672+ NM_SET_OUT (out_bytes, nm_secret_buf_to_gbytes_take (g_steal_pointer (&secret), len));
1673+ return TRUE;
1674+}
1675+
1676+/*****************************************************************************/
1677+
1678+static GBytes *
1679+_cert_get_cert_bytes (const char *ifcfg_path,
1680+ const char *value,
1681+ GError **error)
1682+{
1683+ gs_free char *path = NULL;
1684+
1685+ if (NM_STR_HAS_PREFIX (value, "pkcs11:"))
1686+ return _nm_setting_802_1x_cert_value_to_bytes (NM_SETTING_802_1X_CK_SCHEME_PKCS11, (guint8 *) value, -1, error);
1687+
1688+ path = get_full_file_path (ifcfg_path, value);
1689+ return _nm_setting_802_1x_cert_value_to_bytes (NM_SETTING_802_1X_CK_SCHEME_PATH, (guint8 *) path, -1, error);
1690+}
1691+
1692+static gboolean
1693+_cert_get_cert (shvarFile *ifcfg,
1694+ const char *ifcfg_key,
1695+ GBytes **out_cert,
1696+ NMSetting8021xCKScheme *out_scheme,
1697+ GError **error)
1698+{
1699+ nm_auto_free_secret char *val_free = NULL;
1700+ const char *val;
1701+ gs_unref_bytes GBytes *cert = NULL;
1702+ GError *local = NULL;
1703+ NMSetting8021xCKScheme scheme;
1704+
1705+ val = svGetValueStr (ifcfg, ifcfg_key, &val_free);
1706+ if (!val) {
1707+ NM_SET_OUT (out_cert, NULL);
1708+ NM_SET_OUT (out_scheme, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
1709+ return TRUE;
1710+ }
1711+
1712+ cert = _cert_get_cert_bytes (svFileGetName (ifcfg), val, &local);
1713+ if (!cert)
1714+ goto err;
1715+
1716+ scheme = _nm_setting_802_1x_cert_get_scheme (cert, &local);
1717+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN)
1718+ goto err;
1719+
1720+ NM_SET_OUT (out_cert, g_steal_pointer (&cert));
1721+ NM_SET_OUT (out_scheme, scheme);
1722+ return TRUE;
1723+
1724+err:
1725+ g_set_error (error,
1726+ NM_SETTINGS_ERROR,
1727+ NM_SETTINGS_ERROR_INVALID_CONNECTION,
1728+ "invalid certificate %s: %s",
1729+ ifcfg_key,
1730+ local->message);
1731+ g_error_free (local);
1732+ return FALSE;
1733+}
1734+
1735+static gboolean
1736+_cert_set_from_ifcfg (gpointer setting,
1737+ shvarFile *ifcfg,
1738+ const char *ifcfg_key,
1739+ const char *property_name,
1740+ GBytes **out_cert,
1741+ GError **error)
1742+{
1743+ gs_unref_bytes GBytes *cert = NULL;
1744+
1745+ if (!_cert_get_cert (ifcfg,
1746+ ifcfg_key,
1747+ &cert,
1748+ NULL,
1749+ error))
1750+ return FALSE;
1751+
1752+ g_object_set (setting, property_name, cert, NULL);
1753+
1754+ NM_SET_OUT (out_cert, g_steal_pointer (&cert));
1755+ return TRUE;
1756+}
1757+
1758+/*****************************************************************************/
1759+
1760+static void
1761+check_if_bond_slave (shvarFile *ifcfg,
1762+ NMSettingConnection *s_con)
1763+{
1764+ gs_free char *value = NULL;
1765+ const char *v;
1766+ const char *master;
1767+
1768+ v = svGetValueStr (ifcfg, "MASTER_UUID", &value);
1769+ if (!v)
1770+ v = svGetValueStr (ifcfg, "MASTER", &value);
1771+
1772+ if (v) {
1773+ master = nm_setting_connection_get_master (s_con);
1774+ if (master) {
1775+ PARSE_WARNING ("Already configured as slave of %s. Ignoring MASTER{_UUID}=\"%s\"",
1776+ master, v);
1777+ return;
1778+ }
1779+
1780+ g_object_set (s_con,
1781+ NM_SETTING_CONNECTION_MASTER, v,
1782+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME,
1783+ NULL);
1784+ }
1785+
1786+ /* We should be checking for SLAVE=yes as well, but NM used to not set that,
1787+ * so for backward-compatibility, we don't check.
1788+ */
1789+}
1790+
1791+static void
1792+check_if_team_slave (shvarFile *ifcfg,
1793+ NMSettingConnection *s_con)
1794+{
1795+ gs_free char *value = NULL;
1796+ const char *v;
1797+ const char *master;
1798+
1799+ v = svGetValueStr (ifcfg, "TEAM_MASTER_UUID", &value);
1800+ if (!v)
1801+ v = svGetValueStr (ifcfg, "TEAM_MASTER", &value);
1802+ if (!v)
1803+ return;
1804+
1805+ master = nm_setting_connection_get_master (s_con);
1806+ if (master) {
1807+ PARSE_WARNING ("Already configured as slave of %s. Ignoring TEAM_MASTER{_UUID}=\"%s\"",
1808+ master, v);
1809+ return;
1810+ }
1811+
1812+ g_object_set (s_con,
1813+ NM_SETTING_CONNECTION_MASTER, v,
1814+ NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME,
1815+ NULL);
1816+}
1817+
1818+static char *
1819+make_connection_name (shvarFile *ifcfg,
1820+ const char *ifcfg_name,
1821+ const char *suggested,
1822+ const char *prefix)
1823+{
1824+ char *full_name = NULL, *name;
1825+
1826+ /* If the ifcfg file already has a NAME, always use that */
1827+ name = svGetValueStr_cp (ifcfg, "NAME");
1828+ if (name)
1829+ return name;
1830+
1831+ /* Otherwise construct a new NAME */
1832+ if (!prefix)
1833+ prefix = "System";
1834+
1835+ /* For cosmetic reasons, if the suggested name is the same as
1836+ * the ifcfg files name, don't use it. Mainly for wifi so that
1837+ * the SSID is shown in the connection ID instead of just "wlan0".
1838+ */
1839+ if (suggested && strcmp (ifcfg_name, suggested))
1840+ full_name = g_strdup_printf ("%s %s (%s)", prefix, suggested, ifcfg_name);
1841+ else
1842+ full_name = g_strdup_printf ("%s %s", prefix, ifcfg_name);
1843+
1844+ return full_name;
1845+}
1846+
1847+static NMSetting *
1848+make_connection_setting (const char *file,
1849+ shvarFile *ifcfg,
1850+ const char *type,
1851+ const char *suggested,
1852+ const char *prefix)
1853+{
1854+ NMSettingConnection *s_con;
1855+ NMSettingConnectionLldp lldp;
1856+ const char *ifcfg_name = NULL;
1857+ gs_free char *new_id = NULL;
1858+ const char *uuid;
1859+ gs_free char *uuid_free = NULL;
1860+ gs_free char *value = NULL;
1861+ const char *v;
1862+ gs_free char *stable_id = NULL;
1863+ const char *const *iter;
1864+ int vint64, i_val;
1865+
1866+ ifcfg_name = utils_get_ifcfg_name (file, TRUE);
1867+ if (!ifcfg_name)
1868+ return NULL;
1869+
1870+ s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
1871+
1872+ new_id = make_connection_name (ifcfg, ifcfg_name, suggested, prefix);
1873+ g_object_set (s_con, NM_SETTING_CONNECTION_ID, new_id, NULL);
1874+
1875+ /* Try for a UUID key before falling back to hashing the file name */
1876+ uuid = svGetValueStr (ifcfg, "UUID", &uuid_free);
1877+ if (!uuid) {
1878+ uuid_free = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
1879+ uuid = uuid_free;
1880+ }
1881+
1882+ g_object_set (s_con,
1883+ NM_SETTING_CONNECTION_TYPE, type,
1884+ NM_SETTING_CONNECTION_UUID, uuid,
1885+ NM_SETTING_CONNECTION_STABLE_ID, svGetValue (ifcfg, "STABLE_ID", &stable_id),
1886+ NULL);
1887+
1888+ v = svGetValueStr (ifcfg, "DEVICE", &value);
1889+ if (v) {
1890+ GError *error = NULL;
1891+
1892+ if (nm_utils_is_valid_iface_name (v, &error)) {
1893+ g_object_set (s_con,
1894+ NM_SETTING_CONNECTION_INTERFACE_NAME, v,
1895+ NULL);
1896+ } else {
1897+ PARSE_WARNING ("invalid DEVICE name '%s': %s", v, error->message);
1898+ g_error_free (error);
1899+ }
1900+ }
1901+
1902+ nm_clear_g_free (&value);
1903+ v = svGetValueStr (ifcfg, "LLDP", &value);
1904+ if (nm_streq0 (v, "rx"))
1905+ lldp = NM_SETTING_CONNECTION_LLDP_ENABLE_RX;
1906+ else
1907+ lldp = svParseBoolean (v, NM_SETTING_CONNECTION_LLDP_DEFAULT);
1908+
1909+ /* Missing ONBOOT is treated as "ONBOOT=true" by the old network service */
1910+ g_object_set (s_con,
1911+ NM_SETTING_CONNECTION_AUTOCONNECT,
1912+ svGetValueBoolean (ifcfg, "ONBOOT", TRUE),
1913+ NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY,
1914+ (int) svGetValueInt64 (ifcfg, "AUTOCONNECT_PRIORITY", 10,
1915+ NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN,
1916+ NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX,
1917+ NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT),
1918+ NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,
1919+ (int) svGetValueInt64 (ifcfg, "AUTOCONNECT_RETRIES", 10,
1920+ -1, G_MAXINT32, -1),
1921+ NM_SETTING_CONNECTION_MULTI_CONNECT,
1922+ (gint) svGetValueInt64 (ifcfg, "MULTI_CONNECT", 10,
1923+ G_MININT32, G_MAXINT32, NM_CONNECTION_MULTI_CONNECT_DEFAULT),
1924+ NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
1925+ svGetValueBoolean (ifcfg, "AUTOCONNECT_SLAVES", NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT),
1926+ NM_SETTING_CONNECTION_LLDP, lldp,
1927+ NULL);
1928+
1929+ nm_clear_g_free (&value);
1930+ v = svGetValueStr (ifcfg, "USERS", &value);
1931+ if (v) {
1932+ gs_free const char **items = NULL;
1933+
1934+ items = nm_utils_strsplit_set (v, " ");
1935+ for (iter = items; iter && *iter; iter++) {
1936+ if (!nm_setting_connection_add_permission (s_con, "user", *iter, NULL))
1937+ PARSE_WARNING ("invalid USERS item '%s'", *iter);
1938+ }
1939+ }
1940+
1941+ nm_clear_g_free (&value);
1942+ v = svGetValueStr (ifcfg, "ZONE", &value);
1943+ g_object_set (s_con, NM_SETTING_CONNECTION_ZONE, v, NULL);
1944+
1945+ nm_clear_g_free (&value);
1946+ v = svGetValueStr (ifcfg, "SECONDARY_UUIDS", &value);
1947+ if (v) {
1948+ gs_free const char **items = NULL;
1949+
1950+ items = nm_utils_strsplit_set (v, " \t");
1951+ for (iter = items; iter && *iter; iter++) {
1952+ if (!nm_setting_connection_add_secondary (s_con, *iter))
1953+ PARSE_WARNING ("secondary connection UUID '%s' already added", *iter);
1954+ }
1955+ }
1956+
1957+ nm_clear_g_free (&value);
1958+ v = svGetValueStr (ifcfg, "BRIDGE_UUID", &value);
1959+ if (!v)
1960+ v = svGetValueStr (ifcfg, "BRIDGE", &value);
1961+ if (v) {
1962+ const char *old_value;
1963+
1964+ if ((old_value = nm_setting_connection_get_master (s_con))) {
1965+ PARSE_WARNING ("Already configured as slave of %s. Ignoring BRIDGE=\"%s\"",
1966+ old_value, v);
1967+ } else {
1968+ g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, v, NULL);
1969+ g_object_set (s_con, NM_SETTING_CONNECTION_SLAVE_TYPE,
1970+ NM_SETTING_BRIDGE_SETTING_NAME, NULL);
1971+ }
1972+ }
1973+
1974+ check_if_bond_slave (ifcfg, s_con);
1975+ check_if_team_slave (ifcfg, s_con);
1976+
1977+ nm_clear_g_free (&value);
1978+ v = svGetValueStr (ifcfg, "OVS_PORT_UUID", &value);
1979+ if (!v)
1980+ v = svGetValueStr (ifcfg, "OVS_PORT", &value);
1981+ if (v) {
1982+ const char *old_value;
1983+
1984+ if ((old_value = nm_setting_connection_get_master (s_con))) {
1985+ PARSE_WARNING ("Already configured as slave of %s. Ignoring OVS_PORT=\"%s\"",
1986+ old_value, v);
1987+ } else {
1988+ g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, v, NULL);
1989+ g_object_set (s_con, NM_SETTING_CONNECTION_SLAVE_TYPE,
1990+ NM_SETTING_OVS_PORT_SETTING_NAME, NULL);
1991+ }
1992+ }
1993+
1994+ nm_clear_g_free (&value);
1995+ v = svGetValueStr (ifcfg, "GATEWAY_PING_TIMEOUT", &value);
1996+ if (v) {
1997+ gint64 tmp;
1998+
1999+ tmp = _nm_utils_ascii_str_to_int64 (v, 10, 0, G_MAXINT32 - 1, -1);
2000+ if (tmp >= 0) {
2001+ if (tmp > 600) {
2002+ tmp = 600;
2003+ PARSE_WARNING ("invalid GATEWAY_PING_TIMEOUT time");
2004+ }
2005+ g_object_set (s_con, NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, (guint) tmp, NULL);
2006+ } else
2007+ PARSE_WARNING ("invalid GATEWAY_PING_TIMEOUT time");
2008+ }
2009+
2010+ switch (svGetValueBoolean (ifcfg, "CONNECTION_METERED", -1)) {
2011+ case TRUE:
2012+ g_object_set (s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_YES, NULL);
2013+ break;
2014+ case FALSE:
2015+ g_object_set (s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_NO, NULL);
2016+ break;
2017+ }
2018+
2019+ vint64 = svGetValueInt64 (ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1);
2020+ g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (int) vint64, NULL);
2021+
2022+ nm_clear_g_free (&value);
2023+ v = svGetValueStr (ifcfg, "DEVTIMEOUT", &value);
2024+ if (v) {
2025+ vint64 = _nm_utils_ascii_str_to_int64 (v, 10, 0, ((gint64) G_MAXINT32) / 1000, -1);
2026+ if (vint64 != -1)
2027+ vint64 *= 1000;
2028+ else {
2029+ char *endptr;
2030+ double d;
2031+
2032+ d = g_ascii_strtod (v, &endptr);
2033+ if ( errno == 0
2034+ && endptr[0] == '\0'
2035+ && d >= 0.0) {
2036+ d *= 1000.0;
2037+
2038+ /* We round. Yes, this is not correct to round IEEE 754 floats in general,
2039+ * but sufficient for our case where we know that NetworkManager wrote the
2040+ * setting with up to 3 digits for the milliseconds. */
2041+ d += 0.5;
2042+ if ( d >= 0.0
2043+ && d <= (double) G_MAXINT32)
2044+ vint64 = (gint64) d;
2045+ }
2046+ }
2047+ if (vint64 == -1)
2048+ PARSE_WARNING ("invalid DEVTIMEOUT setting");
2049+ else
2050+ g_object_set (s_con, NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, (int) vint64, NULL);
2051+ }
2052+
2053+ i_val = NM_SETTING_CONNECTION_MDNS_DEFAULT;
2054+ if (!svGetValueEnum (ifcfg, "MDNS",
2055+ nm_setting_connection_mdns_get_type (),
2056+ &i_val, NULL))
2057+ PARSE_WARNING ("invalid MDNS setting");
2058+ g_object_set (s_con, NM_SETTING_CONNECTION_MDNS, i_val, NULL);
2059+
2060+ i_val = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
2061+ if (!svGetValueEnum (ifcfg, "LLMNR",
2062+ nm_setting_connection_llmnr_get_type (),
2063+ &i_val, NULL))
2064+ PARSE_WARNING ("invalid LLMNR setting");
2065+ g_object_set (s_con, NM_SETTING_CONNECTION_LLMNR, i_val, NULL);
2066+
2067+ return NM_SETTING (s_con);
2068+}
2069+
2070+static gboolean
2071+read_ip4_address (shvarFile *ifcfg,
2072+ const char *tag,
2073+ gboolean *out_has_key,
2074+ guint32 *out_addr,
2075+ GError **error)
2076+{
2077+ gs_free char *value_to_free = NULL;
2078+ const char *value;
2079+ in_addr_t a;
2080+
2081+ nm_assert (ifcfg);
2082+ nm_assert (tag);
2083+ nm_assert (!error || !*error);
2084+
2085+ value = svGetValueStr (ifcfg, tag, &value_to_free);
2086+ if (!value) {
2087+ NM_SET_OUT (out_has_key, FALSE);
2088+ NM_SET_OUT (out_addr, 0);
2089+ return TRUE;
2090+ }
2091+
2092+ if (inet_pton (AF_INET, value, &a) != 1) {
2093+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2094+ "Invalid %s IP4 address '%s'", tag, value);
2095+ return FALSE;
2096+ }
2097+
2098+ NM_SET_OUT (out_has_key, TRUE);
2099+ NM_SET_OUT (out_addr, a);
2100+ return TRUE;
2101+}
2102+
2103+static gboolean
2104+is_any_ip4_address_defined (shvarFile *ifcfg, int *idx)
2105+{
2106+ int i, ignore, *ret_idx;
2107+
2108+ ret_idx = idx ?: &ignore;
2109+
2110+ for (i = -1; i <= 2; i++) {
2111+ gs_free char *value = NULL;
2112+ char tag[256];
2113+
2114+ if (svGetValueStr (ifcfg, numbered_tag (tag, "IPADDR", i), &value)) {
2115+ *ret_idx = i;
2116+ return TRUE;
2117+ }
2118+
2119+ if (svGetValueStr (ifcfg, numbered_tag (tag, "PREFIX", i), &value)) {
2120+ *ret_idx = i;
2121+ return TRUE;
2122+ }
2123+
2124+ if (svGetValueStr (ifcfg, numbered_tag (tag, "NETMASK", i), &value)) {
2125+ *ret_idx = i;
2126+ return TRUE;
2127+ }
2128+ }
2129+ return FALSE;
2130+}
2131+
2132+/* Returns TRUE on missing address or valid address */
2133+static gboolean
2134+read_full_ip4_address (shvarFile *ifcfg,
2135+ gint32 which,
2136+ NMIPAddress *base_addr,
2137+ NMIPAddress **out_address,
2138+ char **out_gateway,
2139+ GError **error)
2140+{
2141+ char tag[256];
2142+ char prefix_tag[256];
2143+ guint32 ipaddr;
2144+ gs_free char *value = NULL;
2145+ const char *v;
2146+ int prefix = 0;
2147+ gboolean has_key;
2148+ guint32 a;
2149+ char inet_buf[NM_UTILS_INET_ADDRSTRLEN];
2150+
2151+ g_return_val_if_fail (which >= -1, FALSE);
2152+ g_return_val_if_fail (ifcfg != NULL, FALSE);
2153+ g_return_val_if_fail (out_address != NULL, FALSE);
2154+ g_return_val_if_fail (*out_address == NULL, FALSE);
2155+ g_return_val_if_fail (!error || !*error, FALSE);
2156+
2157+ /* IP address */
2158+ if (!read_ip4_address (ifcfg,
2159+ numbered_tag (tag, "IPADDR", which),
2160+ &has_key, &ipaddr, error))
2161+ return FALSE;
2162+ if (!has_key) {
2163+ if (!base_addr)
2164+ return TRUE;
2165+ nm_ip_address_get_address_binary (base_addr, &ipaddr);
2166+ }
2167+
2168+ /* Gateway */
2169+ if (out_gateway && !*out_gateway) {
2170+ if (!read_ip4_address (ifcfg,
2171+ numbered_tag (tag, "GATEWAY", which),
2172+ &has_key, &a, error))
2173+ return FALSE;
2174+ if (has_key)
2175+ *out_gateway = nm_utils_inet4_ntop_dup (a);
2176+ }
2177+
2178+ /* Prefix */
2179+ numbered_tag (prefix_tag, "PREFIX", which);
2180+ v = svGetValueStr (ifcfg, prefix_tag, &value);
2181+ if (v) {
2182+ prefix = _nm_utils_ascii_str_to_int64 (v, 10, 0, 32, -1);
2183+ if (prefix < 0) {
2184+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2185+ "Invalid IP4 prefix '%s'", v);
2186+ return FALSE;
2187+ }
2188+ } else {
2189+ /* Fall back to NETMASK if no PREFIX was specified */
2190+ if (!read_ip4_address (ifcfg,
2191+ numbered_tag (tag, "NETMASK", which),
2192+ &has_key, &a, error))
2193+ return FALSE;
2194+ if (has_key)
2195+ prefix = nm_utils_ip4_netmask_to_prefix (a);
2196+ else {
2197+ if (base_addr)
2198+ prefix = nm_ip_address_get_prefix (base_addr);
2199+ else {
2200+ /* Try to autodetermine the prefix for the address' class */
2201+ prefix = _nm_utils_ip4_get_default_prefix (ipaddr);
2202+ PARSE_WARNING ("missing %s, assuming %s/%d", prefix_tag, nm_utils_inet4_ntop (ipaddr, inet_buf), prefix);
2203+ }
2204+ }
2205+ }
2206+
2207+ *out_address = nm_ip_address_new_binary (AF_INET, &ipaddr, prefix, error);
2208+ if (*out_address)
2209+ return TRUE;
2210+
2211+ return FALSE;
2212+}
2213+
2214+/*****************************************************************************/
2215+
2216+static gboolean
2217+parse_route_line_is_comment (const char *line)
2218+{
2219+ /* we obtained the line from a legacy route file. Here we skip
2220+ * empty lines and comments.
2221+ *
2222+ * initscripts compares: "$line" =~ '^[[:space:]]*(\#.*)?$'
2223+ */
2224+ while (nm_utils_is_separator (line[0]))
2225+ line++;
2226+ if (NM_IN_SET (line[0], '\0', '#'))
2227+ return TRUE;
2228+ return FALSE;
2229+}
2230+
2231+/*****************************************************************************/
2232+
2233+typedef struct {
2234+ const char *key;
2235+
2236+ /* the element is not available in this case. */
2237+ bool disabled:1;
2238+
2239+ /* whether the element is to be ignored. Ignord is different from
2240+ * "disabled", because we still parse the option, but don't use it. */
2241+ bool ignore:1;
2242+
2243+ bool int_base_16:1;
2244+
2245+ /* whether the command line option was found, and @v is
2246+ * initialized. */
2247+ bool has:1;
2248+
2249+ /* the type, one of PARSE_LINE_TYPE_* */
2250+ char type;
2251+
2252+ union {
2253+ guint8 uint8;
2254+ guint32 uint32;
2255+ struct {
2256+ guint32 uint32;
2257+ bool lock:1;
2258+ } uint32_with_lock;
2259+ struct {
2260+ NMIPAddr addr;
2261+ guint8 plen;
2262+ bool has_plen:1;
2263+ } addr;
2264+ } v;
2265+
2266+} ParseLineInfo;
2267+
2268+enum {
2269+ /* route attributes */
2270+ PARSE_LINE_ATTR_ROUTE_TABLE,
2271+ PARSE_LINE_ATTR_ROUTE_SRC,
2272+ PARSE_LINE_ATTR_ROUTE_FROM,
2273+ PARSE_LINE_ATTR_ROUTE_TOS,
2274+ PARSE_LINE_ATTR_ROUTE_ONLINK,
2275+ PARSE_LINE_ATTR_ROUTE_WINDOW,
2276+ PARSE_LINE_ATTR_ROUTE_CWND,
2277+ PARSE_LINE_ATTR_ROUTE_INITCWND,
2278+ PARSE_LINE_ATTR_ROUTE_INITRWND,
2279+ PARSE_LINE_ATTR_ROUTE_MTU,
2280+
2281+ /* iproute2 arguments that only matter when parsing the file. */
2282+ PARSE_LINE_ATTR_ROUTE_TO,
2283+ PARSE_LINE_ATTR_ROUTE_VIA,
2284+ PARSE_LINE_ATTR_ROUTE_METRIC,
2285+
2286+ /* iproute2 parameters that are well known and that we silently ignore. */
2287+ PARSE_LINE_ATTR_ROUTE_DEV,
2288+};
2289+
2290+#define PARSE_LINE_TYPE_UINT8 '8'
2291+#define PARSE_LINE_TYPE_UINT32 'u'
2292+#define PARSE_LINE_TYPE_UINT32_WITH_LOCK 'l'
2293+#define PARSE_LINE_TYPE_ADDR 'a'
2294+#define PARSE_LINE_TYPE_ADDR_WITH_PREFIX 'p'
2295+#define PARSE_LINE_TYPE_IFNAME 'i'
2296+#define PARSE_LINE_TYPE_FLAG 'f'
2297+
2298+/**
2299+ * parse_route_line:
2300+ * @line: the line to parse. This is either a line from the route-* or route6-* file,
2301+ * or the numbered OPTIONS setting.
2302+ * @addr_family: the address family.
2303+ * @options_route: (in-out): when line is from the OPTIONS setting, this is a pre-created
2304+ * route object that is completed with the settings from options. Otherwise,
2305+ * it shall point to %NULL and a new route is created and returned.
2306+ * @out_route: (out) (transfer-full) (allow-none): the parsed %NMIPRoute instance.
2307+ * In case a @options_route is passed in, it returns the input route that was modified
2308+ * in-place. But the caller must unref the returned route in either case.
2309+ * @error: the failure description.
2310+ *
2311+ * Parsing the route options line has two modes: one for the numbered OPTIONS
2312+ * setting, and one for initscript's handle_ip_file(), which takes the lines
2313+ * and passes them to `ip route add`. The modes are similar, but certain properties
2314+ * are not allowed for OPTIONS.
2315+ * The mode is differentiated by having an @options_route argument.
2316+ *
2317+ * Returns: returns a negative errno on failure. On success, it returns 0
2318+ * and @out_route.
2319+ */
2320+static int
2321+parse_route_line (const char *line,
2322+ int addr_family,
2323+ NMIPRoute *options_route,
2324+ NMIPRoute **out_route,
2325+ GError **error)
2326+{
2327+ nm_auto_unref_ip_route NMIPRoute *route = NULL;
2328+ gs_free const char **words_free = NULL;
2329+ const char *const*words;
2330+ const char *s;
2331+ gsize i_words;
2332+ guint i;
2333+ char buf1[256];
2334+ char buf2[256];
2335+ ParseLineInfo infos[] = {
2336+ [PARSE_LINE_ATTR_ROUTE_TABLE] = { .key = NM_IP_ROUTE_ATTRIBUTE_TABLE,
2337+ .type = PARSE_LINE_TYPE_UINT32, },
2338+ [PARSE_LINE_ATTR_ROUTE_SRC] = { .key = NM_IP_ROUTE_ATTRIBUTE_SRC,
2339+ .type = PARSE_LINE_TYPE_ADDR, },
2340+ [PARSE_LINE_ATTR_ROUTE_FROM] = { .key = NM_IP_ROUTE_ATTRIBUTE_FROM,
2341+ .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
2342+ .disabled = (addr_family != AF_INET6), },
2343+ [PARSE_LINE_ATTR_ROUTE_TOS] = { .key = NM_IP_ROUTE_ATTRIBUTE_TOS,
2344+ .type = PARSE_LINE_TYPE_UINT8,
2345+ .int_base_16 = TRUE,
2346+ .ignore = (addr_family != AF_INET), },
2347+ [PARSE_LINE_ATTR_ROUTE_ONLINK] = { .key = NM_IP_ROUTE_ATTRIBUTE_ONLINK,
2348+ .type = PARSE_LINE_TYPE_FLAG,
2349+ .ignore = (addr_family != AF_INET), },
2350+ [PARSE_LINE_ATTR_ROUTE_WINDOW] = { .key = NM_IP_ROUTE_ATTRIBUTE_WINDOW,
2351+ .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
2352+ [PARSE_LINE_ATTR_ROUTE_CWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_CWND,
2353+ .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
2354+ [PARSE_LINE_ATTR_ROUTE_INITCWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITCWND,
2355+ .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
2356+ [PARSE_LINE_ATTR_ROUTE_INITRWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITRWND,
2357+ .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
2358+ [PARSE_LINE_ATTR_ROUTE_MTU] = { .key = NM_IP_ROUTE_ATTRIBUTE_MTU,
2359+ .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
2360+
2361+ [PARSE_LINE_ATTR_ROUTE_TO] = { .key = "to",
2362+ .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
2363+ .disabled = (options_route != NULL), },
2364+ [PARSE_LINE_ATTR_ROUTE_VIA] = { .key = "via",
2365+ .type = PARSE_LINE_TYPE_ADDR,
2366+ .disabled = (options_route != NULL), },
2367+ [PARSE_LINE_ATTR_ROUTE_METRIC] = { .key = "metric",
2368+ .type = PARSE_LINE_TYPE_UINT32,
2369+ .disabled = (options_route != NULL), },
2370+
2371+ [PARSE_LINE_ATTR_ROUTE_DEV] = { .key = "dev",
2372+ .type = PARSE_LINE_TYPE_IFNAME,
2373+ .ignore = TRUE,
2374+ .disabled = (options_route != NULL), },
2375+ };
2376+
2377+ nm_assert (line);
2378+ nm_assert_addr_family (addr_family);
2379+ nm_assert (!options_route || nm_ip_route_get_family (options_route) == addr_family);
2380+
2381+ /* initscripts read the legacy route file line-by-line and
2382+ * use it as `ip route add $line`, thus doing split+glob.
2383+ * Splitting on IFS (which we consider '<space><tab><newline>')
2384+ * and globbing (which we obviously don't do).
2385+ *
2386+ * I think it's a mess, because it doesn't support escaping or
2387+ * quoting. In fact, it can only encode benign values.
2388+ *
2389+ * We also use the same form for the numbered OPTIONS
2390+ * variable. I think it's bad not to support any form of
2391+ * escaping. But do that for now.
2392+ *
2393+ * Maybe later we want to support some form of quotation here.
2394+ * Which of course, would be incompatible with initscripts.
2395+ */
2396+ words_free = nm_utils_strsplit_set (line, " \t\n");
2397+
2398+ words = words_free ?: NM_PTRARRAY_EMPTY (const char *);
2399+
2400+ for (i_words = 0; words[i_words]; ) {
2401+ const gsize i_words0 = i_words;
2402+ const char *const w = words[i_words0];
2403+ ParseLineInfo *info;
2404+ gboolean unqualified_addr = FALSE;
2405+
2406+ for (i = 0; i < G_N_ELEMENTS (infos); i++) {
2407+ info = &infos[i];
2408+
2409+ if (info->disabled)
2410+ continue;
2411+
2412+ if (!nm_streq (w, info->key))
2413+ continue;
2414+
2415+ if (info->has) {
2416+ /* iproute2 for most arguments allows specifying them multiple times.
2417+ * Let's not do that. */
2418+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2419+ "Duplicate option \"%s\"", w);
2420+ return -EINVAL;
2421+ }
2422+
2423+ info->has = TRUE;
2424+ switch (info->type) {
2425+ case PARSE_LINE_TYPE_UINT8:
2426+ i_words++;
2427+ goto parse_line_type_uint8;
2428+ case PARSE_LINE_TYPE_UINT32:
2429+ i_words++;
2430+ goto parse_line_type_uint32;
2431+ case PARSE_LINE_TYPE_UINT32_WITH_LOCK:
2432+ i_words++;
2433+ goto parse_line_type_uint32_with_lock;
2434+ case PARSE_LINE_TYPE_ADDR:
2435+ i_words++;
2436+ goto parse_line_type_addr;
2437+ case PARSE_LINE_TYPE_ADDR_WITH_PREFIX:
2438+ i_words++;
2439+ goto parse_line_type_addr_with_prefix;
2440+ case PARSE_LINE_TYPE_IFNAME:
2441+ i_words++;
2442+ goto parse_line_type_ifname;
2443+ case PARSE_LINE_TYPE_FLAG:
2444+ i_words++;
2445+ goto next;
2446+ default:
2447+ nm_assert_not_reached ();
2448+ }
2449+ }
2450+
2451+ /* "to" is also accepted unqualified... (once) */
2452+ info = &infos[PARSE_LINE_ATTR_ROUTE_TO];
2453+ if (!info->has && !info->disabled) {
2454+ unqualified_addr = TRUE;
2455+ info->has = TRUE;
2456+ goto parse_line_type_addr;
2457+ }
2458+
2459+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2460+ "Unrecognized argument (\"to\" is duplicate or \"%s\" is garbage)", w);
2461+ return -EINVAL;
2462+
2463+parse_line_type_uint8:
2464+ s = words[i_words];
2465+ if (!s)
2466+ goto err_word_missing_argument;
2467+ info->v.uint8 = _nm_utils_ascii_str_to_int64 (s,
2468+ info->int_base_16 ? 16 : 10,
2469+ 0,
2470+ G_MAXUINT8,
2471+ 0);;
2472+ if (errno) {
2473+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2474+ "Argument for \"%s\" is not a valid number", w);
2475+ return -EINVAL;
2476+ }
2477+ i_words++;
2478+ goto next;
2479+
2480+parse_line_type_uint32:
2481+parse_line_type_uint32_with_lock:
2482+ s = words[i_words];
2483+ if (!s)
2484+ goto err_word_missing_argument;
2485+ if (info->type == PARSE_LINE_TYPE_UINT32_WITH_LOCK) {
2486+ if (nm_streq (s, "lock")) {
2487+ s = words[++i_words];
2488+ if (!s)
2489+ goto err_word_missing_argument;
2490+ info->v.uint32_with_lock.lock = TRUE;
2491+ } else
2492+ info->v.uint32_with_lock.lock = FALSE;
2493+ info->v.uint32_with_lock.uint32 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT32, 0);;
2494+ } else {
2495+ info->v.uint32 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT32, 0);
2496+ }
2497+ if (errno) {
2498+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2499+ "Argument for \"%s\" is not a valid number", w);
2500+ return -EINVAL;
2501+ }
2502+ i_words++;
2503+ goto next;
2504+
2505+parse_line_type_ifname:
2506+ s = words[i_words];
2507+ if (!s)
2508+ goto err_word_missing_argument;
2509+ i_words++;
2510+ goto next;
2511+
2512+parse_line_type_addr:
2513+parse_line_type_addr_with_prefix:
2514+ s = words[i_words];
2515+ if (!s)
2516+ goto err_word_missing_argument;
2517+ {
2518+ int prefix = -1;
2519+
2520+ if (info->type == PARSE_LINE_TYPE_ADDR) {
2521+ if (!nm_utils_parse_inaddr_bin (addr_family,
2522+ s,
2523+ NULL,
2524+ &info->v.addr.addr)) {
2525+ if ( info == &infos[PARSE_LINE_ATTR_ROUTE_VIA]
2526+ && nm_streq (s, "(null)")) {
2527+ /* Due to a bug, would older versions of NM write "via (null)"
2528+ * (rh#1452648). Workaround that, and accept it.*/
2529+ memset (&info->v.addr.addr, 0, sizeof (info->v.addr.addr));
2530+ } else {
2531+ if (unqualified_addr) {
2532+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2533+ "Unrecognized argument (inet prefix is expected rather then \"%s\")", w);
2534+ return -EINVAL;
2535+ } else {
2536+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2537+ "Argument for \"%s\" is not a valid IPv%c address", w,
2538+ addr_family == AF_INET ? '4' : '6');
2539+ }
2540+ return -EINVAL;
2541+ }
2542+ }
2543+ } else {
2544+ nm_assert (info->type == PARSE_LINE_TYPE_ADDR_WITH_PREFIX);
2545+ if ( info == &infos[PARSE_LINE_ATTR_ROUTE_TO]
2546+ && nm_streq (s, "default")) {
2547+ memset (&info->v.addr.addr, 0, sizeof (info->v.addr.addr));
2548+ prefix = 0;
2549+ } else if (!nm_utils_parse_inaddr_prefix_bin (addr_family,
2550+ s,
2551+ NULL,
2552+ &info->v.addr.addr,
2553+ &prefix)) {
2554+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2555+ "Argument for \"%s\" is not ADDR/PREFIX format", w);
2556+ return -EINVAL;
2557+ }
2558+ }
2559+ if (prefix == -1)
2560+ info->v.addr.has_plen = FALSE;
2561+ else {
2562+ info->v.addr.has_plen = TRUE;
2563+ info->v.addr.plen = prefix;
2564+ }
2565+ }
2566+ i_words++;
2567+ goto next;
2568+
2569+err_word_missing_argument:
2570+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2571+ "Missing argument for \"%s\"", w);
2572+ return -EINVAL;
2573+next:
2574+ ;
2575+ }
2576+
2577+ if (options_route) {
2578+ route = options_route;
2579+ nm_ip_route_ref (route);
2580+ } else {
2581+ ParseLineInfo *info_to = &infos[PARSE_LINE_ATTR_ROUTE_TO];
2582+ ParseLineInfo *info_via = &infos[PARSE_LINE_ATTR_ROUTE_VIA];
2583+ ParseLineInfo *info_metric = &infos[PARSE_LINE_ATTR_ROUTE_METRIC];
2584+ guint prefix;
2585+
2586+ if (!info_to->has) {
2587+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2588+ "Missing destination prefix");
2589+ return -EINVAL;
2590+ }
2591+
2592+ prefix = info_to->v.addr.has_plen
2593+ ? info_to->v.addr.plen
2594+ : (addr_family == AF_INET ? 32 : 128);
2595+
2596+ route = nm_ip_route_new_binary (addr_family,
2597+ &info_to->v.addr.addr,
2598+ prefix,
2599+ info_via->has ? &info_via->v.addr.addr : NULL,
2600+ info_metric->has ? (gint64) info_metric->v.uint32 : (gint64) -1,
2601+ error);
2602+ info_to->has = FALSE;
2603+ info_via->has = FALSE;
2604+ info_metric->has = FALSE;
2605+ if (!route)
2606+ return -EINVAL;
2607+ }
2608+
2609+ for (i = 0; i < G_N_ELEMENTS (infos); i++) {
2610+ ParseLineInfo *info = &infos[i];
2611+
2612+ if (!info->has)
2613+ continue;
2614+ if (info->ignore || info->disabled)
2615+ continue;
2616+ switch (info->type) {
2617+ case PARSE_LINE_TYPE_UINT8:
2618+ nm_ip_route_set_attribute (route,
2619+ info->key,
2620+ g_variant_new_byte (info->v.uint8));
2621+ break;
2622+ case PARSE_LINE_TYPE_UINT32:
2623+ nm_ip_route_set_attribute (route,
2624+ info->key,
2625+ g_variant_new_uint32 (info->v.uint32));
2626+ break;
2627+ case PARSE_LINE_TYPE_UINT32_WITH_LOCK:
2628+ if (info->v.uint32_with_lock.lock) {
2629+ nm_ip_route_set_attribute (route,
2630+ nm_sprintf_buf (buf1, "lock-%s", info->key),
2631+ g_variant_new_boolean (TRUE));
2632+ }
2633+ nm_ip_route_set_attribute (route,
2634+ info->key,
2635+ g_variant_new_uint32 (info->v.uint32_with_lock.uint32));
2636+ break;
2637+ case PARSE_LINE_TYPE_ADDR:
2638+ case PARSE_LINE_TYPE_ADDR_WITH_PREFIX:
2639+ nm_ip_route_set_attribute (route,
2640+ info->key,
2641+ g_variant_new_printf ("%s%s",
2642+ inet_ntop (addr_family, &info->v.addr.addr, buf1, sizeof (buf1)),
2643+ info->v.addr.has_plen
2644+ ? nm_sprintf_buf (buf2, "/%u", (unsigned) info->v.addr.plen)
2645+ : ""));
2646+ break;
2647+ case PARSE_LINE_TYPE_FLAG:
2648+ /* NOTE: the flag (for "onlink") only allows to explicitly set "TRUE".
2649+ * There is no way to express an explicit "FALSE" setting
2650+ * of this attribute, hence, the file format cannot encode
2651+ * that configuration. */
2652+ nm_ip_route_set_attribute (route,
2653+ info->key,
2654+ g_variant_new_boolean (TRUE));
2655+ break;
2656+ default:
2657+ nm_assert_not_reached ();
2658+ break;
2659+ }
2660+ }
2661+
2662+ nm_assert (_nm_ip_route_attribute_validate_all (route));
2663+
2664+ NM_SET_OUT (out_route, g_steal_pointer (&route));
2665+ return 0;
2666+}
2667+
2668+/* Returns TRUE on missing route or valid route */
2669+static gboolean
2670+read_one_ip4_route (shvarFile *ifcfg,
2671+ guint32 which,
2672+ NMIPRoute **out_route,
2673+ GError **error)
2674+{
2675+ char tag[256];
2676+ char netmask_tag[256];
2677+ guint32 dest;
2678+ guint32 next_hop;
2679+ guint32 netmask;
2680+ gboolean has_key;
2681+ const char *v;
2682+ gs_free char *value = NULL;
2683+ gint64 prefix, metric;
2684+ char inet_buf[NM_UTILS_INET_ADDRSTRLEN];
2685+
2686+ g_return_val_if_fail (ifcfg != NULL, FALSE);
2687+ g_return_val_if_fail (out_route && !*out_route, FALSE);
2688+ g_return_val_if_fail (!error || !*error, FALSE);
2689+
2690+ /* Destination */
2691+ if (!read_ip4_address (ifcfg,
2692+ numbered_tag (tag, "ADDRESS", which),
2693+ &has_key, &dest, error))
2694+ return FALSE;
2695+ if (!has_key) {
2696+ /* missing route = success */
2697+ *out_route = NULL;
2698+ return TRUE;
2699+ }
2700+
2701+ /* Next hop */
2702+ if (!read_ip4_address (ifcfg,
2703+ numbered_tag (tag, "GATEWAY", which),
2704+ NULL, &next_hop, error))
2705+ return FALSE;
2706+ /* We don't make distinction between missing GATEWAY IP and 0.0.0.0 */
2707+
2708+ /* Prefix */
2709+ if (!read_ip4_address (ifcfg,
2710+ numbered_tag (netmask_tag, "NETMASK", which),
2711+ &has_key, &netmask, error))
2712+ return FALSE;
2713+ if (has_key) {
2714+ prefix = nm_utils_ip4_netmask_to_prefix (netmask);
2715+ if (netmask != _nm_utils_ip4_prefix_to_netmask (prefix)) {
2716+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2717+ "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, inet_buf));
2718+ return FALSE;
2719+ }
2720+ } else {
2721+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2722+ "Missing IP4 route element '%s'", netmask_tag);
2723+ return FALSE;
2724+ }
2725+
2726+ /* Metric */
2727+ nm_clear_g_free (&value);
2728+ v = svGetValueStr (ifcfg, numbered_tag (tag, "METRIC", which), &value);
2729+ if (v) {
2730+ metric = _nm_utils_ascii_str_to_int64 (v, 10, 0, G_MAXUINT32, -1);
2731+ if (metric < 0) {
2732+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2733+ "Invalid IP4 route metric '%s'", v);
2734+ return FALSE;
2735+ }
2736+ } else
2737+ metric = -1;
2738+
2739+ *out_route = nm_ip_route_new_binary (AF_INET, &dest, prefix, &next_hop, metric, error);
2740+ if (!*out_route)
2741+ return FALSE;
2742+
2743+ /* Options */
2744+ nm_clear_g_free (&value);
2745+ v = svGetValueStr (ifcfg, numbered_tag (tag, "OPTIONS", which), &value);
2746+ if (v) {
2747+ if (parse_route_line (v, AF_INET, *out_route, NULL, error) < 0) {
2748+ g_clear_pointer (out_route, nm_ip_route_unref);
2749+ return FALSE;
2750+ }
2751+ }
2752+
2753+ return TRUE;
2754+}
2755+
2756+static gboolean
2757+read_route_file (int addr_family,
2758+ const char *filename,
2759+ NMSettingIPConfig *s_ip,
2760+ GError **error)
2761+{
2762+ gs_free char *contents = NULL;
2763+ char *contents_rest = NULL;
2764+ const char *line;
2765+ gsize len = 0;
2766+ gsize line_num;
2767+
2768+ g_return_val_if_fail (filename, FALSE);
2769+ g_return_val_if_fail ( (addr_family == AF_INET && NM_IS_SETTING_IP4_CONFIG (s_ip))
2770+ || (addr_family == AF_INET6 && NM_IS_SETTING_IP6_CONFIG (s_ip)), FALSE);
2771+ g_return_val_if_fail (!error || !*error, FALSE);
2772+
2773+ if ( !g_file_get_contents (filename, &contents, &len, NULL)
2774+ || !len) {
2775+ return TRUE; /* missing/empty = success */
2776+ }
2777+
2778+ line_num = 0;
2779+ for (line = strtok_r (contents, "\n", &contents_rest);
2780+ line;
2781+ line = strtok_r (NULL, "\n", &contents_rest)) {
2782+ nm_auto_unref_ip_route NMIPRoute *route = NULL;
2783+ gs_free_error GError *local = NULL;
2784+ int e;
2785+
2786+ line_num++;
2787+
2788+ if (parse_route_line_is_comment (line))
2789+ continue;
2790+
2791+ e = parse_route_line (line, addr_family, NULL, &route, &local);
2792+
2793+ if (e < 0) {
2794+ if (e == -ERANGE)
2795+ PARSE_WARNING ("ignoring manual default route: '%s' (%s)", line, filename);
2796+ else {
2797+ /* we accept all unrecognized lines, because otherwise we would reject the
2798+ * entire connection. */
2799+ PARSE_WARNING ("ignoring invalid route at \"%s\" (%s:%lu): %s", line, filename, (long unsigned) line_num, local->message);
2800+ }
2801+ continue;
2802+ }
2803+
2804+ if (!nm_setting_ip_config_add_route (s_ip, route))
2805+ PARSE_WARNING ("duplicate IPv%c route", addr_family == AF_INET ? '4' : '6');
2806+ }
2807+
2808+ return TRUE;
2809+}
2810+
2811+static void
2812+parse_dns_options (NMSettingIPConfig *ip_config, const char *value)
2813+{
2814+ gs_free const char **options = NULL;
2815+ const char *const *item;
2816+
2817+ g_return_if_fail (ip_config);
2818+
2819+ if (!value)
2820+ return;
2821+
2822+ if (!nm_setting_ip_config_has_dns_options (ip_config))
2823+ nm_setting_ip_config_clear_dns_options (ip_config, TRUE);
2824+
2825+ options = nm_utils_strsplit_set (value, " ");
2826+ if (options) {
2827+ for (item = options; *item; item++) {
2828+ if (!nm_setting_ip_config_add_dns_option (ip_config, *item))
2829+ PARSE_WARNING ("can't add DNS option '%s'", *item);
2830+ }
2831+ }
2832+}
2833+
2834+static gboolean
2835+parse_full_ip6_address (shvarFile *ifcfg,
2836+ const char *addr_str,
2837+ int i,
2838+ NMIPAddress **out_address,
2839+ GError **error)
2840+{
2841+ NMIPAddress *addr;
2842+ NMIPAddr addr_bin;
2843+ int prefix;
2844+
2845+ nm_assert (addr_str);
2846+ nm_assert (out_address && !*out_address);
2847+ nm_assert (!error || !*error);
2848+
2849+ if (!nm_utils_parse_inaddr_prefix_bin (AF_INET6,
2850+ addr_str,
2851+ NULL,
2852+ &addr_bin,
2853+ &prefix)) {
2854+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
2855+ "Invalid IP6 address '%s'", addr_str);
2856+ return FALSE;
2857+ }
2858+
2859+ if (prefix < 0)
2860+ prefix = 64;
2861+
2862+ addr = nm_ip_address_new_binary (AF_INET6, &addr_bin, prefix, error);
2863+ if (!addr)
2864+ return FALSE;
2865+
2866+ *out_address = addr;
2867+ return TRUE;
2868+}
2869+
2870+static NMSetting *
2871+make_user_setting (shvarFile *ifcfg)
2872+{
2873+ gboolean has_user_data = FALSE;
2874+ gs_unref_object NMSettingUser *s_user = NULL;
2875+ gs_unref_hashtable GHashTable *keys = NULL;
2876+ GHashTableIter iter;
2877+ const char *key;
2878+ nm_auto_free_gstring GString *str = NULL;
2879+
2880+ keys = svGetKeys (ifcfg, SV_KEY_TYPE_USER);
2881+ if (!keys)
2882+ return NULL;
2883+
2884+ g_hash_table_iter_init (&iter, keys);
2885+ while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
2886+ const char *value;
2887+ gs_free char *value_to_free = NULL;
2888+
2889+ value = svGetValue (ifcfg, key, &value_to_free);
2890+
2891+ if (!value)
2892+ continue;
2893+
2894+ if (!str)
2895+ str = g_string_sized_new (100);
2896+ else
2897+ g_string_set_size (str, 0);
2898+
2899+ if (!nms_ifcfg_rh_utils_user_key_decode (key + NM_STRLEN ("NM_USER_"), str))
2900+ continue;
2901+
2902+ if (!s_user)
2903+ s_user = NM_SETTING_USER (nm_setting_user_new ());
2904+
2905+ if (nm_setting_user_set_data (s_user, str->str,
2906+ value, NULL))
2907+ has_user_data = TRUE;
2908+ }
2909+
2910+ return has_user_data
2911+ ? NM_SETTING (g_steal_pointer (&s_user))
2912+ : NULL;
2913+}
2914+
2915+static NMSetting *
2916+make_match_setting (shvarFile *ifcfg)
2917+{
2918+ NMSettingMatch *s_match = NULL;
2919+ gs_free const char **strv = NULL;
2920+ gs_free char *value = NULL;
2921+ const char *v;
2922+ gsize i;
2923+
2924+ v = svGetValueStr (ifcfg, "MATCH_INTERFACE_NAME", &value);
2925+ if (!v)
2926+ return NULL;
2927+
2928+ strv = nm_utils_escaped_tokens_split (v, NM_ASCII_SPACES);
2929+ if (strv) {
2930+ for (i = 0; strv[i]; i++) {
2931+ if (!s_match)
2932+ s_match = (NMSettingMatch *) nm_setting_match_new ();
2933+ nm_setting_match_add_interface_name (s_match, strv[i]);
2934+ }
2935+ }
2936+
2937+ return (NMSetting *) s_match;
2938+}
2939+
2940+static NMSetting *
2941+make_proxy_setting (shvarFile *ifcfg)
2942+{
2943+ NMSettingProxy *s_proxy = NULL;
2944+ gs_free char *value = NULL;
2945+ const char *v;
2946+ NMSettingProxyMethod method;
2947+
2948+ v = svGetValueStr (ifcfg, "PROXY_METHOD", &value);
2949+ if (!v)
2950+ return NULL;
2951+
2952+ if (!g_ascii_strcasecmp (v, "auto"))
2953+ method = NM_SETTING_PROXY_METHOD_AUTO;
2954+ else
2955+ method = NM_SETTING_PROXY_METHOD_NONE;
2956+
2957+ s_proxy = (NMSettingProxy *) nm_setting_proxy_new ();
2958+
2959+ switch (method) {
2960+ case NM_SETTING_PROXY_METHOD_AUTO:
2961+ g_object_set (s_proxy,
2962+ NM_SETTING_PROXY_METHOD, (int) NM_SETTING_PROXY_METHOD_AUTO,
2963+ NULL);
2964+
2965+ nm_clear_g_free (&value);
2966+ v = svGetValueStr (ifcfg, "PAC_URL", &value);
2967+ if (v)
2968+ g_object_set (s_proxy, NM_SETTING_PROXY_PAC_URL, v, NULL);
2969+
2970+ nm_clear_g_free (&value);
2971+ v = svGetValueStr (ifcfg, "PAC_SCRIPT", &value);
2972+ if (v)
2973+ g_object_set (s_proxy, NM_SETTING_PROXY_PAC_SCRIPT, v, NULL);
2974+
2975+ break;
2976+ case NM_SETTING_PROXY_METHOD_NONE:
2977+ g_object_set (s_proxy,
2978+ NM_SETTING_PROXY_METHOD, (int) NM_SETTING_PROXY_METHOD_NONE,
2979+ NULL);
2980+ break;
2981+ }
2982+
2983+ if (svGetValueBoolean (ifcfg, "BROWSER_ONLY", FALSE))
2984+ g_object_set (s_proxy, NM_SETTING_PROXY_BROWSER_ONLY, TRUE, NULL);
2985+
2986+ return NM_SETTING (s_proxy);
2987+}
2988+
2989+static NMSetting *
2990+make_ip4_setting (shvarFile *ifcfg,
2991+ shvarFile *network_ifcfg,
2992+ gboolean routes_read,
2993+ gboolean *out_has_defroute,
2994+ GError **error)
2995+{
2996+ gs_unref_object NMSettingIPConfig *s_ip4 = NULL;
2997+ gs_free char *route_path = NULL;
2998+ gs_free char *value = NULL;
2999+ const char *v;
3000+ char *method;
3001+ gs_free char *dns_options_free = NULL;
3002+ const char *dns_options = NULL;
3003+ gs_free char *gateway = NULL;
3004+ int i;
3005+ guint32 a;
3006+ gboolean has_key;
3007+ shvarFile *route_ifcfg;
3008+ gboolean never_default;
3009+ gint64 timeout;
3010+ int priority;
3011+ const char *const *item;
3012+ guint32 route_table;
3013+
3014+ nm_assert (out_has_defroute && !*out_has_defroute);
3015+
3016+ s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
3017+
3018+ /* First check if DEFROUTE is set for this device; DEFROUTE has the
3019+ * opposite meaning from never-default. The default if DEFROUTE is not
3020+ * specified is DEFROUTE=yes which means that this connection can be used
3021+ * as a default route
3022+ */
3023+ i = svGetValueBoolean (ifcfg, "DEFROUTE", -1);
3024+ if (i == -1)
3025+ never_default = FALSE;
3026+ else {
3027+ never_default = !i;
3028+ *out_has_defroute = TRUE;
3029+ }
3030+
3031+ /* Then check if GATEWAYDEV; it's global and overrides DEFROUTE */
3032+ if (network_ifcfg) {
3033+ gs_free char *gatewaydev_value = NULL;
3034+ const char *gatewaydev;
3035+
3036+ /* Get the connection ifcfg device name and the global gateway device */
3037+ v = svGetValueStr (ifcfg, "DEVICE", &value);
3038+ gatewaydev = svGetValueStr (network_ifcfg, "GATEWAYDEV", &gatewaydev_value);
3039+ dns_options = svGetValue (network_ifcfg, "RES_OPTIONS", &dns_options_free);
3040+
3041+ /* If there was a global gateway device specified, then only connections
3042+ * for that device can be the default connection.
3043+ */
3044+ if (gatewaydev && v)
3045+ never_default = !!strcmp (v, gatewaydev);
3046+
3047+ nm_clear_g_free (&value);
3048+ }
3049+
3050+ v = svGetValueStr (ifcfg, "BOOTPROTO", &value);
3051+
3052+ if (!v || !*v || !g_ascii_strcasecmp (v, "none")) {
3053+ if (is_any_ip4_address_defined (ifcfg, NULL))
3054+ method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
3055+ else
3056+ method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
3057+ } else if (!g_ascii_strcasecmp (v, "bootp") || !g_ascii_strcasecmp (v, "dhcp")) {
3058+ method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
3059+ } else if (!g_ascii_strcasecmp (v, "static")) {
3060+ if (is_any_ip4_address_defined (ifcfg, NULL))
3061+ method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
3062+ else
3063+ method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
3064+ } else if (!g_ascii_strcasecmp (v, "autoip")) {
3065+ method = NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL;
3066+ } else if (!g_ascii_strcasecmp (v, "shared")) {
3067+ method = NM_SETTING_IP4_CONFIG_METHOD_SHARED;
3068+ } else {
3069+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3070+ "Unknown BOOTPROTO '%s'", v);
3071+ return NULL;
3072+ }
3073+
3074+ /* the route table (policy routing) is ignored if we don't handle routes. */
3075+ route_table = svGetValueInt64 (ifcfg, "IPV4_ROUTE_TABLE", 10,
3076+ 0, G_MAXUINT32, 0);
3077+ if ( route_table != 0
3078+ && !routes_read) {
3079+ PARSE_WARNING ("'rule-' or 'rule6-' files are present; Policy routing (IPV4_ROUTE_TABLE) is ignored");
3080+ route_table = 0;
3081+ }
3082+
3083+ g_object_set (s_ip4,
3084+ NM_SETTING_IP_CONFIG_METHOD, method,
3085+ NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, !svGetValueBoolean (ifcfg, "PEERDNS", TRUE),
3086+ NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, !svGetValueBoolean (ifcfg, "PEERROUTES", TRUE),
3087+ NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default,
3088+ NM_SETTING_IP_CONFIG_MAY_FAIL, !svGetValueBoolean (ifcfg, "IPV4_FAILURE_FATAL", FALSE),
3089+ NM_SETTING_IP_CONFIG_ROUTE_METRIC, svGetValueInt64 (ifcfg, "IPV4_ROUTE_METRIC", 10,
3090+ -1, G_MAXUINT32, -1),
3091+ NM_SETTING_IP_CONFIG_ROUTE_TABLE, (guint) route_table,
3092+ NULL);
3093+
3094+ if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
3095+ return NM_SETTING (g_steal_pointer (&s_ip4));
3096+
3097+ /* Handle DHCP settings */
3098+ nm_clear_g_free (&value);
3099+ v = svGetValueStr (ifcfg, "DHCP_HOSTNAME", &value);
3100+ if (v)
3101+ g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL);
3102+
3103+ nm_clear_g_free (&value);
3104+ v = svGetValueStr (ifcfg, "DHCP_FQDN", &value);
3105+ if (v) {
3106+ g_object_set (s_ip4,
3107+ NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, NULL,
3108+ NM_SETTING_IP4_CONFIG_DHCP_FQDN, v,
3109+ NULL);
3110+ }
3111+
3112+ g_object_set (s_ip4,
3113+ NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCP_SEND_HOSTNAME", TRUE),
3114+ NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0),
3115+ NULL);
3116+
3117+ nm_clear_g_free (&value);
3118+ v = svGetValueStr (ifcfg, "DHCP_CLIENT_ID", &value);
3119+ if (v)
3120+ g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, v, NULL);
3121+
3122+ /* Read static IP addresses.
3123+ * Read them even for AUTO method - in this case the addresses are
3124+ * added to the automatic ones. Note that this is not currently supported by
3125+ * the legacy 'network' service (ifup-eth).
3126+ */
3127+ for (i = -1;; i++) {
3128+ NMIPAddress *addr = NULL;
3129+
3130+ /* gateway will only be set if still unset. Hence, we don't leak gateway
3131+ * here by calling read_full_ip4_address() repeatedly */
3132+ if (!read_full_ip4_address (ifcfg, i, NULL, &addr, &gateway, error))
3133+ return NULL;
3134+
3135+ if (!addr) {
3136+ /* The first mandatory variable is 2-indexed (IPADDR2)
3137+ * Variables IPADDR, IPADDR0 and IPADDR1 are optional */
3138+ if (i > 1)
3139+ break;
3140+ continue;
3141+ }
3142+
3143+ if (!nm_setting_ip_config_add_address (s_ip4, addr))
3144+ PARSE_WARNING ("duplicate IP4 address");
3145+ nm_ip_address_unref (addr);
3146+ }
3147+
3148+ /* Gateway */
3149+ if (!gateway) {
3150+ if (network_ifcfg) {
3151+ gboolean read_success;
3152+
3153+ read_success = read_ip4_address (network_ifcfg, "GATEWAY", &has_key, &a, error);
3154+ if (!read_success)
3155+ return NULL;
3156+ if (has_key) {
3157+ if (nm_setting_ip_config_get_num_addresses (s_ip4) == 0) {
3158+ gs_free char *f = g_path_get_basename (svFileGetName (ifcfg));
3159+ PARSE_WARNING ("ignoring GATEWAY (/etc/sysconfig/network) for %s "
3160+ "because the connection has no static addresses", f);
3161+ } else
3162+ gateway = nm_utils_inet4_ntop_dup (a);
3163+ }
3164+ }
3165+ }
3166+ g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL);
3167+
3168+ if (gateway && never_default)
3169+ PARSE_WARNING ("GATEWAY will be ignored when DEFROUTE is disabled");
3170+
3171+ /* We used to skip saving a lot of unused properties for the ipv4 shared method.
3172+ * We want now to persist them but... unfortunately loading DNS or DOMAIN options
3173+ * would cause a fail in the ipv4 verify() function. As we don't want any regression
3174+ * in the unlikely event that someone has a working ifcfg file for an IPv4 shared ip
3175+ * connection with a crafted "DNS" entry... don't load it. So we will avoid failing
3176+ * the connection) */
3177+ if (!nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
3178+ /* DNS servers
3179+ * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting())
3180+ */
3181+ for (i = 1; i <= 10; i++) {
3182+ char tag[256];
3183+
3184+ numbered_tag (tag, "DNS", i);
3185+ nm_clear_g_free (&value);
3186+ v = svGetValueStr (ifcfg, tag, &value);
3187+ if (v) {
3188+ if (nm_utils_ipaddr_valid (AF_INET, v)) {
3189+ if (!nm_setting_ip_config_add_dns (s_ip4, v))
3190+ PARSE_WARNING ("duplicate DNS server %s", tag);
3191+ } else if (nm_utils_ipaddr_valid (AF_INET6, v)) {
3192+ /* Ignore IPv6 addresses */
3193+ } else {
3194+ PARSE_WARNING ("invalid DNS server address %s", v);
3195+ return NULL;
3196+ }
3197+ }
3198+ }
3199+
3200+ /* DNS searches */
3201+ nm_clear_g_free (&value);
3202+ v = svGetValueStr (ifcfg, "DOMAIN", &value);
3203+ if (v) {
3204+ gs_free const char **searches = NULL;
3205+
3206+ searches = nm_utils_strsplit_set (v, " ");
3207+ if (searches) {
3208+ for (item = searches; *item; item++) {
3209+ if (!nm_setting_ip_config_add_dns_search (s_ip4, *item))
3210+ PARSE_WARNING ("duplicate DNS domain '%s'", *item);
3211+ }
3212+ }
3213+ }
3214+ }
3215+
3216+ /* DNS options */
3217+ nm_clear_g_free (&value);
3218+ parse_dns_options (s_ip4, svGetValue (ifcfg, "RES_OPTIONS", &value));
3219+ parse_dns_options (s_ip4, dns_options);
3220+
3221+ /* DNS priority */
3222+ priority = svGetValueInt64 (ifcfg, "IPV4_DNS_PRIORITY", 10, G_MININT32, G_MAXINT32, 0);
3223+ g_object_set (s_ip4,
3224+ NM_SETTING_IP_CONFIG_DNS_PRIORITY,
3225+ priority,
3226+ NULL);
3227+
3228+ /* Static routes - route-<name> file */
3229+ route_path = utils_get_route_path (svFileGetName (ifcfg));
3230+
3231+ if (!routes_read) {
3232+ /* NOP */
3233+ } else if (utils_has_route_file_new_syntax (route_path)) {
3234+ /* Parse route file in new syntax */
3235+ route_ifcfg = utils_get_route_ifcfg (svFileGetName (ifcfg), FALSE);
3236+ if (route_ifcfg) {
3237+ for (i = 0;; i++) {
3238+ NMIPRoute *route = NULL;
3239+
3240+ if (!read_one_ip4_route (route_ifcfg, i, &route, error)) {
3241+ svCloseFile (route_ifcfg);
3242+ return NULL;
3243+ }
3244+
3245+ if (!route)
3246+ break;
3247+
3248+ if (!nm_setting_ip_config_add_route (s_ip4, route))
3249+ PARSE_WARNING ("duplicate IP4 route");
3250+ nm_ip_route_unref (route);
3251+ }
3252+ svCloseFile (route_ifcfg);
3253+ }
3254+ } else {
3255+ if (!read_route_file (AF_INET, route_path, s_ip4, error))
3256+ return NULL;
3257+ }
3258+
3259+ /* Legacy value NM used for a while but is incorrect (rh #459370) */
3260+ if ( !nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)
3261+ && !nm_setting_ip_config_get_num_dns_searches (s_ip4)) {
3262+ nm_clear_g_free (&value);
3263+ v = svGetValueStr (ifcfg, "SEARCH", &value);
3264+ if (v) {
3265+ gs_free const char **searches = NULL;
3266+
3267+ searches = nm_utils_strsplit_set (v, " ");
3268+ if (searches) {
3269+ for (item = searches; *item; item++) {
3270+ if (!nm_setting_ip_config_add_dns_search (s_ip4, *item))
3271+ PARSE_WARNING ("duplicate DNS search '%s'", *item);
3272+ }
3273+ }
3274+ }
3275+ }
3276+
3277+ timeout = svGetValueInt64 (ifcfg, "ACD_TIMEOUT", 10, -1, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, -2);
3278+ if (timeout == -2) {
3279+ timeout = svGetValueInt64 (ifcfg, "ARPING_WAIT", 10, -1,
3280+ NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX / 1000, -1);
3281+ if (timeout > 0)
3282+ timeout *= 1000;
3283+ }
3284+ g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT, (int) timeout, NULL);
3285+
3286+ return NM_SETTING (g_steal_pointer (&s_ip4));
3287+}
3288+
3289+static void
3290+read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *filename)
3291+{
3292+ GDir *dir;
3293+ gs_free char *dirname = NULL;
3294+ gs_free char *base = NULL;
3295+ NMIPAddress *base_addr = NULL;
3296+ GError *err = NULL;
3297+
3298+ g_return_if_fail (s_ip4 != NULL);
3299+ g_return_if_fail (filename != NULL);
3300+
3301+ if (nm_setting_ip_config_get_num_addresses (s_ip4) > 0)
3302+ base_addr = nm_setting_ip_config_get_address (s_ip4, 0);
3303+
3304+ dirname = g_path_get_dirname (filename);
3305+ nm_assert (dirname != NULL);
3306+ base = g_path_get_basename (filename);
3307+ nm_assert (base != NULL);
3308+
3309+ dir = g_dir_open (dirname, 0, &err);
3310+ if (dir) {
3311+ const char *item;
3312+ NMIPAddress *addr;
3313+ gboolean ok;
3314+
3315+ while ((item = g_dir_read_name (dir))) {
3316+ nm_auto_shvar_file_close shvarFile *parsed = NULL;
3317+ gs_free char *gateway = NULL;
3318+ gs_free char *device_value = NULL;
3319+ gs_free char *full_path = NULL;
3320+ const char *device;
3321+ const char *p;
3322+
3323+ if (!utils_is_ifcfg_alias_file (item, base))
3324+ continue;
3325+
3326+ full_path = g_build_filename (dirname, item, NULL);
3327+
3328+ p = strchr (item, ':');
3329+ g_assert (p != NULL); /* we know this is true from utils_is_ifcfg_alias_file() */
3330+ for (p++; *p; p++) {
3331+ if (!g_ascii_isalnum (*p) && *p != '_')
3332+ break;
3333+ }
3334+ if (*p) {
3335+ PARSE_WARNING ("ignoring alias file '%s' with invalid name", full_path);
3336+ continue;
3337+ }
3338+
3339+ parsed = svOpenFile (full_path, &err);
3340+ if (!parsed) {
3341+ PARSE_WARNING ("couldn't parse alias file '%s': %s", full_path, err->message);
3342+ g_clear_error (&err);
3343+ continue;
3344+ }
3345+
3346+ device = svGetValueStr (parsed, "DEVICE", &device_value);
3347+ if (!device) {
3348+ PARSE_WARNING ("alias file '%s' has no DEVICE", full_path);
3349+ continue;
3350+ }
3351+ /* We know that item starts with IFCFG_TAG from utils_is_ifcfg_alias_file() */
3352+ if (strcmp (device, item + strlen (IFCFG_TAG)) != 0) {
3353+ PARSE_WARNING ("alias file '%s' has invalid DEVICE (%s) for filename",
3354+ full_path, device);
3355+ continue;
3356+ }
3357+
3358+ addr = NULL;
3359+ ok = read_full_ip4_address (parsed, -1, base_addr, &addr,
3360+ read_defroute ? &gateway : NULL,
3361+ &err);
3362+ if (ok) {
3363+ nm_ip_address_set_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string (device));
3364+ if (!nm_setting_ip_config_add_address (s_ip4, addr))
3365+ PARSE_WARNING ("duplicate IP4 address in alias file %s", item);
3366+ if (nm_streq0 (nm_setting_ip_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
3367+ g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
3368+ if (read_defroute) {
3369+ int i;
3370+
3371+ if (gateway) {
3372+ g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL);
3373+ read_defroute = FALSE;
3374+ }
3375+ i = svGetValueBoolean (parsed, "DEFROUTE", -1);
3376+ if (i != -1) {
3377+ g_object_set (s_ip4,
3378+ NM_SETTING_IP_CONFIG_NEVER_DEFAULT, (gboolean) !i,
3379+ NULL);
3380+ read_defroute = FALSE;
3381+ }
3382+ }
3383+ } else {
3384+ PARSE_WARNING ("error reading IP4 address from alias file '%s': %s",
3385+ full_path, err ? err->message : "no address");
3386+ g_clear_error (&err);
3387+ }
3388+ nm_ip_address_unref (addr);
3389+ }
3390+
3391+ g_dir_close (dir);
3392+ } else {
3393+ PARSE_WARNING ("can not read directory '%s': %s", dirname, err->message);
3394+ g_error_free (err);
3395+ }
3396+}
3397+
3398+static NMSetting *
3399+make_ip6_setting (shvarFile *ifcfg,
3400+ shvarFile *network_ifcfg,
3401+ gboolean routes_read,
3402+ GError **error)
3403+{
3404+ gs_unref_object NMSettingIPConfig *s_ip6 = NULL;
3405+ const char *v;
3406+ gs_free char *value = NULL;
3407+ gboolean ipv6init;
3408+ gboolean ipv6forwarding;
3409+ gboolean disabled;
3410+ gboolean dhcp6 = FALSE;
3411+ char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
3412+ const char *ipv6addr, *ipv6addr_secondaries;
3413+ gs_free char *ipv6addr_to_free = NULL;
3414+ gs_free char *ipv6addr_secondaries_to_free = NULL;
3415+ gs_free const char **list = NULL;
3416+ const char *const *iter;
3417+ guint32 i;
3418+ int i_val;
3419+ GError *local = NULL;
3420+ int priority;
3421+ gboolean never_default = FALSE;
3422+ gboolean ip6_privacy = FALSE, ip6_privacy_prefer_public_ip;
3423+ NMSettingIP6ConfigPrivacy ip6_privacy_val;
3424+ guint32 route_table;
3425+
3426+ s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
3427+
3428+ /* First check if IPV6_DEFROUTE is set for this device; IPV6_DEFROUTE has the
3429+ * opposite meaning from never-default. The default if IPV6_DEFROUTE is not
3430+ * specified is IPV6_DEFROUTE=yes which means that this connection can be used
3431+ * as a default route
3432+ */
3433+ never_default = !svGetValueBoolean (ifcfg, "IPV6_DEFROUTE", TRUE);
3434+
3435+ /* Then check if IPV6_DEFAULTGW or IPV6_DEFAULTDEV is specified;
3436+ * they are global and override IPV6_DEFROUTE
3437+ * When both are set, the device specified in IPV6_DEFAULTGW takes preference.
3438+ */
3439+ if (network_ifcfg) {
3440+ const char *ipv6_defaultgw, *ipv6_defaultdev;
3441+ gs_free char *ipv6_defaultgw_to_free = NULL;
3442+ gs_free char *ipv6_defaultdev_to_free = NULL;
3443+ const char *default_dev = NULL;
3444+
3445+ /* Get the connection ifcfg device name and the global default route device */
3446+ nm_clear_g_free (&value);
3447+ v = svGetValueStr (ifcfg, "DEVICE", &value);
3448+ ipv6_defaultgw = svGetValueStr (network_ifcfg, "IPV6_DEFAULTGW", &ipv6_defaultgw_to_free);
3449+ ipv6_defaultdev = svGetValueStr (network_ifcfg, "IPV6_DEFAULTDEV", &ipv6_defaultdev_to_free);
3450+
3451+ if (ipv6_defaultgw) {
3452+ default_dev = strchr (ipv6_defaultgw, '%');
3453+ if (default_dev)
3454+ default_dev++;
3455+ }
3456+ if (!default_dev)
3457+ default_dev = ipv6_defaultdev;
3458+
3459+ /* If there was a global default route device specified, then only connections
3460+ * for that device can be the default connection.
3461+ */
3462+ if (default_dev && v)
3463+ never_default = !!strcmp (v, default_dev);
3464+ }
3465+
3466+ /* Find out method property */
3467+ /* Is IPV6 enabled? Set method to "ignored", when not enabled */
3468+ disabled = svGetValueBoolean(ifcfg, "IPV6_DISABLED", FALSE);
3469+ nm_clear_g_free (&value);
3470+ v = svGetValueStr (ifcfg, "IPV6INIT", &value);
3471+ ipv6init = svGetValueBoolean (ifcfg, "IPV6INIT", FALSE);
3472+ if (!v) {
3473+ if (network_ifcfg)
3474+ ipv6init = svGetValueBoolean (network_ifcfg, "IPV6INIT", FALSE);
3475+ }
3476+
3477+ if (disabled)
3478+ method = NM_SETTING_IP6_CONFIG_METHOD_DISABLED;
3479+ else if (!ipv6init)
3480+ method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
3481+ else {
3482+ ipv6forwarding = svGetValueBoolean (ifcfg, "IPV6FORWARDING", FALSE);
3483+ nm_clear_g_free (&value);
3484+ v = svGetValueStr (ifcfg, "IPV6_AUTOCONF", &value);
3485+ dhcp6 = svGetValueBoolean (ifcfg, "DHCPV6C", FALSE);
3486+
3487+ if (!g_strcmp0 (v, "shared"))
3488+ method = NM_SETTING_IP6_CONFIG_METHOD_SHARED;
3489+ else if (svParseBoolean (v, !ipv6forwarding))
3490+ method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
3491+ else if (dhcp6)
3492+ method = NM_SETTING_IP6_CONFIG_METHOD_DHCP;
3493+ else {
3494+ /* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */
3495+ nm_clear_g_free (&value);
3496+ v = svGetValueStr (ifcfg, "IPV6ADDR", &value);
3497+ if (!v) {
3498+ nm_clear_g_free (&value);
3499+ v = svGetValueStr (ifcfg, "IPV6ADDR_SECONDARIES", &value);
3500+ }
3501+
3502+ if (!v)
3503+ method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
3504+ }
3505+ }
3506+ /* TODO - handle other methods */
3507+
3508+ /* Read IPv6 Privacy Extensions configuration */
3509+ nm_clear_g_free (&value);
3510+ v = svGetValueStr (ifcfg, "IPV6_PRIVACY", &value);
3511+ if (v) {
3512+ ip6_privacy = svParseBoolean (v, FALSE);
3513+ if (!ip6_privacy)
3514+ ip6_privacy = (g_strcmp0 (v, "rfc4941") == 0) ||
3515+ (g_strcmp0 (v, "rfc3041") == 0);
3516+ }
3517+ ip6_privacy_prefer_public_ip = svGetValueBoolean (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", FALSE);
3518+ ip6_privacy_val = v ?
3519+ (ip6_privacy ?
3520+ (ip6_privacy_prefer_public_ip ? NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR : NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR) :
3521+ NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED) :
3522+ NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
3523+
3524+ /* the route table (policy routing) is ignored if we don't handle routes. */
3525+ route_table = svGetValueInt64 (ifcfg, "IPV6_ROUTE_TABLE", 10,
3526+ 0, G_MAXUINT32, 0);
3527+ if ( route_table != 0
3528+ && !routes_read) {
3529+ PARSE_WARNING ("'rule-' or 'rule6-' files are present; Policy routing (IPV6_ROUTE_TABLE) is ignored");
3530+ route_table = 0;
3531+ }
3532+
3533+ g_object_set (s_ip6,
3534+ NM_SETTING_IP_CONFIG_METHOD, method,
3535+ NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, !svGetValueBoolean (ifcfg, "IPV6_PEERDNS", TRUE),
3536+ NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, !svGetValueBoolean (ifcfg, "IPV6_PEERROUTES", TRUE),
3537+ NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default,
3538+ NM_SETTING_IP_CONFIG_MAY_FAIL, !svGetValueBoolean (ifcfg, "IPV6_FAILURE_FATAL", FALSE),
3539+ NM_SETTING_IP_CONFIG_ROUTE_METRIC, svGetValueInt64 (ifcfg, "IPV6_ROUTE_METRIC", 10,
3540+ -1, G_MAXUINT32, -1),
3541+ NM_SETTING_IP_CONFIG_ROUTE_TABLE, (guint) route_table,
3542+ NM_SETTING_IP6_CONFIG_IP6_PRIVACY, ip6_privacy_val,
3543+ NULL);
3544+
3545+ /* Don't bother to read IP, DNS and routes when IPv6 is disabled */
3546+ if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
3547+ NM_SETTING_IP6_CONFIG_METHOD_DISABLED))
3548+ return NM_SETTING (g_steal_pointer (&s_ip6));
3549+
3550+ nm_clear_g_free (&value);
3551+ v = svGetValueStr (ifcfg, "DHCPV6_DUID", &value);
3552+ if (v)
3553+ g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_DHCP_DUID, v, NULL);
3554+
3555+ nm_clear_g_free (&value);
3556+ v = svGetValueStr (ifcfg, "DHCPV6_HOSTNAME", &value);
3557+ /* Use DHCP_HOSTNAME as fallback if it is in FQDN format and ipv6.method is
3558+ * auto or dhcp: this is required to support old ifcfg files
3559+ */
3560+ if (!v && ( !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
3561+ || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP))) {
3562+ nm_clear_g_free (&value);
3563+ v = svGetValueStr (ifcfg, "DHCP_HOSTNAME", &value);
3564+ if (v && !strchr (v, '.'))
3565+ v = NULL;
3566+ }
3567+ if (v)
3568+ g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL);
3569+
3570+ g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME,
3571+ svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NULL);
3572+
3573+ /* Read static IP addresses.
3574+ * Read them even for AUTO and DHCP methods - in this case the addresses are
3575+ * added to the automatic ones. Note that this is not currently supported by
3576+ * the legacy 'network' service (ifup-eth).
3577+ */
3578+ ipv6addr = svGetValueStr (ifcfg, "IPV6ADDR", &ipv6addr_to_free);
3579+ ipv6addr_secondaries = svGetValueStr (ifcfg, "IPV6ADDR_SECONDARIES", &ipv6addr_secondaries_to_free);
3580+
3581+ nm_clear_g_free (&value);
3582+ value = g_strjoin (ipv6addr && ipv6addr_secondaries ? " " : NULL,
3583+ ipv6addr ?: "",
3584+ ipv6addr_secondaries ?: "",
3585+ NULL);
3586+
3587+ list = nm_utils_strsplit_set (value, " ");
3588+ for (iter = list, i = 0; iter && *iter; iter++, i++) {
3589+ NMIPAddress *addr = NULL;
3590+
3591+ if (!parse_full_ip6_address (ifcfg, *iter, i, &addr, error))
3592+ return NULL;
3593+
3594+ if (!nm_setting_ip_config_add_address (s_ip6, addr))
3595+ PARSE_WARNING ("duplicate IP6 address");
3596+ nm_ip_address_unref (addr);
3597+ }
3598+
3599+ /* Gateway */
3600+ if (nm_setting_ip_config_get_num_addresses (s_ip6)) {
3601+ nm_clear_g_free (&value);
3602+ v = svGetValueStr (ifcfg, "IPV6_DEFAULTGW", &value);
3603+ if (!v) {
3604+ /* If no gateway in the ifcfg, try global /etc/sysconfig/network instead */
3605+ if (network_ifcfg) {
3606+ nm_clear_g_free (&value);
3607+ v = svGetValueStr (network_ifcfg, "IPV6_DEFAULTGW", &value);
3608+ }
3609+ }
3610+ if (v) {
3611+ char *ptr;
3612+ if ((ptr = strchr (v, '%')) != NULL)
3613+ *ptr = '\0'; /* remove %interface prefix if present */
3614+ if (!nm_utils_ipaddr_valid (AF_INET6, v)) {
3615+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3616+ "Invalid IP6 address '%s'", v);
3617+ return NULL;
3618+ }
3619+
3620+ g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, v, NULL);
3621+ }
3622+ }
3623+
3624+ i_val = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64;
3625+ if (!svGetValueEnum (ifcfg, "IPV6_ADDR_GEN_MODE",
3626+ nm_setting_ip6_config_addr_gen_mode_get_type (),
3627+ &i_val, &local)) {
3628+ PARSE_WARNING ("%s", local->message);
3629+ g_clear_error (&local);
3630+ }
3631+ g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, i_val, NULL);
3632+
3633+ /* IPv6 tokenized interface identifier */
3634+ nm_clear_g_free (&value);
3635+ v = svGetValueStr (ifcfg, "IPV6_TOKEN", &value);
3636+ if (v)
3637+ g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, v, NULL);
3638+
3639+ /* DNS servers
3640+ * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting())
3641+ */
3642+ for (i = 1; i <= 10; i++) {
3643+ char tag[256];
3644+
3645+ numbered_tag (tag, "DNS", i);
3646+ nm_clear_g_free (&value);
3647+ v = svGetValueStr (ifcfg, tag, &value);
3648+ if (!v) {
3649+ /* all done */
3650+ break;
3651+ }
3652+
3653+ if (nm_utils_ipaddr_valid (AF_INET6, v)) {
3654+ if (!nm_setting_ip_config_add_dns (s_ip6, v))
3655+ PARSE_WARNING ("duplicate DNS server %s", tag);
3656+ } else if (nm_utils_ipaddr_valid (AF_INET, v)) {
3657+ /* Ignore IPv4 addresses */
3658+ } else {
3659+ PARSE_WARNING ("invalid DNS server address %s", v);
3660+ return NULL;
3661+ }
3662+ }
3663+
3664+ if (!routes_read) {
3665+ /* NOP */
3666+ } else {
3667+ gs_free char *route6_path = NULL;
3668+
3669+ /* Read static routes from route6-<interface> file */
3670+ route6_path = utils_get_route6_path (svFileGetName (ifcfg));
3671+ if (!read_route_file (AF_INET6, route6_path, s_ip6, error))
3672+ return NULL;
3673+ }
3674+
3675+ /* DNS searches */
3676+ nm_clear_g_free (&value);
3677+ v = svGetValueStr (ifcfg, "IPV6_DOMAIN", &value);
3678+ if (v) {
3679+ gs_free const char **searches = NULL;
3680+
3681+ searches = nm_utils_strsplit_set (v, " ");
3682+ if (searches) {
3683+ for (iter = searches; *iter; iter++) {
3684+ if (!nm_setting_ip_config_add_dns_search (s_ip6, *iter))
3685+ PARSE_WARNING ("duplicate DNS domain '%s'", *iter);
3686+ }
3687+ }
3688+ }
3689+
3690+ /* DNS options */
3691+ nm_clear_g_free (&value);
3692+ parse_dns_options (s_ip6, svGetValue (ifcfg, "IPV6_RES_OPTIONS", &value));
3693+
3694+ /* DNS priority */
3695+ priority = svGetValueInt64 (ifcfg, "IPV6_DNS_PRIORITY", 10, G_MININT32, G_MAXINT32, 0);
3696+ g_object_set (s_ip6,
3697+ NM_SETTING_IP_CONFIG_DNS_PRIORITY,
3698+ priority,
3699+ NULL);
3700+
3701+ return NM_SETTING (g_steal_pointer (&s_ip6));
3702+}
3703+
3704+static NMSetting *
3705+make_sriov_setting (shvarFile *ifcfg)
3706+{
3707+ gs_unref_hashtable GHashTable *keys = NULL;
3708+ gs_unref_ptrarray GPtrArray *vfs = NULL;
3709+ int autoprobe_drivers;
3710+ NMSettingSriov *s_sriov;
3711+ gint64 total_vfs;
3712+
3713+
3714+ total_vfs = svGetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", 10, 0, G_MAXUINT32, -1);
3715+
3716+ autoprobe_drivers = svGetValueInt64 (ifcfg,
3717+ "SRIOV_AUTOPROBE_DRIVERS",
3718+ 10,
3719+ NM_TERNARY_DEFAULT,
3720+ NM_TERNARY_TRUE,
3721+ -2);
3722+
3723+ keys = svGetKeys (ifcfg, SV_KEY_TYPE_SRIOV_VF);
3724+ if (keys) {
3725+ GHashTableIter iter;
3726+ const char *key;
3727+
3728+ g_hash_table_iter_init (&iter, keys);
3729+ while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
3730+ gs_free_error GError *error = NULL;
3731+ gs_free char *value_to_free = NULL;
3732+ const char *value;
3733+ NMSriovVF *vf;
3734+
3735+ nm_assert (g_str_has_prefix (key, "SRIOV_VF"));
3736+
3737+ value = svGetValue (ifcfg, key, &value_to_free);
3738+ if (!value)
3739+ continue;
3740+
3741+ key += NM_STRLEN ("SRIOV_VF");
3742+
3743+ vf = _nm_utils_sriov_vf_from_strparts (key, value, TRUE, &error);
3744+ if (!vf) {
3745+ PARSE_WARNING ("ignoring invalid SR-IOV VF '%s %s': %s",
3746+ key, value, error->message);
3747+ continue;
3748+ }
3749+ if (!vfs)
3750+ vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
3751+ g_ptr_array_add (vfs, vf);
3752+ }
3753+ }
3754+
3755+ /* Create the setting when at least one key is set */
3756+ if ( total_vfs < 0
3757+ && !vfs
3758+ && autoprobe_drivers < NM_TERNARY_DEFAULT)
3759+ return NULL;
3760+
3761+ s_sriov = (NMSettingSriov *) nm_setting_sriov_new ();
3762+
3763+ autoprobe_drivers = NM_MAX (autoprobe_drivers, NM_TERNARY_DEFAULT);
3764+ total_vfs = NM_MAX (total_vfs, 0);
3765+
3766+ g_object_set (s_sriov,
3767+ NM_SETTING_SRIOV_TOTAL_VFS, (guint) total_vfs,
3768+ NM_SETTING_SRIOV_VFS, vfs,
3769+ NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, autoprobe_drivers,
3770+ NULL);
3771+
3772+ return (NMSetting *) s_sriov;
3773+}
3774+
3775+static NMSetting *
3776+make_tc_setting (shvarFile *ifcfg)
3777+{
3778+ NMSettingTCConfig *s_tc = NULL;
3779+ char tag[256];
3780+ int i;
3781+
3782+ s_tc = (NMSettingTCConfig *) nm_setting_tc_config_new ();
3783+
3784+ for (i = 1;; i++) {
3785+ NMTCQdisc *qdisc = NULL;
3786+ gs_free char *value_to_free = NULL;
3787+ const char *value = NULL;
3788+ GError *local = NULL;
3789+
3790+ value = svGetValueStr (ifcfg, numbered_tag (tag, "QDISC", i), &value_to_free);
3791+ if (!value)
3792+ break;
3793+
3794+ qdisc = nm_utils_tc_qdisc_from_str (value, &local);
3795+ if (!qdisc) {
3796+ PARSE_WARNING ("ignoring bad tc qdisc: '%s': %s", value, local->message);
3797+ continue;
3798+ }
3799+
3800+ if (!nm_setting_tc_config_add_qdisc (s_tc, qdisc))
3801+ PARSE_WARNING ("duplicate tc qdisc");
3802+
3803+ nm_tc_qdisc_unref (qdisc);
3804+ }
3805+
3806+ for (i = 1;; i++) {
3807+ NMTCTfilter *tfilter = NULL;
3808+ gs_free char *value_to_free = NULL;
3809+ const char *value = NULL;
3810+ GError *local = NULL;
3811+
3812+ value = svGetValueStr (ifcfg, numbered_tag (tag, "FILTER", i), &value_to_free);
3813+ if (!value)
3814+ break;
3815+
3816+ tfilter = nm_utils_tc_tfilter_from_str (value, &local);
3817+ if (!tfilter) {
3818+ PARSE_WARNING ("ignoring bad tc filter: '%s': %s", value, local->message);
3819+ continue;
3820+ }
3821+
3822+ if (!nm_setting_tc_config_add_tfilter (s_tc, tfilter))
3823+ PARSE_WARNING ("duplicate tc filter");
3824+
3825+ nm_tc_tfilter_unref (tfilter);
3826+ }
3827+
3828+ if ( nm_setting_tc_config_get_num_qdiscs (s_tc) > 0
3829+ || nm_setting_tc_config_get_num_tfilters (s_tc) > 0)
3830+ return NM_SETTING (s_tc);
3831+
3832+ g_object_unref (s_tc);
3833+ return NULL;
3834+}
3835+
3836+typedef struct {
3837+ const char *enable_key;
3838+ const char *advertise_key;
3839+ const char *willing_key;
3840+ const char *flags_prop;
3841+} DcbFlagsProperty;
3842+
3843+enum {
3844+ DCB_APP_FCOE_FLAGS = 0,
3845+ DCB_APP_ISCSI_FLAGS = 1,
3846+ DCB_APP_FIP_FLAGS = 2,
3847+ DCB_PFC_FLAGS = 3,
3848+ DCB_PG_FLAGS = 4,
3849+};
3850+
3851+static DcbFlagsProperty dcb_flags_props[] = {
3852+ { KEY_DCB_APP_FCOE_ENABLE, KEY_DCB_APP_FCOE_ADVERTISE, KEY_DCB_APP_FCOE_WILLING, NM_SETTING_DCB_APP_FCOE_FLAGS },
3853+ { KEY_DCB_APP_ISCSI_ENABLE, KEY_DCB_APP_ISCSI_ADVERTISE, KEY_DCB_APP_ISCSI_WILLING, NM_SETTING_DCB_APP_ISCSI_FLAGS },
3854+ { KEY_DCB_APP_FIP_ENABLE, KEY_DCB_APP_FIP_ADVERTISE, KEY_DCB_APP_FIP_WILLING, NM_SETTING_DCB_APP_FIP_FLAGS },
3855+ { KEY_DCB_PFC_ENABLE, KEY_DCB_PFC_ADVERTISE, KEY_DCB_PFC_WILLING, NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS },
3856+ { KEY_DCB_PG_ENABLE, KEY_DCB_PG_ADVERTISE, KEY_DCB_PG_WILLING, NM_SETTING_DCB_PRIORITY_GROUP_FLAGS },
3857+ { NULL },
3858+};
3859+
3860+static NMSettingDcbFlags
3861+read_dcb_flags (shvarFile *ifcfg, DcbFlagsProperty *property)
3862+{
3863+ NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE;
3864+
3865+ if (svGetValueBoolean (ifcfg, property->enable_key, FALSE))
3866+ flags |= NM_SETTING_DCB_FLAG_ENABLE;
3867+ if (svGetValueBoolean (ifcfg, property->advertise_key, FALSE))
3868+ flags |= NM_SETTING_DCB_FLAG_ADVERTISE;
3869+ if (svGetValueBoolean (ifcfg, property->willing_key, FALSE))
3870+ flags |= NM_SETTING_DCB_FLAG_WILLING;
3871+
3872+ return flags;
3873+}
3874+
3875+static gboolean
3876+read_dcb_app (shvarFile *ifcfg,
3877+ NMSettingDcb *s_dcb,
3878+ const char *app,
3879+ DcbFlagsProperty *flags_prop,
3880+ const char *priority_prop,
3881+ GError **error)
3882+{
3883+ NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE;
3884+ gs_free char *value = NULL;
3885+ const char *v;
3886+ gboolean success = TRUE;
3887+ int priority = -1;
3888+ char key[255];
3889+
3890+ flags = read_dcb_flags (ifcfg, flags_prop);
3891+
3892+ /* Priority */
3893+ nm_sprintf_buf (key, "DCB_APP_%s_PRIORITY", app);
3894+ v = svGetValueStr (ifcfg, key, &value);
3895+ if (v) {
3896+ priority = _nm_utils_ascii_str_to_int64 (v, 0, 0, 7, -1);
3897+ if (priority < 0) {
3898+ success = FALSE;
3899+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3900+ "Invalid %s value '%s' (expected 0 - 7)",
3901+ key, v);
3902+ }
3903+
3904+ if (!(flags & NM_SETTING_DCB_FLAG_ENABLE))
3905+ PARSE_WARNING ("ignoring DCB %s priority; app not enabled", app);
3906+ }
3907+
3908+ if (success) {
3909+ g_object_set (G_OBJECT (s_dcb),
3910+ flags_prop->flags_prop, flags,
3911+ priority_prop, (guint) priority,
3912+ NULL);
3913+ }
3914+
3915+ return success;
3916+}
3917+
3918+typedef void (*DcbSetBoolFunc) (NMSettingDcb *, guint, gboolean);
3919+
3920+static gboolean
3921+read_dcb_bool_array (shvarFile *ifcfg,
3922+ NMSettingDcb *s_dcb,
3923+ NMSettingDcbFlags flags,
3924+ const char *prop,
3925+ const char *desc,
3926+ DcbSetBoolFunc set_func,
3927+ GError **error)
3928+{
3929+ gs_free char *value = NULL;
3930+ const char *v;
3931+ guint i;
3932+
3933+ v = svGetValueStr (ifcfg, prop, &value);
3934+ if (!v)
3935+ return TRUE;
3936+
3937+ if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
3938+ PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
3939+ return TRUE;
3940+ }
3941+
3942+ if (strlen (v) != 8) {
3943+ PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, v);
3944+ g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3945+ "boolean array must be 8 characters");
3946+ return FALSE;
3947+ }
3948+
3949+ /* All characters must be either 0 or 1 */
3950+ for (i = 0; i < 8; i++) {
3951+ if (v[i] != '0' && v[i] != '1') {
3952+ PARSE_WARNING ("invalid %s value '%s': not all 0s and 1s", prop, v);
3953+ g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3954+ "invalid boolean digit");
3955+ return FALSE;
3956+ }
3957+ set_func (s_dcb, i, (v[i] == '1'));
3958+ }
3959+ return TRUE;
3960+}
3961+
3962+typedef void (*DcbSetUintFunc) (NMSettingDcb *, guint, guint);
3963+
3964+static gboolean
3965+read_dcb_uint_array (shvarFile *ifcfg,
3966+ NMSettingDcb *s_dcb,
3967+ NMSettingDcbFlags flags,
3968+ const char *prop,
3969+ const char *desc,
3970+ gboolean f_allowed,
3971+ DcbSetUintFunc set_func,
3972+ GError **error)
3973+{
3974+ gs_free char *val = NULL;
3975+ guint i;
3976+
3977+ val = svGetValueStr_cp (ifcfg, prop);
3978+ if (!val)
3979+ return TRUE;
3980+
3981+ if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
3982+ PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
3983+ return TRUE;
3984+ }
3985+
3986+ if (strlen (val) != 8) {
3987+ PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, val);
3988+ g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
3989+ "uint array must be 8 characters");
3990+ return FALSE;
3991+ }
3992+
3993+ /* All characters must be either 0 - 7 or (optionally) f */
3994+ for (i = 0; i < 8; i++) {
3995+ if (val[i] >= '0' && val[i] <= '7')
3996+ set_func (s_dcb, i, val[i] - '0');
3997+ else if (f_allowed && (val[i] == 'f' || val[i] == 'F'))
3998+ set_func (s_dcb, i, 15);
3999+ else {
4000+ PARSE_WARNING ("invalid %s value '%s': not 0 - 7%s",
4001+ prop, val, f_allowed ? " or 'f'" : "");
4002+ g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4003+ "invalid uint digit");
4004+ return FALSE;
4005+ }
4006+ }
4007+
4008+ return TRUE;
4009+}
4010+
4011+static gboolean
4012+read_dcb_percent_array (shvarFile *ifcfg,
4013+ NMSettingDcb *s_dcb,
4014+ NMSettingDcbFlags flags,
4015+ const char *prop,
4016+ const char *desc,
4017+ gboolean sum_pct,
4018+ DcbSetUintFunc set_func,
4019+ GError **error)
4020+{
4021+ gs_free char *val = NULL;
4022+ gs_free const char **split = NULL;
4023+ const char *const *iter;
4024+ guint i, sum = 0;
4025+
4026+ val = svGetValueStr_cp (ifcfg, prop);
4027+ if (!val)
4028+ return TRUE;
4029+
4030+ if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
4031+ PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
4032+ return TRUE;
4033+ }
4034+
4035+ split = nm_utils_strsplit_set (val, ",");
4036+ if (NM_PTRARRAY_LEN (split) != 8) {
4037+ PARSE_WARNING ("invalid %s percentage list value '%s'", prop, val);
4038+ g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4039+ "percent array must be 8 elements");
4040+ return FALSE;
4041+ }
4042+
4043+ for (iter = split, i = 0; iter && *iter; iter++, i++) {
4044+ int tmp;
4045+
4046+ tmp = _nm_utils_ascii_str_to_int64 (*iter, 0, 0, 100, -1);
4047+ if (tmp < 0) {
4048+ PARSE_WARNING ("invalid %s percentage value '%s'", prop, *iter);
4049+ g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4050+ "invalid percent element");
4051+ return FALSE;
4052+ }
4053+ set_func (s_dcb, i, (guint) tmp);
4054+ sum += (guint) tmp;
4055+ }
4056+
4057+ if (sum_pct && (sum != 100)) {
4058+ PARSE_WARNING ("%s percentages do not equal 100%%", prop);
4059+ g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4060+ "invalid percentage sum");
4061+ return FALSE;
4062+ }
4063+
4064+ return TRUE;
4065+}
4066+
4067+static gboolean
4068+make_dcb_setting (shvarFile *ifcfg,
4069+ NMSetting **out_setting,
4070+ GError **error)
4071+{
4072+ gs_unref_object NMSettingDcb *s_dcb = NULL;
4073+ gboolean dcb_on;
4074+ NMSettingDcbFlags flags = NM_SETTING_DCB_FLAG_NONE;
4075+
4076+ g_return_val_if_fail (out_setting != NULL, FALSE);
4077+
4078+ dcb_on = !!svGetValueBoolean (ifcfg, "DCB", FALSE);
4079+ if (!dcb_on)
4080+ return TRUE;
4081+
4082+ s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
4083+
4084+ /* FCOE */
4085+ if (!read_dcb_app (ifcfg, s_dcb, "FCOE",
4086+ &dcb_flags_props[DCB_APP_FCOE_FLAGS],
4087+ NM_SETTING_DCB_APP_FCOE_PRIORITY,
4088+ error)) {
4089+ return FALSE;
4090+ }
4091+ if (nm_setting_dcb_get_app_fcoe_flags (s_dcb) & NM_SETTING_DCB_FLAG_ENABLE) {
4092+ gs_free char *val = NULL;
4093+
4094+ val = svGetValueStr_cp (ifcfg, KEY_DCB_APP_FCOE_MODE);
4095+ if (val) {
4096+ if (NM_IN_STRSET (val, NM_SETTING_DCB_FCOE_MODE_FABRIC,
4097+ NM_SETTING_DCB_FCOE_MODE_VN2VN))
4098+ g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_FCOE_MODE, val, NULL);
4099+ else {
4100+ PARSE_WARNING ("invalid FCoE mode '%s'", val);
4101+ g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4102+ "invalid FCoE mode");
4103+ return FALSE;
4104+ }
4105+ }
4106+ }
4107+
4108+ /* iSCSI */
4109+ if (!read_dcb_app (ifcfg, s_dcb, "ISCSI",
4110+ &dcb_flags_props[DCB_APP_ISCSI_FLAGS],
4111+ NM_SETTING_DCB_APP_ISCSI_PRIORITY,
4112+ error)) {
4113+ return FALSE;
4114+ }
4115+
4116+ /* FIP */
4117+ if (!read_dcb_app (ifcfg, s_dcb, "FIP",
4118+ &dcb_flags_props[DCB_APP_FIP_FLAGS],
4119+ NM_SETTING_DCB_APP_FIP_PRIORITY,
4120+ error)) {
4121+ return FALSE;
4122+ }
4123+
4124+ /* Priority Flow Control */
4125+ flags = read_dcb_flags (ifcfg, &dcb_flags_props[DCB_PFC_FLAGS]);
4126+ g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, flags, NULL);
4127+
4128+ if (!read_dcb_bool_array (ifcfg,
4129+ s_dcb,
4130+ flags,
4131+ KEY_DCB_PFC_UP,
4132+ "PFC",
4133+ nm_setting_dcb_set_priority_flow_control,
4134+ error)) {
4135+ return FALSE;
4136+ }
4137+
4138+ /* Priority Groups */
4139+ flags = read_dcb_flags (ifcfg, &dcb_flags_props[DCB_PG_FLAGS]);
4140+ g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, flags, NULL);
4141+
4142+ if (!read_dcb_uint_array (ifcfg,
4143+ s_dcb,
4144+ flags,
4145+ KEY_DCB_PG_ID,
4146+ "PGID",
4147+ TRUE,
4148+ nm_setting_dcb_set_priority_group_id,
4149+ error)) {
4150+ return FALSE;
4151+ }
4152+
4153+ /* Group bandwidth */
4154+ if (!read_dcb_percent_array (ifcfg,
4155+ s_dcb,
4156+ flags,
4157+ KEY_DCB_PG_PCT,
4158+ "PGPCT",
4159+ TRUE,
4160+ nm_setting_dcb_set_priority_group_bandwidth,
4161+ error)) {
4162+ return FALSE;
4163+ }
4164+
4165+ /* Priority bandwidth */
4166+ if (!read_dcb_percent_array (ifcfg,
4167+ s_dcb,
4168+ flags,
4169+ KEY_DCB_PG_UPPCT,
4170+ "UPPCT",
4171+ FALSE,
4172+ nm_setting_dcb_set_priority_bandwidth,
4173+ error)) {
4174+ return FALSE;
4175+ }
4176+
4177+ /* Strict Bandwidth */
4178+ if (!read_dcb_bool_array (ifcfg,
4179+ s_dcb,
4180+ flags,
4181+ KEY_DCB_PG_STRICT,
4182+ "STRICT",
4183+ nm_setting_dcb_set_priority_strict_bandwidth,
4184+ error)) {
4185+ return FALSE;
4186+ }
4187+
4188+ if (!read_dcb_uint_array (ifcfg,
4189+ s_dcb,
4190+ flags,
4191+ KEY_DCB_PG_UP2TC,
4192+ "UP2TC",
4193+ FALSE,
4194+ nm_setting_dcb_set_priority_traffic_class,
4195+ error)) {
4196+ return FALSE;
4197+ }
4198+
4199+ *out_setting = NM_SETTING (g_steal_pointer (&s_dcb));
4200+ return TRUE;
4201+}
4202+
4203+static gboolean
4204+add_one_wep_key (shvarFile *ifcfg,
4205+ const char *shvar_key,
4206+ guint8 key_idx,
4207+ gboolean passphrase,
4208+ NMSettingWirelessSecurity *s_wsec,
4209+ GError **error)
4210+{
4211+ gs_free char *value_free = NULL;
4212+ const char *value;
4213+ const char *key = NULL;
4214+
4215+ g_return_val_if_fail (ifcfg != NULL, FALSE);
4216+ g_return_val_if_fail (shvar_key != NULL, FALSE);
4217+ g_return_val_if_fail (key_idx <= 3, FALSE);
4218+ g_return_val_if_fail (s_wsec != NULL, FALSE);
4219+
4220+ value = svGetValueStr (ifcfg, shvar_key, &value_free);
4221+ if (!value)
4222+ return TRUE;
4223+
4224+ /* Validate keys */
4225+ if (passphrase) {
4226+ if (value[0] && strlen (value) < 64)
4227+ key = value;
4228+ } else {
4229+ if (NM_IN_SET (strlen (value), 10, 26)) {
4230+ /* Hexadecimal WEP key */
4231+ if (NM_STRCHAR_ANY (value, ch, !g_ascii_isxdigit (ch))) {
4232+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4233+ "Invalid hexadecimal WEP key.");
4234+ return FALSE;
4235+ }
4236+ key = value;
4237+ } else if ( !strncmp (value, "s:", 2)
4238+ && NM_IN_SET (strlen (value), 7, 15)) {
4239+ /* ASCII key */
4240+ if (NM_STRCHAR_ANY (value + 2, ch, !g_ascii_isprint (ch))) {
4241+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4242+ "Invalid ASCII WEP key.");
4243+ return FALSE;
4244+ }
4245+
4246+ /* Remove 's:' prefix.
4247+ * Don't convert to hex string. wpa_supplicant takes 'wep_key0' option over D-Bus as byte array
4248+ * and converts it to hex string itself. Even though we convert hex string keys into a bin string
4249+ * before passing to wpa_supplicant, this prevents two unnecessary conversions. And mainly,
4250+ * ASCII WEP key doesn't change to HEX WEP key in UI, which could confuse users.
4251+ */
4252+ key = value + 2;
4253+ }
4254+ }
4255+
4256+ if (!key) {
4257+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4258+ "Invalid WEP key length.");
4259+ return FALSE;
4260+ }
4261+
4262+ nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, key);
4263+ return TRUE;
4264+}
4265+
4266+static gboolean
4267+read_wep_keys (shvarFile *ifcfg,
4268+ NMWepKeyType key_type,
4269+ guint8 def_idx,
4270+ NMSettingWirelessSecurity *s_wsec,
4271+ GError **error)
4272+{
4273+ if (key_type != NM_WEP_KEY_TYPE_PASSPHRASE) {
4274+ if (!add_one_wep_key (ifcfg, "KEY1", 0, FALSE, s_wsec, error))
4275+ return FALSE;
4276+ if (!add_one_wep_key (ifcfg, "KEY2", 1, FALSE, s_wsec, error))
4277+ return FALSE;
4278+ if (!add_one_wep_key (ifcfg, "KEY3", 2, FALSE, s_wsec, error))
4279+ return FALSE;
4280+ if (!add_one_wep_key (ifcfg, "KEY4", 3, FALSE, s_wsec, error))
4281+ return FALSE;
4282+ if (!add_one_wep_key (ifcfg, "KEY", def_idx, FALSE, s_wsec, error))
4283+ return FALSE;
4284+ }
4285+
4286+ if (key_type != NM_WEP_KEY_TYPE_KEY) {
4287+ if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE1", 0, TRUE, s_wsec, error))
4288+ return FALSE;
4289+ if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE2", 1, TRUE, s_wsec, error))
4290+ return FALSE;
4291+ if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE3", 2, TRUE, s_wsec, error))
4292+ return FALSE;
4293+ if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE4", 3, TRUE, s_wsec, error))
4294+ return FALSE;
4295+ }
4296+
4297+ return TRUE;
4298+}
4299+
4300+static NMSetting *
4301+make_wep_setting (shvarFile *ifcfg,
4302+ const char *file,
4303+ GError **error)
4304+{
4305+ gs_unref_object NMSettingWirelessSecurity *s_wsec = NULL;
4306+ gs_free char *value = NULL;
4307+ shvarFile *keys_ifcfg = NULL;
4308+ int default_key_idx = 0;
4309+ gboolean has_default_key = FALSE;
4310+ NMSettingSecretFlags key_flags;
4311+
4312+ s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
4313+ g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NULL);
4314+
4315+ value = svGetValueStr_cp (ifcfg, "DEFAULTKEY");
4316+ if (value) {
4317+ default_key_idx = _nm_utils_ascii_str_to_int64 (value, 0, 1, 4, 0);
4318+ if (default_key_idx == 0) {
4319+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4320+ "Invalid default WEP key '%s'", value);
4321+ return NULL;
4322+ }
4323+ has_default_key = TRUE;
4324+ default_key_idx--; /* convert to [0...3] */
4325+ g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, (guint) default_key_idx, NULL);
4326+ nm_clear_g_free (&value);
4327+ }
4328+
4329+ /* Read WEP key flags */
4330+ key_flags = _secret_read_ifcfg_flags (ifcfg, "WEP_KEY_FLAGS");
4331+ g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, key_flags, NULL);
4332+
4333+ /* Read keys in the ifcfg file if they are system-owned */
4334+ if (key_flags == NM_SETTING_SECRET_FLAG_NONE) {
4335+ NMWepKeyType key_type;
4336+ const char *v;
4337+ gs_free char *to_free = NULL;
4338+
4339+ v = svGetValueStr (ifcfg, "KEY_TYPE", &to_free);
4340+ if (!v)
4341+ key_type = NM_WEP_KEY_TYPE_UNKNOWN;
4342+ else if (nm_streq (v, "key"))
4343+ key_type = NM_WEP_KEY_TYPE_KEY;
4344+ else if (nm_streq (v, "passphrase"))
4345+ key_type = NM_WEP_KEY_TYPE_PASSPHRASE;
4346+ else {
4347+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4348+ "Invalid KEY_TYPE value '%s'", v);
4349+ return FALSE;
4350+ }
4351+
4352+ if (!read_wep_keys (ifcfg, key_type, default_key_idx, s_wsec, error))
4353+ return NULL;
4354+
4355+ /* Try to get keys from the "shadow" key file */
4356+ keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
4357+ if (keys_ifcfg) {
4358+ if (!read_wep_keys (keys_ifcfg, key_type, default_key_idx, s_wsec, error)) {
4359+ svCloseFile (keys_ifcfg);
4360+ return NULL;
4361+ }
4362+ svCloseFile (keys_ifcfg);
4363+ g_assert (error == NULL || *error == NULL);
4364+ }
4365+
4366+ g_object_set (G_OBJECT (s_wsec),
4367+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, key_type,
4368+ NULL);
4369+ }
4370+
4371+ value = svGetValueStr_cp (ifcfg, "SECURITYMODE");
4372+ if (value) {
4373+ gs_free char *lcase = NULL;
4374+
4375+ lcase = g_ascii_strdown (value, -1);
4376+ nm_clear_g_free (&value);
4377+
4378+ if (nm_streq (lcase, "open")) {
4379+ g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", NULL);
4380+ } else if (nm_streq (lcase, "restricted")) {
4381+ g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", NULL);
4382+ } else {
4383+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4384+ "Invalid WEP authentication algorithm '%s'",
4385+ lcase);
4386+ return NULL;
4387+ }
4388+ }
4389+
4390+ /* If no WEP keys were given, and the keys are not agent-owned, and no
4391+ * default WEP key index was given, then the connection is unencrypted.
4392+ */
4393+ if ( !nm_setting_wireless_security_get_wep_key (s_wsec, 0)
4394+ && !nm_setting_wireless_security_get_wep_key (s_wsec, 1)
4395+ && !nm_setting_wireless_security_get_wep_key (s_wsec, 2)
4396+ && !nm_setting_wireless_security_get_wep_key (s_wsec, 3)
4397+ && (has_default_key == FALSE)
4398+ && (key_flags == NM_SETTING_SECRET_FLAG_NONE)) {
4399+ const char *auth_alg;
4400+
4401+ auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
4402+ if (auth_alg && !strcmp (auth_alg, "shared")) {
4403+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4404+ "WEP Shared Key authentication is invalid for "
4405+ "unencrypted connections.");
4406+ return NULL;
4407+ }
4408+
4409+ /* Unencrypted */
4410+ return NULL;
4411+ }
4412+
4413+ return NM_SETTING (g_steal_pointer (&s_wsec));
4414+}
4415+
4416+static gboolean
4417+fill_wpa_ciphers (shvarFile *ifcfg,
4418+ NMSettingWirelessSecurity *wsec,
4419+ gboolean group,
4420+ gboolean adhoc)
4421+{
4422+ gs_free char *value = NULL;
4423+ const char *p;
4424+ gs_free const char **list = NULL;
4425+ const char *const *iter;
4426+ int i = 0;
4427+
4428+ p = svGetValueStr (ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE", &value);
4429+ if (!p)
4430+ return TRUE;
4431+
4432+ list = nm_utils_strsplit_set (p, " ");
4433+ for (iter = list; iter && *iter; iter++, i++) {
4434+ if (!strcmp (*iter, "CCMP")) {
4435+ if (group)
4436+ nm_setting_wireless_security_add_group (wsec, "ccmp");
4437+ else
4438+ nm_setting_wireless_security_add_pairwise (wsec, "ccmp");
4439+ } else if (!strcmp (*iter, "TKIP")) {
4440+ if (group)
4441+ nm_setting_wireless_security_add_group (wsec, "tkip");
4442+ else
4443+ nm_setting_wireless_security_add_pairwise (wsec, "tkip");
4444+ } else if (group && !strcmp (*iter, "WEP104"))
4445+ nm_setting_wireless_security_add_group (wsec, "wep104");
4446+ else if (group && !strcmp (*iter, "WEP40"))
4447+ nm_setting_wireless_security_add_group (wsec, "wep40");
4448+ else {
4449+ PARSE_WARNING ("ignoring invalid %s cipher '%s'",
4450+ group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE",
4451+ *iter);
4452+ }
4453+ }
4454+
4455+ return TRUE;
4456+}
4457+
4458+#define WPA_PMK_LEN 32
4459+
4460+static char *
4461+parse_wpa_psk (shvarFile *ifcfg,
4462+ const char *file,
4463+ GBytes *ssid,
4464+ GError **error)
4465+{
4466+ shvarFile *keys_ifcfg;
4467+ gs_free char *psk = NULL;
4468+ size_t plen;
4469+
4470+ /* Passphrase must be between 10 and 66 characters in length because WPA
4471+ * hex keys are exactly 64 characters (no quoting), and WPA passphrases
4472+ * are between 8 and 63 characters (inclusive), plus optional quoting if
4473+ * the passphrase contains spaces.
4474+ */
4475+
4476+ /* Try to get keys from the "shadow" key file */
4477+ keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
4478+ if (keys_ifcfg) {
4479+ psk = svGetValueStr_cp (keys_ifcfg, "WPA_PSK");
4480+ svCloseFile (keys_ifcfg);
4481+ }
4482+
4483+ /* Fall back to the original ifcfg */
4484+ if (!psk)
4485+ psk = svGetValueStr_cp (ifcfg, "WPA_PSK");
4486+
4487+ if (!psk)
4488+ return NULL;
4489+
4490+ plen = strlen (psk);
4491+
4492+ if (plen == 64) {
4493+ /* Verify the hex PSK; 64 digits */
4494+ if (!NM_STRCHAR_ALL (psk, ch, g_ascii_isxdigit (ch))) {
4495+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4496+ "Invalid WPA_PSK (contains non-hexadecimal characters)");
4497+ return NULL;
4498+ }
4499+ } else {
4500+ if (plen < 8 || plen > 63) {
4501+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4502+ "Invalid WPA_PSK (passphrases must be between "
4503+ "8 and 63 characters long (inclusive))");
4504+ return NULL;
4505+ }
4506+ }
4507+
4508+ return g_steal_pointer (&psk);
4509+}
4510+
4511+static gboolean
4512+eap_simple_reader (const char *eap_method,
4513+ shvarFile *ifcfg,
4514+ shvarFile *keys_ifcfg,
4515+ NMSetting8021x *s_8021x,
4516+ gboolean phase2,
4517+ GError **error)
4518+{
4519+ NMSettingSecretFlags flags;
4520+ gs_free char *identity_free = NULL;
4521+ nm_auto_free_secret char *password_raw_str = NULL;
4522+ gs_unref_bytes GBytes *password_raw_bytes = NULL;
4523+
4524+ g_object_set (s_8021x,
4525+ NM_SETTING_802_1X_IDENTITY,
4526+ svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free),
4527+ NULL);
4528+
4529+ _secret_set_from_ifcfg (s_8021x,
4530+ ifcfg,
4531+ keys_ifcfg,
4532+ "IEEE_8021X_PASSWORD",
4533+ NM_SETTING_802_1X_PASSWORD);
4534+
4535+ _secret_read_ifcfg (ifcfg, keys_ifcfg, "IEEE_8021X_PASSWORD_RAW", &password_raw_str, &flags);
4536+ if (!_secret_password_raw_to_bytes ("IEEE_8021X_PASSWORD_RAW",
4537+ password_raw_str,
4538+ &password_raw_bytes,
4539+ error))
4540+ return FALSE;
4541+
4542+ g_object_set (s_8021x,
4543+ NM_SETTING_802_1X_PASSWORD_RAW_FLAGS,
4544+ flags,
4545+ NM_SETTING_802_1X_PASSWORD_RAW,
4546+ password_raw_bytes,
4547+ NULL);
4548+
4549+ return TRUE;
4550+}
4551+
4552+static gboolean
4553+eap_tls_reader (const char *eap_method,
4554+ shvarFile *ifcfg,
4555+ shvarFile *keys_ifcfg,
4556+ NMSetting8021x *s_8021x,
4557+ gboolean phase2,
4558+ GError **error)
4559+{
4560+ gs_unref_bytes GBytes *privkey = NULL;
4561+ gs_unref_bytes GBytes *client_cert = NULL;
4562+ gs_free char *identity_free = NULL;
4563+ gs_free char *value_to_free = NULL;
4564+ const char *client_cert_var;
4565+ const char *client_cert_prop;
4566+ NMSetting8021xCKFormat format;
4567+
4568+ g_object_set (s_8021x,
4569+ NM_SETTING_802_1X_IDENTITY,
4570+ svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free),
4571+ NULL);
4572+
4573+ /* CA certificate */
4574+ if (!_cert_set_from_ifcfg (s_8021x,
4575+ ifcfg,
4576+ phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT",
4577+ phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT : NM_SETTING_802_1X_CA_CERT,
4578+ NULL,
4579+ error))
4580+ return FALSE;
4581+ _secret_set_from_ifcfg (s_8021x,
4582+ ifcfg,
4583+ keys_ifcfg,
4584+ phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD",
4585+ phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD);
4586+
4587+ /* Private key */
4588+ if (!_cert_set_from_ifcfg (s_8021x,
4589+ ifcfg,
4590+ phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY",
4591+ phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY : NM_SETTING_802_1X_PRIVATE_KEY,
4592+ &privkey,
4593+ error))
4594+ return FALSE;
4595+ _secret_set_from_ifcfg (s_8021x,
4596+ ifcfg,
4597+ keys_ifcfg,
4598+ phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : "IEEE_8021X_PRIVATE_KEY_PASSWORD",
4599+ phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
4600+
4601+ /* Client certificate */
4602+ client_cert_var = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT";
4603+ client_cert_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT;
4604+ if (!_cert_set_from_ifcfg (s_8021x,
4605+ ifcfg,
4606+ client_cert_var,
4607+ client_cert_prop,
4608+ &client_cert,
4609+ error))
4610+ return FALSE;
4611+ _secret_set_from_ifcfg (s_8021x,
4612+ ifcfg,
4613+ keys_ifcfg,
4614+ phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD",
4615+ phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD);
4616+
4617+ /* In the past when the private key and client certificate
4618+ * were the same PKCS #12 file we used to write only the
4619+ * private key variable. Still support that even if it means
4620+ * that we have to look into the file content, which makes
4621+ * the connection not self-contained.
4622+ */
4623+ if ( !client_cert
4624+ && privkey
4625+ && !svGetValue (ifcfg, client_cert_var, &value_to_free)) {
4626+ if (phase2)
4627+ format = nm_setting_802_1x_get_phase2_private_key_format (s_8021x);
4628+ else
4629+ format = nm_setting_802_1x_get_private_key_format (s_8021x);
4630+
4631+ if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
4632+ g_object_set (s_8021x, client_cert_prop, privkey, NULL);
4633+ }
4634+
4635+ return TRUE;
4636+}
4637+
4638+static gboolean
4639+eap_peap_reader (const char *eap_method,
4640+ shvarFile *ifcfg,
4641+ shvarFile *keys_ifcfg,
4642+ NMSetting8021x *s_8021x,
4643+ gboolean phase2,
4644+ GError **error)
4645+{
4646+ gs_free char *value = NULL;
4647+ const char *v;
4648+ gs_free const char **list = NULL;
4649+ const char *const *iter;
4650+
4651+ if (!_cert_set_from_ifcfg (s_8021x,
4652+ ifcfg,
4653+ "IEEE_8021X_CA_CERT",
4654+ NM_SETTING_802_1X_CA_CERT,
4655+ NULL,
4656+ error))
4657+ return FALSE;
4658+ _secret_set_from_ifcfg (s_8021x,
4659+ ifcfg,
4660+ keys_ifcfg,
4661+ "IEEE_8021X_CA_CERT_PASSWORD",
4662+ NM_SETTING_802_1X_CA_CERT_PASSWORD);
4663+
4664+ nm_clear_g_free (&value);
4665+ v = svGetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", &value);
4666+ if (v) {
4667+ if (!strcmp (v, "0"))
4668+ g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "0", NULL);
4669+ else if (!strcmp (v, "1"))
4670+ g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "1", NULL);
4671+ else {
4672+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4673+ "Unknown IEEE_8021X_PEAP_VERSION value '%s'",
4674+ v);
4675+ return FALSE;
4676+ }
4677+ }
4678+
4679+ if (svGetValueBoolean (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE))
4680+ g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL);
4681+
4682+ nm_clear_g_free (&value);
4683+ v = svGetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY", &value);
4684+ if (v)
4685+ g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, v, NULL);
4686+
4687+ nm_clear_g_free (&value);
4688+ v = svGetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", &value);
4689+ if (!v) {
4690+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4691+ "Missing IEEE_8021X_INNER_AUTH_METHODS.");
4692+ return FALSE;
4693+ }
4694+
4695+ /* Handle options for the inner auth method */
4696+ list = nm_utils_strsplit_set (v, " ");
4697+ iter = list;
4698+ if (iter) {
4699+ if (NM_IN_STRSET (*iter, "MSCHAPV2",
4700+ "MD5",
4701+ "GTC")) {
4702+ if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
4703+ return FALSE;
4704+ } else if (nm_streq (*iter, "TLS")) {
4705+ if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
4706+ return FALSE;
4707+ } else {
4708+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4709+ "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
4710+ *iter);
4711+ return FALSE;
4712+ }
4713+
4714+ {
4715+ gs_free char *lower = NULL;
4716+
4717+ lower = g_ascii_strdown (*iter, -1);
4718+ g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL);
4719+ }
4720+ }
4721+
4722+ if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) {
4723+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4724+ "No valid IEEE_8021X_INNER_AUTH_METHODS found.");
4725+ return FALSE;
4726+ }
4727+
4728+ return TRUE;
4729+}
4730+
4731+static gboolean
4732+eap_ttls_reader (const char *eap_method,
4733+ shvarFile *ifcfg,
4734+ shvarFile *keys_ifcfg,
4735+ NMSetting8021x *s_8021x,
4736+ gboolean phase2,
4737+ GError **error)
4738+{
4739+ gs_free char *inner_auth = NULL;
4740+ gs_free char *value = NULL;
4741+ const char *v;
4742+ gs_free const char **list = NULL;
4743+ const char *const *iter;
4744+
4745+ if (!_cert_set_from_ifcfg (s_8021x,
4746+ ifcfg,
4747+ "IEEE_8021X_CA_CERT",
4748+ NM_SETTING_802_1X_CA_CERT,
4749+ NULL,
4750+ error))
4751+ return FALSE;
4752+ _secret_set_from_ifcfg (s_8021x,
4753+ ifcfg,
4754+ keys_ifcfg,
4755+ "IEEE_8021X_CA_CERT_PASSWORD",
4756+ NM_SETTING_802_1X_CA_CERT_PASSWORD);
4757+
4758+ nm_clear_g_free (&value);
4759+ v = svGetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY", &value);
4760+ if (v)
4761+ g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, v, NULL);
4762+
4763+ nm_clear_g_free (&value);
4764+ v = svGetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", &value);
4765+ if (!v) {
4766+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4767+ "Missing IEEE_8021X_INNER_AUTH_METHODS.");
4768+ return FALSE;
4769+ }
4770+
4771+ inner_auth = g_ascii_strdown (v, -1);
4772+
4773+ /* Handle options for the inner auth method */
4774+ list = nm_utils_strsplit_set (inner_auth, " ");
4775+ iter = list;
4776+ if (iter) {
4777+ if (NM_IN_STRSET (*iter, "mschapv2",
4778+ "mschap",
4779+ "pap",
4780+ "chap")) {
4781+ if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
4782+ return FALSE;
4783+ g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, *iter, NULL);
4784+ } else if (nm_streq (*iter, "eap-tls")) {
4785+ if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
4786+ return FALSE;
4787+ g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL);
4788+ } else if (NM_IN_STRSET (*iter, "eap-mschapv2",
4789+ "eap-md5",
4790+ "eap-gtc")) {
4791+ if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
4792+ return FALSE;
4793+ g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, (*iter + NM_STRLEN ("eap-")), NULL);
4794+ } else {
4795+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4796+ "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
4797+ *iter);
4798+ return FALSE;
4799+ }
4800+ }
4801+
4802+ return TRUE;
4803+}
4804+
4805+static gboolean
4806+eap_fast_reader (const char *eap_method,
4807+ shvarFile *ifcfg,
4808+ shvarFile *keys_ifcfg,
4809+ NMSetting8021x *s_8021x,
4810+ gboolean phase2,
4811+ GError **error)
4812+{
4813+ char *anon_ident = NULL;
4814+ char *pac_file = NULL;
4815+ char *real_pac_path = NULL;
4816+ char *inner_auth = NULL;
4817+ char *fast_provisioning = NULL;
4818+ char *lower;
4819+ gs_free const char **list = NULL;
4820+ const char *const *iter;
4821+ const char *pac_prov_str;
4822+ gboolean allow_unauth = FALSE, allow_auth = FALSE;
4823+ gboolean success = FALSE;
4824+
4825+ pac_file = svGetValueStr_cp (ifcfg, "IEEE_8021X_PAC_FILE");
4826+ if (pac_file) {
4827+ real_pac_path = get_full_file_path (svFileGetName (ifcfg), pac_file);
4828+ g_object_set (s_8021x, NM_SETTING_802_1X_PAC_FILE, real_pac_path, NULL);
4829+ }
4830+
4831+ fast_provisioning = svGetValueStr_cp (ifcfg, "IEEE_8021X_FAST_PROVISIONING");
4832+ if (fast_provisioning) {
4833+ gs_free const char **list1 = NULL;
4834+
4835+ list1 = nm_utils_strsplit_set (fast_provisioning, " \t");
4836+ for (iter = list1; iter && *iter; iter++) {
4837+ if (strcmp (*iter, "allow-unauth") == 0)
4838+ allow_unauth = TRUE;
4839+ else if (strcmp (*iter, "allow-auth") == 0)
4840+ allow_auth = TRUE;
4841+ else {
4842+ PARSE_WARNING ("invalid IEEE_8021X_FAST_PROVISIONING '%s' "
4843+ "(space-separated list of these values [allow-auth, allow-unauth] expected)",
4844+ *iter);
4845+ }
4846+ }
4847+ }
4848+ pac_prov_str = allow_unauth ? (allow_auth ? "3" : "1") : (allow_auth ? "2" : "0");
4849+ g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, pac_prov_str, NULL);
4850+
4851+ if (!pac_file && !(allow_unauth || allow_auth)) {
4852+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4853+ "IEEE_8021X_PAC_FILE not provided and EAP-FAST automatic PAC provisioning disabled.");
4854+ goto done;
4855+ }
4856+
4857+ anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY");
4858+ if (anon_ident)
4859+ g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
4860+
4861+ inner_auth = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
4862+ if (!inner_auth) {
4863+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4864+ "Missing IEEE_8021X_INNER_AUTH_METHODS.");
4865+ goto done;
4866+ }
4867+
4868+ /* Handle options for the inner auth method */
4869+ list = nm_utils_strsplit_set (inner_auth, " ");
4870+ iter = list;
4871+ if (iter) {
4872+ if ( !strcmp (*iter, "MSCHAPV2")
4873+ || !strcmp (*iter, "GTC")) {
4874+ if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
4875+ goto done;
4876+ } else {
4877+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4878+ "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
4879+ *iter);
4880+ goto done;
4881+ }
4882+
4883+ lower = g_ascii_strdown (*iter, -1);
4884+ g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, NULL);
4885+ g_free (lower);
4886+ }
4887+
4888+ if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) {
4889+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4890+ "No valid IEEE_8021X_INNER_AUTH_METHODS found.");
4891+ goto done;
4892+ }
4893+
4894+ success = TRUE;
4895+
4896+done:
4897+ g_free (inner_auth);
4898+ g_free (fast_provisioning);
4899+ g_free (real_pac_path);
4900+ g_free (pac_file);
4901+ g_free (anon_ident);
4902+ return success;
4903+}
4904+
4905+typedef struct {
4906+ const char *method;
4907+ gboolean (*reader) (const char *eap_method,
4908+ shvarFile *ifcfg,
4909+ shvarFile *keys_ifcfg,
4910+ NMSetting8021x *s_8021x,
4911+ gboolean phase2,
4912+ GError **error);
4913+ gboolean wifi_phase2_only;
4914+} EAPReader;
4915+
4916+static EAPReader eap_readers[] = {
4917+ { "md5", eap_simple_reader, TRUE },
4918+ { "pap", eap_simple_reader, TRUE },
4919+ { "chap", eap_simple_reader, TRUE },
4920+ { "mschap", eap_simple_reader, TRUE },
4921+ { "mschapv2", eap_simple_reader, TRUE },
4922+ { "leap", eap_simple_reader, FALSE },
4923+ { "pwd", eap_simple_reader, FALSE },
4924+ { "tls", eap_tls_reader, FALSE },
4925+ { "peap", eap_peap_reader, FALSE },
4926+ { "ttls", eap_ttls_reader, FALSE },
4927+ { "fast", eap_fast_reader, FALSE },
4928+ { NULL, NULL }
4929+};
4930+
4931+static void
4932+read_8021x_list_value (shvarFile *ifcfg,
4933+ const char *ifcfg_var_name,
4934+ NMSetting8021x *setting,
4935+ const char *prop_name)
4936+{
4937+ gs_free char *value = NULL;
4938+ gs_free const char **strv = NULL;
4939+ const char *v;
4940+
4941+ g_return_if_fail (ifcfg != NULL);
4942+ g_return_if_fail (ifcfg_var_name != NULL);
4943+ g_return_if_fail (prop_name != NULL);
4944+
4945+ v = svGetValueStr (ifcfg, ifcfg_var_name, &value);
4946+ if (!v)
4947+ return;
4948+
4949+ strv = nm_utils_strsplit_set (v, " \t");
4950+ if (strv)
4951+ g_object_set (setting, prop_name, strv, NULL);
4952+}
4953+
4954+static NMSetting8021x *
4955+fill_8021x (shvarFile *ifcfg,
4956+ const char *file,
4957+ const char *key_mgmt,
4958+ gboolean wifi,
4959+ GError **error)
4960+{
4961+ nm_auto_shvar_file_close shvarFile *keys_ifcfg = NULL;
4962+ gs_unref_object NMSetting8021x *s_8021x = NULL;
4963+ gs_free char *value = NULL;
4964+ const char *v;
4965+ gs_free const char **list = NULL;
4966+ const char *const *iter;
4967+ gint64 timeout;
4968+ int i_val;
4969+
4970+ v = svGetValueStr (ifcfg, "IEEE_8021X_EAP_METHODS", &value);
4971+ if (!v) {
4972+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
4973+ "Missing IEEE_8021X_EAP_METHODS for key management '%s'",
4974+ key_mgmt);
4975+ return NULL;
4976+ }
4977+
4978+ list = nm_utils_strsplit_set (v, " ");
4979+
4980+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
4981+
4982+ /* Read in the lookaside keys_ifcfg file, if present */
4983+ keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
4984+
4985+ /* Validate and handle each EAP method */
4986+ for (iter = list; iter && *iter; iter++) {
4987+ EAPReader *eap = &eap_readers[0];
4988+ gboolean found = FALSE;
4989+ gs_free char *lower = NULL;
4990+
4991+ lower = g_ascii_strdown (*iter, -1);
4992+ while (eap->method) {
4993+ if (strcmp (eap->method, lower))
4994+ goto next;
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches