summary refs log tree commit diff
path: root/src/settings/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/settings/plugins')
-rw-r--r--src/settings/plugins/ifcfg-rh/meson.build2
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c14
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c244
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c529
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h75
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c464
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c338
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/meson.build2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-owe5
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-autoip1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-424
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected14
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c240
-rw-r--r--src/settings/plugins/ifupdown/meson.build2
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.c15
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.c2
-rw-r--r--src/settings/plugins/ifupdown/tests/meson.build2
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c2
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c2
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.c2
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.c6
-rw-r--r--src/settings/plugins/keyfile/tests/meson.build2
-rw-r--r--src/settings/plugins/meson.build2
28 files changed, 1388 insertions, 625 deletions
diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build
index d9dd3edb..e193ff96 100644
--- a/src/settings/plugins/ifcfg-rh/meson.build
+++ b/src/settings/plugins/ifcfg-rh/meson.build
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
 install_data(
   'nm-ifcfg-rh.conf',
   install_dir: dbus_conf_dir,
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
index f57ca1a7..f5c9bb5e 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -20,6 +20,7 @@
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "nm-config.h"
+#include "nm-dbus-manager.h"
 #include "settings/nm-settings-plugin.h"
 #include "settings/nm-settings-utils.h"
 #include "NetworkManagerUtils.h"
@@ -205,9 +206,9 @@ _load_file (NMSIfcfgRHPlugin *self,
 		const char *unmanaged_spec;
 		const char *unrecognized_spec;
 
-		if (!nms_ifcfg_rh_util_parse_unhandled_spec (unhandled_spec,
-		                                             &unmanaged_spec,
-		                                             &unrecognized_spec)) {
+		if (!nms_ifcfg_rh_utils_parse_unhandled_spec (unhandled_spec,
+		                                              &unmanaged_spec,
+		                                              &unrecognized_spec)) {
 			nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
 			                    "invalid unhandled spec \"%s\"",
 			                    unhandled_spec);
@@ -1132,6 +1133,13 @@ _dbus_setup (NMSIfcfgRHPlugin *self)
 
 	_dbus_clear (self);
 
+	if (!NM_MAIN_DBUS_CONNECTION_GET) {
+		_LOGW ("dbus: don't use D-Bus for %s service", IFCFGRH1_BUS_NAME);
+		return;
+	}
+
+	/* We use a separate D-Bus connection so that org.freedesktop.NetworkManager and com.redhat.ifcfgrh1
+	 * are exported by different connections. */
 	address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
 	if (address == NULL) {
 		_LOGW ("dbus: failed getting address for system bus: %s", error->message);
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index e01f7344..8e153412 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -399,7 +399,9 @@ make_connection_setting (const char *file,
 	if (v) {
 		GError *error = NULL;
 
-		if (nm_utils_ifname_valid_kernel (v, &error)) {
+		/* Only validate for NMU_IFACE_KERNEL, because ifcfg plugin anyway
+		 * doesn't support OVS types. */
+		if (nm_utils_ifname_valid (v, NMU_IFACE_KERNEL, &error)) {
 			g_object_set (s_con,
 			              NM_SETTING_CONNECTION_INTERFACE_NAME, v,
 			              NULL);
@@ -502,6 +504,24 @@ make_connection_setting (const char *file,
 	}
 
 	nm_clear_g_free (&value);
+	v = svGetValueStr (ifcfg, "VRF_UUID", &value);
+	if (!v)
+		v = svGetValueStr (ifcfg, "VRF", &value);
+	if (v) {
+		const char *old_value;
+
+		if ((old_value = nm_setting_connection_get_master (s_con))) {
+			PARSE_WARNING ("Already configured as slave of %s. Ignoring VRF{_UUID}=\"%s\"",
+			               old_value, v);
+		} else {
+			g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, v, NULL);
+			g_object_set (s_con, NM_SETTING_CONNECTION_SLAVE_TYPE,
+			              NM_SETTING_VRF_SETTING_NAME, NULL);
+		}
+	}
+
+
+	nm_clear_g_free (&value);
 	v = svGetValueStr (ifcfg, "GATEWAY_PING_TIMEOUT", &value);
 	if (v) {
 		gint64 tmp;
@@ -530,16 +550,18 @@ make_connection_setting (const char *file,
 	g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (int) vint64, NULL);
 
 	nm_clear_g_free (&value);
-	v = svGetValueStr (ifcfg, "DEVTIMEOUT", &value);
+	v = svGetValue (ifcfg, "DEVTIMEOUT", &value);
 	if (v) {
+		v = nm_str_skip_leading_spaces (v);
 		vint64 = _nm_utils_ascii_str_to_int64 (v, 10, 0, ((gint64) G_MAXINT32) / 1000, -1);
 		if (vint64 != -1)
 			vint64 *= 1000;
-		else {
+		else if (v[0] != '\0') {
 			char *endptr;
 			double d;
 
-			d = g_ascii_strtod (v, &endptr);
+			d = nm_g_ascii_strtod (v, &endptr);
+			endptr = nm_str_skip_leading_spaces (endptr);
 			if (   errno == 0
 			    && endptr[0] == '\0'
 			    && d >= 0.0) {
@@ -709,7 +731,7 @@ read_full_ip4_address (shvarFile *ifcfg,
 			else {
 				/* Try to autodetermine the prefix for the address' class */
 				prefix = _nm_utils_ip4_get_default_prefix (ipaddr);
-				PARSE_WARNING ("missing %s, assuming %s/%d", prefix_tag, nm_utils_inet4_ntop (ipaddr, inet_buf), prefix);
+				PARSE_WARNING ("missing %s, assuming %s/%d", prefix_tag, _nm_utils_inet4_ntop (ipaddr, inet_buf), prefix);
 			}
 		}
 	}
@@ -1287,7 +1309,7 @@ read_one_ip4_route (shvarFile *ifcfg,
 		prefix = nm_utils_ip4_netmask_to_prefix (netmask);
 		if (netmask != _nm_utils_ip4_prefix_to_netmask (prefix)) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, inet_buf));
+			             "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, _nm_utils_inet4_ntop (netmask, inet_buf));
 			return FALSE;
 		}
 	} else {
@@ -1318,7 +1340,7 @@ read_one_ip4_route (shvarFile *ifcfg,
 	v = svGetValueStr (ifcfg, numbered_tag (tag, "OPTIONS", which), &value);
 	if (v) {
 		if (parse_route_line (v, AF_INET, *out_route, NULL, error) < 0) {
-			g_clear_pointer (out_route, nm_ip_route_unref);
+			nm_clear_pointer (out_route, nm_ip_route_unref);
 			return FALSE;
 		}
 	}
@@ -1327,39 +1349,40 @@ read_one_ip4_route (shvarFile *ifcfg,
 }
 
 static gboolean
-read_route_file (int addr_family,
-                 const char *filename,
-                 NMSettingIPConfig *s_ip,
-                 GError **error)
+read_route_file_parse (int addr_family,
+                       const char *filename,
+                       const char *contents,
+                       gsize len,
+                       NMSettingIPConfig *s_ip,
+                       GError **error)
 {
-	gs_free char *contents = NULL;
-	char *contents_rest = NULL;
-	const char *line;
-	gsize len = 0;
 	gsize line_num;
 
-	g_return_val_if_fail (filename, FALSE);
-	g_return_val_if_fail (   (addr_family == AF_INET  && NM_IS_SETTING_IP4_CONFIG (s_ip))
-	                      || (addr_family == AF_INET6 && NM_IS_SETTING_IP6_CONFIG (s_ip)), FALSE);
-	g_return_val_if_fail (!error || !*error, FALSE);
+	nm_assert (filename);
+	nm_assert (addr_family == nm_setting_ip_config_get_addr_family (s_ip));
+	nm_assert (!error || !*error);
 
-	if (   !g_file_get_contents (filename, &contents, &len, NULL)
-	    || !len) {
+	if (len <= 0)
 		return TRUE;  /* missing/empty = success */
-	}
 
 	line_num = 0;
-	for (line = strtok_r (contents, "\n", &contents_rest);
-	     line;
-	     line = strtok_r (NULL, "\n", &contents_rest)) {
+	while (TRUE) {
 		nm_auto_unref_ip_route NMIPRoute *route = NULL;
 		gs_free_error GError *local = NULL;
+		const char *line = contents;
+		char *eol;
 		int e;
 
+		eol = strchr (contents, '\n');
+		if (eol) {
+			eol[0] = '\0';
+			contents = &eol[1];
+		}
+
 		line_num++;
 
 		if (parse_route_line_is_comment (line))
-			continue;
+			goto next;
 
 		e = parse_route_line (line, addr_family, NULL, &route, &local);
 
@@ -1371,14 +1394,38 @@ read_route_file (int addr_family,
 				 * entire connection. */
 				PARSE_WARNING ("ignoring invalid route at \"%s\" (%s:%lu): %s", line, filename, (long unsigned) line_num, local->message);
 			}
-			continue;
+			goto next;
 		}
 
 		if (!nm_setting_ip_config_add_route (s_ip, route))
 			PARSE_WARNING ("duplicate IPv%c route", addr_family == AF_INET ? '4' : '6');
+
+next:
+		if (!eol)
+			return TRUE;
+
+		/* restore original content. */
+		eol[0] = '\n';
 	}
+}
 
-	return TRUE;
+static gboolean
+read_route_file (int addr_family,
+                 const char *filename,
+                 NMSettingIPConfig *s_ip,
+                 GError **error)
+{
+	gs_free char *contents = NULL;
+	gsize len;
+
+	nm_assert (filename);
+	nm_assert (addr_family == nm_setting_ip_config_get_addr_family (s_ip));
+	nm_assert (!error || !*error);
+
+	if (!g_file_get_contents (filename, &contents, &len, NULL))
+		return TRUE;  /* missing/empty = success */
+
+	return read_route_file_parse (addr_family, filename, contents, len, s_ip, error);
 }
 
 static void
@@ -1577,7 +1624,6 @@ make_ip4_setting (shvarFile *ifcfg,
 	int i;
 	guint32 a;
 	gboolean has_key;
-	shvarFile *route_ifcfg;
 	gboolean never_default;
 	gint64 i64;
 	int priority;
@@ -1771,10 +1817,10 @@ make_ip4_setting (shvarFile *ifcfg,
 			nm_clear_g_free (&value);
 			v = svGetValueStr (ifcfg, tag, &value);
 			if (v) {
-				if (nm_utils_ipaddr_valid (AF_INET, v)) {
+				if (nm_utils_ipaddr_is_valid (AF_INET, v)) {
 					if (!nm_setting_ip_config_add_dns (s_ip4, v))
 						PARSE_WARNING ("duplicate DNS server %s", tag);
-				} else if (nm_utils_ipaddr_valid (AF_INET6, v)) {
+				} else if (nm_utils_ipaddr_is_valid (AF_INET6, v)) {
 					/* Ignore IPv6 addresses */
 				} else {
 					PARSE_WARNING ("invalid DNS server address %s", v);
@@ -1814,32 +1860,34 @@ make_ip4_setting (shvarFile *ifcfg,
 	/* Static routes  - route-<name> file */
 	route_path = utils_get_route_path (svFileGetName (ifcfg));
 
-	if (!routes_read) {
-		/* NOP */
-	} else if (utils_has_route_file_new_syntax (route_path)) {
-		/* Parse route file in new syntax */
-		route_ifcfg = utils_get_route_ifcfg (svFileGetName (ifcfg), FALSE);
-		if (route_ifcfg) {
+	if (routes_read) {
+		gs_free char *contents = NULL;
+		gsize len;
+
+		if (!g_file_get_contents (route_path, &contents, &len, NULL))
+			len = 0;
+
+		if (utils_has_route_file_new_syntax_content (contents, len)) {
+			nm_auto_shvar_file_close shvarFile *route_ifcfg = NULL;
+
+			/* Parse route file in new syntax */
+			route_ifcfg = svFile_new (route_path, -1, contents);
 			for (i = 0;; i++) {
-				NMIPRoute *route = NULL;
+				nm_auto_unref_ip_route NMIPRoute *route = NULL;
 
-				if (!read_one_ip4_route (route_ifcfg, i, &route, error)) {
-					svCloseFile (route_ifcfg);
+				if (!read_one_ip4_route (route_ifcfg, i, &route, error))
 					return NULL;
-				}
 
 				if (!route)
 					break;
 
 				if (!nm_setting_ip_config_add_route (s_ip4, route))
 					PARSE_WARNING ("duplicate IP4 route");
-				nm_ip_route_unref (route);
 			}
-			svCloseFile (route_ifcfg);
+		} else {
+			if (!read_route_file_parse (AF_INET, route_path, contents, len, s_ip4, error))
+				return NULL;
 		}
-	} else {
-		if (!read_route_file (AF_INET, route_path, s_ip4, error))
-			return NULL;
 	}
 
 	/* Legacy value NM used for a while but is incorrect (rh #459370) */
@@ -2214,7 +2262,7 @@ make_ip6_setting (shvarFile *ifcfg,
 			char *ptr;
 			if ((ptr = strchr (v, '%')) != NULL)
 				*ptr = '\0';  /* remove %interface prefix if present */
-			if (!nm_utils_ipaddr_valid (AF_INET6, v)) {
+			if (!nm_utils_ipaddr_is_valid (AF_INET6, v)) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 				             "Invalid IP6 address '%s'", v);
 				return NULL;
@@ -2253,10 +2301,10 @@ make_ip6_setting (shvarFile *ifcfg,
 			break;
 		}
 
-		if (nm_utils_ipaddr_valid (AF_INET6, v)) {
+		if (nm_utils_ipaddr_is_valid (AF_INET6, v)) {
 			if (!nm_setting_ip_config_add_dns (s_ip6, v))
 				PARSE_WARNING ("duplicate DNS server %s", tag);
-		} else if (nm_utils_ipaddr_valid (AF_INET, v)) {
+		} else if (nm_utils_ipaddr_is_valid (AF_INET, v)) {
 			/* Ignore IPv4 addresses */
 		} else {
 			PARSE_WARNING ("invalid DNS server address %s", v);
@@ -3623,6 +3671,14 @@ next:
 	v = svGetValueStr (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH", &value);
 	g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, v, NULL);
 
+	nm_clear_g_free (&value);
+	v = svGetValueStr (ifcfg, "IEEE_8021X_DOMAIN_MATCH", &value);
+	g_object_set (s_8021x, NM_SETTING_802_1X_DOMAIN_MATCH, v, NULL);
+
+	nm_clear_g_free (&value);
+	v = svGetValueStr (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_MATCH", &value);
+	g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH, v, NULL);
+
 	timeout = svGetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", 10, 0, G_MAXINT32, 0);
 	g_object_set (s_8021x, NM_SETTING_802_1X_AUTH_TIMEOUT, (int) timeout, NULL);
 
@@ -3645,7 +3701,7 @@ make_wpa_setting (shvarFile *ifcfg,
 	gs_unref_object NMSettingWirelessSecurity *wsec = NULL;
 	gs_free char *value = NULL;
 	const char *v;
-	gboolean wpa_psk = FALSE, wpa_sae = FALSE, wpa_eap = FALSE, ieee8021x = FALSE;
+	gboolean wpa_psk = FALSE, wpa_sae = FALSE, wpa_owe = FALSE, wpa_eap = FALSE, ieee8021x = FALSE;
 	int i_val;
 	GError *local = NULL;
 
@@ -3654,10 +3710,12 @@ make_wpa_setting (shvarFile *ifcfg,
 	v = svGetValueStr (ifcfg, "KEY_MGMT", &value);
 	wpa_psk = nm_streq0 (v, "WPA-PSK");
 	wpa_sae = nm_streq0 (v, "SAE");
+	wpa_owe = nm_streq0 (v, "OWE");
 	wpa_eap = nm_streq0 (v, "WPA-EAP");
 	ieee8021x = nm_streq0 (v, "IEEE8021X");
 	if (   !wpa_psk
 	    && !wpa_sae
+	    && !wpa_owe
 	    && !wpa_eap
 	    && !ieee8021x)
 		return NULL; /* Not WPA or Dynamic WEP */
@@ -3673,7 +3731,7 @@ make_wpa_setting (shvarFile *ifcfg,
 	              NULL);
 
 	/* Pairwise and Group ciphers (only relevant for WPA/RSN) */
-	if (wpa_psk || wpa_sae || wpa_eap) {
+	if (wpa_psk || wpa_sae || wpa_owe || wpa_eap) {
 		fill_wpa_ciphers (ifcfg, wsec, FALSE, adhoc);
 		fill_wpa_ciphers (ifcfg, wsec, TRUE, adhoc);
 	}
@@ -3722,7 +3780,7 @@ make_wpa_setting (shvarFile *ifcfg,
 			g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "sae", NULL);
 		}
 	} else {
-		nm_assert (wpa_eap || ieee8021x);
+		nm_assert (wpa_eap || ieee8021x || wpa_owe);
 
 		/* Adhoc mode is mutually exclusive with any 802.1x-based authentication */
 		if (adhoc) {
@@ -3731,14 +3789,17 @@ make_wpa_setting (shvarFile *ifcfg,
 			return NULL;
 		}
 
-		*s_8021x = fill_8021x (ifcfg, file, v, TRUE, error);
-		if (!*s_8021x)
-			return NULL;
-
-		{
-			gs_free char *lower = g_ascii_strdown (v, -1);
+		if (wpa_owe) {
+			g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "owe", NULL);
+		} else {
+			*s_8021x = fill_8021x (ifcfg, file, v, TRUE, error);
+			if (!*s_8021x)
+				return NULL;
 
-			g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, lower, NULL);
+			{
+				gs_free char *lower = g_ascii_strdown (v, -1);
+				g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, lower, NULL);
+			}
 		}
 	}
 
@@ -3782,7 +3843,7 @@ make_leap_setting (shvarFile *ifcfg,
 	nm_clear_g_free (&value);
 
 	value = svGetValueStr_cp (ifcfg, "SECURITYMODE");
-	if (!value || strcasecmp (value, "leap"))
+	if (!value || g_ascii_strcasecmp (value, "leap"))
 		return NULL; /* Not LEAP */
 	nm_clear_g_free (&value);
 
@@ -4339,7 +4400,7 @@ parse_ethtool_option (const char *value,
 			i++;
 
 			if (nm_utils_hwaddr_valid (opt_val, ETH_ALEN)) {
-				g_clear_pointer (out_password, g_free);
+				nm_clear_g_free (out_password);
 				*out_password = g_strdup (opt_val);
 			} else
 				PARSE_WARNING ("Wake-on-LAN password '%s' is invalid", opt_val);
@@ -5091,18 +5152,24 @@ handle_bridge_option (NMSetting *setting,
 		gboolean only_with_stp;
 		gboolean extended_bool;
 	} m/*etadata*/[] = {
-		{ "DELAY",              NM_SETTING_BRIDGE_FORWARD_DELAY,      BRIDGE_OPT_TYPE_MAIN,   .only_with_stp = TRUE },
-		{ "priority",           NM_SETTING_BRIDGE_PRIORITY,           BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE },
-		{ "hello_time",         NM_SETTING_BRIDGE_HELLO_TIME,         BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE },
-		{ "max_age",            NM_SETTING_BRIDGE_MAX_AGE,            BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE },
-		{ "ageing_time",        NM_SETTING_BRIDGE_AGEING_TIME,        BRIDGE_OPT_TYPE_OPTION },
-		{ "multicast_snooping", NM_SETTING_BRIDGE_MULTICAST_SNOOPING, BRIDGE_OPT_TYPE_OPTION },
-		{ "vlan_filtering",     NM_SETTING_BRIDGE_VLAN_FILTERING,     BRIDGE_OPT_TYPE_OPTION },
-		{ "default_pvid",       NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID,  BRIDGE_OPT_TYPE_OPTION },
-		{ "group_fwd_mask",     NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, BRIDGE_OPT_TYPE_OPTION },
-		{ "priority",           NM_SETTING_BRIDGE_PORT_PRIORITY,      BRIDGE_OPT_TYPE_PORT_OPTION },
-		{ "path_cost",          NM_SETTING_BRIDGE_PORT_PATH_COST,     BRIDGE_OPT_TYPE_PORT_OPTION },
-		{ "hairpin_mode",       NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE,  BRIDGE_OPT_TYPE_PORT_OPTION, .extended_bool = TRUE, },
+		{ "DELAY",                      NM_SETTING_BRIDGE_FORWARD_DELAY,              BRIDGE_OPT_TYPE_MAIN,        .only_with_stp = TRUE },
+		{ "priority",                   NM_SETTING_BRIDGE_PRIORITY,                   BRIDGE_OPT_TYPE_OPTION,      .only_with_stp = TRUE },
+		{ "hello_time",                 NM_SETTING_BRIDGE_HELLO_TIME,                 BRIDGE_OPT_TYPE_OPTION,      .only_with_stp = TRUE },
+		{ "max_age",                    NM_SETTING_BRIDGE_MAX_AGE,                    BRIDGE_OPT_TYPE_OPTION,      .only_with_stp = TRUE },
+		{ "ageing_time",                NM_SETTING_BRIDGE_AGEING_TIME,                BRIDGE_OPT_TYPE_OPTION },
+		{ "multicast_querier",          NM_SETTING_BRIDGE_MULTICAST_QUERIER,          BRIDGE_OPT_TYPE_OPTION },
+		{ "multicast_query_use_ifaddr", NM_SETTING_BRIDGE_MULTICAST_QUERY_USE_IFADDR, BRIDGE_OPT_TYPE_OPTION },
+		{ "multicast_snooping",         NM_SETTING_BRIDGE_MULTICAST_SNOOPING,         BRIDGE_OPT_TYPE_OPTION },
+		{ "multicast_router",           NM_SETTING_BRIDGE_MULTICAST_ROUTER,           BRIDGE_OPT_TYPE_OPTION },
+		{ "vlan_filtering",             NM_SETTING_BRIDGE_VLAN_FILTERING,             BRIDGE_OPT_TYPE_OPTION },
+		{ "default_pvid",               NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID,          BRIDGE_OPT_TYPE_OPTION },
+		{ "group_address",              NM_SETTING_BRIDGE_GROUP_ADDRESS,              BRIDGE_OPT_TYPE_OPTION },
+		{ "group_fwd_mask",             NM_SETTING_BRIDGE_GROUP_FORWARD_MASK,         BRIDGE_OPT_TYPE_OPTION },
+		{ "vlan_protocol",              NM_SETTING_BRIDGE_VLAN_PROTOCOL,              BRIDGE_OPT_TYPE_OPTION },
+		{ "vlan_stats_enabled",         NM_SETTING_BRIDGE_VLAN_STATS_ENABLED,         BRIDGE_OPT_TYPE_OPTION },
+		{ "priority",                   NM_SETTING_BRIDGE_PORT_PRIORITY,              BRIDGE_OPT_TYPE_PORT_OPTION },
+		{ "path_cost",                  NM_SETTING_BRIDGE_PORT_PATH_COST,             BRIDGE_OPT_TYPE_PORT_OPTION },
+		{ "hairpin_mode",               NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE,          BRIDGE_OPT_TYPE_PORT_OPTION, .extended_bool = TRUE, },
 	};
 	const char *error_message = NULL;
 	int i;
@@ -5124,9 +5191,9 @@ handle_bridge_option (NMSetting *setting,
 		switch (param_spec->value_type) {
 		case G_TYPE_BOOLEAN:
 			if (m[i].extended_bool) {
-				if (!strcasecmp (value, "on") || !strcasecmp (value, "yes") || !strcmp (value, "1"))
+				if (!g_ascii_strcasecmp (value, "on") || !g_ascii_strcasecmp (value, "yes") || !strcmp (value, "1"))
 					v = TRUE;
-				else if (!strcasecmp (value, "off") || !strcasecmp (value, "no"))
+				else if (!g_ascii_strcasecmp (value, "off") || !g_ascii_strcasecmp (value, "no"))
 					v = FALSE;
 				else {
 					error_message = "is not a boolean";
@@ -5155,6 +5222,9 @@ handle_bridge_option (NMSetting *setting,
 				goto warn;
 			}
 			return;
+		case G_TYPE_STRING:
+			nm_g_object_set_property_string (G_OBJECT (setting), m[i].property_name, value, NULL);
+			return;
 		default:
 			nm_assert_not_reached ();
 			continue;
@@ -5259,11 +5329,11 @@ make_bridge_setting (shvarFile *ifcfg,
 
 	value = svGetValueStr (ifcfg, "STP", &value_to_free);
 	if (value) {
-		if (!strcasecmp (value, "on") || !strcasecmp (value, "yes")) {
+		if (!g_ascii_strcasecmp (value, "on") || !g_ascii_strcasecmp (value, "yes")) {
 			g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, TRUE, NULL);
 			stp = TRUE;
 			stp_set = TRUE;
-		} else if (!strcasecmp (value, "off") || !strcasecmp (value, "no")) {
+		} else if (!g_ascii_strcasecmp (value, "off") || !g_ascii_strcasecmp (value, "no")) {
 			g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL);
 			stp_set = TRUE;
 		} else
@@ -5774,9 +5844,9 @@ connection_from_file_full (const char *filename,
 
 	devtype = svGetValueStr_cp (main_ifcfg, "DEVICETYPE");
 	if (devtype) {
-		if (!strcasecmp (devtype, TYPE_TEAM))
+		if (!g_ascii_strcasecmp (devtype, TYPE_TEAM))
 			type = g_strdup (TYPE_TEAM);
-		else if (!strcasecmp (devtype, TYPE_TEAM_PORT)) {
+		else if (!g_ascii_strcasecmp (devtype, TYPE_TEAM_PORT)) {
 			gs_free char *device = NULL;
 
 			type = svGetValueStr_cp (main_ifcfg, "TYPE");
@@ -5910,26 +5980,26 @@ connection_from_file_full (const char *filename,
 	}
 
 	if (svGetValueBoolean (main_ifcfg, "BONDING_MASTER", FALSE) &&
-	    strcasecmp (type, TYPE_BOND)) {
+	    g_ascii_strcasecmp (type, TYPE_BOND)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "BONDING_MASTER=yes key only allowed in TYPE=bond connections");
 		return NULL;
 	}
 
 	/* Construct the connection */
-	if (!strcasecmp (type, TYPE_ETHERNET))
+	if (!g_ascii_strcasecmp (type, TYPE_ETHERNET))
 		connection = wired_connection_from_ifcfg (filename, main_ifcfg, error);
-	else if (!strcasecmp (type, TYPE_WIRELESS))
+	else if (!g_ascii_strcasecmp (type, TYPE_WIRELESS))
 		connection = wireless_connection_from_ifcfg (filename, main_ifcfg, error);
-	else if (!strcasecmp (type, TYPE_INFINIBAND))
+	else if (!g_ascii_strcasecmp (type, TYPE_INFINIBAND))
 		connection = infiniband_connection_from_ifcfg (filename, main_ifcfg, error);
-	else if (!strcasecmp (type, TYPE_BOND))
+	else if (!g_ascii_strcasecmp (type, TYPE_BOND))
 		connection = bond_connection_from_ifcfg (filename, main_ifcfg, error);
-	else if (!strcasecmp (type, TYPE_TEAM))
+	else if (!g_ascii_strcasecmp (type, TYPE_TEAM))
 		connection = team_connection_from_ifcfg (filename, main_ifcfg, error);
-	else if (!strcasecmp (type, TYPE_VLAN))
+	else if (!g_ascii_strcasecmp (type, TYPE_VLAN))
 		connection = vlan_connection_from_ifcfg (filename, main_ifcfg, error);
-	else if (!strcasecmp (type, TYPE_BRIDGE))
+	else if (!g_ascii_strcasecmp (type, TYPE_BRIDGE))
 		connection = bridge_connection_from_ifcfg (filename, main_ifcfg, error);
 	else {
 		connection = create_unhandled_connection (filename, main_ifcfg, "unrecognized", out_unhandled);
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
index ee389bd9..28f57f50 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
@@ -17,9 +17,9 @@
 /*****************************************************************************/
 
 gboolean
-nms_ifcfg_rh_util_parse_unhandled_spec (const char *unhandled_spec,
-                                        const char **out_unmanaged_spec,
-                                        const char **out_unrecognized_spec)
+nms_ifcfg_rh_utils_parse_unhandled_spec (const char *unhandled_spec,
+                                         const char **out_unmanaged_spec,
+                                         const char **out_unrecognized_spec)
 {
 	if (unhandled_spec) {
 		if (NM_STR_HAS_PREFIX (unhandled_spec, "unmanaged:")) {
@@ -72,7 +72,7 @@ check_suffix (const char *base, const char *tag)
 
 	len = strlen (base);
 	tag_len = strlen (tag);
-	if ((len > tag_len) && !strcasecmp (base + len - tag_len, tag))
+	if ((len > tag_len) && !g_ascii_strcasecmp (base + len - tag_len, tag))
 		return TRUE;
 	return FALSE;
 }
@@ -260,27 +260,60 @@ utils_get_route_ifcfg (const char *parent, gboolean should_create)
 gboolean
 utils_has_route_file_new_syntax (const char *filename)
 {
-	char *contents = NULL;
-	gsize len = 0;
-	gboolean ret = FALSE;
-	const char *pattern = "^[[:space:]]*ADDRESS[0-9]+=";
+	gs_free char *contents_data = NULL;
+	gsize len;
 
 	g_return_val_if_fail (filename != NULL, TRUE);
 
-	if (!g_file_get_contents (filename, &contents, &len, NULL))
+	if (!g_file_get_contents (filename, &contents_data, &len, NULL))
 		return TRUE;
 
-	if (len <= 0) {
-		ret = TRUE;
-		goto gone;
-	}
+	return utils_has_route_file_new_syntax_content (contents_data, len);
+}
+
+gboolean
+utils_has_route_file_new_syntax_content (const char *contents,
+                                         gsize len)
+{
+	if (len <= 0)
+		return TRUE;
+
+	while (TRUE) {
+		const char *line = contents;
+		char *eol;
+		gboolean found = FALSE;
+
+		/* matches regex "^[[:space:]]*ADDRESS[0-9]+=" */
+
+		eol = (char *) strchr (contents, '\n');
+		if (eol) {
+			eol[0] = '\0';
+			contents = &eol[1];
+		}
 
-	if (g_regex_match_simple (pattern, contents, G_REGEX_MULTILINE, 0))
-		ret = TRUE;
+		line = nm_str_skip_leading_spaces (line);
+		if (NM_STR_HAS_PREFIX (line, "ADDRESS")) {
+			line += NM_STRLEN ("ADDRESS");
+			if (g_ascii_isdigit (line[0])) {
+				while (g_ascii_isdigit ((++line)[0])) {
+					/* pass */
+				}
+				if (line[0] == '=')
+					found = TRUE;
+			}
+		}
+
+		if (eol) {
+			/* restore the line ending. We don't want to mangle the content from
+			 * POV of the caller. */
+			eol[0] = '\n';
+		}
 
-gone:
-	g_free (contents);
-	return ret;
+		if (found)
+			return TRUE;
+		if (!eol)
+			return FALSE;
+	}
 }
 
 gboolean
@@ -546,31 +579,15 @@ const char *const _nm_ethtool_ifcfg_names[] = {
 	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT,       "tx-vlan-stag-hw-insert"),
 };
 
-const NMEthtoolData *
-nms_ifcfg_rh_utils_get_ethtool_by_name (const char *name)
-{
-	static const struct {
-		NMEthtoolID ethtool_id;
-		const char *kernel_name;
-	} kernel_names[] = {
-		{ NM_ETHTOOL_ID_FEATURE_GRO,    "rx-gro" },
-		{ NM_ETHTOOL_ID_FEATURE_GSO,    "tx-generic-segmentation" },
-		{ NM_ETHTOOL_ID_FEATURE_LRO,    "rx-lro" },
-		{ NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter" },
-		{ NM_ETHTOOL_ID_FEATURE_RX,     "rx-checksum" },
-		{ NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing" },
-		{ NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse" },
-		{ NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert" },
-	};
-	guint i;
-
-	for (i = 0; i < G_N_ELEMENTS (_nm_ethtool_ifcfg_names); i++) {
-		if (nm_streq (name, _nm_ethtool_ifcfg_names[i]))
-			return nm_ethtool_data[i];
-	}
+static
+NM_UTILS_STRING_TABLE_LOOKUP_DEFINE (
+	_get_ethtoolid_by_name,
+	NMEthtoolID,
+	{ nm_assert (name); },
+	{ return NM_ETHTOOL_ID_UNKNOWN; },
 
-	/* Option not found. Note that ethtool utility has built-in features and
-	 * NetworkManager's API follows the naming of these built-in features, whenever
+	/* Map the names from kernel/ethtool/ifcfg to NMEthtoolID. Note that ethtool utility has built-in
+	 * features and NetworkManager's API follows the naming of these built-in features, whenever
 	 * they exist.
 	 * For example, NM's "ethtool.feature-ntuple" corresponds to ethtool utility's "ntuple"
 	 * feature. However the underlying kernel feature is called "rx-ntuple-filter" (as reported
@@ -578,10 +595,430 @@ nms_ifcfg_rh_utils_get_ethtool_by_name (const char *name)
 	 *
 	 * With ethtool utility, whose command line we attempt to parse here, the user can also
 	 * specify the name of the underlying kernel feature directly. So, check whether that is
-	 * the case and if yes, map them to the corresponding NetworkManager's features. */
-	for (i = 0; i < G_N_ELEMENTS (kernel_names); i++) {
-		if (nm_streq (name, kernel_names[i].kernel_name))
-			return nm_ethtool_data[kernel_names[i].ethtool_id];
+	 * the case and if yes, map them to the corresponding NetworkManager's features.
+	 *
+	 * That is why there are duplicate IDs in this list. */
+	{ "esp-hw-offload",               NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD               },
+	{ "esp-tx-csum-hw-offload",       NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD       },
+	{ "fcoe-mtu",                     NM_ETHTOOL_ID_FEATURE_FCOE_MTU                     },
+	{ "gro",                          NM_ETHTOOL_ID_FEATURE_GRO                          },
+	{ "gso",                          NM_ETHTOOL_ID_FEATURE_GSO                          },
+	{ "highdma",                      NM_ETHTOOL_ID_FEATURE_HIGHDMA                      },
+	{ "hw-tc-offload",                NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD                },
+	{ "l2-fwd-offload",               NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD               },
+	{ "loopback",                     NM_ETHTOOL_ID_FEATURE_LOOPBACK                     },
+	{ "lro",                          NM_ETHTOOL_ID_FEATURE_LRO                          },
+	{ "ntuple",                       NM_ETHTOOL_ID_FEATURE_NTUPLE                       },
+	{ "rx",                           NM_ETHTOOL_ID_FEATURE_RX                           },
+	{ "rx-all",                       NM_ETHTOOL_ID_FEATURE_RX_ALL                       },
+	{ "rx-checksum",                  NM_ETHTOOL_ID_FEATURE_RX                           }, // kernel-only name
+	{ "rx-fcs",                       NM_ETHTOOL_ID_FEATURE_RX_FCS                       },
+	{ "rx-gro",                       NM_ETHTOOL_ID_FEATURE_GRO                          }, // kernel-only name
+	{ "rx-gro-hw",                    NM_ETHTOOL_ID_FEATURE_RX_GRO_HW                    },
+	{ "rx-hashing",                   NM_ETHTOOL_ID_FEATURE_RXHASH                       }, // kernel-only name
+	{ "rx-lro",                       NM_ETHTOOL_ID_FEATURE_LRO                          }, // kernel-only name
+	{ "rx-ntuple-filter",             NM_ETHTOOL_ID_FEATURE_NTUPLE                       }, // kernel-only name
+	{ "rx-udp_tunnel-port-offload",   NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD   },
+	{ "rx-vlan-filter",               NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER               },
+	{ "rx-vlan-hw-parse",             NM_ETHTOOL_ID_FEATURE_RXVLAN                       }, // kernel-only name
+	{ "rx-vlan-stag-filter",          NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER          },
+	{ "rx-vlan-stag-hw-parse",        NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE        },
+	{ "rxhash",                       NM_ETHTOOL_ID_FEATURE_RXHASH                       },
+	{ "rxvlan",                       NM_ETHTOOL_ID_FEATURE_RXVLAN                       },
+	{ "sg",                           NM_ETHTOOL_ID_FEATURE_SG                           },
+	{ "tls-hw-record",                NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD                },
+	{ "tls-hw-tx-offload",            NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD            },
+	{ "tso",                          NM_ETHTOOL_ID_FEATURE_TSO                          },
+	{ "tx",                           NM_ETHTOOL_ID_FEATURE_TX                           },
+	{ "tx-checksum-fcoe-crc",         NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC         },
+	{ "tx-checksum-ip-generic",       NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC       },
+	{ "tx-checksum-ipv4",             NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4             },
+	{ "tx-checksum-ipv6",             NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6             },
+	{ "tx-checksum-sctp",             NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP             },
+	{ "tx-esp-segmentation",          NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION          },
+	{ "tx-fcoe-segmentation",         NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION         },
+	{ "tx-generic-segmentation",      NM_ETHTOOL_ID_FEATURE_GSO                          }, // kernel-only name
+	{ "tx-gre-csum-segmentation",     NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION     },
+	{ "tx-gre-segmentation",          NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION          },
+	{ "tx-gso-partial",               NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL               },
+	{ "tx-gso-robust",                NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST                },
+	{ "tx-ipxip4-segmentation",       NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION       },
+	{ "tx-ipxip6-segmentation",       NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION       },
+	{ "tx-nocache-copy",              NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY              },
+	{ "tx-scatter-gather",            NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER            },
+	{ "tx-scatter-gather-fraglist",   NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST   },
+	{ "tx-sctp-segmentation",         NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION         },
+	{ "tx-tcp-ecn-segmentation",      NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION      },
+	{ "tx-tcp-mangleid-segmentation", NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION },
+	{ "tx-tcp-segmentation",          NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION          },
+	{ "tx-tcp6-segmentation",         NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION         },
+	{ "tx-udp-segmentation",          NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION          },
+	{ "tx-udp_tnl-csum-segmentation", NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION },
+	{ "tx-udp_tnl-segmentation",      NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION      },
+	{ "tx-vlan-hw-insert",            NM_ETHTOOL_ID_FEATURE_TXVLAN                       }, // kernel-only name
+	{ "tx-vlan-stag-hw-insert",       NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT       },
+	{ "txvlan",                       NM_ETHTOOL_ID_FEATURE_TXVLAN                       },
+);
+
+const NMEthtoolData *
+nms_ifcfg_rh_utils_get_ethtool_by_name (const char *name)
+{
+	NMEthtoolID id;
+
+	id = _get_ethtoolid_by_name (name);
+	if (id == NM_ETHTOOL_ID_UNKNOWN)
+		return NULL;
+
+	nm_assert (_NM_INT_NOT_NEGATIVE (id));
+	nm_assert (id < G_N_ELEMENTS (nm_ethtool_data));
+	nm_assert (nm_ethtool_data[id]);
+	nm_assert (nm_ethtool_data[id]->id == id);
+	return nm_ethtool_data[id];
+}
+
+/*****************************************************************************/
+
+gboolean
+nms_ifcfg_rh_utils_is_numbered_tag_impl (const char *key,
+                                         const char *tag,
+                                         gsize tag_len,
+                                         gint64 *out_idx)
+{
+	gint64 idx;
+
+	nm_assert (key);
+	nm_assert (tag);
+	nm_assert (tag_len == strlen (tag));
+	nm_assert (tag_len > 0);
+
+	if (strncmp (key, tag, tag_len) != 0)
+		return FALSE;
+
+	key += tag_len;
+
+	if (key[0] == '\0') {
+		/* The key has no number suffix. We treat this also as a numbered
+		 * tag, and it is for certain tags like "IPADDR", but not so much
+		 * for others like "ROUTING_RULE_". The caller may want to handle
+		 * this case specially. */
+		NM_SET_OUT (out_idx, -1);
+		return TRUE;
+	}
+
+	if (!NM_STRCHAR_ALL (key, ch, g_ascii_isdigit (ch)))
+		return FALSE;
+
+	idx = _nm_utils_ascii_str_to_int64 (key, 10, 0, G_MAXINT64, -1);
+	if (idx == -1)
+		return FALSE;
+
+	NM_SET_OUT (out_idx, idx);
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+#define _KEY_TYPE(key, flags) { .key_name = ""key"", .key_flags = ((NMS_IFCFG_KEY_TYPE_WELL_KNOWN) | (flags)), }
+
+const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = {
+	_KEY_TYPE ("ACD_TIMEOUT",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("ADDRESS",                                     NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("ARPING_WAIT",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("AUTH_RETRIES",                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("AUTOCONNECT_PRIORITY",                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("AUTOCONNECT_RETRIES",                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("AUTOCONNECT_SLAVES",                          NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BAND",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BONDING_MASTER",                              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BONDING_OPTS",                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BOOTPROTO",                                   NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BRIDGE",                                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BRIDGE_MACADDR",                              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BRIDGE_PORT_VLANS",                           NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BRIDGE_UUID",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BRIDGE_VLANS",                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BRIDGING_OPTS",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BROWSER_ONLY",                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("BSSID",                                       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("CHANNEL",                                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("CIPHER_GROUP",                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("CIPHER_PAIRWISE",                             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("CONNECTED_MODE",                              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("CONNECTION_METERED",                          NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("CTCPROT",                                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DCB",                                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_APP_FCOE_ADVERTISE,                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_APP_FCOE_ENABLE,                       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_APP_FCOE_MODE,                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DCB_APP_FCOE_PRIORITY",                       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_APP_FCOE_WILLING,                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_APP_FIP_ADVERTISE,                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_APP_FIP_ENABLE,                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DCB_APP_FIP_PRIORITY",                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_APP_FIP_WILLING,                       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_APP_ISCSI_ADVERTISE,                   NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_APP_ISCSI_ENABLE,                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DCB_APP_ISCSI_PRIORITY",                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_APP_ISCSI_WILLING,                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PFC_ADVERTISE,                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PFC_ENABLE,                            NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PFC_UP,                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PFC_WILLING,                           NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PG_ADVERTISE,                          NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PG_ENABLE,                             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PG_ID,                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PG_PCT,                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PG_STRICT,                             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PG_UP2TC,                              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PG_UPPCT,                              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE (KEY_DCB_PG_WILLING,                            NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DEFAULTKEY",                                  NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DEFROUTE",                                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DELAY",                                       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DEVICE",                                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DEVICETYPE",                                  NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DEVTIMEOUT",                                  NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCPV6C",                                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCPV6_DUID",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCPV6_HOSTNAME",                             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCPV6_HOSTNAME_FLAGS",                       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCPV6_IAID",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCPV6_SEND_HOSTNAME",                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCP_CLIENT_ID",                              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCP_FQDN",                                   NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCP_HOSTNAME",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCP_HOSTNAME_FLAGS",                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCP_IAID",                                   NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCP_SEND_HOSTNAME",                          NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCPv6_DUID",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DHCPv6_IAID",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("DNS",                                         NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("DOMAIN",                                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("ESSID",                                       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("ETHTOOL_OPTS",                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("ETHTOOL_WAKE_ON_LAN",                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("FILS",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("FILTER",                                      NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("GATEWAY",                                     NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("GATEWAYDEV",                                  NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("GATEWAY_PING_TIMEOUT",                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("GENERATE_MAC_ADDRESS_MASK",                   NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("GVRP",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("HWADDR",                                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("HWADDR_BLACKLIST",                            NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_ALTSUBJECT_MATCHES",               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_ANON_IDENTITY",                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_AUTH_TIMEOUT",                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_CA_CERT",                          NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_CA_CERT_PASSWORD",                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_CA_CERT_PASSWORD_FLAGS",           NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_CLIENT_CERT",                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_CLIENT_CERT_PASSWORD",             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_CLIENT_CERT_PASSWORD_FLAGS",       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_DOMAIN_MATCH",                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_DOMAIN_SUFFIX_MATCH",              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_EAP_METHODS",                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_FAST_PROVISIONING",                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_IDENTITY",                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_INNER_AUTH_METHODS",               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_INNER_CA_CERT",                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_INNER_CA_CERT_PASSWORD",           NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_INNER_CA_CERT_PASSWORD_FLAGS",     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_INNER_CLIENT_CERT",                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_INNER_CLIENT_CERT_PASSWORD",       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_INNER_CLIENT_CERT_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_INNER_PRIVATE_KEY",                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD",       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_OPTIONAL",                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PAC_FILE",                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PASSWORD",                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PASSWORD_FLAGS",                   NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PASSWORD_RAW",                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PASSWORD_RAW_FLAGS",               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PEAP_FORCE_NEW_LABEL",             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PEAP_VERSION",                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PHASE1_AUTH_FLAGS",                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES",        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PHASE2_DOMAIN_MATCH",              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH",       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PHASE2_SUBJECT_MATCH",             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PRIVATE_KEY",                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PRIVATE_KEY_PASSWORD",             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS",       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_SUBJECT_MATCH",                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IEEE_8021X_SYSTEM_CA_CERTS",                  NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPADDR",                                      NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("IPV4_DHCP_TIMEOUT",                           NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV4_DNS_PRIORITY",                           NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV4_FAILURE_FATAL",                          NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV4_ROUTE_METRIC",                           NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV4_ROUTE_TABLE",                            NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6ADDR",                                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6ADDR_SECONDARIES",                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6FORWARDING",                              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6INIT",                                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6TUNNELIPV4",                              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_ADDR_GEN_MODE",                          NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_AUTOCONF",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_DEFAULTDEV",                             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_DEFAULTGW",                              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_DEFROUTE",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_DHCP_TIMEOUT",                           NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_DISABLED",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_DNS_PRIORITY",                           NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_DOMAIN",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_FAILURE_FATAL",                          NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_PEERDNS",                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_PEERROUTES",                             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_PRIVACY",                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_PRIVACY_PREFER_PUBLIC_IP",               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_RA_TIMEOUT",                             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_RES_OPTIONS",                            NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_ROUTE_METRIC",                           NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_ROUTE_TABLE",                            NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("IPV6_TOKEN",                                  NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("KEY",                                         NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("KEY_MGMT",                                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("KEY_PASSPHRASE",                              NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("KEY_TYPE",                                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("LLDP",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("LLMNR",                                       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("MACADDR",                                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("MAC_ADDRESS_RANDOMIZATION",                   NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("MASTER",                                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("MASTER_UUID",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("MATCH_INTERFACE_NAME",                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("MDNS",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("METRIC",                                      NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("MODE",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("MTU",                                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("MULTI_CONNECT",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("MVRP",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("NAME",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("NETMASK",                                     NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("NETTYPE",                                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("NM_CONTROLLED",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN | NMS_IFCFG_KEY_TYPE_KEEP_WHEN_DIRTY ),
+	_KEY_TYPE ("NM_USER_",                                    NMS_IFCFG_KEY_TYPE_IS_PREFIX ),
+	_KEY_TYPE ("ONBOOT",                                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("OPTIONS",                                     NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("OVS_PORT",                                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("OVS_PORT_UUID",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("PAC_SCRIPT",                                  NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("PAC_URL",                                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("PEERDNS",                                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("PEERROUTES",                                  NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("PHYSDEV",                                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("PKEY",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("PMF",                                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("PORTNAME",                                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("POWERSAVE",                                   NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("PREFIX",                                      NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("PROXY_METHOD",                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("QDISC",                                       NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("REORDER_HDR",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("RES_OPTIONS",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("ROUTING_RULE6_",                              NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("ROUTING_RULE_",                               NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("SEARCH",                                      NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("SECONDARY_UUIDS",                             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("SECURITYMODE",                                NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("SLAVE",                                       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("SRIOV_AUTOPROBE_DRIVERS",                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("SRIOV_TOTAL_VFS",                             NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("SRIOV_VF",                                    NMS_IFCFG_KEY_TYPE_IS_NUMBERED ),
+	_KEY_TYPE ("SSID_HIDDEN",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("STABLE_ID",                                   NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("STP",                                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("SUBCHANNELS",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("TEAM_CONFIG",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("TEAM_MASTER",                                 NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("TEAM_MASTER_UUID",                            NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("TEAM_PORT_CONFIG",                            NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("TYPE",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("USERS",                                       NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("UUID",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("VLAN",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("VLAN_EGRESS_PRIORITY_MAP",                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("VLAN_FLAGS",                                  NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("VLAN_ID",                                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("VLAN_INGRESS_PRIORITY_MAP",                   NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("VRF",                                         NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("VRF_UUID",                                    NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("WEP_KEY_FLAGS",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("WPA_ALLOW_WPA",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("WPA_ALLOW_WPA2",                              NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("WPA_PSK",                                     NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("WPA_PSK_FLAGS",                               NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("WPS_METHOD",                                  NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+	_KEY_TYPE ("ZONE",                                        NMS_IFCFG_KEY_TYPE_IS_PLAIN ),
+};
+
+const NMSIfcfgKeyTypeInfo *
+nms_ifcfg_well_known_key_find_info (const char *key, gssize *out_idx)
+{
+	gssize idx;
+
+	G_STATIC_ASSERT (G_STRUCT_OFFSET (NMSIfcfgKeyTypeInfo, key_name) == 0);
+
+	idx = nm_utils_array_find_binary_search (nms_ifcfg_well_known_keys,
+	                                         sizeof (nms_ifcfg_well_known_keys[0]),
+	                                         G_N_ELEMENTS (nms_ifcfg_well_known_keys),
+	                                         &key,
+	                                         nm_strcmp_p_with_data,
+	                                         NULL);
+	NM_SET_OUT (out_idx, idx);
+	if (idx < 0)
+		return NULL;
+	return &nms_ifcfg_well_known_keys[idx];
+}
+
+const NMSIfcfgKeyTypeInfo *
+nms_ifcfg_rh_utils_is_well_known_key (const char *key)
+{
+	const NMSIfcfgKeyTypeInfo *ti;
+	gssize idx;
+
+	nm_assert (key);
+
+	ti = nms_ifcfg_well_known_key_find_info (key, &idx);
+
+	if (ti) {
+		if (NM_FLAGS_ANY (ti->key_flags,   NMS_IFCFG_KEY_TYPE_IS_PLAIN
+		                                 | NMS_IFCFG_KEY_TYPE_IS_NUMBERED)) {
+			/* These tags are valid on full match.
+			 *
+			 * Note that numbered tags we also treat as valid if they have no
+			 * suffix. That is correct for "IPADDR", but less so for "ROUTING_RULE_". */
+			return ti;
+		}
+		nm_assert (NM_FLAGS_HAS (ti->key_flags, NMS_IFCFG_KEY_TYPE_IS_PREFIX));
+		/* a prefix tag needs some extra suffix afterwards to be valid. */
+		return NULL;
+	}
+
+	/* Not found. Maybe it's a numbered/prefixed key? With idx we got the index where
+	 * we should insert the key. Since the numbered/prefixed keys share a prefix, we can
+	 * find the possible prefix at the index before the insert position. */
+	idx = ~idx;
+	if (idx == 0)
+		return NULL;
+
+	ti = &nms_ifcfg_well_known_keys[idx - 1];
+
+	if (NM_FLAGS_HAS (ti->key_flags, NMS_IFCFG_KEY_TYPE_IS_NUMBERED)) {
+		if (nms_ifcfg_rh_utils_is_numbered_tag (key, ti->key_name, NULL))
+			return ti;
+		return NULL;
+	}
+
+	if (NM_FLAGS_HAS (ti->key_flags, NMS_IFCFG_KEY_TYPE_IS_PREFIX)) {
+		gsize l = strlen (ti->key_name);
+
+		if (   strncmp (key, ti->key_name, l) == 0
+		    && key[l] != '\0')
+			return ti;
+		return NULL;
 	}
 
 	return NULL;
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
index 035146ff..8941fc19 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
@@ -11,9 +11,48 @@
 
 #include "shvar.h"
 
-gboolean nms_ifcfg_rh_util_parse_unhandled_spec (const char *unhandled_spec,
-                                                 const char **out_unmanaged_spec,
-                                                 const char **out_unrecognized_spec);
+/*****************************************************************************/
+
+typedef enum {
+	NMS_IFCFG_KEY_TYPE_UNKNOWN         = 0,
+	NMS_IFCFG_KEY_TYPE_WELL_KNOWN      = (1u << 0),
+
+	NMS_IFCFG_KEY_TYPE_IS_PLAIN        = (1u << 1),
+	NMS_IFCFG_KEY_TYPE_IS_NUMBERED     = (1u << 2),
+	NMS_IFCFG_KEY_TYPE_IS_PREFIX       = (1u << 3),
+
+	/* by default, well knowns keys that are not explicitly set
+	 * by the writer (the unvisited, dirty ones) are removed.
+	 * With this flag, such keys are kept if they are present. */
+	NMS_IFCFG_KEY_TYPE_KEEP_WHEN_DIRTY = (1u << 4),
+
+} NMSIfcfgKeyTypeFlags;
+
+typedef struct {
+	const char *key_name;
+	NMSIfcfgKeyTypeFlags key_flags;
+} NMSIfcfgKeyTypeInfo;
+
+extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[231];
+
+const NMSIfcfgKeyTypeInfo *nms_ifcfg_well_known_key_find_info (const char *key, gssize *out_idx);
+
+static inline NMSIfcfgKeyTypeFlags
+nms_ifcfg_well_known_key_find_info_flags (const char *key)
+{
+	const NMSIfcfgKeyTypeInfo *ti;
+
+	ti = nms_ifcfg_well_known_key_find_info (key, NULL);
+	if (!ti)
+		return NMS_IFCFG_KEY_TYPE_UNKNOWN;
+	return ti->key_flags;
+}
+
+/*****************************************************************************/
+
+gboolean nms_ifcfg_rh_utils_parse_unhandled_spec (const char *unhandled_spec,
+                                                  const char **out_unmanaged_spec,
+                                                  const char **out_unrecognized_spec);
 
 #define NM_IFCFG_CONNECTION_LOG_PATH(path)  ((path) ?: "in-memory")
 #define NM_IFCFG_CONNECTION_LOG_FMT         "%s (%s,\"%s\")"
@@ -37,6 +76,8 @@ shvarFile *utils_get_keys_ifcfg (const char *parent, gboolean should_create);
 shvarFile *utils_get_route_ifcfg (const char *parent, gboolean should_create);
 
 gboolean utils_has_route_file_new_syntax (const char *filename);
+gboolean utils_has_route_file_new_syntax_content (const char *contents,
+                                                  gsize len);
 gboolean utils_has_complex_routes (const char *filename, int addr_family);
 
 gboolean utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg);
@@ -51,6 +92,12 @@ _nms_ifcfg_rh_utils_numbered_tag (char *buf, gsize buf_len, const char *tag_name
 {
 	gsize l;
 
+#if NM_MORE_ASSERTS > 5
+	nm_assert (NM_FLAGS_ALL (nms_ifcfg_well_known_key_find_info_flags (tag_name),
+	                           NMS_IFCFG_KEY_TYPE_WELL_KNOWN
+	                         | NMS_IFCFG_KEY_TYPE_IS_NUMBERED));
+#endif
+
 	l = g_strlcpy (buf, tag_name, buf_len);
 	nm_assert (l < buf_len);
 	if (which != -1) {
@@ -70,6 +117,28 @@ _nms_ifcfg_rh_utils_numbered_tag (char *buf, gsize buf_len, const char *tag_name
 		_nms_ifcfg_rh_utils_numbered_tag (buf, sizeof (buf), ""tag_name"", (which)); \
 	})
 
+gboolean nms_ifcfg_rh_utils_is_numbered_tag_impl (const char *key,
+                                                  const char *tag,
+                                                  gsize tag_len,
+                                                  gint64 *out_idx);
+
+static inline gboolean
+nms_ifcfg_rh_utils_is_numbered_tag (const char *key,
+                                    const char *tag,
+                                    gint64 *out_idx)
+{
+	nm_assert (tag);
+
+	return nms_ifcfg_rh_utils_is_numbered_tag_impl (key, tag, strlen (tag), out_idx);
+}
+
+#define NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG(key, tag, out_idx) \
+	nms_ifcfg_rh_utils_is_numbered_tag_impl (key, tag, NM_STRLEN (tag), out_idx)
+
+/*****************************************************************************/
+
+const NMSIfcfgKeyTypeInfo *nms_ifcfg_rh_utils_is_well_known_key (const char *key);
+
 /*****************************************************************************/
 
 extern const char *const _nm_ethtool_ifcfg_names[_NM_ETHTOOL_ID_FEATURE_NUM];
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index 90a1a2b8..a0797fdc 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -66,10 +66,8 @@ save_secret_flags (shvarFile *ifcfg,
 	g_return_if_fail (ifcfg != NULL);
 	g_return_if_fail (key != NULL);
 
-	if (flags == NM_SETTING_SECRET_FLAG_NONE) {
-		svUnsetValue (ifcfg, key);
+	if (flags == NM_SETTING_SECRET_FLAG_NONE)
 		return;
-	}
 
 	/* Convert flags bitfield into string representation */
 	str = g_string_sized_new (20);
@@ -100,9 +98,6 @@ set_secret (shvarFile *ifcfg,
             const char *flags_key,
             NMSettingSecretFlags flags)
 {
-	/* Clear the secret from the ifcfg and the associated "keys" file */
-	svUnsetValue (ifcfg, key);
-
 	/* Save secret flags */
 	save_secret_flags (ifcfg, flags_key, flags);
 
@@ -131,9 +126,6 @@ write_secrets (shvarFile *ifcfg,
 		return FALSE;
 	}
 
-	/* we purge all existing secrets. */
-	svUnsetAll (keyfile, SV_KEY_TYPE_ANY);
-
 	secrets_keys = nm_utils_strdict_get_keys (secrets, TRUE, &secrets_keys_n);
 	for (i = 0; i < secrets_keys_n; i++) {
 		const char *k = secrets_keys[i];
@@ -147,7 +139,7 @@ write_secrets (shvarFile *ifcfg,
 
 	if (!any_secrets)
 		(void) unlink (svFileGetName (keyfile));
-	else if (!svWriteFile (keyfile, 0600, &local)) {
+	else if (!svWriteFileWithoutDirtyWellknown (keyfile, 0600, &local)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Failure to write secrets to '%s': %s", svFileGetName (keyfile), local->message);
 		return FALSE;
@@ -372,12 +364,8 @@ write_8021x_setting (NMConnection *connection,
 	int vint;
 
 	s_8021x = nm_connection_get_setting_802_1x (connection);
-	if (!s_8021x) {
-		/* If wired, clear KEY_MGMT */
-		if (wired)
-			svUnsetValue (ifcfg, "KEY_MGMT");
+	if (!s_8021x)
 		return TRUE;
-	}
 
 	/* If wired, write KEY_MGMT */
 	if (wired)
@@ -424,10 +412,7 @@ write_8021x_setting (NMConnection *connection,
 	                             nm_setting_802_1x_get_system_ca_certs (s_8021x));
 
 	value = nm_setting_802_1x_get_phase1_peapver (s_8021x);
-	if (NM_IN_STRSET (value, "0", "1"))
-		svSetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", value);
-	else
-		svUnsetValue (ifcfg, "IEEE_8021X_PEAP_VERSION");
+	svSetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", value);
 
 	svSetValueBoolean_cond_true (ifcfg,
 	                             "IEEE_8021X_PEAP_FORCE_NEW_LABEL",
@@ -452,7 +437,6 @@ write_8021x_setting (NMConnection *connection,
 	svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", value);
 
 	/* Phase2 auth methods */
-	svUnsetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
 	phase2_auth = g_string_new (NULL);
 
 	value = nm_setting_802_1x_get_phase2_auth (s_8021x);
@@ -473,9 +457,7 @@ write_8021x_setting (NMConnection *connection,
 	}
 
 	auth_flags = nm_setting_802_1x_get_phase1_auth_flags (s_8021x);
-	if (auth_flags == NM_SETTING_802_1X_AUTH_FLAGS_NONE) {
-		svUnsetValue (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS");
-	} else {
+	if (auth_flags != NM_SETTING_802_1X_AUTH_FLAGS_NONE) {
 		svSetValueEnum (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS",
 		                nm_setting_802_1x_auth_flags_get_type(),
 		                auth_flags);
@@ -492,7 +474,6 @@ write_8021x_setting (NMConnection *connection,
 	svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH",
 	               nm_setting_802_1x_get_phase2_subject_match (s_8021x));
 
-	svUnsetValue (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES");
 	str = g_string_new (NULL);
 	num = nm_setting_802_1x_get_num_altsubject_matches (s_8021x);
 	for (i = 0; i < num; i++) {
@@ -505,7 +486,6 @@ write_8021x_setting (NMConnection *connection,
 		svSetValueStr (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES", str->str);
 	g_string_free (str, TRUE);
 
-	svUnsetValue (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES");
 	str = g_string_new (NULL);
 	num = nm_setting_802_1x_get_num_phase2_altsubject_matches (s_8021x);
 	for (i = 0; i < num; i++) {
@@ -523,6 +503,11 @@ write_8021x_setting (NMConnection *connection,
 	svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH",
 	               nm_setting_802_1x_get_phase2_domain_suffix_match (s_8021x));
 
+	svSetValueStr (ifcfg, "IEEE_8021X_DOMAIN_MATCH",
+	               nm_setting_802_1x_get_domain_match (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_MATCH",
+	               nm_setting_802_1x_get_phase2_domain_match (s_8021x));
+
 	vint = nm_setting_802_1x_get_auth_timeout (s_8021x);
 	svSetValueInt64_cond (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", vint > 0, vint);
 
@@ -565,14 +550,11 @@ write_wireless_security_setting (NMConnection *connection,
 	}
 
 	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
-	g_assert (key_mgmt);
+	nm_assert (key_mgmt);
 
 	auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
 
-	svUnsetValue (ifcfg, "DEFAULTKEY");
-
 	if (!strcmp (key_mgmt, "none")) {
-		svUnsetValue (ifcfg, "KEY_MGMT");
 		wep = TRUE;
 		*no_8021x = TRUE;
 	} else if (!strcmp (key_mgmt, "wpa-psk")) {
@@ -583,6 +565,10 @@ write_wireless_security_setting (NMConnection *connection,
 		svSetValueStr (ifcfg, "KEY_MGMT", "SAE");
 		wpa = TRUE;
 		*no_8021x = TRUE;
+	} else if (!strcmp (key_mgmt, "owe")) {
+		svSetValueStr (ifcfg, "KEY_MGMT", "OWE");
+		wpa = FALSE;
+		*no_8021x = TRUE;
 	} else if (!strcmp (key_mgmt, "ieee8021x")) {
 		svSetValueStr (ifcfg, "KEY_MGMT", "IEEE8021X");
 		dynamic_wep = TRUE;
@@ -591,7 +577,6 @@ write_wireless_security_setting (NMConnection *connection,
 		wpa = TRUE;
 	}
 
-	svUnsetValue (ifcfg, "SECURITYMODE");
 	if (auth_alg) {
 		if (!strcmp (auth_alg, "shared"))
 			svSetValueStr (ifcfg, "SECURITYMODE", "restricted");
@@ -611,11 +596,8 @@ write_wireless_security_setting (NMConnection *connection,
 		}
 	}
 
-	/* WPS */
 	wps_method = nm_setting_wireless_security_get_wps_method (s_wsec);
-	if (wps_method == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT)
-		svUnsetValue (ifcfg, "WPS_METHOD");
-	else
+	if (wps_method != NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT)
 		svSetValueEnum (ifcfg, "WPS_METHOD", nm_setting_wireless_security_wps_method_get_type (), wps_method);
 
 	/* WEP keys */
@@ -701,8 +683,6 @@ write_wireless_security_setting (NMConnection *connection,
 	}
 
 	/* WPA protos */
-	svUnsetValue (ifcfg, "WPA_ALLOW_WPA");
-	svUnsetValue (ifcfg, "WPA_ALLOW_WPA2");
 	num = nm_setting_wireless_security_get_num_protos (s_wsec);
 	for (i = 0; i < num; i++) {
 		proto = nm_setting_wireless_security_get_proto (s_wsec, i);
@@ -713,7 +693,6 @@ write_wireless_security_setting (NMConnection *connection,
 	}
 
 	/* WPA Pairwise ciphers */
-	svUnsetValue (ifcfg, "CIPHER_PAIRWISE");
 	str = g_string_new (NULL);
 	num = nm_setting_wireless_security_get_num_pairwise (s_wsec);
 	for (i = 0; i < num; i++) {
@@ -735,7 +714,6 @@ write_wireless_security_setting (NMConnection *connection,
 	g_string_free (str, TRUE);
 
 	/* WPA Group ciphers */
-	svUnsetValue (ifcfg, "CIPHER_GROUP");
 	str = g_string_new (NULL);
 	num = nm_setting_wireless_security_get_num_groups (s_wsec);
 	for (i = 0; i < num; i++) {
@@ -760,16 +738,12 @@ write_wireless_security_setting (NMConnection *connection,
 	            "WPA_PSK_FLAGS",
 	            wpa ? nm_setting_wireless_security_get_psk_flags (s_wsec) : NM_SETTING_SECRET_FLAG_NONE);
 
-	if (nm_setting_wireless_security_get_pmf (s_wsec) == NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT)
-		svUnsetValue (ifcfg, "PMF");
-	else {
+	if (nm_setting_wireless_security_get_pmf (s_wsec) != NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT) {
 		svSetValueEnum (ifcfg, "PMF", nm_setting_wireless_security_pmf_get_type (),
 		                nm_setting_wireless_security_get_pmf (s_wsec));
 	}
 
-	if (nm_setting_wireless_security_get_fils (s_wsec) == NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT)
-		svUnsetValue (ifcfg, "FILS");
-	else {
+	if (nm_setting_wireless_security_get_fils (s_wsec) != NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT) {
 		svSetValueEnum (ifcfg, "FILS", nm_setting_wireless_security_fils_get_type (),
 		                nm_setting_wireless_security_get_fils (s_wsec));
 	}
@@ -810,14 +784,12 @@ write_wireless_setting (NMConnection *connection,
 	svSetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
 	               nm_setting_wireless_get_generate_mac_address_mask (s_wireless));
 
-	svUnsetValue (ifcfg, "HWADDR_BLACKLIST");
 	macaddr_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
 	if (macaddr_blacklist[0]) {
-		char *blacklist_str;
+		gs_free char *blacklist_str = NULL;
 
 		blacklist_str = g_strjoinv (" ", (char **) macaddr_blacklist);
 		svSetValueStr (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
-		g_free (blacklist_str);
 	}
 
 	mtu = nm_setting_wireless_get_mtu (s_wireless);
@@ -879,9 +851,9 @@ write_wireless_setting (NMConnection *connection,
 	}
 
 	mode = nm_setting_wireless_get_mode (s_wireless);
-	if (!mode)
-		svUnsetValue(ifcfg, "MODE");
-	else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_INFRA))
+	if (!mode) {
+		/* pass */
+	} else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_INFRA))
 		svSetValueStr (ifcfg, "MODE", "Managed");
 	else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) {
 		svSetValueStr (ifcfg, "MODE", "Ad-Hoc");
@@ -895,8 +867,6 @@ write_wireless_setting (NMConnection *connection,
 		return FALSE;
 	}
 
-	svUnsetValue (ifcfg, "CHANNEL");
-	svUnsetValue (ifcfg, "BAND");
 	chan = nm_setting_wireless_get_channel (s_wireless);
 	if (chan) {
 		svSetValueInt64 (ifcfg, "CHANNEL", chan);
@@ -912,18 +882,13 @@ write_wireless_setting (NMConnection *connection,
 	 * otherwise there's no way to detect WEP vs. open when WEP keys aren't
 	 * saved.
 	 */
-	svUnsetValue (ifcfg, "DEFAULTKEY");
-	svUnsetValue (ifcfg, "SECURITYMODE");
 
 	if (nm_connection_get_setting_wireless_security (connection)) {
 		if (!write_wireless_security_setting (connection, ifcfg, secrets, adhoc, no_8021x, error))
 			return FALSE;
 	} else {
 		/* Clear out wifi security keys */
-		svUnsetValue (ifcfg, "KEY_MGMT");
-		svUnsetValue (ifcfg, "IEEE_8021X_IDENTITY");
 		set_secret (ifcfg, secrets, "IEEE_8021X_PASSWORD", NULL, "IEEE_8021X_PASSWORD_FLAGS", NM_SETTING_SECRET_FLAG_NONE);
-		svUnsetValue (ifcfg, "SECURITYMODE");
 
 		/* Clear existing keys */
 		set_secret (ifcfg, secrets, "KEY", NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE);
@@ -937,11 +902,6 @@ write_wireless_setting (NMConnection *connection,
 			set_secret (ifcfg, secrets, tag, NULL, "WEP_KEY_FLAGS", NM_SETTING_SECRET_FLAG_NONE);
 		}
 
-		svUnsetValue (ifcfg, "DEFAULTKEY");
-		svUnsetValue (ifcfg, "WPA_ALLOW_WPA");
-		svUnsetValue (ifcfg, "WPA_ALLOW_WPA2");
-		svUnsetValue (ifcfg, "CIPHER_PAIRWISE");
-		svUnsetValue (ifcfg, "CIPHER_GROUP");
 		set_secret (ifcfg, secrets, "WPA_PSK", NULL, "WPA_PSK_FLAGS", NM_SETTING_SECRET_FLAG_NONE);
 	}
 
@@ -959,7 +919,6 @@ write_wireless_setting (NMConnection *connection,
 		break;
 	default:
 	case NM_SETTING_WIRELESS_POWERSAVE_DEFAULT:
-		svUnsetValue (ifcfg, "POWERSAVE");
 		break;
 	}
 
@@ -1050,8 +1009,7 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 
 		blacklist_str = g_strjoinv (" ", (char **) macaddr_blacklist);
 		svSetValueStr (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
-	} else
-		svUnsetValue (ifcfg, "HWADDR_BLACKLIST");
+	}
 
 	mtu = nm_setting_wired_get_mtu (s_wired);
 	svSetValueInt64_cond (ifcfg, "MTU", mtu != 0, mtu);
@@ -1085,7 +1043,6 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	svSetValueStr (ifcfg, "CTCPROT",
 	               nm_setting_wired_get_s390_option_by_key (s_wired, "ctcprot"));
 
-	svUnsetValue (ifcfg, "OPTIONS");
 	num_opts = nm_setting_wired_get_num_s390_options (s_wired);
 	if (s390_subchannels && num_opts) {
 		nm_auto_free_gstring GString *tmp = NULL;
@@ -1137,11 +1094,9 @@ write_ethtool_setting (NMConnection *connection, shvarFile *ifcfg, GError **erro
 	s_wired = nm_connection_get_setting_wired (connection);
 	s_ethtool = NM_SETTING_ETHTOOL (nm_connection_get_setting (connection, NM_TYPE_SETTING_ETHTOOL));
 
-	if (!s_wired && !s_ethtool) {
-		svUnsetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN");
-		svUnsetValue (ifcfg, "ETHTOOL_OPTS");
+	if (   !s_wired
+	    && !s_ethtool)
 		return TRUE;
-	}
 
 	if (s_wired) {
 		auto_negotiate = nm_setting_wired_get_auto_negotiate (s_wired);
@@ -1200,8 +1155,7 @@ write_ethtool_setting (NMConnection *connection, shvarFile *ifcfg, GError **erro
 			if (wol_password && NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
 				g_string_append_printf (str, "s sopass %s", wol_password);
 		}
-	} else
-		svUnsetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN");
+	}
 
 	if (s_ethtool) {
 		NMEthtoolID ethtool_id;
@@ -1246,8 +1200,7 @@ write_ethtool_setting (NMConnection *connection, shvarFile *ifcfg, GError **erro
 	if (str) {
 		svSetValueStr (ifcfg, "ETHTOOL_OPTS", str->str);
 		g_string_free (str, TRUE);
-	} else
-		svUnsetValue (ifcfg, "ETHTOOL_OPTS");
+	}
 
 	return TRUE;
 }
@@ -1346,10 +1299,6 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 	svSetValueStr (ifcfg, "VLAN_EGRESS_PRIORITY_MAP", tmp);
 	g_free (tmp);
 
-	svUnsetValue (ifcfg, "HWADDR");
-	svUnsetValue (ifcfg, "MACADDR");
-	svUnsetValue (ifcfg, "MTU");
-
 	*wired = write_wired_for_virtual (connection, ifcfg);
 
 	return TRUE;
@@ -1368,8 +1317,6 @@ write_bond_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 		return FALSE;
 	}
 
-	svUnsetValue (ifcfg, "BONDING_OPTS");
-
 	num_opts = nm_setting_bond_get_num_options (s_bond);
 	if (num_opts) {
 		nm_auto_free_gstring GString *str = NULL;
@@ -1463,10 +1410,9 @@ write_bridge_vlans (NMSetting *setting,
 
 	g_object_get (setting, property_name, &vlans, NULL);
 
-	if (!vlans || !vlans->len) {
-		svUnsetValue (ifcfg, key);
+	if (   !vlans
+	    || !vlans->len)
 		return TRUE;
-	}
 
 	string = g_string_new ("");
 	for (i = 0; i < vlans->len; i++) {
@@ -1492,8 +1438,8 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire
 	NMSettingBridge *s_bridge;
 	guint32 i;
 	gboolean b;
+	const char *s;
 	GString *opts;
-	const char *mac;
 
 	s_bridge = nm_connection_get_setting_bridge (connection);
 	if (!s_bridge) {
@@ -1502,12 +1448,10 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire
 		return FALSE;
 	}
 
-	svUnsetValue (ifcfg, "BRIDGING_OPTS");
 	svSetValueBoolean (ifcfg, "STP", FALSE);
-	svUnsetValue (ifcfg, "DELAY");
 
-	mac = nm_setting_bridge_get_mac_address (s_bridge);
-	svSetValueStr (ifcfg, "BRIDGE_MACADDR", mac);
+	s = nm_setting_bridge_get_mac_address (s_bridge);
+	svSetValueStr (ifcfg, "BRIDGE_MACADDR", s);
 
 	/* Bridge options */
 	opts = g_string_sized_new (32);
@@ -1543,6 +1487,13 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire
 		g_string_append_printf (opts, "ageing_time=%u", i);
 	}
 
+	s = nm_setting_bridge_get_group_address (s_bridge);
+	if (s) {
+		if (opts->len)
+			g_string_append_c (opts, ' ');
+		g_string_append_printf (opts, "group_address=%s", s);
+	}
+
 	i = nm_setting_bridge_get_group_forward_mask (s_bridge);
 	if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_GROUP_FORWARD_MASK)) {
 		if (opts->len)
@@ -1550,6 +1501,20 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire
 		g_string_append_printf (opts, "group_fwd_mask=%u", i);
 	}
 
+	b = nm_setting_bridge_get_multicast_querier (s_bridge);
+	if (b != get_setting_default_boolean (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_MULTICAST_QUERIER)) {
+		if (opts->len)
+			g_string_append_c (opts, ' ');
+		g_string_append_printf (opts, "multicast_querier=%u", (guint) b);
+	}
+
+	b = nm_setting_bridge_get_multicast_query_use_ifaddr (s_bridge);
+	if (b != get_setting_default_boolean (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_MULTICAST_QUERY_USE_IFADDR)) {
+		if (opts->len)
+			g_string_append_c (opts, ' ');
+		g_string_append_printf (opts, "multicast_query_use_ifaddr=%u", (guint) b);
+	}
+
 	b = nm_setting_bridge_get_multicast_snooping (s_bridge);
 	if (b != get_setting_default_boolean (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_MULTICAST_SNOOPING)) {
 		if (opts->len)
@@ -1557,6 +1522,13 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire
 		g_string_append_printf (opts, "multicast_snooping=%u", (guint32) b);
 	}
 
+	s = nm_setting_bridge_get_multicast_router (s_bridge);
+	if (s) {
+		if (opts->len)
+			g_string_append_c (opts, ' ');
+		g_string_append_printf (opts, "multicast_router=%s", s);
+	}
+
 	b = nm_setting_bridge_get_vlan_filtering (s_bridge);
 	if (b != get_setting_default_boolean (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_VLAN_FILTERING)) {
 		if (opts->len)
@@ -1571,6 +1543,20 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire
 		g_string_append_printf (opts, "default_pvid=%u", i);
 	}
 
+	s = nm_setting_bridge_get_vlan_protocol (s_bridge);
+	if (s) {
+		if (opts->len)
+			g_string_append_c (opts, ' ');
+		g_string_append_printf (opts, "vlan_protocol=%s", s);
+	}
+
+	b = nm_setting_bridge_get_vlan_stats_enabled (s_bridge);
+	if (b != get_setting_default_boolean (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_VLAN_STATS_ENABLED)) {
+		if (opts->len)
+			g_string_append_c (opts, ' ');
+		g_string_append_printf (opts, "vlan_stats_enabled=%u", (guint) b);
+	}
+
 	if (opts->len)
 		svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str);
 	g_string_free (opts, TRUE);
@@ -1600,8 +1586,6 @@ write_bridge_port_setting (NMConnection *connection, shvarFile *ifcfg, GError **
 	if (!s_port)
 		return TRUE;
 
-	svUnsetValue (ifcfg, "BRIDGING_OPTS");
-
 	/* Bridge options */
 	string = g_string_sized_new (32);
 
@@ -1677,11 +1661,11 @@ write_dcb_app (shvarFile *ifcfg,
 
 	write_dcb_flags (ifcfg, tag, flags);
 
-	nm_sprintf_buf (prop, "DCB_%s_PRIORITY", tag);
-	if ((flags & NM_SETTING_DCB_FLAG_ENABLE) && (priority >= 0))
+	if (   (flags & NM_SETTING_DCB_FLAG_ENABLE)
+	    && (priority >= 0)) {
+		nm_sprintf_buf (prop, "DCB_%s_PRIORITY", tag);
 		svSetValueInt64 (ifcfg, prop, priority);
-	else
-		svUnsetValue (ifcfg, prop);
+	}
 }
 
 typedef gboolean (*DcbGetBoolFunc) (NMSettingDcb *, guint);
@@ -1696,10 +1680,8 @@ write_dcb_bool_array (shvarFile *ifcfg,
 	char str[9];
 	guint i;
 
-	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
-		svUnsetValue (ifcfg, key);
+	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE))
 		return;
-	}
 
 	str[8] = 0;
 	for (i = 0; i < 8; i++)
@@ -1719,10 +1701,8 @@ write_dcb_uint_array (shvarFile *ifcfg,
 	char str[9];
 	guint i, num;
 
-	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
-		svUnsetValue (ifcfg, key);
+	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE))
 		return;
-	}
 
 	str[8] = 0;
 	for (i = 0; i < 8; i++) {
@@ -1747,10 +1727,8 @@ write_dcb_percent_array (shvarFile *ifcfg,
 	GString *str;
 	guint i;
 
-	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
-		svUnsetValue (ifcfg, key);
+	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE))
 		return;
-	}
 
 	str = g_string_sized_new (30);
 	for (i = 0; i < 8; i++) {
@@ -1769,38 +1747,8 @@ write_dcb_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	NMSettingDcbFlags flags;
 
 	s_dcb = nm_connection_get_setting_dcb (connection);
-	if (!s_dcb) {
-		static const char *clear_keys[] = {
-		    "DCB",
-		    KEY_DCB_APP_FCOE_ENABLE,
-		    KEY_DCB_APP_FCOE_ADVERTISE,
-		    KEY_DCB_APP_FCOE_WILLING,
-		    KEY_DCB_APP_FCOE_MODE,
-		    KEY_DCB_APP_ISCSI_ENABLE,
-		    KEY_DCB_APP_ISCSI_ADVERTISE,
-		    KEY_DCB_APP_ISCSI_WILLING,
-		    KEY_DCB_APP_FIP_ENABLE,
-		    KEY_DCB_APP_FIP_ADVERTISE,
-		    KEY_DCB_APP_FIP_WILLING,
-		    KEY_DCB_PFC_ENABLE,
-		    KEY_DCB_PFC_ADVERTISE,
-		    KEY_DCB_PFC_WILLING,
-		    KEY_DCB_PFC_UP,
-		    KEY_DCB_PG_ENABLE,
-		    KEY_DCB_PG_ADVERTISE,
-		    KEY_DCB_PG_WILLING,
-		    KEY_DCB_PG_ID,
-		    KEY_DCB_PG_PCT,
-		    KEY_DCB_PG_UPPCT,
-		    KEY_DCB_PG_STRICT,
-		    KEY_DCB_PG_UP2TC,
-		    NULL };
-		const char **iter;
-
-		for (iter = clear_keys; *iter; iter++)
-			svUnsetValue (ifcfg, *iter);
+	if (!s_dcb)
 		return TRUE;
-	}
 
 	svSetValueStr (ifcfg, "DCB", "yes");
 
@@ -1809,8 +1757,6 @@ write_dcb_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	               nm_setting_dcb_get_app_fcoe_priority (s_dcb));
 	if (nm_setting_dcb_get_app_fcoe_flags (s_dcb) & NM_SETTING_DCB_FLAG_ENABLE)
 		svSetValueStr (ifcfg, KEY_DCB_APP_FCOE_MODE, nm_setting_dcb_get_app_fcoe_mode (s_dcb));
-	else
-		svUnsetValue (ifcfg, KEY_DCB_APP_FCOE_MODE);
 
 	write_dcb_app (ifcfg, "APP_ISCSI",
 	               nm_setting_dcb_get_app_iscsi_flags (s_dcb),
@@ -1877,9 +1823,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 		svSetValueStr (ifcfg, "AUTOCONNECT_SLAVES",
 		               autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES ? "yes" :
 		               autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO ? "no" : NULL);
-	} else
-		svUnsetValue (ifcfg, "AUTOCONNECT_SLAVES");
-
+	}
 	switch (nm_setting_connection_get_lldp (s_con)) {
 	case NM_SETTING_CONNECTION_LLDP_ENABLE_RX:
 		tmp = "rx";
@@ -1893,7 +1837,6 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	svSetValueStr (ifcfg, "LLDP", tmp);
 
 	/* Permissions */
-	svUnsetValue (ifcfg, "USERS");
 	n = nm_setting_connection_get_num_permissions (s_con);
 	if (n > 0) {
 		str = g_string_sized_new (n * 20);
@@ -1923,8 +1866,6 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	svSetValueStr (ifcfg, "BRIDGE", NULL);
 	svSetValueStr (ifcfg, "TEAM_MASTER_UUID", NULL);
 	svSetValueStr (ifcfg, "TEAM_MASTER", NULL);
-	svSetValueStr (ifcfg, "OVS_PORT_UUID", NULL);
-	svSetValueStr (ifcfg, "OVS_PORT", NULL);
 
 	master = nm_setting_connection_get_master (s_con);
 	if (master) {
@@ -1957,6 +1898,9 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 		} else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_OVS_PORT_SETTING_NAME)) {
 			svSetValueStr (ifcfg, "OVS_PORT_UUID", master);
 			svSetValueStr (ifcfg, "OVS_PORT", master_iface);
+		} else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_VRF_SETTING_NAME)) {
+			svSetValueStr (ifcfg, "VRF_UUID", master);
+			svSetValueStr (ifcfg, "VRF", master_iface);
 		} else {
 			_LOGW ("don't know how to set master for a %s slave",
 			       nm_setting_connection_get_slave_type (s_con));
@@ -1967,11 +1911,8 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 		svSetValueStr (ifcfg, "DEVICETYPE", TYPE_TEAM);
 	else if (master_iface && nm_setting_connection_is_slave_type (s_con, NM_SETTING_TEAM_SETTING_NAME))
 		svSetValueStr (ifcfg, "DEVICETYPE", TYPE_TEAM_PORT);
-	else
-		svUnsetValue (ifcfg, "DEVICETYPE");
 
 	/* secondary connection UUIDs */
-	svUnsetValue (ifcfg, "SECONDARY_UUIDS");
 	n = nm_setting_connection_get_num_secondaries (s_con);
 	if (n > 0) {
 		str = g_string_sized_new (n * 37);
@@ -2004,17 +1945,19 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	case NM_METERED_NO:
 		svSetValueStr (ifcfg, "CONNECTION_METERED", "no");
 		break;
-	default:
-		svUnsetValue (ifcfg, "CONNECTION_METERED");
+	case NM_METERED_UNKNOWN:
+	case NM_METERED_GUESS_YES:
+	case NM_METERED_GUESS_NO:
+		break;
 	}
 
 	vint = nm_setting_connection_get_auth_retries (s_con);
 	svSetValueInt64_cond (ifcfg, "AUTH_RETRIES", vint >= 0, vint);
 
 	vint32 = nm_setting_connection_get_wait_device_timeout (s_con);
-	if (vint32 == -1)
-		svUnsetValue (ifcfg, "DEVTIMEOUT");
-	else if ((vint32 % 1000) == 0)
+	if (vint32 == -1) {
+		/* pass */
+	} else if ((vint32 % 1000) == 0)
 		svSetValueInt64 (ifcfg, "DEVTIMEOUT", vint32 / 1000);
 	else {
 		char b[100];
@@ -2028,15 +1971,13 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) {
 		svSetValueEnum (ifcfg, "MDNS", nm_setting_connection_mdns_get_type (),
 		                mdns);
-	} else
-		svUnsetValue (ifcfg, "MDNS");
+	}
 
 	llmnr = nm_setting_connection_get_llmnr (s_con);
 	if (llmnr != NM_SETTING_CONNECTION_LLMNR_DEFAULT) {
 		svSetValueEnum (ifcfg, "LLMNR", nm_setting_connection_llmnr_get_type (),
 		                llmnr);
-	} else
-		svUnsetValue (ifcfg, "LLMNR");
+	}
 }
 
 static char *
@@ -2119,8 +2060,6 @@ write_route_file_svformat (const char *filename, NMSettingIPConfig *s_ip4)
 
 	routefile = utils_get_route_ifcfg (filename, TRUE);
 
-	svUnsetAll (routefile, SV_KEY_TYPE_ROUTE_SVFORMAT);
-
 	num = nm_setting_ip_config_get_num_routes (s_ip4);
 	for (i = 0; i < num; i++) {
 		char buf[INET_ADDRSTRLEN];
@@ -2144,7 +2083,7 @@ write_route_file_svformat (const char *filename, NMSettingIPConfig *s_ip4)
 
 		netmask = _nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route));
 		svSetValueStr (routefile, netmask_key,
-		               nm_utils_inet4_ntop (netmask, buf));
+		               _nm_utils_inet4_ntop (netmask, buf));
 
 		svSetValueStr (routefile, gw_key, nm_ip_route_get_next_hop (route));
 
@@ -2221,10 +2160,6 @@ write_proxy_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	if (!s_proxy)
 		return TRUE;
 
-	svUnsetValue (ifcfg, "BROWSER_ONLY");
-	svUnsetValue (ifcfg, "PAC_URL");
-	svUnsetValue (ifcfg, "PAC_SCRIPT");
-
 	method = nm_setting_proxy_get_method (s_proxy);
 	switch (method) {
 	case NM_SETTING_PROXY_METHOD_AUTO:
@@ -2258,8 +2193,6 @@ write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 
 	s_user = NM_SETTING_USER (nm_connection_get_setting (connection, NM_TYPE_SETTING_USER));
 
-	svUnsetAll (ifcfg, SV_KEY_TYPE_USER);
-
 	if (!s_user)
 		return TRUE;
 
@@ -2292,13 +2225,9 @@ write_sriov_setting (NMConnection *connection, shvarFile *ifcfg)
 	char key[32];
 	char *str;
 
-	svUnsetAll (ifcfg, SV_KEY_TYPE_SRIOV_VF);
-
 	s_sriov = NM_SETTING_SRIOV (nm_connection_get_setting (connection,
 	                                                       NM_TYPE_SETTING_SRIOV));
 	if (!s_sriov) {
-		svUnsetValue (ifcfg, "SRIOV_TOTAL_VFS");
-		svUnsetValue (ifcfg, "SRIOV_AUTOPROBE_DRIVERS");
 		return;
 	}
 
@@ -2307,8 +2236,6 @@ write_sriov_setting (NMConnection *connection, shvarFile *ifcfg)
 	b = nm_setting_sriov_get_autoprobe_drivers (s_sriov);
 	if (b != NM_TERNARY_DEFAULT)
 		svSetValueInt64 (ifcfg, "SRIOV_AUTOPROBE_DRIVERS", b);
-	else
-		svUnsetValue (ifcfg, "SRIOV_AUTOPROBE_DRIVERS");
 
 	num = nm_setting_sriov_get_num_vfs (s_sriov);
 	for (i = 0; i < num; i++) {
@@ -2327,8 +2254,6 @@ write_tc_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	guint i, num, n;
 	char tag[64];
 
-	svUnsetAll (ifcfg, SV_KEY_TYPE_TC);
-
 	s_tc = nm_connection_get_setting_tc_config (connection);
 	if (!s_tc)
 		return TRUE;
@@ -2371,8 +2296,6 @@ write_match_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	nm_auto_free_gstring GString *str = NULL;
 	guint i, num;
 
-	svUnsetValue (ifcfg, "MATCH_INTERFACE_NAME");
-
 	s_match = (NMSettingMatch *) nm_connection_get_setting (connection, NM_TYPE_SETTING_MATCH);
 	if (!s_match)
 		return TRUE;
@@ -2404,10 +2327,8 @@ write_res_options (shvarFile *ifcfg, NMSettingIPConfig *s_ip, const char *var)
 	nm_auto_free_gstring GString *value = NULL;
 	guint i, num_options;
 
-	if (!nm_setting_ip_config_has_dns_options (s_ip)) {
-		svUnsetValue (ifcfg, var);
+	if (!nm_setting_ip_config_has_dns_options (s_ip))
 		return;
-	}
 
 	value = g_string_new (NULL);
 	num_options = nm_setting_ip_config_get_num_dns_options (s_ip);
@@ -2420,6 +2341,47 @@ write_res_options (shvarFile *ifcfg, NMSettingIPConfig *s_ip, const char *var)
 	svSetValue (ifcfg, var, value->str);
 }
 
+static void
+write_dns_setting (shvarFile *ifcfg,
+                   NMConnection *connection,
+                   int addr_family)
+{
+	NMSettingIPConfig *s_ip;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6 = NULL;
+	guint num4;
+	guint num6 = 0;
+	guint num;
+	guint i;
+	guint offset;
+
+	if (addr_family == AF_INET6) {
+		s_ip6 = nm_connection_get_setting_ip6_config (connection);
+		num6 = s_ip6 ? nm_setting_ip_config_get_num_dns (s_ip6) : 0u;
+	}
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	num4 = s_ip4 ? nm_setting_ip_config_get_num_dns (s_ip4) : 0u;
+
+	if (addr_family == AF_INET6) {
+		num = num6;
+		offset = num4;
+		s_ip = s_ip6;
+	} else {
+		num = num4;
+		offset = 0;
+		s_ip = s_ip4;
+	}
+
+	for (i = 0; i < num; i++) {
+		char tag[64];
+
+		svSetValueStr (ifcfg,
+		               numbered_tag (tag, "DNS", offset + i + 1u),
+		               nm_setting_ip_config_get_dns (s_ip, i));
+	}
+}
+
 static gboolean
 write_ip4_setting (NMConnection *connection,
                    shvarFile *ifcfg,
@@ -2446,16 +2408,8 @@ write_ip4_setting (NMConnection *connection,
 	NM_SET_OUT (out_route_content, NULL);
 
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
-	if (!s_ip4) {
-		/* slave-type: clear IPv4 settings.
-		 *
-		 * Some IPv4 setting related options are not cleared,
-		 * for no strong reason. */
-		svUnsetValue (ifcfg, "BOOTPROTO");
-		svUnsetValue (ifcfg, "RES_OPTIONS");
-		svUnsetAll (ifcfg, SV_KEY_TYPE_IP4_ADDRESS);
+	if (!s_ip4)
 		return TRUE;
-	}
 
 	method = nm_setting_ip_config_get_method (s_ip4);
 
@@ -2463,17 +2417,8 @@ write_ip4_setting (NMConnection *connection,
 	if (!method)
 		method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
 
-	if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) {
-		/* IPv4 disabled, clear IPv4 related parameters */
-		svUnsetValue (ifcfg, "BOOTPROTO");
-		for (j = -1; j < 256; j++) {
-			svUnsetValue (ifcfg, numbered_tag (tag, "IPADDR", j));
-			svUnsetValue (ifcfg, numbered_tag (tag, "PREFIX", j));
-			svUnsetValue (ifcfg, numbered_tag (tag, "NETMASK", j));
-			svUnsetValue (ifcfg, numbered_tag (tag, "GATEWAY", j));
-		}
+	if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
 		return TRUE;
-	}
 
 	num = nm_setting_ip_config_get_num_addresses (s_ip4);
 
@@ -2490,7 +2435,7 @@ write_ip4_setting (NMConnection *connection,
 	else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
 		svSetValueStr (ifcfg, "BOOTPROTO", "shared");
 
-	has_netmask = !!svFindFirstKeyWithPrefix (ifcfg, "NETMASK");
+	has_netmask = !!svFindFirstNumberedKey (ifcfg, "NETMASK");
 
 	/* Write out IPADDR<n>, PREFIX<n>, GATEWAY<n> for current IP addresses
 	 * without labels. Unset obsolete NETMASK<n>.
@@ -2532,45 +2477,15 @@ write_ip4_setting (NMConnection *connection,
 			char buf[INET_ADDRSTRLEN];
 
 			svSetValueStr (ifcfg, tag,
-			               nm_utils_inet4_ntop (_nm_utils_ip4_prefix_to_netmask (prefix), buf));
-		} else
-			svUnsetValue (ifcfg, tag);
+			               _nm_utils_inet4_ntop (_nm_utils_ip4_prefix_to_netmask (prefix), buf));
+		}
 
 		n++;
 	}
 
-	svUnsetValue (ifcfg, numbered_tag (tag, "IPADDR", 0));
-	svUnsetValue (ifcfg, numbered_tag (tag, "PREFIX", 0));
-	svUnsetValue (ifcfg, numbered_tag (tag, "NETMASK", 0));
-	if (n == 0) {
-		svUnsetValue (ifcfg, "IPADDR");
-		svUnsetValue (ifcfg, "PREFIX");
-		svUnsetValue (ifcfg, "NETMASK");
-	}
-	for (j = n; j < 256; j++) {
-		svUnsetValue (ifcfg, numbered_tag (tag, "IPADDR", j));
-		svUnsetValue (ifcfg, numbered_tag (tag, "PREFIX", j));
-		svUnsetValue (ifcfg, numbered_tag (tag, "NETMASK", j));
-	}
-
-	for (j = -1; j < 256; j++) {
-		if (j != 0)
-			svUnsetValue (ifcfg, numbered_tag (tag, "GATEWAY", j));
-	}
 	svSetValueStr (ifcfg, "GATEWAY", nm_setting_ip_config_get_gateway (s_ip4));
 
-	num = nm_setting_ip_config_get_num_dns (s_ip4);
-	for (i = 0; i < 254; i++) {
-		const char *dns;
-
-		numbered_tag (tag, "DNS", i + 1);
-		if (i >= num)
-			svUnsetValue (ifcfg, tag);
-		else {
-			dns = nm_setting_ip_config_get_dns (s_ip4, i);
-			svSetValueStr (ifcfg, tag, dns);
-		}
-	}
+	write_dns_setting (ifcfg, connection, AF_INET);
 
 	num = nm_setting_ip_config_get_num_dns_searches (s_ip4);
 	if (num > 0) {
@@ -2582,8 +2497,7 @@ write_ip4_setting (NMConnection *connection,
 		}
 		svSetValueStr (ifcfg, "DOMAIN", searches->str);
 		g_string_free (searches, TRUE);
-	} else
-		svUnsetValue (ifcfg, "DOMAIN");
+	}
 
 	/* DEFROUTE; remember that it has the opposite meaning from never-default */
 	svSetValueBoolean (ifcfg, "DEFROUTE", !nm_setting_ip_config_get_never_default (s_ip4));
@@ -2644,8 +2558,7 @@ write_ip4_setting (NMConnection *connection,
 
 	timeout = nm_setting_ip_config_get_dad_timeout (s_ip4);
 	if (timeout < 0) {
-		svUnsetValue (ifcfg, "ACD_TIMEOUT");
-		svUnsetValue (ifcfg, "ARPING_WAIT");
+		/* pass */
 	} else if (timeout == 0) {
 		svSetValueStr (ifcfg, "ACD_TIMEOUT", "0");
 		svSetValueStr (ifcfg, "ARPING_WAIT", "0");
@@ -2658,8 +2571,6 @@ write_ip4_setting (NMConnection *connection,
 	priority = nm_setting_ip_config_get_dns_priority (s_ip4);
 	if (priority)
 		svSetValueInt64 (ifcfg, "IPV4_DNS_PRIORITY", priority);
-	else
-		svUnsetValue (ifcfg, "IPV4_DNS_PRIORITY");
 
 	write_res_options (ifcfg, s_ip4, "RES_OPTIONS");
 
@@ -2745,7 +2656,7 @@ write_ip4_aliases (NMConnection *connection, const char *base_ifcfg_path)
 
 		svSetValueInt64 (ifcfg, "PREFIX", nm_ip_address_get_prefix(addr));
 
-		svWriteFile (ifcfg, 0644, NULL);
+		svWriteFileWithoutDirtyWellknown (ifcfg, 0644, NULL);
 		svCloseFile (ifcfg);
 	}
 }
@@ -2757,12 +2668,10 @@ write_ip6_setting (NMConnection *connection,
                    GError **error)
 {
 	NMSettingIPConfig *s_ip6;
-	NMSettingIPConfig *s_ip4;
 	const char *value;
-	guint i, num, num4;
+	guint i, num;
 	int priority;
 	NMIPAddress *addr;
-	const char *dns;
 	gint64 route_metric;
 	NMIPRouteTableSyncMode route_table;
 	GString *ip_str1, *ip_str2, *ip_ptr;
@@ -2774,45 +2683,21 @@ write_ip6_setting (NMConnection *connection,
 	NM_SET_OUT (out_route6_content, NULL);
 
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
-	if (!s_ip6) {
-		/* slave-type: clear IPv6 settings
-		 *
-		 * Some IPv6 setting related options are not cleared,
-		 * for no strong reason. */
-		svUnsetValue (ifcfg, "IPV6INIT");
-		svUnsetValue (ifcfg, "IPV6_AUTOCONF");
-		svUnsetValue (ifcfg, "DHCPV6C");
-		svUnsetValue (ifcfg, "DHCPv6_DUID");
-		svUnsetValue (ifcfg, "DHCPv6_IAID");
-		svUnsetValue (ifcfg, "DHCPV6_HOSTNAME");
-		svUnsetValue (ifcfg, "DHCPV6_SEND_HOSTNAME");
-		svUnsetValue (ifcfg, "IPV6_DEFROUTE");
-		svUnsetValue (ifcfg, "IPV6_PEERDNS");
-		svUnsetValue (ifcfg, "IPV6_PEERROUTES");
-		svUnsetValue (ifcfg, "IPV6_FAILURE_FATAL");
-		svUnsetValue (ifcfg, "IPV6_ROUTE_METRIC");
-		svUnsetValue (ifcfg, "IPV6_ADDR_GEN_MODE");
-		svUnsetValue (ifcfg, "IPV6_RES_OPTIONS");
+	if (!s_ip6)
 		return TRUE;
-	}
 
 	value = nm_setting_ip_config_get_method (s_ip6);
 	g_assert (value);
-	svUnsetValue (ifcfg, "IPV6_DISABLED");
 	if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
 		svSetValueStr (ifcfg, "IPV6INIT", "no");
-		svUnsetValue (ifcfg, "DHCPV6C");
 		return TRUE;
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) {
 		svSetValueStr (ifcfg, "IPV6_DISABLED", "yes");
 		svSetValueStr (ifcfg, "IPV6INIT", "no");
-		svUnsetValue (ifcfg, "DHCPV6C");
-		svUnsetValue (ifcfg, "IPV6_AUTOCONF");
 		return TRUE;
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
 		svSetValueStr (ifcfg, "IPV6INIT", "yes");
 		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "yes");
-		svUnsetValue (ifcfg, "DHCPV6C");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
 		svSetValueStr (ifcfg, "IPV6INIT", "yes");
 		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "no");
@@ -2820,15 +2705,12 @@ write_ip6_setting (NMConnection *connection,
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
 		svSetValueStr (ifcfg, "IPV6INIT", "yes");
 		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "no");
-		svUnsetValue (ifcfg, "DHCPV6C");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
 		svSetValueStr (ifcfg, "IPV6INIT", "yes");
 		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "no");
-		svUnsetValue (ifcfg, "DHCPV6C");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
 		svSetValueStr (ifcfg, "IPV6INIT", "yes");
 		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "shared");
-		svUnsetValue (ifcfg, "DHCPV6C");
 	}
 
 	svSetValueStr (ifcfg, "DHCPV6_DUID",
@@ -2842,9 +2724,7 @@ write_ip6_setting (NMConnection *connection,
 	/* Missing DHCPV6_SEND_HOSTNAME means TRUE, and we prefer not write it
 	 * explicitly in that case, because it is NM-specific variable
 	 */
-	if (nm_setting_ip_config_get_dhcp_send_hostname (s_ip6))
-		svUnsetValue (ifcfg, "DHCPV6_SEND_HOSTNAME");
-	else
+	if (!nm_setting_ip_config_get_dhcp_send_hostname (s_ip6))
 		svSetValueStr (ifcfg, "DHCPV6_SEND_HOSTNAME", "no");
 
 	timeout = nm_setting_ip6_config_get_ra_timeout (NM_SETTING_IP6_CONFIG (s_ip6));
@@ -2889,22 +2769,7 @@ write_ip6_setting (NMConnection *connection,
 	g_string_free (ip_str1, TRUE);
 	g_string_free (ip_str2, TRUE);
 
-	/* Write out DNS - 'DNS' key is used both for IPv4 and IPv6 */
-	s_ip4 = nm_connection_get_setting_ip4_config (connection);
-	num4 = s_ip4 ? nm_setting_ip_config_get_num_dns (s_ip4) : 0; /* from where to start with IPv6 entries */
-	num = nm_setting_ip_config_get_num_dns (s_ip6);
-	for (i = 0; i < 254; i++) {
-		char tag[64];
-
-		numbered_tag (tag, "DNS", i + num4 + 1);
-
-		if (i >= num)
-			svUnsetValue (ifcfg, tag);
-		else {
-			dns = nm_setting_ip_config_get_dns (s_ip6, i);
-			svSetValueStr (ifcfg, tag, dns);
-		}
-	}
+	write_dns_setting (ifcfg, connection, AF_INET6);
 
 	/* Write out DNS domains */
 	num = nm_setting_ip_config_get_num_dns_searches (s_ip6);
@@ -2918,15 +2783,11 @@ write_ip6_setting (NMConnection *connection,
 			g_string_append (searches, nm_setting_ip_config_get_dns_search (s_ip6, i));
 		}
 		svSetValueStr (ifcfg, "IPV6_DOMAIN", searches->str);
-	} else
-		svUnsetValue (ifcfg, "IPV6_DOMAIN");
+	}
 
 	/* handle IPV6_DEFROUTE */
 	/* IPV6_DEFROUTE has the opposite meaning from 'never-default' */
-	if (nm_setting_ip_config_get_never_default (s_ip6))
-		svSetValueStr (ifcfg, "IPV6_DEFROUTE", "no");
-	else
-		svSetValueStr (ifcfg, "IPV6_DEFROUTE", "yes");
+	svSetValueBoolean (ifcfg, "IPV6_DEFROUTE", !nm_setting_ip_config_get_never_default (s_ip6));
 
 	svSetValueStr (ifcfg, "IPV6_PEERDNS",
 	               nm_setting_ip_config_get_ignore_auto_dns (s_ip6) ? "no" : NULL);
@@ -2950,8 +2811,6 @@ write_ip6_setting (NMConnection *connection,
 	                      route_table);
 
 	/* IPv6 Privacy Extensions */
-	svUnsetValue (ifcfg, "IPV6_PRIVACY");
-	svUnsetValue (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP");
 	switch (nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6))) {
 	case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
 		svSetValueStr (ifcfg, "IPV6_PRIVACY", "no");
@@ -2972,8 +2831,6 @@ write_ip6_setting (NMConnection *connection,
 	if (addr_gen_mode != NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) {
 		svSetValueEnum (ifcfg, "IPV6_ADDR_GEN_MODE", nm_setting_ip6_config_addr_gen_mode_get_type (),
 		                addr_gen_mode);
-	} else {
-		svUnsetValue (ifcfg, "IPV6_ADDR_GEN_MODE");
 	}
 
 	/* IPv6 tokenized interface identifier */
@@ -2983,8 +2840,6 @@ write_ip6_setting (NMConnection *connection,
 	priority = nm_setting_ip_config_get_dns_priority (s_ip6);
 	if (priority)
 		svSetValueInt64 (ifcfg, "IPV6_DNS_PRIORITY", priority);
-	else
-		svUnsetValue (ifcfg, "IPV6_DNS_PRIORITY");
 
 	write_res_options (ifcfg, s_ip6, "IPV6_RES_OPTIONS");
 
@@ -3001,8 +2856,6 @@ write_ip_routing_rules (NMConnection *connection,
 	gsize idx;
 	int is_ipv4;
 
-	svUnsetAll (ifcfg, SV_KEY_TYPE_ROUTING_RULE4 | SV_KEY_TYPE_ROUTING_RULE6);
-
 	if (route_ignore)
 		return;
 
@@ -3233,8 +3086,6 @@ do_write_construct (NMConnection *connection,
 	if (!write_tc_setting (connection, ifcfg, error))
 		return FALSE;
 
-	svUnsetValue (ifcfg, "DHCP_HOSTNAME");
-	svUnsetValue (ifcfg, "DHCP_FQDN");
 
 	route_path_is_svformat = utils_has_route_file_new_syntax (route_path);
 
@@ -3321,7 +3172,7 @@ do_write_to_disk (NMConnection *connection,
 	 * only. But we loaded the ifcfg files from disk, and managled our
 	 * new settings (in-memory). */
 
-	if (!svWriteFile (ifcfg, 0644, error))
+	if (!svWriteFileWithoutDirtyWellknown (ifcfg, 0644, error))
 		return FALSE;
 
 	write_ip4_aliases (connection, svFileGetName (ifcfg));
@@ -3340,7 +3191,7 @@ do_write_to_disk (NMConnection *connection,
 		else {
 			nm_assert (route_content_svformat || route_content);
 			if (route_content_svformat) {
-				if (!svWriteFile (route_content_svformat, 0644, error))
+				if (!svWriteFileWithoutDirtyWellknown (route_content_svformat, 0644, error))
 					return FALSE;
 			} else {
 				if (!g_file_set_contents (route_path, route_content->str, route_content->len, NULL)) {
@@ -3450,6 +3301,11 @@ nms_ifcfg_rh_writer_write_connection (NMConnection *connection,
 			_LOGW ("write: failure to re-read connection \"%s\": %s",
 			       svFileGetName (ifcfg), "connection is unhandled");
 		} else {
+			/* ifcfg-rh doesn't support the 'timestamp' property, let's add it here */
+			g_object_set (nm_connection_get_setting_connection (reread),
+			              NM_SETTING_CONNECTION_TIMESTAMP,
+			              nm_setting_connection_get_timestamp (nm_connection_get_setting_connection (connection)),
+			              NULL);
 			if (out_reread_same) {
 				reread_same = nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT);
 				if (!reread_same) {
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 16b2dd37..f2a1dd78 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -19,13 +19,23 @@
 #include "nm-glib-aux/nm-enum-utils.h"
 #include "nm-glib-aux/nm-io-utils.h"
 #include "c-list/src/c-list.h"
+#include "nms-ifcfg-rh-utils.h"
 
 /*****************************************************************************/
 
 struct _shvarLine {
 
+	const char *key;
+
 	CList lst;
 
+	/* We index variables by their key in shvarFile.lst_idx. One shell variable might
+	 * occur multiple times in a file (in which case the last occurrence wins).
+	 * Hence, we need to keep a list of all the same keys.
+	 *
+	 * This is a pointer to the next shadowed line. */
+	struct _shvarLine *prev_shadowed;
+
 	/* There are three cases:
 	 *
 	 * 1) the line is not a valid variable assignment (that is, it doesn't
@@ -43,21 +53,41 @@ struct _shvarLine {
 	 *   @key/@key_with_prefix.
 	 * */
 	char *line;
-	const char *key;
 	char *key_with_prefix;
+
+	/* svSetValue() will clear the dirty flag. */
+	bool dirty:1;
 };
 
 typedef struct _shvarLine shvarLine;
 
 struct _shvarFile {
-	char      *fileName;
-	int        fd;
-	CList      lst_head;
-	gboolean   modified;
+	char *fileName;
+	CList lst_head;
+	GHashTable *lst_idx;
+	int fd;
+	bool modified:1;
 };
 
 /*****************************************************************************/
 
+static void _line_link_parse (shvarFile *s, const char *value, gsize len);
+
+/*****************************************************************************/
+
+#define ASSERT_key_is_well_known(key) \
+	nm_assert ( ({ \
+		const char *_key = (key); \
+		gboolean _is_wellknown = TRUE; \
+		\
+		if (!nms_ifcfg_rh_utils_is_well_known_key (_key)) { \
+			_is_wellknown = FALSE; \
+			g_critical ("ifcfg-rh key \"%s\" is not well-known", _key); \
+		} \
+		\
+		_is_wellknown; \
+	}) )
+
 /**
  * svParseBoolean:
  * @value: the input string
@@ -603,15 +633,33 @@ out_error:
 
 /*****************************************************************************/
 
-static shvarFile *
-svFile_new (const char *name)
+shvarFile *
+svFile_new (const char *name,
+            int fd,
+            const char *content)
 {
 	shvarFile *s;
+	const char *p;
+	const char *q;
+
+	nm_assert (name);
+	nm_assert (fd >= -1);
+
+	s = g_slice_new (shvarFile);
+	*s = (shvarFile) {
+		.fileName = g_strdup (name),
+		.fd       = fd,
+		.lst_head = C_LIST_INIT (s->lst_head),
+		.lst_idx  = g_hash_table_new (nm_pstr_hash, nm_pstr_equal),
+	};
+
+	if (content) {
+		for (p = content; (q = strchr (p, '\n')) != NULL; p = q + 1)
+			_line_link_parse (s, p, q - p);
+		if (p[0])
+			_line_link_parse (s, p, strlen (p));
+	}
 
-	s = g_slice_new0 (shvarFile);
-	s->fd = -1;
-	s->fileName = g_strdup (name);
-	c_list_init (&s->lst_head);
 	return s;
 }
 
@@ -671,8 +719,11 @@ line_new_parse (const char *value, gsize len)
 
 	nm_assert (value);
 
-	line = g_slice_new0 (shvarLine);
-	c_list_init (&line->lst);
+	line = g_slice_new (shvarLine);
+	*line = (shvarLine) {
+		.lst      = C_LIST_INIT (line->lst),
+		.dirty    = TRUE,
+	};
 
 	for (k = 0; k < len; k++) {
 		if (g_ascii_isspace (value[k]))
@@ -706,14 +757,19 @@ line_new_build (const char *key, const char *value)
 {
 	char *value_escaped = NULL;
 	shvarLine *line;
+	char *new_key;
 
 	value = svEscape (value, &value_escaped);
 
 	line = g_slice_new (shvarLine);
-	c_list_init (&line->lst);
-	line->line = value_escaped ?: g_strdup (value);
-	line->key_with_prefix = g_strdup (key);
-	line->key = line->key_with_prefix;
+	new_key = g_strdup (key),
+	*line = (shvarLine) {
+		.lst             = C_LIST_INIT (line->lst),
+		.line            = value_escaped ?: g_strdup (value),
+		.key_with_prefix = new_key,
+		.key             = new_key,
+		.dirty           = FALSE,
+	};
 	ASSERT_shvarLine (line);
 	return line;
 }
@@ -727,6 +783,8 @@ line_set (shvarLine *line, const char *value)
 	ASSERT_shvarLine (line);
 	nm_assert (line->key);
 
+	line->dirty = FALSE;
+
 	if (line->key != line->key_with_prefix) {
 		memmove (line->key_with_prefix, line->key, strlen (line->key) + 1);
 		line->key = line->key_with_prefix;
@@ -753,14 +811,48 @@ static void
 line_free (shvarLine *line)
 {
 	ASSERT_shvarLine (line);
+	c_list_unlink_stale (&line->lst);
 	g_free (line->line);
 	g_free (line->key_with_prefix);
-	c_list_unlink_stale (&line->lst);
 	g_slice_free (shvarLine, line);
 }
 
 /*****************************************************************************/
 
+static void
+_line_link_parse (shvarFile *s, const char *value, gsize len)
+{
+	shvarLine *line;
+
+	line = line_new_parse (value, len);
+	if (!line->key)
+		goto do_link;
+
+	if (G_UNLIKELY (!g_hash_table_insert (s->lst_idx, line, line))) {
+		shvarLine *existing_key;
+		shvarLine *existing_val;
+
+		/* Slow-path: we have duplicate keys. Fix the mess we created.
+		 * Unfortunately, g_hash_table_insert() now had to allocate an extra
+		 * array to track the keys/values differently. I wish there was an
+		 * GHashTable API to add a key only if it does not exist yet. */
+
+		if (!g_hash_table_lookup_extended (s->lst_idx, line, (gpointer *) &existing_key, (gpointer *) &existing_val))
+			nm_assert_not_reached ();
+
+		nm_assert (existing_val == line);
+		nm_assert (existing_key != line);
+		line->prev_shadowed = existing_key;
+		g_hash_table_replace (s->lst_idx, line, line);
+	}
+
+do_link:
+	c_list_link_tail (&s->lst_head, &line->lst);
+}
+
+/*****************************************************************************/
+
+
 /* Open the file <name>, returning a shvarFile on success and NULL on failure.
  * Add a wrinkle to let the caller specify whether or not to create the file
  * (actually, return a structure anyway) if it doesn't exist.
@@ -768,11 +860,9 @@ line_free (shvarLine *line)
 static shvarFile *
 svOpenFileInternal (const char *name, gboolean create, GError **error)
 {
-	shvarFile *s;
 	gboolean closefd = FALSE;
 	int errsv = 0;
-	gs_free char *arena = NULL;
-	const char *p, *q;
+	gs_free char *content = NULL;
 	gs_free_error GError *local = NULL;
 	nm_auto_close int fd = -1;
 
@@ -789,7 +879,7 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
 
 	if (fd < 0) {
 		if (create)
-			return svFile_new (name);
+			return svFile_new (name, -1, NULL);
 
 		g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
 		             "Could not read file '%s': %s",
@@ -801,12 +891,12 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
 	                               closefd,
 	                               10 * 1024 * 1024,
 	                               NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-	                               &arena,
+	                               &content,
 	                               NULL,
 	                               NULL,
 	                               &local)) {
 		if (create)
-			return svFile_new (name);
+			return svFile_new (name, -1, NULL);
 
 		g_set_error (error, G_FILE_ERROR,
 		             local->domain == G_FILE_ERROR ? local->code : G_FILE_ERROR_FAILED,
@@ -815,21 +905,14 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
 		return NULL;
 	}
 
-	s = svFile_new (name);
-
-	for (p = arena; (q = strchr (p, '\n')) != NULL; p = q + 1)
-		c_list_link_tail (&s->lst_head, &line_new_parse (p, q - p)->lst);
-	if (p[0])
-		c_list_link_tail (&s->lst_head, &line_new_parse (p, strlen (p))->lst);
-
 	/* closefd is set if we opened the file read-only, so go ahead and
 	 * close it, because we can't write to it anyway */
-	if (!closefd) {
-		nm_assert (fd > 0);
-		s->fd = nm_steal_fd (&fd);
-	}
-
-	return s;
+	nm_assert (closefd || fd >= 0);
+	return svFile_new (name,
+	                     !closefd
+	                   ? nm_steal_fd (&fd)
+	                   : -1,
+	                   content);
 }
 
 /* Open the file <name>, return shvarFile on success, NULL on failure */
@@ -851,54 +934,32 @@ svCreateFile (const char *name)
 /*****************************************************************************/
 
 static gboolean
-_is_all_digits (const char *str)
+_svKeyMatchesType (const char *key, SvKeyType match_key_type)
 {
-	return    str[0]
-	       && NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch));
-}
-
-#define IS_NUMBERED_TAG(key, tab_name) \
-	({ \
-		const char *_key2 = (key); \
-		\
-		(   (strncmp (_key2, tab_name, NM_STRLEN (tab_name)) == 0) \
-		 && _is_all_digits (&_key2[NM_STRLEN (tab_name)])); \
-	})
+	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY))
+		return TRUE;
 
-#define IS_NUMBERED_TAG_PARSE(key, tab_name, out_idx) \
+#define _IS_NUMBERED(key, tag) \
 	({ \
-		const char *_key = (key); \
 		gint64 _idx; \
-		gboolean _good = FALSE; \
-		gint64 *_out_idx = (out_idx); \
 		\
-		if (    IS_NUMBERED_TAG (_key, ""tab_name"") \
-		    && (_idx = _nm_utils_ascii_str_to_int64 (&_key[NM_STRLEN (tab_name)], 10, 0, G_MAXINT64, -1)) != -1) { \
-			NM_SET_OUT (_out_idx, _idx); \
-			_good = TRUE; \
-		} \
-		_good; \
+		   NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG (key, tag, &_idx) \
+		&& _idx >= 0; \
 	})
 
-static gboolean
-_svKeyMatchesType (const char *key, SvKeyType match_key_type)
-{
-	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY))
-		return TRUE;
-
 	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) {
-		if (   IS_NUMBERED_TAG (key, "ADDRESS")
-		    || IS_NUMBERED_TAG (key, "NETMASK")
-		    || IS_NUMBERED_TAG (key, "GATEWAY")
-		    || IS_NUMBERED_TAG (key, "METRIC")
-		    || IS_NUMBERED_TAG (key, "OPTIONS"))
+		if (   _IS_NUMBERED (key, "ADDRESS")
+		    || _IS_NUMBERED (key, "NETMASK")
+		    || _IS_NUMBERED (key, "GATEWAY")
+		    || _IS_NUMBERED (key, "METRIC")
+		    || _IS_NUMBERED (key, "OPTIONS"))
 			return TRUE;
 	}
 	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) {
-		if (   IS_NUMBERED_TAG (key, "IPADDR")
-		    || IS_NUMBERED_TAG (key, "PREFIX")
-		    || IS_NUMBERED_TAG (key, "NETMASK")
-		    || IS_NUMBERED_TAG (key, "GATEWAY"))
+		if (   _IS_NUMBERED (key, "IPADDR")
+		    || _IS_NUMBERED (key, "PREFIX")
+		    || _IS_NUMBERED (key, "NETMASK")
+		    || _IS_NUMBERED (key, "GATEWAY"))
 			return TRUE;
 	}
 	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) {
@@ -906,20 +967,20 @@ _svKeyMatchesType (const char *key, SvKeyType match_key_type)
 			return TRUE;
 	}
 	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_TC)) {
-		if (   IS_NUMBERED_TAG (key, "QDISC")
-		    || IS_NUMBERED_TAG (key, "FILTER"))
+		if (   _IS_NUMBERED (key, "QDISC")
+		    || _IS_NUMBERED (key, "FILTER"))
 			return TRUE;
 	}
 	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_SRIOV_VF)) {
-		if (IS_NUMBERED_TAG (key, "SRIOV_VF"))
+		if (_IS_NUMBERED (key, "SRIOV_VF"))
 			return TRUE;
 	}
 	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTING_RULE4)) {
-		if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE_", NULL))
+		if (_IS_NUMBERED (key, "ROUTING_RULE_"))
 			return TRUE;
 	}
 	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTING_RULE6)) {
-		if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE6_", NULL))
+		if (_IS_NUMBERED (key, "ROUTING_RULE6_"))
 			return TRUE;
 	}
 
@@ -931,9 +992,8 @@ svNumberedParseKey (const char *key)
 {
 	gint64 idx;
 
-	if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE_", &idx))
-		return idx;
-	if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE6_", &idx))
+	if (   NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG (key, "ROUTING_RULE_", &idx)
+	    || NMS_IFCFG_RH_UTIL_IS_NUMBERED_TAG (key, "ROUTING_RULE6_", &idx))
 		return idx;
 	return -1;
 }
@@ -1001,20 +1061,18 @@ svGetKeysSorted (shvarFile *s,
 /*****************************************************************************/
 
 const char *
-svFindFirstKeyWithPrefix (shvarFile *s, const char *key_prefix)
+svFindFirstNumberedKey (shvarFile *s, const char *key_prefix)
 {
-	CList *current;
-	const shvarLine *l;
+	const shvarLine *line;
 
 	g_return_val_if_fail (s, NULL);
 	g_return_val_if_fail (key_prefix, NULL);
 
-	c_list_for_each (current, &s->lst_head) {
-		l = c_list_entry (current, shvarLine, lst);
-		if (   l->key
-		    && l->line
-		    && g_str_has_prefix (l->key, key_prefix))
-			return l->key;
+	c_list_for_each_entry (line, &s->lst_head, lst) {
+		if (   line->key
+		    && line->line
+		    && nms_ifcfg_rh_utils_is_numbered_tag (line->key, key_prefix, NULL))
+			return line->key;
 	}
 
 	return NULL;
@@ -1025,20 +1083,16 @@ svFindFirstKeyWithPrefix (shvarFile *s, const char *key_prefix)
 static const char *
 _svGetValue (shvarFile *s, const char *key, char **to_free)
 {
-	CList *current;
-	const shvarLine *line, *l;
+	const shvarLine *line;
 	const char *v;
 
 	nm_assert (s);
 	nm_assert (_shell_is_name (key, -1));
 	nm_assert (to_free);
 
-	line = NULL;
-	c_list_for_each (current, &s->lst_head) {
-		l = c_list_entry (current, shvarLine, lst);
-		if (l->key && nm_streq (l->key, key))
-			line = l;
-	}
+	ASSERT_key_is_well_known (key);
+
+	line = g_hash_table_lookup (s->lst_idx, &key);
 
 	if (line && line->line) {
 		v = svUnescape (line->line, to_free);
@@ -1229,24 +1283,53 @@ svGetValueEnum (shvarFile *s, const char *key,
 gboolean
 svUnsetAll (shvarFile *s, SvKeyType match_key_type)
 {
-	CList *current;
 	shvarLine *line;
 	gboolean changed = FALSE;
 
 	g_return_val_if_fail (s, FALSE);
 
-	c_list_for_each (current, &s->lst_head) {
-		line = c_list_entry (current, shvarLine, lst);
+	c_list_for_each_entry (line, &s->lst_head, lst) {
 		ASSERT_shvarLine (line);
-		if (!line->key)
-			continue;
+		if (   line->key
+		    && _svKeyMatchesType (line->key, match_key_type)) {
+			if (nm_clear_g_free (&line->line)) {
+				ASSERT_shvarLine (line);
+				changed = TRUE;
+			}
+		}
+	}
 
-		if (_svKeyMatchesType (line->key, match_key_type)) {
+	if (changed)
+		s->modified = TRUE;
+	return changed;
+}
+
+gboolean
+svUnsetDirtyWellknown (shvarFile *s, NMTernary new_dirty_value)
+{
+	shvarLine *line;
+	gboolean changed = FALSE;
+
+	g_return_val_if_fail (s, FALSE);
+
+	c_list_for_each_entry (line, &s->lst_head, lst) {
+		const NMSIfcfgKeyTypeInfo *ti;
+
+		ASSERT_shvarLine (line);
+
+		if (   line->dirty
+		    && line->key
+		    && line->line
+		    && (ti = nms_ifcfg_rh_utils_is_well_known_key (line->key))
+		    && !NM_FLAGS_HAS (ti->key_flags, NMS_IFCFG_KEY_TYPE_KEEP_WHEN_DIRTY)) {
 			if (nm_clear_g_free (&line->line)) {
 				ASSERT_shvarLine (line);
 				changed = TRUE;
 			}
 		}
+
+		if (new_dirty_value != NM_TERNARY_DEFAULT)
+			line->dirty = (new_dirty_value != NM_TERNARY_FALSE);
 	}
 
 	if (changed)
@@ -1259,8 +1342,8 @@ svUnsetAll (shvarFile *s, SvKeyType match_key_type)
 gboolean
 svSetValue (shvarFile *s, const char *key, const char *value)
 {
-	CList *current;
-	shvarLine *line, *l;
+	shvarLine *line;
+	shvarLine *l_shadowed;
 	gboolean changed = FALSE;
 
 	g_return_val_if_fail (s, FALSE);
@@ -1268,29 +1351,37 @@ svSetValue (shvarFile *s, const char *key, const char *value)
 
 	nm_assert (_shell_is_name (key, -1));
 
-	line = NULL;
-	c_list_for_each (current, &s->lst_head) {
-		l = c_list_entry (current, shvarLine, lst);
-		if (l->key && nm_streq (l->key, key)) {
-			if (line) {
-				/* if we find multiple entries for the same key, we can
-				 * delete all but the last. */
-				line_free (line);
-				changed = TRUE;
-			}
-			line = l;
-		}
+	ASSERT_key_is_well_known (key);
+
+	line = g_hash_table_lookup (s->lst_idx, &key);
+	if (   line
+	    && (l_shadowed = line->prev_shadowed)) {
+		/* if we find multiple entries for the same key, we can
+		 * delete the shadowed ones. */
+		line->prev_shadowed = NULL;
+		changed = TRUE;
+		do {
+			shvarLine *l = l_shadowed;
+
+			l_shadowed = l_shadowed->prev_shadowed;
+			line_free (l);
+		} while (l_shadowed);
 	}
 
 	if (!value) {
 		if (line) {
+			/* We only clear the value, but leave the line entry. This way, if we
+			 * happen to re-add the value, we write it to the same line again. */
 			if (nm_clear_g_free (&line->line)) {
 				changed = TRUE;
 			}
 		}
 	} else {
 		if (!line) {
-			c_list_link_tail (&s->lst_head, &line_new_build (key, value)->lst);
+			line = line_new_build (key, value);
+			if (!g_hash_table_add (s->lst_idx, line))
+				nm_assert_not_reached ();
+			c_list_link_tail (&s->lst_head, &line->lst);
 			changed = TRUE;
 		} else {
 			if (line_set (line, value))
@@ -1442,14 +1533,15 @@ svWriteFile (shvarFile *s, int mode, GError **error)
 void
 svCloseFile (shvarFile *s)
 {
-	CList *current, *safe;
+	shvarLine *line;
 
 	g_return_if_fail (s != NULL);
 
 	if (s->fd >= 0)
 		nm_close (s->fd);
 	g_free (s->fileName);
-	c_list_for_each_safe (current, safe, &s->lst_head)
-		line_free (c_list_entry (current, shvarLine, lst));
+	g_hash_table_destroy (s->lst_idx);
+	while ((line = c_list_first_entry (&s->lst_head, shvarLine, lst)))
+		line_free (line);
 	g_slice_free (shvarFile, s);
 }
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index c3bbabab..410284f8 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -24,13 +24,21 @@ const char *svFileGetName (const shvarFile *s);
 void _nmtst_svFileSetName (shvarFile *s, const char *fileName);
 void _nmtst_svFileSetModified (shvarFile *s);
 
+/*****************************************************************************/
+
+shvarFile *svFile_new (const char *name,
+                       int fd,
+                       const char *content);
+
 /* Create the file <name>, return a shvarFile (never fails) */
 shvarFile *svCreateFile (const char *name);
 
 /* Open the file <name>, return shvarFile on success, NULL on failure */
 shvarFile *svOpenFile (const char *name, GError **error);
 
-const char *svFindFirstKeyWithPrefix (shvarFile *s, const char *key_prefix);
+/*****************************************************************************/
+
+const char *svFindFirstNumberedKey (shvarFile *s, const char *key_prefix);
 
 /* Get the value associated with the key, and leave the current pointer
  * pointing at the line containing the value.  The char* returned MUST
@@ -79,6 +87,7 @@ gboolean svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value);
 
 gboolean svUnsetValue (shvarFile *s, const char *key);
 gboolean svUnsetAll (shvarFile *s, SvKeyType match_key_type);
+gboolean svUnsetDirtyWellknown (shvarFile *s, NMTernary new_dirty_value);
 
 /* Write the current contents iff modified.  Returns FALSE on error
  * and TRUE on success.  Do not write if no values have been modified.
@@ -88,6 +97,13 @@ gboolean svUnsetAll (shvarFile *s, SvKeyType match_key_type);
  */
 gboolean svWriteFile (shvarFile *s, int mode, GError **error);
 
+static inline gboolean
+svWriteFileWithoutDirtyWellknown (shvarFile *s, int mode, GError **error)
+{
+	svUnsetDirtyWellknown (s, NM_TERNARY_FALSE);
+	return svWriteFile (s, mode, error);
+}
+
 /* Close the file descriptor (if open) and free the shvarFile. */
 void svCloseFile (shvarFile *s);
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/meson.build b/src/settings/plugins/ifcfg-rh/tests/meson.build
index 21699045..1ce61c90 100644
--- a/src/settings/plugins/ifcfg-rh/tests/meson.build
+++ b/src/settings/plugins/ifcfg-rh/tests/meson.build
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
 test_unit = 'test-ifcfg-rh'
 
 exe = executable(
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
index d4785ff5..2e69c5e7 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
@@ -1,6 +1,5 @@
 HWADDR=00:22:15:59:62:97
 TYPE=Ethernet
-BRIDGING_OPTS="priority=28 hairpin_mode=1"
 NAME="System test-bridge-component"
 UUID=${UUID}
 DEVICE=eth0
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected
index 36df7712..b288c04a 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected
@@ -1,4 +1,4 @@
-BONDING_OPTS="downdelay=5 miimon=100 mode=balance-rr updelay=10"
+BONDING_OPTS="mode=balance-rr downdelay=5 miimon=100 updelay=10"
 TYPE=Bond
 BONDING_MASTER=yes
 HWADDR=
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected
index 5dfdce4d..87493ac2 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1.cexpected
@@ -2,7 +2,6 @@ DNS1=192.0.2.1
 IPADDR=102.0.2.2
 GATEWAY=192.0.2.1
 NETMASK=255.254.0.0
-BOOTPROTO="static"
 DEVICE=eth1
 ONBOOT=yes
 IPV6INIT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-owe b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-owe
new file mode 100644
index 00000000..354046ad
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-owe
@@ -0,0 +1,5 @@
+TYPE=Wireless
+DEVICE=wlan1
+ESSID=blahblah_owe
+MODE=Managed
+KEY_MGMT=OWE
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk
index a78051aa..4ab45ae3 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-psk
@@ -16,4 +16,4 @@ CIPHER_GROUP="TKIP CCMP WEP40 WEP104"
 KEY_MGMT=WPA-PSK
 WPA_ALLOW_WPA=yes
 WPA_ALLOW_WPA2=yes
-LAST_ENTRY=no-newline
\ No newline at end of file
+CTCPROT=no-newline
\ No newline at end of file
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-autoip b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-autoip
index dc47126c..e683db3c 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-autoip
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-autoip
@@ -3,3 +3,4 @@ DEVICE=eth0
 BOOTPROTO=autoip
 IPV4_FAILURE_FATAL=yes
 PEERDNS=no
+DEVTIMEOUT=2.6
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4
index 2c1b7fb4..f834464e 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4
@@ -5,32 +5,32 @@
 # don't consider only line-by-line, thus this is
 # expected.
 #
-# Also note that setting NAME will replace the last
+# Also note that setting IPADDR will replace the last
 # occurrence, and delete all previous once.
 
 #L1
-NAME=l2
+IPADDR=l2
 
 #L2
-NAME=l3
+IPADDR=l3
 
-some_key1=''
-some_key2=$'\U0x'
-some_key3=$'x\U0'
+METRIC1=''
+METRIC2=$'\U0x'
+METRIC3=$'x\U0'
 
 #L4
-NAME='
-NAME=l4x
+IPADDR='
+IPADDR=l4x
 '
 
 #Lx-1
-NAME2=not-visible
+IPADDR2=not-visible
 #Lx-2
-NAME2='invalid
+IPADDR2='invalid
 #Lx-3
 
 #Ly-1
-NAME3='invalid
+IPADDR3='invalid
 #Ly-2
-NAME3=name3-value
+IPADDR3=name3-value
 #Ly-3
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected
index cf3f45bd..040ddc9d 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected
@@ -5,27 +5,27 @@
 # don't consider only line-by-line, thus this is
 # expected.
 #
-# Also note that setting NAME will replace the last
+# Also note that setting IPADDR will replace the last
 # occurrence, and delete all previous once.
 
 #L1
 
 #L2
 
-some_key1=''
-some_key2=$'\U0x'
-some_key3=$'x\U0'
+METRIC1=''
+METRIC2=$'\U0x'
+METRIC3=$'x\U0'
 
 #L4
-NAME=set-by-test1
+IPADDR=set-by-test1
 #NM: '
 
 #Lx-1
 #Lx-2
-NAME2=set-by-test2
+IPADDR2=set-by-test2
 #Lx-3
 
 #Ly-1
 #Ly-2
-NAME3=set-by-test3
+IPADDR3=set-by-test3
 #Ly-3
diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 675421d3..efa9ea9b 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -1738,6 +1738,7 @@ static void
 test_read_wired_autoip (void)
 {
 	gs_unref_object NMConnection *connection = NULL;
+	NMSettingConnection *s_con;
 	NMSettingIPConfig *s_ip4;
 	char *unmanaged = NULL;
 
@@ -1751,6 +1752,9 @@ test_read_wired_autoip (void)
 	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL);
 	g_assert (!nm_setting_ip_config_get_may_fail (s_ip4));
 	g_assert (nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, 2600);
 }
 
 static void
@@ -2094,7 +2098,7 @@ test_read_wired_aliases_good (gconstpointer test_data)
 		g_assert (ip4_addr != NULL);
 
 		addr = nm_ip_address_get_address (ip4_addr);
-		g_assert (nm_utils_ipaddr_valid (AF_INET, addr));
+		g_assert (nm_utils_ipaddr_is_valid (AF_INET, addr));
 
 		for (j = 0; j < expected_num_addresses; j++) {
 			if (!g_strcmp0 (addr, expected_address[j]))
@@ -2868,7 +2872,7 @@ test_ifcfg_no_trailing_newline (void)
 	shvarFile *sv;
 
 	sv = _svOpenFile (TEST_IFCFG_DIR"/ifcfg-test-wifi-wpa-psk");
-	_svGetValue_check (sv, "LAST_ENTRY", "no-newline");
+	_svGetValue_check (sv, "CTCPROT", "no-newline");
 	svCloseFile (sv);
 }
 
@@ -3035,6 +3039,45 @@ test_read_wifi_sae (void)
 }
 
 static void
+test_read_wifi_owe (void)
+{
+	gs_unref_object NMConnection *connection = NULL;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wireless;
+	NMSettingWirelessSecurity *s_wsec;
+	GBytes *ssid;
+	const char *expected_ssid = "blahblah_owe";
+
+	connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wifi-owe",
+	                                    NULL, TYPE_WIRELESS, NULL);
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "System blahblah_owe (test-wifi-owe)");
+
+	g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0);
+	g_assert (nm_setting_connection_get_autoconnect (s_con));
+
+	s_wireless = nm_connection_get_setting_wireless (connection);
+	g_assert (s_wireless);
+
+	g_assert_cmpint (nm_setting_wireless_get_mtu (s_wireless), ==, 0);
+
+	ssid = nm_setting_wireless_get_ssid (s_wireless);
+	g_assert (ssid);
+	g_assert_cmpmem (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid), expected_ssid, strlen (expected_ssid));
+
+	g_assert (!nm_setting_wireless_get_bssid (s_wireless));
+	g_assert_cmpstr (nm_setting_wireless_get_mode (s_wireless), ==, "infrastructure");
+
+	s_wsec = nm_connection_get_setting_wireless_security (connection);
+	g_assert (s_wsec);
+	g_assert_cmpstr (nm_setting_wireless_security_get_key_mgmt (s_wsec), ==, "owe");
+	g_assert (!nm_setting_wireless_security_get_psk (s_wsec));
+	g_assert (!nm_setting_wireless_security_get_auth_alg (s_wsec));
+}
+
+static void
 test_read_wifi_wpa_psk_2 (void)
 {
 	NMConnection *connection;
@@ -9742,23 +9785,23 @@ test_write_unknown (gconstpointer test_data)
 	_nmtst_svFileSetModified (sv);
 
 	if (g_str_has_suffix (testfile, "ifcfg-test-write-unknown-4")) {
-		_svGetValue_check (sv, "NAME", "l4x");
-		_svGetValue_check (sv, "NAME2", "");
-		_svGetValue_check (sv, "NAME3", "name3-value");
-
-		svSetValue (sv, "NAME", "set-by-test1");
-		svSetValue (sv, "NAME2", NULL);
-		svSetValue (sv, "NAME2", "set-by-test2");
-		svSetValue (sv, "NAME3", "set-by-test3");
-
-		_svGetValue_check (sv, "some_key", NULL);
-		_svGetValue_check (sv, "some_key1", "");
-		_svGetValue_check (sv, "some_key2", "");
-		_svGetValue_check (sv, "some_key3", "x");
-
-		_svGetValue_check (sv, "NAME", "set-by-test1");
-		_svGetValue_check (sv, "NAME2", "set-by-test2");
-		_svGetValue_check (sv, "NAME3", "set-by-test3");
+		_svGetValue_check (sv, "IPADDR", "l4x");
+		_svGetValue_check (sv, "IPADDR2", "");
+		_svGetValue_check (sv, "IPADDR3", "name3-value");
+
+		svSetValue (sv, "IPADDR", "set-by-test1");
+		svSetValue (sv, "IPADDR2", NULL);
+		svSetValue (sv, "IPADDR2", "set-by-test2");
+		svSetValue (sv, "IPADDR3", "set-by-test3");
+
+		_svGetValue_check (sv, "METRIC", NULL);
+		_svGetValue_check (sv, "METRIC1", "");
+		_svGetValue_check (sv, "METRIC2", "");
+		_svGetValue_check (sv, "METRIC3", "x");
+
+		_svGetValue_check (sv, "IPADDR", "set-by-test1");
+		_svGetValue_check (sv, "IPADDR2", "set-by-test2");
+		_svGetValue_check (sv, "IPADDR3", "set-by-test3");
 	}
 
 	success = svWriteFile (sv, 0644, &error);
@@ -10203,6 +10246,160 @@ test_tc_write (void)
 
 /*****************************************************************************/
 
+static void
+test_well_known_keys (void)
+{
+	gsize i;
+
+	for (i = 0; i < G_N_ELEMENTS (nms_ifcfg_well_known_keys); i++) {
+		const NMSIfcfgKeyTypeInfo *ti = &nms_ifcfg_well_known_keys[i];
+
+		g_assert (ti->key_name);
+		g_assert (ti->key_name[0]);
+		g_assert (NM_FLAGS_HAS (ti->key_flags, NMS_IFCFG_KEY_TYPE_WELL_KNOWN));
+		g_assert (nm_utils_is_power_of_two (ti->key_flags & (  NMS_IFCFG_KEY_TYPE_IS_PLAIN
+		                                                     | NMS_IFCFG_KEY_TYPE_IS_NUMBERED
+		                                                     | NMS_IFCFG_KEY_TYPE_IS_PREFIX)));
+	}
+
+	for (i = 1; i < G_N_ELEMENTS (nms_ifcfg_well_known_keys); i++) {
+		const NMSIfcfgKeyTypeInfo *ti_prev = &nms_ifcfg_well_known_keys[i - 1];
+		const NMSIfcfgKeyTypeInfo *ti = &nms_ifcfg_well_known_keys[i];
+
+		g_assert_cmpstr (ti_prev->key_name, <, ti->key_name);
+	}
+
+	for (i = 0; i < G_N_ELEMENTS (nms_ifcfg_well_known_keys); i++) {
+		const NMSIfcfgKeyTypeInfo *ti = &nms_ifcfg_well_known_keys[i];
+		gs_free char *key_name = NULL;
+		gssize idx;
+
+		g_assert (ti == nms_ifcfg_well_known_key_find_info (ti->key_name, &idx));
+		g_assert_cmpint (i, ==, idx);
+
+		key_name = g_strdup (ti->key_name);
+		g_assert (ti == nms_ifcfg_well_known_key_find_info (key_name, &idx));
+		g_assert_cmpint (i, ==, idx);
+	}
+
+#define _test_well_known(key, expected) \
+	G_STMT_START { \
+		const NMSIfcfgKeyTypeInfo *_ti; \
+		const char *_expected = (expected); \
+		\
+		_ti = nms_ifcfg_rh_utils_is_well_known_key (""key""); \
+		if (!_expected) { \
+			g_assert (!_ti); \
+		} else { \
+			g_assert (_ti); \
+			g_assert_cmpstr (_ti->key_name, ==, _expected); \
+		} \
+	} G_STMT_END
+
+#define _test_well_known_plain(key) \
+	_test_well_known (""key"", ""key"")
+
+	_test_well_known_plain ("ONBOOT");
+	_test_well_known ("NM_USER_",  NULL);
+	_test_well_known ("NM_USER_x", "NM_USER_");
+	_test_well_known ("IPADDR",    "IPADDR");
+	_test_well_known ("IPADDR1",   "IPADDR");
+	_test_well_known ("IPADDRx",   NULL);
+}
+
+/*****************************************************************************/
+
+static void
+_do_utils_has_route_file_new_syntax_size (gboolean has_new_syntax,
+                                          const char *content,
+                                          gssize content_len)
+{
+	nmtst_auto_unlinkfile char *testfile = g_strdup (TEST_SCRATCH_DIR"/utils-has-route-file-new-syntax-test.txt");
+	gboolean val;
+
+	nmtst_file_set_contents_size (testfile, content, content_len);
+
+	val = utils_has_route_file_new_syntax (testfile);
+
+	g_assert_cmpint (val, ==, has_new_syntax);
+}
+#define _do_utils_has_route_file_new_syntax(has_new_syntax, content) \
+	_do_utils_has_route_file_new_syntax_size (has_new_syntax, (content), NM_STRLEN (content))
+
+static void
+test_utils_has_route_file_new_syntax (void)
+{
+	_do_utils_has_route_file_new_syntax (TRUE,  "");
+	_do_utils_has_route_file_new_syntax (FALSE, "\0");
+	_do_utils_has_route_file_new_syntax (FALSE, "\n");
+	_do_utils_has_route_file_new_syntax (FALSE, "ADDRESS=bogus");
+	_do_utils_has_route_file_new_syntax (FALSE, "ADDRESS=bogus\0");
+	_do_utils_has_route_file_new_syntax (TRUE,  "ADDRESS1=b\0ogus\0");
+	_do_utils_has_route_file_new_syntax (TRUE,  "ADDRESS1=bogus\0");
+	_do_utils_has_route_file_new_syntax (TRUE,  "\n\n\tADDRESS1=bogus\0");
+	_do_utils_has_route_file_new_syntax (FALSE, "\n\n\tADDRESS=bogus\n");
+	_do_utils_has_route_file_new_syntax (TRUE,  "\n\n\tADDRESS=bogus\n  ADDRESS000=\n");
+	_do_utils_has_route_file_new_syntax (FALSE, "\n\n\tROUTE1=bogus\n  ADDRES=\n");
+	_do_utils_has_route_file_new_syntax (FALSE, "\n\n\tADDRESS=bogus\n  ADDRESS\000000=\n");
+}
+
+/*****************************************************************************/
+
+static void
+test_ethtool_names (void)
+{
+	static const struct {
+		NMEthtoolID ethtool_id;
+		const char *kernel_name;
+	} kernel_names[] = {
+		{ NM_ETHTOOL_ID_FEATURE_GRO,    "rx-gro" },
+		{ NM_ETHTOOL_ID_FEATURE_GSO,    "tx-generic-segmentation" },
+		{ NM_ETHTOOL_ID_FEATURE_LRO,    "rx-lro" },
+		{ NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter" },
+		{ NM_ETHTOOL_ID_FEATURE_RX,     "rx-checksum" },
+		{ NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing" },
+		{ NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse" },
+		{ NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert" },
+	};
+	const NMEthtoolData *data;
+	NMEthtoolID id;
+	int i;
+
+	for (id = _NM_ETHTOOL_ID_FEATURE_FIRST; id <= _NM_ETHTOOL_ID_FEATURE_LAST; id++) {
+		const char *ifcfg_rh_name;
+		int idx;
+
+		idx = id - _NM_ETHTOOL_ID_FEATURE_FIRST;
+		g_assert (idx >= 0);
+		g_assert (idx < G_N_ELEMENTS (_nm_ethtool_ifcfg_names));
+		ifcfg_rh_name = _nm_ethtool_ifcfg_names[idx];
+		g_assert (ifcfg_rh_name && ifcfg_rh_name[0]);
+
+		for (i = 0; i < G_N_ELEMENTS (_nm_ethtool_ifcfg_names); i++) {
+			if (i != idx)
+				g_assert_cmpstr (ifcfg_rh_name, !=, _nm_ethtool_ifcfg_names[i]);
+		}
+
+		g_assert_cmpstr (nms_ifcfg_rh_utils_get_ethtool_name (id), ==, ifcfg_rh_name);
+
+		data = nms_ifcfg_rh_utils_get_ethtool_by_name (ifcfg_rh_name);
+		g_assert (data);
+		g_assert (data->id == id);
+	}
+
+	for (i = 0; i < G_N_ELEMENTS (kernel_names); i++) {
+		const char *name = kernel_names[i].kernel_name;
+
+		id = kernel_names[i].ethtool_id;
+		data = nms_ifcfg_rh_utils_get_ethtool_by_name (name);
+		g_assert (data);
+		g_assert (data->id == id);
+		g_assert_cmpstr (nms_ifcfg_rh_utils_get_ethtool_name (id), !=, name);
+	}
+}
+
+/*****************************************************************************/
+
 #define TPATH "/settings/plugins/ifcfg-rh/"
 
 #define TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED TEST_IFCFG_DIR"/ifcfg-test-wifi-open-ssid-long-quoted"
@@ -10322,6 +10519,7 @@ int main (int argc, char **argv)
 	g_test_add_func (TPATH "wifi/read/wpa-psk/adhoc", test_read_wifi_wpa_psk_adhoc);
 	g_test_add_func (TPATH "wifi/read/wpa-psk/hex", test_read_wifi_wpa_psk_hex);
 	g_test_add_func (TPATH "wifi/read/sae", test_read_wifi_sae);
+	g_test_add_func (TPATH "wifi/read/owe", test_read_wifi_owe);
 	g_test_add_func (TPATH "wifi/read/dynamic-wep/leap", test_read_wifi_dynamic_wep_leap);
 	g_test_add_func (TPATH "wifi/read/wpa/eap/tls", test_read_wifi_wpa_eap_tls);
 	g_test_add_func (TPATH "wifi/read/wpa/eap/ttls/tls", test_read_wifi_wpa_eap_ttls_tls);
@@ -10498,6 +10696,10 @@ int main (int argc, char **argv)
 
 	g_test_add_func (TPATH "tc/read", test_tc_read);
 	g_test_add_func (TPATH "tc/write", test_tc_write);
+	g_test_add_func (TPATH "utils/test_well_known_keys", test_well_known_keys);
+	g_test_add_func (TPATH "utils/test_utils_has_route_file_new_syntax", test_utils_has_route_file_new_syntax);
+
+	g_test_add_func (TPATH "utils/test_ethtool_names", test_ethtool_names);
 
 	return g_test_run ();
 }
diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build
index 0cabe771..7e2b44fb 100644
--- a/src/settings/plugins/ifupdown/meson.build
+++ b/src/settings/plugins/ifupdown/meson.build
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
 sources = files(
   'nms-ifupdown-interface-parser.c',
   'nms-ifupdown-parser.c',
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
index 1db9ef15..ac3ed1ce 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
@@ -484,7 +484,7 @@ update_ip4_setting_from_if_block (NMConnection *connection,
 		/* gateway */
 		gateway_v = ifparser_getkey (block, "gateway");
 		if (gateway_v) {
-			if (!nm_utils_ipaddr_valid (AF_INET, gateway_v)) {
+			if (!nm_utils_ipaddr_is_valid (AF_INET, gateway_v)) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 				             "Invalid IPv4 gateway '%s'", gateway_v);
 				return FALSE;
@@ -565,9 +565,8 @@ update_ip6_setting_from_if_block (NMConnection *connection,
 		const char *nameserver_v;
 		const char *nameservers_v;
 		const char *search_v;
-		int prefix_int = 128;
+		guint prefix_int;
 
-		/* Address */
 		address_v = ifparser_getkey (block, "address");
 		if (!address_v) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -575,12 +574,12 @@ update_ip6_setting_from_if_block (NMConnection *connection,
 			return FALSE;
 		}
 
-		/* Prefix */
 		prefix_v = ifparser_getkey (block, "netmask");
 		if (prefix_v)
-			prefix_int = g_ascii_strtoll (prefix_v, NULL, 10);
+			prefix_int = _nm_utils_ascii_str_to_int64 (prefix_v, 10, 0, 128, G_MAXINT);
+		else
+			prefix_int = 128;
 
-		/* Add the new address to the setting */
 		addr = nm_ip_address_new (AF_INET6, address_v, prefix_int, error);
 		if (!addr)
 			return FALSE;
@@ -593,10 +592,9 @@ update_ip6_setting_from_if_block (NMConnection *connection,
 		}
 		nm_ip_address_unref (addr);
 
-		/* gateway */
 		gateway_v = ifparser_getkey (block, "gateway");
 		if (gateway_v) {
-			if (!nm_utils_ipaddr_valid (AF_INET6, gateway_v)) {
+			if (!nm_utils_ipaddr_is_valid (AF_INET6, gateway_v)) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 				             "Invalid IPv6 gateway '%s'", gateway_v);
 				return FALSE;
@@ -614,7 +612,6 @@ update_ip6_setting_from_if_block (NMConnection *connection,
 		if (!nm_setting_ip_config_get_num_dns (s_ip6))
 			_LOGI ("No dns-nameserver configured in /etc/network/interfaces");
 
-		/* DNS searches */
 		search_v = ifparser_getkey (block, "dns-search");
 		if (search_v) {
 			gs_free const char **list = NULL;
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index d19db0fd..808df49f 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -376,7 +376,7 @@ dispose (GObject *object)
 	NMSIfupdownPlugin *plugin = NMS_IFUPDOWN_PLUGIN (object);
 	NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (plugin);
 
-	g_clear_pointer (&priv->eni_ifaces, g_hash_table_destroy);
+	nm_clear_pointer (&priv->eni_ifaces, g_hash_table_destroy);
 
 	G_OBJECT_CLASS (nms_ifupdown_plugin_parent_class)->dispose (object);
 }
diff --git a/src/settings/plugins/ifupdown/tests/meson.build b/src/settings/plugins/ifupdown/tests/meson.build
index 1ca094b5..a39e4e21 100644
--- a/src/settings/plugins/ifupdown/tests/meson.build
+++ b/src/settings/plugins/ifupdown/tests/meson.build
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
 test_unit = 'test-ifupdown'
 
 exe = executable(
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index fdb88d2a..2906b399 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -23,7 +23,7 @@
 #include "nm-utils.h"
 #include "nm-config.h"
 #include "nm-core-internal.h"
-#include "nm-keyfile-internal.h"
+#include "nm-keyfile/nm-keyfile-internal.h"
 
 #include "systemd/nm-sd-utils-shared.h"
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index af9e6726..e8c9f9fe 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -9,7 +9,7 @@
 
 #include <sys/stat.h>
 
-#include "nm-keyfile-internal.h"
+#include "nm-keyfile/nm-keyfile-internal.h"
 
 #include "NetworkManagerUtils.h"
 #include "nms-keyfile-utils.h"
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c
index f03c601a..bc8dfd12 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c
@@ -11,7 +11,7 @@
 #include <sys/stat.h>
 
 #include "nm-glib-aux/nm-io-utils.h"
-#include "nm-keyfile-internal.h"
+#include "nm-keyfile/nm-keyfile-internal.h"
 #include "nm-utils.h"
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless.h"
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c
index fa95198c..7554becc 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -12,7 +12,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "nm-keyfile-internal.h"
+#include "nm-keyfile/nm-keyfile-internal.h"
 
 #include "nms-keyfile-utils.h"
 #include "nms-keyfile-reader.h"
@@ -60,7 +60,7 @@ cert_writer (NMConnection *connection,
 					 * that would be interpreted as legacy binary format by reader. */
 					tmp = nm_keyfile_detect_unqualified_path_scheme (info->keyfile_dir, p, -1, FALSE, NULL);
 					if (tmp) {
-						g_clear_pointer (&tmp, g_free);
+						nm_clear_g_free (&tmp);
 						accepted_path = p;
 					}
 				}
@@ -71,7 +71,7 @@ cert_writer (NMConnection *connection,
 			 * Otherwise, add a file:// prefix */
 			tmp = nm_keyfile_detect_unqualified_path_scheme (info->keyfile_dir, path, -1, FALSE, NULL);
 			if (tmp) {
-				g_clear_pointer (&tmp, g_free);
+				nm_clear_g_free (&tmp);
 				accepted_path = path;
 			}
 		}
diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build
index 7bf9fda0..cd1dc3aa 100644
--- a/src/settings/plugins/keyfile/tests/meson.build
+++ b/src/settings/plugins/keyfile/tests/meson.build
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
 test_unit = 'test-keyfile-settings'
 
 exe = executable(
diff --git a/src/settings/plugins/meson.build b/src/settings/plugins/meson.build
index 83981aab..cde0844b 100644
--- a/src/settings/plugins/meson.build
+++ b/src/settings/plugins/meson.build
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
 if enable_ifcfg_rh
   subdir('ifcfg-rh')
 endif