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

Subscribers

People subscribed via source and target branches