summary refs log tree commit diff
path: root/libnm-core
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-core')
-rw-r--r--libnm-core/nm-connection-private.h8
-rw-r--r--libnm-core/nm-connection.c623
-rw-r--r--libnm-core/nm-connection.h11
-rw-r--r--libnm-core/nm-core-internal.h84
-rw-r--r--libnm-core/nm-core-types.h4
-rw-r--r--libnm-core/nm-dbus-interface.h170
-rw-r--r--libnm-core/nm-dbus-types.xml100
-rw-r--r--libnm-core/nm-errors.h3
-rw-r--r--libnm-core/nm-keyfile-internal.h2
-rw-r--r--libnm-core/nm-keyfile-reader.c84
-rw-r--r--libnm-core/nm-keyfile-writer.c23
-rw-r--r--libnm-core/nm-setting-8021x.c32
-rw-r--r--libnm-core/nm-setting-adsl.c2
-rw-r--r--libnm-core/nm-setting-bluetooth.c95
-rw-r--r--libnm-core/nm-setting-bluetooth.h12
-rw-r--r--libnm-core/nm-setting-bond.c35
-rw-r--r--libnm-core/nm-setting-bridge-port.c4
-rw-r--r--libnm-core/nm-setting-bridge.c67
-rw-r--r--libnm-core/nm-setting-bridge.h3
-rw-r--r--libnm-core/nm-setting-cdma.c2
-rw-r--r--libnm-core/nm-setting-connection.c107
-rw-r--r--libnm-core/nm-setting-connection.h4
-rw-r--r--libnm-core/nm-setting-dcb.c6
-rw-r--r--libnm-core/nm-setting-dummy.c2
-rw-r--r--libnm-core/nm-setting-generic.c2
-rw-r--r--libnm-core/nm-setting-gsm.c4
-rw-r--r--libnm-core/nm-setting-infiniband.c7
-rw-r--r--libnm-core/nm-setting-ip-config.c245
-rw-r--r--libnm-core/nm-setting-ip-config.h16
-rw-r--r--libnm-core/nm-setting-ip-tunnel.c4
-rw-r--r--libnm-core/nm-setting-ip4-config.c10
-rw-r--r--libnm-core/nm-setting-ip6-config.c27
-rw-r--r--libnm-core/nm-setting-macsec.c2
-rw-r--r--libnm-core/nm-setting-macvlan.c2
-rw-r--r--libnm-core/nm-setting-olpc-mesh.c2
-rw-r--r--libnm-core/nm-setting-ovs-bridge.c337
-rw-r--r--libnm-core/nm-setting-ovs-bridge.h63
-rw-r--r--libnm-core/nm-setting-ovs-interface.c392
-rw-r--r--libnm-core/nm-setting-ovs-interface.h54
-rw-r--r--libnm-core/nm-setting-ovs-patch.c215
-rw-r--r--libnm-core/nm-setting-ovs-patch.h54
-rw-r--r--libnm-core/nm-setting-ovs-port.c471
-rw-r--r--libnm-core/nm-setting-ovs-port.h69
-rw-r--r--libnm-core/nm-setting-ppp.c6
-rw-r--r--libnm-core/nm-setting-pppoe.c58
-rw-r--r--libnm-core/nm-setting-pppoe.h3
-rw-r--r--libnm-core/nm-setting-private.h12
-rw-r--r--libnm-core/nm-setting-proxy.c2
-rw-r--r--libnm-core/nm-setting-serial.c2
-rw-r--r--libnm-core/nm-setting-team-port.c2
-rw-r--r--libnm-core/nm-setting-team.c3
-rw-r--r--libnm-core/nm-setting-tun.c2
-rw-r--r--libnm-core/nm-setting-user.c14
-rw-r--r--libnm-core/nm-setting-vlan.c2
-rw-r--r--libnm-core/nm-setting-vpn.c4
-rw-r--r--libnm-core/nm-setting-vxlan.c4
-rw-r--r--libnm-core/nm-setting-wimax.c2
-rw-r--r--libnm-core/nm-setting-wired.c12
-rw-r--r--libnm-core/nm-setting-wireless-security.c160
-rw-r--r--libnm-core/nm-setting-wireless-security.h50
-rw-r--r--libnm-core/nm-setting-wireless.c6
-rw-r--r--libnm-core/nm-setting.c121
-rw-r--r--libnm-core/nm-utils.c532
-rw-r--r--libnm-core/nm-version.h14
-rw-r--r--libnm-core/tests/test-general.c1170
-rw-r--r--libnm-core/tests/test-secrets.c13
66 files changed, 4667 insertions, 986 deletions
diff --git a/libnm-core/nm-connection-private.h b/libnm-core/nm-connection-private.h
index 620e6a81..ee2d7264 100644
--- a/libnm-core/nm-connection-private.h
+++ b/libnm-core/nm-connection-private.h
@@ -29,7 +29,15 @@ NMSetting  *_nm_connection_find_base_type_setting         (NMConnection *connect
 const char *_nm_connection_detect_slave_type              (NMConnection *connection,
                                                            NMSetting **out_s_port);
 
+const char *_nm_connection_detect_bluetooth_type (NMConnection *self);
+
 gboolean    _nm_connection_verify_required_interface_name (NMConnection *connection,
                                                            GError **error);
 
+int _nm_setting_ovs_interface_verify_interface_type (NMSettingOvsInterface *self,
+                                                     NMConnection *connection,
+                                                     gboolean normalize,
+                                                     gboolean *out_modified,
+                                                     GError **error);
+
 #endif  /* __NM_CONNECTION_PRIVATE_H__ */
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index c1b75068..d5e28f7b 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -93,10 +93,16 @@ _setting_release (gpointer key, gpointer value, gpointer user_data)
 static void
 _nm_connection_add_setting (NMConnection *connection, NMSetting *setting)
 {
-	NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
-	const char *name = G_OBJECT_TYPE_NAME (setting);
+	NMConnectionPrivate *priv;
+	const char *name;
 	NMSetting *s_old;
 
+	nm_assert (NM_IS_CONNECTION (connection));
+	nm_assert (NM_IS_SETTING (setting));
+
+	priv = NM_CONNECTION_GET_PRIVATE (connection);
+	name = G_OBJECT_TYPE_NAME (setting);
+
 	if ((s_old = g_hash_table_lookup (priv->settings, (gpointer) name)))
 		g_signal_handlers_disconnect_by_func (s_old, setting_changed_cb, connection);
 	g_hash_table_insert (priv->settings, (gpointer) name, setting);
@@ -160,6 +166,24 @@ nm_connection_remove_setting (NMConnection *connection, GType setting_type)
 	_nm_connection_remove_setting (connection, setting_type);
 }
 
+static gpointer
+_connection_get_setting (NMConnection *connection, GType setting_type)
+{
+	nm_assert (NM_IS_CONNECTION (connection));
+	nm_assert (g_type_is_a (setting_type, NM_TYPE_SETTING));
+
+	return g_hash_table_lookup (NM_CONNECTION_GET_PRIVATE (connection)->settings,
+	                            g_type_name (setting_type));
+}
+
+static gpointer
+_connection_get_setting_check (NMConnection *connection, GType setting_type)
+{
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+	return _connection_get_setting (connection, setting_type);
+}
+
 /**
  * nm_connection_get_setting:
  * @connection: a #NMConnection
@@ -174,11 +198,9 @@ nm_connection_remove_setting (NMConnection *connection, GType setting_type)
 NMSetting *
 nm_connection_get_setting (NMConnection *connection, GType setting_type)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (g_type_is_a (setting_type, NM_TYPE_SETTING), NULL);
 
-	return (NMSetting *) g_hash_table_lookup (NM_CONNECTION_GET_PRIVATE (connection)->settings,
-	                                          g_type_name (setting_type));
+	return _connection_get_setting_check (connection, setting_type);
 }
 
 /**
@@ -198,11 +220,9 @@ nm_connection_get_setting_by_name (NMConnection *connection, const char *name)
 	GType type;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-	g_return_val_if_fail (name != NULL, NULL);
 
 	type = nm_setting_lookup_type (name);
-
-	return type ? nm_connection_get_setting (connection, type) : NULL;
+	return type ? _connection_get_setting (connection, type) : NULL;
 }
 
 static gboolean
@@ -496,7 +516,7 @@ nm_connection_compare (NMConnection *a,
 }
 
 
-static void
+static gboolean
 diff_one_connection (NMConnection *a,
                      NMConnection *b,
                      NMSettingCompareFlags flags,
@@ -506,6 +526,7 @@ diff_one_connection (NMConnection *a,
 	NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (a);
 	GHashTableIter iter;
 	NMSetting *a_setting = NULL;
+	gboolean diff_found = FALSE;
 
 	g_hash_table_iter_init (&iter, priv->settings);
 	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &a_setting)) {
@@ -521,11 +542,14 @@ diff_one_connection (NMConnection *a,
 		if (results)
 			new_results = FALSE;
 
-		if (!nm_setting_diff (a_setting, b_setting, flags, invert_results, &results)) {
-			if (new_results)
-				g_hash_table_insert (diffs, g_strdup (setting_name), results);
-		}
+		if (!nm_setting_diff (a_setting, b_setting, flags, invert_results, &results))
+			diff_found = TRUE;
+
+		if (new_results && results)
+			g_hash_table_insert (diffs, g_strdup (setting_name), results);
 	}
+
+	return diff_found;
 }
 
 /**
@@ -554,12 +578,11 @@ nm_connection_diff (NMConnection *a,
                     GHashTable **out_settings)
 {
 	GHashTable *diffs;
+	gboolean diff_found = FALSE;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (a), FALSE);
-	g_return_val_if_fail (out_settings != NULL, FALSE);
-	g_return_val_if_fail (*out_settings == NULL, FALSE);
-	if (b)
-		g_return_val_if_fail (NM_IS_CONNECTION (b), FALSE);
+	g_return_val_if_fail (!out_settings || !*out_settings, FALSE);
+	g_return_val_if_fail (!b || NM_IS_CONNECTION (b), FALSE);
 
 	if (a == b)
 		return TRUE;
@@ -567,16 +590,22 @@ nm_connection_diff (NMConnection *a,
 	diffs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
 
 	/* Diff A to B, then B to A to capture keys in B that aren't in A */
-	diff_one_connection (a, b, flags, FALSE, diffs);
-	if (b)
-		diff_one_connection (b, a, flags, TRUE, diffs);
+	if (diff_one_connection (a, b, flags, FALSE, diffs))
+		diff_found = TRUE;
+	if (   b
+	    && diff_one_connection (b, a, flags, TRUE, diffs))
+		diff_found = TRUE;
 
-	if (g_hash_table_size (diffs) == 0)
+	nm_assert (diff_found == (g_hash_table_size (diffs) != 0));
+
+	if (g_hash_table_size (diffs) == 0) {
 		g_hash_table_destroy (diffs);
-	else
-		*out_settings = diffs;
+		diffs = NULL;
+	}
+
+	NM_SET_OUT (out_settings, diffs);
 
-	return *out_settings ? FALSE : TRUE;
+	return !diff_found;
 }
 
 NMSetting *
@@ -585,19 +614,31 @@ _nm_connection_find_base_type_setting (NMConnection *connection)
 	NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
 	GHashTableIter iter;
 	NMSetting *setting = NULL, *s_iter;
+	NMSettingPriority setting_prio, s_iter_prio;
 
 	g_hash_table_iter_init (&iter, priv->settings);
 	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &s_iter)) {
-		if (!_nm_setting_is_base_type (s_iter))
+		s_iter_prio = _nm_setting_get_base_type_priority (s_iter);
+		if (s_iter_prio == NM_SETTING_PRIORITY_INVALID)
 			continue;
 
 		if (setting) {
-			/* FIXME: currently, if there is more than one matching base type,
-			 * we cannot detect the base setting.
-			 * See: https://bugzilla.gnome.org/show_bug.cgi?id=696936#c8 */
-			return NULL;
+			if (s_iter_prio > setting_prio) {
+				continue;
+			} else if (s_iter_prio == setting_prio) {
+				NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
+				const char *type;
+
+				if (s_con) {
+					type = nm_setting_connection_get_connection_type (s_con);
+					if (type)
+						return nm_connection_get_setting_by_name (connection, type);
+				}
+				return NULL;
+			}
 		}
 		setting = s_iter;
+		setting_prio = s_iter_prio;
 	}
 	return setting;
 }
@@ -652,6 +693,26 @@ _normalize_connection_type (NMConnection *self)
 }
 
 const char *
+_nm_connection_detect_bluetooth_type (NMConnection *self)
+{
+	NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (self);
+
+	if (   s_bt
+	    && nm_setting_bluetooth_get_connection_type (s_bt)) {
+		if (   nm_connection_get_setting_gsm (self)
+		    || nm_connection_get_setting_cdma (self))
+			return NM_SETTING_BLUETOOTH_TYPE_DUN;
+		if (nm_connection_get_setting_bridge (self))
+			return NM_SETTING_BLUETOOTH_TYPE_NAP;
+		return NM_SETTING_BLUETOOTH_TYPE_PANU;
+	}
+
+	/* NULL means the connection is not a bluetooth type, or it needs
+	 * no normalization, as the type is set explicitly. */
+	return NULL;
+}
+
+const char *
 _nm_connection_detect_slave_type (NMConnection *connection, NMSetting **out_s_port)
 {
 	NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
@@ -668,6 +729,10 @@ _nm_connection_detect_slave_type (NMConnection *connection, NMSetting **out_s_po
 			i_slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
 		else if (!strcmp (name, NM_SETTING_TEAM_PORT_SETTING_NAME))
 			i_slave_type = NM_SETTING_TEAM_SETTING_NAME;
+		else if (!strcmp (name, NM_SETTING_OVS_PORT_SETTING_NAME))
+			i_slave_type = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
+		else if (!strcmp (name, NM_SETTING_OVS_INTERFACE_SETTING_NAME))
+			i_slave_type = NM_SETTING_OVS_PORT_SETTING_NAME;
 		else
 			continue;
 
@@ -748,9 +813,20 @@ _normalize_ethernet_link_neg (NMConnection *self)
 }
 
 static gboolean
+_without_ip_config (NMConnection *self)
+{
+	const char *connection_type = nm_connection_get_connection_type (self);
+
+	g_return_val_if_fail (connection_type, FALSE);
+	if (strcmp (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) == 0)
+		return FALSE;
+
+	return !!nm_setting_connection_get_master (nm_connection_get_setting_connection (self));
+}
+
+static gboolean
 _normalize_ip_config (NMConnection *self, GHashTable *parameters)
 {
-	NMSettingConnection *s_con = nm_connection_get_setting_connection (self);
 	const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
 	const char *default_ip6_method = NULL;
 	NMSettingIPConfig *s_ip4, *s_ip6;
@@ -768,7 +844,7 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters)
 	s_ip6 = nm_connection_get_setting_ip6_config (self);
 	s_proxy = nm_connection_get_setting_proxy (self);
 
-	if (nm_setting_connection_get_master (s_con)) {
+	if (_without_ip_config (self)) {
 		/* Slave connections don't have IP configuration. */
 
 		if (s_ip4)
@@ -1021,15 +1097,62 @@ _normalize_team_port_config (NMConnection *self, GHashTable *parameters)
 }
 
 static gboolean
+_normalize_bluetooth_type (NMConnection *self, GHashTable *parameters)
+{
+	const char *type = _nm_connection_detect_bluetooth_type (self);
+
+	if (type) {
+		g_object_set (nm_connection_get_setting_bluetooth (self),
+		              NM_SETTING_BLUETOOTH_TYPE, type,
+		              NULL);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean
+_normalize_ovs_interface_type (NMConnection *self, GHashTable *parameters)
+{
+	NMSettingOvsInterface *s_ovs_interface = nm_connection_get_setting_ovs_interface (self);
+	gboolean modified;
+	int v;
+
+	if (!s_ovs_interface)
+		return FALSE;
+
+	v = _nm_setting_ovs_interface_verify_interface_type (s_ovs_interface,
+	                                                     self,
+	                                                     TRUE,
+	                                                     &modified,
+	                                                     NULL);
+	if (v != TRUE)
+		g_return_val_if_reached (modified);
+
+	return modified;
+}
+
+static gboolean
 _normalize_required_settings (NMConnection *self, GHashTable *parameters)
 {
+	NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (self);
+	NMSetting *s_bridge;
+	gboolean changed = FALSE;
+
 	if (nm_connection_get_setting_vlan (self)) {
 		if (!nm_connection_get_setting_wired (self)) {
 			nm_connection_add_setting (self, nm_setting_wired_new ());
-			return TRUE;
+			changed = TRUE;
 		}
 	}
-	return FALSE;
+	if (s_bt && nm_streq0 (nm_setting_bluetooth_get_connection_type (s_bt), NM_SETTING_BLUETOOTH_TYPE_NAP)) {
+		if (!nm_connection_get_setting_bridge (self)) {
+			s_bridge = nm_setting_bridge_new ();
+			g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL);
+			nm_connection_add_setting (self, s_bridge);
+			changed = TRUE;
+		}
+	}
+	return changed;
 }
 
 static gboolean
@@ -1165,40 +1288,42 @@ _nm_connection_verify (NMConnection *connection, GError **error)
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	s_proxy = nm_connection_get_setting_proxy (connection);
 
-	if (nm_setting_connection_get_master (s_con)) {
-		if (   NM_IN_SET (normalizable_error_type, NM_SETTING_VERIFY_SUCCESS,
-		                                           NM_SETTING_VERIFY_NORMALIZABLE)
-		    && (s_ip4 || s_ip6 || s_proxy)) {
-			g_clear_error (&normalizable_error);
-			g_set_error_literal (&normalizable_error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_SETTING,
-			                     _("setting not allowed in slave connection"));
-			g_prefix_error (&normalizable_error, "%s: ",
-			                s_ip4
-			                ? NM_SETTING_IP4_CONFIG_SETTING_NAME
-			                : (s_ip6
-			                   ? NM_SETTING_IP6_CONFIG_SETTING_NAME
-			                   : NM_SETTING_PROXY_SETTING_NAME));
-			/* having a slave with IP config *was* and is a verify() error. */
-			normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
-		}
-	} else {
-		if (   NM_IN_SET (normalizable_error_type, NM_SETTING_VERIFY_SUCCESS)
-		    && (!s_ip4 || !s_ip6 || !s_proxy)) {
-			g_set_error_literal (&normalizable_error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_MISSING_SETTING,
-			                     _("setting is required for non-slave connections"));
-			g_prefix_error (&normalizable_error, "%s: ",
-			                !s_ip4
-			                ? NM_SETTING_IP4_CONFIG_SETTING_NAME
-			                : (!s_ip6
-			                   ? NM_SETTING_IP6_CONFIG_SETTING_NAME
-			                   : NM_SETTING_PROXY_SETTING_NAME));
-			/* having a master without IP config was not a verify() error, accept
-			 * it for backward compatibility. */
-			normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE;
+	nm_assert (normalizable_error_type != NM_SETTING_VERIFY_ERROR);
+	if (NM_IN_SET (normalizable_error_type, NM_SETTING_VERIFY_SUCCESS,
+	                                        NM_SETTING_VERIFY_NORMALIZABLE)) {
+		if (_without_ip_config (connection)) {
+			if (s_ip4 || s_ip6 || s_proxy) {
+				g_clear_error (&normalizable_error);
+				g_set_error_literal (&normalizable_error,
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_SETTING,
+				                     _("setting not allowed in slave connection"));
+				g_prefix_error (&normalizable_error, "%s: ",
+				                s_ip4
+				                ? NM_SETTING_IP4_CONFIG_SETTING_NAME
+				                : (s_ip6
+				                   ? NM_SETTING_IP6_CONFIG_SETTING_NAME
+				                   : NM_SETTING_PROXY_SETTING_NAME));
+				/* having a slave with IP config *was* and is a verify() error. */
+				normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+			}
+		} else {
+			if (   normalizable_error_type == NM_SETTING_VERIFY_SUCCESS
+			    && (!s_ip4 || !s_ip6 || !s_proxy)) {
+				g_set_error_literal (&normalizable_error,
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_MISSING_SETTING,
+				                     _("setting is required for non-slave connections"));
+				g_prefix_error (&normalizable_error, "%s: ",
+				                !s_ip4
+				                ? NM_SETTING_IP4_CONFIG_SETTING_NAME
+				                : (!s_ip6
+				                   ? NM_SETTING_IP6_CONFIG_SETTING_NAME
+				                   : NM_SETTING_PROXY_SETTING_NAME));
+				/* having a master without IP config was not a verify() error, accept
+				 * it for backward compatibility. */
+				normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE;
+			}
 		}
 	}
 
@@ -1309,6 +1434,8 @@ nm_connection_normalize (NMConnection *connection,
 	was_modified |= _normalize_wireless_mac_address_randomization (connection, parameters);
 	was_modified |= _normalize_team_config (connection, parameters);
 	was_modified |= _normalize_team_port_config (connection, parameters);
+	was_modified |= _normalize_bluetooth_type (connection, parameters);
+	was_modified |= _normalize_ovs_interface_type (connection, parameters);
 
 	/* Verify anew. */
 	success = _nm_connection_verify (connection, error);
@@ -1326,6 +1453,7 @@ nm_connection_normalize (NMConnection *connection,
 			                     NM_CONNECTION_ERROR_FAILED,
 			                     _("Unexpected failure to normalize the connection"));
 		}
+		g_warning ("connection did not verify after normalization: %s", error ? (*error)->message : "??");
 		g_return_val_if_reached (FALSE);
 	}
 
@@ -1640,19 +1768,9 @@ nm_connection_to_dbus (NMConnection *connection,
 gboolean
 nm_connection_is_type (NMConnection *connection, const char *type)
 {
-	NMSettingConnection *s_con;
-	const char *type2;
-
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
-	g_return_val_if_fail (type != NULL, FALSE);
+	g_return_val_if_fail (type, FALSE);
 
-	s_con = nm_connection_get_setting_connection (connection);
-	if (!s_con)
-		return FALSE;
-
-	type2 = nm_setting_connection_get_connection_type (s_con);
-
-	return (g_strcmp0 (type2, type) == 0);
+	return nm_streq0 (type, nm_connection_get_connection_type (connection));
 }
 
 static int
@@ -1669,52 +1787,82 @@ _for_each_sort (NMSetting **p_a, NMSetting **p_b, void *unused)
 }
 
 /**
- * nm_connection_for_each_setting_value:
- * @connection: the #NMConnection
- * @func: (scope call): user-supplied function called for each setting's property
- * @user_data: user data passed to @func at each invocation
+ * nm_connection_get_settings:
+ * @connection: the #NMConnection instance
+ * @out_length: (allow-none): (out): the length of the returned array
  *
- * Iterates over the properties of each #NMSetting object in the #NMConnection,
- * calling the supplied user function for each property.
- **/
-void
-nm_connection_for_each_setting_value (NMConnection *connection,
-                                      NMSettingValueIterFn func,
-                                      gpointer user_data)
+ * Retrieves the settings in @connection.
+ *
+ * The returned array is %NULL-terminated.
+ *
+ * Returns: (array length=out_length) (transfer container): a
+ *   %NULL-terminated array containing every setting of
+ *   @connection.
+ *   If the connection has no settings, %NULL is returned.
+ *
+ * Since: 1.10
+ */
+NMSetting **
+nm_connection_get_settings (NMConnection *connection,
+                            guint *out_length)
 {
 	NMConnectionPrivate *priv;
-	gs_free NMSetting **arr_free = NULL;
-	NMSetting *arr_temp[20], **arr;
+	NMSetting **arr;
 	GHashTableIter iter;
-	gpointer value;
+	NMSetting *setting;
 	guint i, size;
 
-	g_return_if_fail (NM_IS_CONNECTION (connection));
-	g_return_if_fail (func != NULL);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 
 	priv = NM_CONNECTION_GET_PRIVATE (connection);
 
 	size = g_hash_table_size (priv->settings);
-	if (!size)
-		return;
 
-	if (size > G_N_ELEMENTS (arr_temp))
-		arr = arr_free = g_new (NMSetting *, size);
-	else
-		arr = arr_temp;
+	if (!size) {
+		NM_SET_OUT (out_length, 0);
+		return NULL;
+	}
+
+	arr = g_new (NMSetting *, size + 1);
 
 	g_hash_table_iter_init (&iter, priv->settings);
-	for (i = 0; g_hash_table_iter_next (&iter, NULL, &value); i++)
-		arr[i] = NM_SETTING (value);
-	g_assert (i == size);
+	for (i = 0; g_hash_table_iter_next (&iter, NULL, (gpointer *) &setting); i++)
+		arr[i] = setting;
+	nm_assert (i == size);
+	arr[size] = NULL;
 
 	/* sort the settings. This has an effect on the order in which keyfile
 	 * prints them. */
 	if (size > 1)
 		g_qsort_with_data (arr, size, sizeof (NMSetting *), (GCompareDataFunc) _for_each_sort, NULL);
 
-	for (i = 0; i < size; i++)
-		nm_setting_enumerate_values (arr[i], func, user_data);
+	NM_SET_OUT (out_length, size);
+	return arr;
+}
+
+/**
+ * nm_connection_for_each_setting_value:
+ * @connection: the #NMConnection
+ * @func: (scope call): user-supplied function called for each setting's property
+ * @user_data: user data passed to @func at each invocation
+ *
+ * Iterates over the properties of each #NMSetting object in the #NMConnection,
+ * calling the supplied user function for each property.
+ **/
+void
+nm_connection_for_each_setting_value (NMConnection *connection,
+                                      NMSettingValueIterFn func,
+                                      gpointer user_data)
+{
+	gs_free NMSetting **settings = NULL;
+	guint i, length = 0;
+
+	g_return_if_fail (NM_IS_CONNECTION (connection));
+	g_return_if_fail (func);
+
+	settings = nm_connection_get_settings (connection, &length);
+	for (i = 0; i < length; i++)
+		nm_setting_enumerate_values (settings[i], func, user_data);
 }
 
 /**
@@ -1764,10 +1912,7 @@ nm_connection_set_path (NMConnection *connection, const char *path)
 	priv = NM_CONNECTION_GET_PRIVATE (connection);
 
 	g_free (priv->path);
-	priv->path = NULL;
-
-	if (path)
-		priv->path = g_strdup (path);
+	priv->path = g_strdup (path);
 }
 
 /**
@@ -1805,10 +1950,7 @@ nm_connection_get_interface_name (NMConnection *connection)
 {
 	NMSettingConnection *s_con;
 
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
 	s_con = nm_connection_get_setting_connection (connection);
-
 	return s_con ? nm_setting_connection_get_interface_name (s_con) : NULL;
 }
 
@@ -1818,6 +1960,9 @@ _nm_connection_verify_required_interface_name (NMConnection *connection,
 {
 	const char *interface_name;
 
+	if (!connection)
+		return TRUE;
+
 	interface_name = nm_connection_get_interface_name (connection);
 	if (interface_name)
 		return TRUE;
@@ -1843,13 +1988,8 @@ nm_connection_get_uuid (NMConnection *connection)
 {
 	NMSettingConnection *s_con;
 
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
 	s_con = nm_connection_get_setting_connection (connection);
-	if (!s_con)
-		return NULL;
-
-	return nm_setting_connection_get_uuid (s_con);
+	return s_con ? nm_setting_connection_get_uuid (s_con) : NULL;
 }
 
 /**
@@ -1865,13 +2005,8 @@ nm_connection_get_id (NMConnection *connection)
 {
 	NMSettingConnection *s_con;
 
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
 	s_con = nm_connection_get_setting_connection (connection);
-	if (!s_con)
-		return NULL;
-
-	return nm_setting_connection_get_id (s_con);
+	return s_con ? nm_setting_connection_get_id (s_con) : NULL;
 }
 
 /**
@@ -1887,13 +2022,8 @@ nm_connection_get_connection_type (NMConnection *connection)
 {
 	NMSettingConnection *s_con;
 
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
 	s_con = nm_connection_get_setting_connection (connection);
-	if (!s_con)
-		return NULL;
-
-	return nm_setting_connection_get_connection_type (s_con);
+	return s_con ? nm_setting_connection_get_connection_type (s_con) : NULL;
 }
 
 /**
@@ -1911,7 +2041,8 @@ nm_connection_is_virtual (NMConnection *connection)
 	const char *type;
 
 	type = nm_connection_get_connection_type (connection);
-	g_return_val_if_fail (type != NULL, FALSE);
+	if (!type)
+		return FALSE;
 
 	if (   !strcmp (type, NM_SETTING_BOND_SETTING_NAME)
 	    || !strcmp (type, NM_SETTING_DUMMY_SETTING_NAME)
@@ -1922,6 +2053,9 @@ nm_connection_is_virtual (NMConnection *connection)
 	    || !strcmp (type, NM_SETTING_IP_TUNNEL_SETTING_NAME)
 	    || !strcmp (type, NM_SETTING_MACSEC_SETTING_NAME)
 	    || !strcmp (type, NM_SETTING_MACVLAN_SETTING_NAME)
+	    || !strcmp (type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)
+	    || !strcmp (type, NM_SETTING_OVS_INTERFACE_SETTING_NAME)
+	    || !strcmp (type, NM_SETTING_OVS_PORT_SETTING_NAME)
 	    || !strcmp (type, NM_SETTING_VXLAN_SETTING_NAME))
 		return TRUE;
 
@@ -1929,8 +2063,17 @@ nm_connection_is_virtual (NMConnection *connection)
 		NMSettingInfiniband *s_ib;
 
 		s_ib = nm_connection_get_setting_infiniband (connection);
-		g_return_val_if_fail (s_ib != NULL, FALSE);
-		return nm_setting_infiniband_get_virtual_interface_name (s_ib) != NULL;
+		return s_ib && nm_setting_infiniband_get_virtual_interface_name (s_ib);
+	}
+
+	if (nm_streq (type, NM_SETTING_BLUETOOTH_SETTING_NAME))
+		return !!_nm_connection_get_setting_bluetooth_for_nap (connection);
+
+	if (nm_streq (type, NM_SETTING_PPPOE_SETTING_NAME)) {
+		NMSettingPppoe *s_pppoe;
+
+		s_pppoe = nm_connection_get_setting_pppoe (connection);
+		return !!nm_setting_pppoe_get_parent (s_pppoe);
 	}
 
 	return FALSE;
@@ -1953,10 +2096,11 @@ nm_connection_get_virtual_device_description (NMConnection *connection)
 	const char *type;
 	const char *iface = NULL, *display_type = NULL;
 
-	iface = nm_connection_get_interface_name (connection);
-
 	type = nm_connection_get_connection_type (connection);
-	g_return_val_if_fail (type != NULL, FALSE);
+	if (!type)
+		return NULL;
+
+	iface = nm_connection_get_interface_name (connection);
 
 	if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME))
 		display_type = _("Bond");
@@ -1991,9 +2135,7 @@ nm_connection_get_virtual_device_description (NMConnection *connection)
 NMSetting8021x *
 nm_connection_get_setting_802_1x (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_802_1X);
 }
 
 /**
@@ -2007,9 +2149,7 @@ nm_connection_get_setting_802_1x (NMConnection *connection)
 NMSettingBluetooth *
 nm_connection_get_setting_bluetooth (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingBluetooth *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_BLUETOOTH);
 }
 
 /**
@@ -2023,9 +2163,7 @@ nm_connection_get_setting_bluetooth (NMConnection *connection)
 NMSettingBond *
 nm_connection_get_setting_bond (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingBond *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BOND);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_BOND);
 }
 
 /**
@@ -2039,9 +2177,7 @@ nm_connection_get_setting_bond (NMConnection *connection)
 NMSettingTeam *
 nm_connection_get_setting_team (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingTeam *) nm_connection_get_setting (connection, NM_TYPE_SETTING_TEAM);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_TEAM);
 }
 
 /**
@@ -2055,9 +2191,7 @@ nm_connection_get_setting_team (NMConnection *connection)
 NMSettingTeamPort *
 nm_connection_get_setting_team_port (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingTeamPort *) nm_connection_get_setting (connection, NM_TYPE_SETTING_TEAM_PORT);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_TEAM_PORT);
 }
 
 /**
@@ -2071,9 +2205,7 @@ nm_connection_get_setting_team_port (NMConnection *connection)
 NMSettingBridge *
 nm_connection_get_setting_bridge (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingBridge *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BRIDGE);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_BRIDGE);
 }
 
 /**
@@ -2087,9 +2219,7 @@ nm_connection_get_setting_bridge (NMConnection *connection)
 NMSettingCdma *
 nm_connection_get_setting_cdma (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_CDMA);
 }
 
 /**
@@ -2103,9 +2233,7 @@ nm_connection_get_setting_cdma (NMConnection *connection)
 NMSettingConnection *
 nm_connection_get_setting_connection (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_CONNECTION);
 }
 
 /**
@@ -2119,9 +2247,7 @@ nm_connection_get_setting_connection (NMConnection *connection)
 NMSettingDcb *
 nm_connection_get_setting_dcb (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingDcb *) nm_connection_get_setting (connection, NM_TYPE_SETTING_DCB);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_DCB);
 }
 
 /**
@@ -2137,9 +2263,7 @@ nm_connection_get_setting_dcb (NMConnection *connection)
 NMSettingDummy *
 nm_connection_get_setting_dummy (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingDummy *) nm_connection_get_setting (connection, NM_TYPE_SETTING_DUMMY);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_DUMMY);
 }
 
 /**
@@ -2153,9 +2277,7 @@ nm_connection_get_setting_dummy (NMConnection *connection)
 NMSettingGeneric *
 nm_connection_get_setting_generic (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingGeneric *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GENERIC);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_GENERIC);
 }
 
 /**
@@ -2169,9 +2291,7 @@ nm_connection_get_setting_generic (NMConnection *connection)
 NMSettingGsm *
 nm_connection_get_setting_gsm (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_GSM);
 }
 
 /**
@@ -2185,9 +2305,7 @@ nm_connection_get_setting_gsm (NMConnection *connection)
 NMSettingInfiniband *
 nm_connection_get_setting_infiniband (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingInfiniband *) nm_connection_get_setting (connection, NM_TYPE_SETTING_INFINIBAND);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_INFINIBAND);
 }
 
 /**
@@ -2206,9 +2324,7 @@ nm_connection_get_setting_infiniband (NMConnection *connection)
 NMSettingIPConfig *
 nm_connection_get_setting_ip4_config (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingIPConfig *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_IP4_CONFIG);
 }
 
 /**
@@ -2224,9 +2340,7 @@ nm_connection_get_setting_ip4_config (NMConnection *connection)
 NMSettingIPTunnel *
 nm_connection_get_setting_ip_tunnel (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingIPTunnel *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP_TUNNEL);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_IP_TUNNEL);
 }
 
 /**
@@ -2245,9 +2359,7 @@ nm_connection_get_setting_ip_tunnel (NMConnection *connection)
 NMSettingIPConfig *
 nm_connection_get_setting_ip6_config (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingIPConfig *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_IP6_CONFIG);
 }
 
 /**
@@ -2263,9 +2375,7 @@ nm_connection_get_setting_ip6_config (NMConnection *connection)
 NMSettingMacsec *
 nm_connection_get_setting_macsec (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingMacsec *) nm_connection_get_setting (connection, NM_TYPE_SETTING_MACSEC);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_MACSEC);
 }
 
 /**
@@ -2281,9 +2391,7 @@ nm_connection_get_setting_macsec (NMConnection *connection)
 NMSettingMacvlan *
 nm_connection_get_setting_macvlan (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingMacvlan *) nm_connection_get_setting (connection, NM_TYPE_SETTING_MACVLAN);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_MACVLAN);
 }
 
 /**
@@ -2297,9 +2405,71 @@ nm_connection_get_setting_macvlan (NMConnection *connection)
 NMSettingOlpcMesh *
 nm_connection_get_setting_olpc_mesh (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_OLPC_MESH);
+}
 
-	return (NMSettingOlpcMesh *) nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH);
+/**
+ * nm_connection_get_setting_ovs_bridge:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingOvsBridge the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingOvsBridge if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.10
+ **/
+NMSettingOvsBridge *
+nm_connection_get_setting_ovs_bridge (NMConnection *connection)
+{
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_BRIDGE);
+}
+
+/**
+ * nm_connection_get_setting_ovs_interface:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingOvsInterface the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingOvsInterface if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.10
+ **/
+NMSettingOvsInterface *
+nm_connection_get_setting_ovs_interface (NMConnection *connection)
+{
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_INTERFACE);
+}
+
+/**
+ * nm_connection_get_setting_ovs_patch:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingOvsPatch the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingOvsPatch if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.10
+ **/
+NMSettingOvsPatch *
+nm_connection_get_setting_ovs_patch (NMConnection *connection)
+{
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_PATCH);
+}
+ 
+/**
+ * nm_connection_get_setting_ovs_port:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingOvsPort the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingOvsPort if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.10
+ **/
+NMSettingOvsPort *
+nm_connection_get_setting_ovs_port (NMConnection *connection)
+{
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_PORT);
 }
 
 /**
@@ -2313,9 +2483,7 @@ nm_connection_get_setting_olpc_mesh (NMConnection *connection)
 NMSettingPpp *
 nm_connection_get_setting_ppp (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingPpp *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_PPP);
 }
 
 /**
@@ -2329,9 +2497,7 @@ nm_connection_get_setting_ppp (NMConnection *connection)
 NMSettingPppoe *
 nm_connection_get_setting_pppoe (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingPppoe *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_PPPOE);
 }
 
 /**
@@ -2347,9 +2513,7 @@ nm_connection_get_setting_pppoe (NMConnection *connection)
 NMSettingProxy *
 nm_connection_get_setting_proxy (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingProxy *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PROXY);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_PROXY);
 }
 
 /**
@@ -2363,9 +2527,7 @@ nm_connection_get_setting_proxy (NMConnection *connection)
 NMSettingSerial *
 nm_connection_get_setting_serial (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingSerial *) nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_SERIAL);
 }
 
 /**
@@ -2381,9 +2543,7 @@ nm_connection_get_setting_serial (NMConnection *connection)
 NMSettingTun *
 nm_connection_get_setting_tun (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingTun *) nm_connection_get_setting (connection, NM_TYPE_SETTING_TUN);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_TUN);
 }
 
 /**
@@ -2397,9 +2557,7 @@ nm_connection_get_setting_tun (NMConnection *connection)
 NMSettingVpn *
 nm_connection_get_setting_vpn (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingVpn *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_VPN);
 }
 
 /**
@@ -2415,9 +2573,7 @@ nm_connection_get_setting_vpn (NMConnection *connection)
 NMSettingVxlan *
 nm_connection_get_setting_vxlan (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingVxlan *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VXLAN);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_VXLAN);
 }
 
 /**
@@ -2431,9 +2587,7 @@ nm_connection_get_setting_vxlan (NMConnection *connection)
 NMSettingWimax *
 nm_connection_get_setting_wimax (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingWimax *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIMAX);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_WIMAX);
 }
 
 /**
@@ -2447,9 +2601,7 @@ nm_connection_get_setting_wimax (NMConnection *connection)
 NMSettingWired *
 nm_connection_get_setting_wired (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_WIRED);
 }
 
 /**
@@ -2463,9 +2615,7 @@ nm_connection_get_setting_wired (NMConnection *connection)
 NMSettingAdsl *
 nm_connection_get_setting_adsl (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingAdsl *) nm_connection_get_setting (connection, NM_TYPE_SETTING_ADSL);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_ADSL);
 }
 
 /**
@@ -2479,9 +2629,7 @@ nm_connection_get_setting_adsl (NMConnection *connection)
 NMSettingWireless *
 nm_connection_get_setting_wireless (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_WIRELESS);
 }
 
 /**
@@ -2495,9 +2643,7 @@ nm_connection_get_setting_wireless (NMConnection *connection)
 NMSettingWirelessSecurity *
 nm_connection_get_setting_wireless_security (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
 }
 
 /**
@@ -2511,9 +2657,7 @@ nm_connection_get_setting_wireless_security (NMConnection *connection)
 NMSettingBridgePort *
 nm_connection_get_setting_bridge_port (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	return (NMSettingBridgePort *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BRIDGE_PORT);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_BRIDGE_PORT);
 }
 
 /**
@@ -2527,9 +2671,18 @@ nm_connection_get_setting_bridge_port (NMConnection *connection)
 NMSettingVlan *
 nm_connection_get_setting_vlan (NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_VLAN);
+}
+
+NMSettingBluetooth *
+_nm_connection_get_setting_bluetooth_for_nap (NMConnection *connection)
+{
+	NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (connection);
 
-	return (NMSettingVlan *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VLAN);
+	if (   s_bt
+	    && nm_streq0 (nm_setting_bluetooth_get_connection_type (s_bt), NM_SETTING_BLUETOOTH_TYPE_NAP))
+		return s_bt;
+	return NULL;
 }
 
 /*****************************************************************************/
@@ -2557,7 +2710,7 @@ nm_connection_get_private (NMConnection *connection)
 	key = NM_CACHED_QUARK ("NMConnectionPrivate");
 
 	priv = g_object_get_qdata ((GObject *) connection, key);
-	if (!priv) {
+	if (G_UNLIKELY (!priv)) {
 		priv = g_slice_new0 (NMConnectionPrivate);
 		g_object_set_qdata_full ((GObject *) connection, key,
 		                         priv, (GDestroyNotify) nm_connection_private_free);
diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h
index fae9862a..c6b23a00 100644
--- a/libnm-core/nm-connection.h
+++ b/libnm-core/nm-connection.h
@@ -179,6 +179,10 @@ void          nm_connection_for_each_setting_value (NMConnection *connection,
                                                     NMSettingValueIterFn func,
                                                     gpointer user_data);
 
+NM_AVAILABLE_IN_1_10
+NMSetting **  nm_connection_get_settings (NMConnection *connection,
+                                          guint *out_length);
+
 void          nm_connection_dump          (NMConnection *connection);
 
 /* Helpers */
@@ -213,6 +217,13 @@ NMSettingMacsec *          nm_connection_get_setting_macsec            (NMConnec
 NM_AVAILABLE_IN_1_2
 NMSettingMacvlan *         nm_connection_get_setting_macvlan           (NMConnection *connection);
 NMSettingOlpcMesh *        nm_connection_get_setting_olpc_mesh         (NMConnection *connection);
+NM_AVAILABLE_IN_1_10
+NMSettingOvsBridge *       nm_connection_get_setting_ovs_bridge        (NMConnection *connection);
+NM_AVAILABLE_IN_1_10
+NMSettingOvsInterface *    nm_connection_get_setting_ovs_interface     (NMConnection *connection);
+NMSettingOvsPatch *        nm_connection_get_setting_ovs_patch         (NMConnection *connection);
+NM_AVAILABLE_IN_1_10
+NMSettingOvsPort *         nm_connection_get_setting_ovs_port          (NMConnection *connection);
 NMSettingPpp *             nm_connection_get_setting_ppp               (NMConnection *connection);
 NMSettingPppoe *           nm_connection_get_setting_pppoe             (NMConnection *connection);
 NM_AVAILABLE_IN_1_6
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 91967ce3..59ebfcb1 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -56,6 +56,10 @@
 #include "nm-setting-macsec.h"
 #include "nm-setting-macvlan.h"
 #include "nm-setting-olpc-mesh.h"
+#include "nm-setting-ovs-bridge.h"
+#include "nm-setting-ovs-interface.h"
+#include "nm-setting-ovs-patch.h"
+#include "nm-setting-ovs-port.h"
 #include "nm-setting-ppp.h"
 #include "nm-setting-pppoe.h"
 #include "nm-setting-serial.h"
@@ -142,7 +146,46 @@ NMConnection *_nm_simple_connection_new_from_dbus (GVariant      *dict,
                                                    NMSettingParseFlags parse_flags,
                                                    GError       **error);
 
-guint32 _nm_setting_get_setting_priority (NMSetting *setting);
+/*
+ * A setting's priority should roughly follow the OSI layer model, but it also
+ * controls which settings get asked for secrets first.  Thus settings which
+ * relate to things that must be working first, like hardware, should get a
+ * higher priority than things which layer on top of the hardware.  For example,
+ * the GSM/CDMA settings should provide secrets before the PPP setting does,
+ * because a PIN is required to unlock the device before PPP can even start.
+ * Even settings without secrets should be assigned the right priority.
+ *
+ * 0: reserved for invalid
+ *
+ * 1: reserved for the Connection setting
+ *
+ * 2,3: hardware-related settings like Ethernet, Wi-Fi, InfiniBand, Bridge, etc.
+ * These priority 1 settings are also "base types", which means that at least
+ * one of them is required for the connection to be valid, and their name is
+ * valid in the 'type' property of the Connection setting.
+ *
+ * 4: hardware-related auxiliary settings that require a base setting to be
+ * successful first, like Wi-Fi security, 802.1x, etc.
+ *
+ * 5: hardware-independent settings that are required before IP connectivity
+ * can be established, like PPP, PPPoE, etc.
+ *
+ * 6: IP-level stuff
+ *
+ * 10: NMSettingUser
+ */
+typedef enum { /*< skip >*/
+	NM_SETTING_PRIORITY_INVALID     = 0,
+	NM_SETTING_PRIORITY_CONNECTION  = 1,
+	NM_SETTING_PRIORITY_HW_BASE     = 2,
+	NM_SETTING_PRIORITY_HW_NON_BASE = 3,
+	NM_SETTING_PRIORITY_HW_AUX      = 4,
+	NM_SETTING_PRIORITY_AUX         = 5,
+	NM_SETTING_PRIORITY_IP          = 6,
+	NM_SETTING_PRIORITY_USER        = 10,
+} NMSettingPriority;
+
+NMSettingPriority _nm_setting_get_setting_priority (NMSetting *setting);
 
 gboolean _nm_setting_get_property (NMSetting *setting, const char *name, GValue *value);
 
@@ -159,6 +202,18 @@ GHashTable *_nm_utils_copy_strdict (GHashTable *strdict);
 
 typedef gpointer (*NMUtilsCopyFunc) (gpointer);
 
+gboolean _nm_ip_route_attribute_validate_all (const NMIPRoute *route);
+const char **_nm_ip_route_get_attribute_names (const NMIPRoute *route, gboolean sorted, guint *out_length);
+GHashTable *_nm_ip_route_get_attributes_direct (NMIPRoute *route);
+
+static inline void
+_nm_auto_ip_route_unref (NMIPRoute **v)
+{
+	if (*v)
+		nm_ip_route_unref (*v);
+}
+#define nm_auto_ip_route_unref nm_auto (_nm_auto_ip_route_unref)
+
 GPtrArray *_nm_utils_copy_slist_to_array (const GSList *list,
                                           NMUtilsCopyFunc copy_func,
                                           GDestroyNotify unref_func);
@@ -175,11 +230,6 @@ gssize _nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpoi
 gssize _nm_utils_ptrarray_find_binary_search (gconstpointer *list, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data);
 gssize _nm_utils_array_find_binary_search (gconstpointer list, gsize elem_size, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data);
 
-char **_nm_utils_strv_cleanup (char **strv,
-                               gboolean strip_whitespace,
-                               gboolean skip_empty,
-                               gboolean skip_repeated);
-
 char **     _nm_utils_strsplit_set (const char *str,
                                     const char *delimiters,
                                     int max_tokens);
@@ -204,14 +254,12 @@ gboolean _nm_utils_check_module_file (const char *name,
                                       gpointer user_data,
                                       GError **error);
 
-char *_nm_utils_enum_to_str_full (GType type, int value, const char *sep);
-
 #define NM_UTILS_UUID_TYPE_LEGACY            0
 #define NM_UTILS_UUID_TYPE_VARIANT3          1
 
 char *nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, gpointer type_args);
 
-/* arbitrarily choosen namespace UUID for _nm_utils_uuid_generate_from_strings() */
+/* arbitrarily chosen namespace UUID for _nm_utils_uuid_generate_from_strings() */
 #define NM_UTILS_UUID_NS "b425e9fb-7598-44b4-9e3b-5a2e3aaa4905"
 
 char *_nm_utils_uuid_generate_from_strings (const char *string1, ...) G_GNUC_NULL_TERMINATED;
@@ -294,7 +342,7 @@ extern const NMUtilsDNSOptionDesc _nm_utils_dns_option_descs[];
 gboolean    _nm_utils_dns_option_validate (const char *option, char **out_name,
                                            long *out_value, gboolean ipv6,
                                            const NMUtilsDNSOptionDesc *option_descs);
-int         _nm_utils_dns_option_find_idx (GPtrArray *array, const char *option);
+gssize      _nm_utils_dns_option_find_idx (GPtrArray *array, const char *option);
 
 /*****************************************************************************/
 
@@ -377,6 +425,10 @@ gboolean _nm_setting_bond_option_supported (const char *option, NMBondMode mode)
 
 /*****************************************************************************/
 
+NMSettingBluetooth *_nm_connection_get_setting_bluetooth_for_nap (NMConnection *connection);
+
+/*****************************************************************************/
+
 gboolean _nm_utils_inet6_is_token (const struct in6_addr *in6addr);
 
 /*****************************************************************************/
@@ -385,4 +437,16 @@ gboolean    _nm_utils_team_config_equal (const char *conf1, const char *conf2, g
 
 /*****************************************************************************/
 
+static inline int
+nm_setting_ip_config_get_addr_family (NMSettingIPConfig *s_ip)
+{
+	if (NM_IS_SETTING_IP4_CONFIG (s_ip))
+		return AF_INET;
+	if (NM_IS_SETTING_IP6_CONFIG (s_ip))
+		return AF_INET6;
+	g_return_val_if_reached (AF_UNSPEC);
+}
+
+/*****************************************************************************/
+
 #endif
diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h
index 3b9ec37b..ae680b7a 100644
--- a/libnm-core/nm-core-types.h
+++ b/libnm-core/nm-core-types.h
@@ -50,6 +50,10 @@ typedef struct _NMSettingIP6Config        NMSettingIP6Config;
 typedef struct _NMSettingMacsec           NMSettingMacsec;
 typedef struct _NMSettingMacvlan          NMSettingMacvlan;
 typedef struct _NMSettingOlpcMesh         NMSettingOlpcMesh;
+typedef struct _NMSettingOvsBridge        NMSettingOvsBridge;
+typedef struct _NMSettingOvsInterface     NMSettingOvsInterface;
+typedef struct _NMSettingOvsPatch         NMSettingOvsPatch;
+typedef struct _NMSettingOvsPort          NMSettingOvsPort;
 typedef struct _NMSettingPpp              NMSettingPpp;
 typedef struct _NMSettingPppoe            NMSettingPppoe;
 typedef struct _NMSettingSerial           NMSettingSerial;
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index 4c8e46fc..7f75c849 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -14,7 +14,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2004 - 2014 Red Hat, Inc.
+ * Copyright 2004 - 2017 Red Hat, Inc.
  */
 
 /* Definitions related to NetworkManager's D-Bus interfaces.
@@ -36,42 +36,46 @@
 /*
  * dbus services details
  */
-#define NM_DBUS_SERVICE                     "org.freedesktop.NetworkManager"
-
-#define NM_DBUS_PATH                        "/org/freedesktop/NetworkManager"
-#define NM_DBUS_INTERFACE                   "org.freedesktop.NetworkManager"
-#define NM_DBUS_INTERFACE_DEVICE            NM_DBUS_INTERFACE ".Device"
-#define NM_DBUS_INTERFACE_DEVICE_WIRED      NM_DBUS_INTERFACE_DEVICE ".Wired"
-#define NM_DBUS_INTERFACE_DEVICE_ADSL       NM_DBUS_INTERFACE_DEVICE ".Adsl"
-#define NM_DBUS_INTERFACE_DEVICE_WIRELESS   NM_DBUS_INTERFACE_DEVICE ".Wireless"
-#define NM_DBUS_INTERFACE_DEVICE_BLUETOOTH  NM_DBUS_INTERFACE_DEVICE ".Bluetooth"
-#define NM_DBUS_INTERFACE_DEVICE_OLPC_MESH  NM_DBUS_INTERFACE_DEVICE ".OlpcMesh"
-#define NM_DBUS_PATH_ACCESS_POINT           NM_DBUS_PATH "/AccessPoint"
-#define NM_DBUS_INTERFACE_ACCESS_POINT      NM_DBUS_INTERFACE ".AccessPoint"
-#define NM_DBUS_INTERFACE_DEVICE_MODEM      NM_DBUS_INTERFACE_DEVICE ".Modem"
-#define NM_DBUS_INTERFACE_DEVICE_WIMAX      NM_DBUS_INTERFACE_DEVICE ".WiMax"
-#define NM_DBUS_INTERFACE_WIMAX_NSP         NM_DBUS_INTERFACE ".WiMax.Nsp"
-#define NM_DBUS_PATH_WIMAX_NSP              NM_DBUS_PATH "/Nsp"
-#define NM_DBUS_INTERFACE_ACTIVE_CONNECTION NM_DBUS_INTERFACE ".Connection.Active"
-#define NM_DBUS_INTERFACE_IP4_CONFIG        NM_DBUS_INTERFACE ".IP4Config"
-#define NM_DBUS_INTERFACE_DHCP4_CONFIG      NM_DBUS_INTERFACE ".DHCP4Config"
-#define NM_DBUS_INTERFACE_IP6_CONFIG        NM_DBUS_INTERFACE ".IP6Config"
-#define NM_DBUS_INTERFACE_DHCP6_CONFIG      NM_DBUS_INTERFACE ".DHCP6Config"
-#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband"
-#define NM_DBUS_INTERFACE_DEVICE_BOND       NM_DBUS_INTERFACE_DEVICE ".Bond"
-#define NM_DBUS_INTERFACE_DEVICE_DUMMY      NM_DBUS_INTERFACE_DEVICE ".Dummy"
-#define NM_DBUS_INTERFACE_DEVICE_TEAM       NM_DBUS_INTERFACE_DEVICE ".Team"
-#define NM_DBUS_INTERFACE_DEVICE_VLAN       NM_DBUS_INTERFACE_DEVICE ".Vlan"
-#define NM_DBUS_INTERFACE_DEVICE_BRIDGE     NM_DBUS_INTERFACE_DEVICE ".Bridge"
-#define NM_DBUS_INTERFACE_DEVICE_GENERIC    NM_DBUS_INTERFACE_DEVICE ".Generic"
-#define NM_DBUS_INTERFACE_DEVICE_VETH       NM_DBUS_INTERFACE_DEVICE ".Veth"
-#define NM_DBUS_INTERFACE_DEVICE_TUN        NM_DBUS_INTERFACE_DEVICE ".Tun"
-#define NM_DBUS_INTERFACE_DEVICE_MACSEC     NM_DBUS_INTERFACE_DEVICE ".Macsec"
-#define NM_DBUS_INTERFACE_DEVICE_MACVLAN    NM_DBUS_INTERFACE_DEVICE ".Macvlan"
-#define NM_DBUS_INTERFACE_DEVICE_VXLAN      NM_DBUS_INTERFACE_DEVICE ".Vxlan"
-#define NM_DBUS_INTERFACE_DEVICE_GRE        NM_DBUS_INTERFACE_DEVICE ".Gre"
-#define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL  NM_DBUS_INTERFACE_DEVICE ".IPTunnel"
-#define NM_DBUS_INTERFACE_DEVICE_STATISTICS NM_DBUS_INTERFACE_DEVICE ".Statistics"
+#define NM_DBUS_SERVICE                        "org.freedesktop.NetworkManager"
+
+#define NM_DBUS_PATH                           "/org/freedesktop/NetworkManager"
+#define NM_DBUS_INTERFACE                      "org.freedesktop.NetworkManager"
+#define NM_DBUS_INTERFACE_DEVICE               NM_DBUS_INTERFACE ".Device"
+#define NM_DBUS_INTERFACE_DEVICE_WIRED         NM_DBUS_INTERFACE_DEVICE ".Wired"
+#define NM_DBUS_INTERFACE_DEVICE_ADSL          NM_DBUS_INTERFACE_DEVICE ".Adsl"
+#define NM_DBUS_INTERFACE_DEVICE_WIRELESS      NM_DBUS_INTERFACE_DEVICE ".Wireless"
+#define NM_DBUS_INTERFACE_DEVICE_BLUETOOTH     NM_DBUS_INTERFACE_DEVICE ".Bluetooth"
+#define NM_DBUS_INTERFACE_DEVICE_OLPC_MESH     NM_DBUS_INTERFACE_DEVICE ".OlpcMesh"
+#define NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE NM_DBUS_INTERFACE_DEVICE ".OvsInterface"
+#define NM_DBUS_INTERFACE_DEVICE_OVS_PORT      NM_DBUS_INTERFACE_DEVICE ".OvsPort"
+#define NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE    NM_DBUS_INTERFACE_DEVICE ".OvsBridge"
+#define NM_DBUS_PATH_ACCESS_POINT              NM_DBUS_PATH "/AccessPoint"
+#define NM_DBUS_INTERFACE_ACCESS_POINT         NM_DBUS_INTERFACE ".AccessPoint"
+#define NM_DBUS_INTERFACE_DEVICE_MODEM         NM_DBUS_INTERFACE_DEVICE ".Modem"
+#define NM_DBUS_INTERFACE_DEVICE_WIMAX         NM_DBUS_INTERFACE_DEVICE ".WiMax"
+#define NM_DBUS_INTERFACE_WIMAX_NSP            NM_DBUS_INTERFACE ".WiMax.Nsp"
+#define NM_DBUS_PATH_WIMAX_NSP                 NM_DBUS_PATH "/Nsp"
+#define NM_DBUS_INTERFACE_ACTIVE_CONNECTION    NM_DBUS_INTERFACE ".Connection.Active"
+#define NM_DBUS_INTERFACE_IP4_CONFIG           NM_DBUS_INTERFACE ".IP4Config"
+#define NM_DBUS_INTERFACE_DHCP4_CONFIG         NM_DBUS_INTERFACE ".DHCP4Config"
+#define NM_DBUS_INTERFACE_IP6_CONFIG           NM_DBUS_INTERFACE ".IP6Config"
+#define NM_DBUS_INTERFACE_DHCP6_CONFIG         NM_DBUS_INTERFACE ".DHCP6Config"
+#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND    NM_DBUS_INTERFACE_DEVICE ".Infiniband"
+#define NM_DBUS_INTERFACE_DEVICE_BOND          NM_DBUS_INTERFACE_DEVICE ".Bond"
+#define NM_DBUS_INTERFACE_DEVICE_DUMMY         NM_DBUS_INTERFACE_DEVICE ".Dummy"
+#define NM_DBUS_INTERFACE_DEVICE_TEAM          NM_DBUS_INTERFACE_DEVICE ".Team"
+#define NM_DBUS_INTERFACE_DEVICE_VLAN          NM_DBUS_INTERFACE_DEVICE ".Vlan"
+#define NM_DBUS_INTERFACE_DEVICE_BRIDGE        NM_DBUS_INTERFACE_DEVICE ".Bridge"
+#define NM_DBUS_INTERFACE_DEVICE_GENERIC       NM_DBUS_INTERFACE_DEVICE ".Generic"
+#define NM_DBUS_INTERFACE_DEVICE_VETH          NM_DBUS_INTERFACE_DEVICE ".Veth"
+#define NM_DBUS_INTERFACE_DEVICE_TUN           NM_DBUS_INTERFACE_DEVICE ".Tun"
+#define NM_DBUS_INTERFACE_DEVICE_MACSEC        NM_DBUS_INTERFACE_DEVICE ".Macsec"
+#define NM_DBUS_INTERFACE_DEVICE_MACVLAN       NM_DBUS_INTERFACE_DEVICE ".Macvlan"
+#define NM_DBUS_INTERFACE_DEVICE_PPP           NM_DBUS_INTERFACE_DEVICE ".Ppp"
+#define NM_DBUS_INTERFACE_DEVICE_VXLAN         NM_DBUS_INTERFACE_DEVICE ".Vxlan"
+#define NM_DBUS_INTERFACE_DEVICE_GRE           NM_DBUS_INTERFACE_DEVICE ".Gre"
+#define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL     NM_DBUS_INTERFACE_DEVICE ".IPTunnel"
+#define NM_DBUS_INTERFACE_DEVICE_STATISTICS    NM_DBUS_INTERFACE_DEVICE ".Statistics"
 
 #define NM_DBUS_INTERFACE_SETTINGS        "org.freedesktop.NetworkManager.Settings"
 #define NM_DBUS_PATH_SETTINGS             "/org/freedesktop/NetworkManager/Settings"
@@ -203,34 +207,42 @@ typedef enum {
  * @NM_DEVICE_TYPE_VETH: a VETH interface
  * @NM_DEVICE_TYPE_MACSEC: a MACsec interface
  * @NM_DEVICE_TYPE_DUMMY: a dummy interface
+ * @NM_DEVICE_TYPE_PPP: a PPP interface
+ * @NM_DEVICE_TYPE_OVS_INTERFACE: a OpenVSwitch interface
+ * @NM_DEVICE_TYPE_OVS_PORT: a OpenVSwitch port
+ * @NM_DEVICE_TYPE_OVS_BRIDGE: a OpenVSwitch bridge
  *
  * #NMDeviceType values indicate the type of hardware represented by a
  * device object.
  **/
 typedef enum {
-	NM_DEVICE_TYPE_UNKNOWN    = 0,
-	NM_DEVICE_TYPE_ETHERNET   = 1,
-	NM_DEVICE_TYPE_WIFI       = 2,
-	NM_DEVICE_TYPE_UNUSED1    = 3,
-	NM_DEVICE_TYPE_UNUSED2    = 4,
-	NM_DEVICE_TYPE_BT         = 5,  /* Bluetooth */
-	NM_DEVICE_TYPE_OLPC_MESH  = 6,
-	NM_DEVICE_TYPE_WIMAX      = 7,
-	NM_DEVICE_TYPE_MODEM      = 8,
-	NM_DEVICE_TYPE_INFINIBAND = 9,
-	NM_DEVICE_TYPE_BOND       = 10,
-	NM_DEVICE_TYPE_VLAN       = 11,
-	NM_DEVICE_TYPE_ADSL       = 12,
-	NM_DEVICE_TYPE_BRIDGE     = 13,
-	NM_DEVICE_TYPE_GENERIC    = 14,
-	NM_DEVICE_TYPE_TEAM       = 15,
-	NM_DEVICE_TYPE_TUN        = 16,
-	NM_DEVICE_TYPE_IP_TUNNEL  = 17,
-	NM_DEVICE_TYPE_MACVLAN    = 18,
-	NM_DEVICE_TYPE_VXLAN      = 19,
-	NM_DEVICE_TYPE_VETH       = 20,
-	NM_DEVICE_TYPE_MACSEC     = 21,
-	NM_DEVICE_TYPE_DUMMY      = 22,
+	NM_DEVICE_TYPE_UNKNOWN       = 0,
+	NM_DEVICE_TYPE_ETHERNET      = 1,
+	NM_DEVICE_TYPE_WIFI          = 2,
+	NM_DEVICE_TYPE_UNUSED1       = 3,
+	NM_DEVICE_TYPE_UNUSED2       = 4,
+	NM_DEVICE_TYPE_BT            = 5,  /* Bluetooth */
+	NM_DEVICE_TYPE_OLPC_MESH     = 6,
+	NM_DEVICE_TYPE_WIMAX         = 7,
+	NM_DEVICE_TYPE_MODEM         = 8,
+	NM_DEVICE_TYPE_INFINIBAND    = 9,
+	NM_DEVICE_TYPE_BOND          = 10,
+	NM_DEVICE_TYPE_VLAN          = 11,
+	NM_DEVICE_TYPE_ADSL          = 12,
+	NM_DEVICE_TYPE_BRIDGE        = 13,
+	NM_DEVICE_TYPE_GENERIC       = 14,
+	NM_DEVICE_TYPE_TEAM          = 15,
+	NM_DEVICE_TYPE_TUN           = 16,
+	NM_DEVICE_TYPE_IP_TUNNEL     = 17,
+	NM_DEVICE_TYPE_MACVLAN       = 18,
+	NM_DEVICE_TYPE_VXLAN         = 19,
+	NM_DEVICE_TYPE_VETH          = 20,
+	NM_DEVICE_TYPE_MACSEC        = 21,
+	NM_DEVICE_TYPE_DUMMY         = 22,
+	NM_DEVICE_TYPE_PPP           = 23,
+	NM_DEVICE_TYPE_OVS_INTERFACE = 24,
+	NM_DEVICE_TYPE_OVS_PORT      = 25,
+	NM_DEVICE_TYPE_OVS_BRIDGE    = 26,
 } NMDeviceType;
 
 /**
@@ -289,12 +301,18 @@ typedef enum { /*< flags >*/
  * @NM_802_11_AP_FLAGS_NONE: access point has no special capabilities
  * @NM_802_11_AP_FLAGS_PRIVACY: access point requires authentication and
  * encryption (usually means WEP)
+ * @NM_802_11_AP_FLAGS_WPS: access point supports some WPS method
+ * @NM_802_11_AP_FLAGS_WPS_PBC: access point supports push-button WPS
+ * @NM_802_11_AP_FLAGS_WPS_PIN: access point supports PIN-based WPS
  *
  * 802.11 access point flags.
  **/
 typedef enum { /*< underscore_name=nm_802_11_ap_flags, flags >*/
 	NM_802_11_AP_FLAGS_NONE    = 0x00000000,
 	NM_802_11_AP_FLAGS_PRIVACY = 0x00000001,
+	NM_802_11_AP_FLAGS_WPS     = 0x00000002,
+	NM_802_11_AP_FLAGS_WPS_PBC = 0x00000004,
+	NM_802_11_AP_FLAGS_WPS_PIN = 0x00000008,
 } NM80211ApFlags;
 
 /**
@@ -532,6 +550,7 @@ typedef enum {
  * @NM_DEVICE_STATE_REASON_NEW_ACTIVATION: New connection activation was enqueued
  * @NM_DEVICE_STATE_REASON_PARENT_CHANGED: the device's parent changed
  * @NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED: the device parent's management changed
+ * @NM_DEVICE_STATE_REASON_OVSDB_FAILED: problem communicating with OpenVSwitch database
  *
  * Device state change reason codes
  */
@@ -599,6 +618,7 @@ typedef enum {
 	NM_DEVICE_STATE_REASON_NEW_ACTIVATION                 = 60,
 	NM_DEVICE_STATE_REASON_PARENT_CHANGED                 = 61,
 	NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED         = 62,
+	NM_DEVICE_STATE_REASON_OVSDB_FAILED                   = 63,
 } NMDeviceStateReason;
 
 /**
@@ -714,6 +734,9 @@ typedef enum {
  *   initiated by user-requested action via the D-Bus interface, as opposed to
  *   automatically initiated by NetworkManager in response to (for example) scan
  *   results or carrier changes.
+ * @NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE: indicates that WPS enrollment
+ *   is active with PBC method. The agent may suggest that the user pushes a button
+ *   on the router instead of supplying a PSK.
  * @NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM: Internal flag, not part of
  *   the D-Bus API.
  * @NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS: Internal flag, not part of
@@ -726,6 +749,7 @@ typedef enum { /*< flags >*/
 	NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION = 0x1,
 	NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW       = 0x2,
 	NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED    = 0x4,
+	NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE    = 0x8,
 
 	/* Internal to NM; not part of the D-Bus API */
 	NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM       = 0x80000000,
@@ -843,4 +867,30 @@ typedef enum { /*< skip >*/
 	NM_ROLLBACK_RESULT_ERR_FAILED           = 3,
 } NMRollbackResult;
 
+/**
+ * NMActivationStateFlags:
+ * @NM_ACTIVATION_STATE_FLAG_NONE: an alias for numeric zero, no flags set.
+ * @NM_ACTIVATION_STATE_FLAG_IS_MASTER: the device is a master.
+ * @NM_ACTIVATION_STATE_FLAG_IS_SLAVE: the device is a slave.
+ * @NM_ACTIVATION_STATE_FLAG_LAYER2_READY: layer2 is activated and ready.
+ * @NM_ACTIVATION_STATE_FLAG_IP4_READY: IPv4 setting is completed.
+ * @NM_ACTIVATION_STATE_FLAG_IP6_READY: IPv6 setting is completed.
+ * @NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES: The master has any slave devices attached.
+ *   This only makes sense if the device is a master.
+ *
+ * Flags describing the current activation state.
+ *
+ * Since: 1.10
+ **/
+typedef enum { /*< flags >*/
+	NM_ACTIVATION_STATE_FLAG_NONE                       = 0,
+
+	NM_ACTIVATION_STATE_FLAG_IS_MASTER                  = (1LL <<  0),
+	NM_ACTIVATION_STATE_FLAG_IS_SLAVE                   = (1LL <<  1),
+	NM_ACTIVATION_STATE_FLAG_LAYER2_READY               = (1LL <<  2),
+	NM_ACTIVATION_STATE_FLAG_IP4_READY                  = (1LL <<  3),
+	NM_ACTIVATION_STATE_FLAG_IP6_READY                  = (1LL <<  4),
+	NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES          = (1LL <<  5),
+} NMActivationStateFlags;
+
 #endif /* __NM_DBUS_INTERFACE_H__ */
diff --git a/libnm-core/nm-dbus-types.xml b/libnm-core/nm-dbus-types.xml
index c9d5d74d..ba58a615 100644
--- a/libnm-core/nm-dbus-types.xml
+++ b/libnm-core/nm-dbus-types.xml
@@ -272,6 +272,26 @@
               <entry role="enum_member_value"><para>= <literal>22</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>a dummy interface</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_PPP</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>23</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>a PPP interface</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_OVS_INTERFACE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>24</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>a OpenVSwitch interface</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_OVS_PORT</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>25</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>a OpenVSwitch port</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_OVS_BRIDGE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>26</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>a OpenVSwitch bridge</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -427,6 +447,21 @@
               <entry role="enum_member_value"><para>= <literal>0x00000001</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>access point requires authentication and encryption (usually means WEP)</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_802_11_AP_FLAGS_WPS</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x00000002</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>access point supports some WPS method</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_802_11_AP_FLAGS_WPS_PBC</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x00000004</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>access point supports push-button WPS</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_802_11_AP_FLAGS_WPS_PIN</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x00000008</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>access point supports PIN-based WPS</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -1082,6 +1117,11 @@
               <entry role="enum_member_value"><para>= <literal>62</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>the device parent's management changed</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_OVSDB_FAILED</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>63</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>problem communicating with OpenVSwitch database</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -1308,6 +1348,11 @@
               <entry role="enum_member_description"><para>set if the request was initiated by user-requested action via the D-Bus interface, as opposed to automatically initiated by NetworkManager in response to (for example) scan results or carrier changes.</para><para></para></entry>
             </row>
             <row role="constant">
+              <entry role="enum_member_name"><para>NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x8</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>indicates that WPS enrollment is active with PBC method. The agent may suggest that the user pushes a button on the router instead of supplying a PSK.</para><para></para></entry>
+            </row>
+            <row role="constant">
               <entry role="enum_member_name"><para>NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM</para><para></para></entry>
               <entry role="enum_member_value"><para>= <literal>0x80000000</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>Internal flag, not part of the D-Bus API.</para><para></para></entry>
@@ -1503,4 +1548,59 @@
     </refsect3>
   </refsect2>
 
+  <refsect2 id="NMActivationStateFlags" role="enum">
+    <title>enum NMActivationStateFlags</title>
+    <indexterm zone="NMActivationStateFlags">
+      <primary>NMActivationStateFlags</primary>
+    </indexterm>
+    <para><para>Flags describing the current activation state.</para><para>Since: 1.10</para><para></para></para>
+    <refsect3 role="enum_members">
+      <title>Values</title>
+      <informaltable role="enum_members_table" pgwide="1" frame="none">
+        <tgroup cols="4">
+          <colspec colname="enum_members_name" colwidth="300px" />
+          <colspec colname="enum_members_value" colwidth="100px"/>
+          <colspec colname="enum_members_description" />
+          <tbody>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_NONE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>an alias for numeric zero, no flags set.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IS_MASTER</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>the device is a master.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IS_SLAVE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>the device is a slave.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_LAYER2_READY</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>layer2 is activated and ready.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IP4_READY</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>IPv4 setting is completed.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IP6_READY</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>IPv6 setting is completed.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>The master has any slave devices attached. This only makes sense if the device is a master.</para><para></para></entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </informaltable>
+    </refsect3>
+  </refsect2>
+
 </refentry>
diff --git a/libnm-core/nm-errors.h b/libnm-core/nm-errors.h
index 35f7dc2f..6bacc8da 100644
--- a/libnm-core/nm-errors.h
+++ b/libnm-core/nm-errors.h
@@ -143,6 +143,8 @@ GQuark nm_crypto_error_quark (void);
  *   activation request (eg, the #NMAccessPoint or #NMWimaxNsp) was not
  *   found.
  * @NM_DEVICE_ERROR_VERSION_ID_MISMATCH: the version id did not match.
+ * @NM_DEVICE_ERROR_MISSING_DEPENDENCIES: the requested operation could not
+ *   be completed due to missing dependencies.
  *
  * Device-related errors.
  *
@@ -160,6 +162,7 @@ typedef enum {
 	NM_DEVICE_ERROR_NOT_ALLOWED,               /*< nick=NotAllowed >*/
 	NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND, /*< nick=SpecificObjectNotFound >*/
 	NM_DEVICE_ERROR_VERSION_ID_MISMATCH,       /*< nick=VersionIdMismatch >*/
+	NM_DEVICE_ERROR_MISSING_DEPENDENCIES,      /*< nick=MissingDependencies >*/
 } NMDeviceError;
 
 #define NM_DEVICE_ERROR nm_device_error_quark ()
diff --git a/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h
index be11a708..5b709c02 100644
--- a/libnm-core/nm-keyfile-internal.h
+++ b/libnm-core/nm-keyfile-internal.h
@@ -28,7 +28,7 @@
 #include "nm-setting-8021x.h"
 
 #include "nm-core-internal.h"
-#include "nm-setting-metadata.h"
+#include "nm-meta-setting.h"
 
 /*****************************************************************************/
 
diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c
index eb257eeb..0ac417cd 100644
--- a/libnm-core/nm-keyfile-reader.c
+++ b/libnm-core/nm-keyfile-reader.c
@@ -133,8 +133,7 @@ read_array_of_uint (GKeyFile *file,
 static gboolean
 get_one_int (KeyfileReaderInfo *info, const char *property_name, const char *str, guint32 max_val, guint32 *out)
 {
-	long tmp;
-	char *endptr;
+	gint64 tmp;
 
 	g_return_val_if_fail (!info == !property_name, FALSE);
 
@@ -145,13 +144,13 @@ get_one_int (KeyfileReaderInfo *info, const char *property_name, const char *str
 		return FALSE;
 	}
 
-	errno = 0;
-	tmp = strtol (str, &endptr, 10);
-	if (errno || (tmp < 0) || (tmp > max_val) || *endptr != 0) {
-		if (property_name)
+	tmp = _nm_utils_ascii_str_to_int64 (str, 10, 0, max_val, -1);
+	if (tmp == -1) {
+		if (property_name) {
 			handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
 			             _("ignoring invalid number '%s'"),
 			            str);
+		}
 		return FALSE;
 	}
 
@@ -186,7 +185,8 @@ build_route (KeyfileReaderInfo *info,
              const char *gateway_str, const char *metric_str)
 {
 	NMIPRoute *route;
-	guint32 metric = 0;
+	guint32 u32;
+	gint64 metric = -1;
 	GError *error = NULL;
 
 	g_return_val_if_fail (plen, NULL);
@@ -205,9 +205,10 @@ build_route (KeyfileReaderInfo *info,
 			 **/
 			if (   family == AF_INET6
 			    && !metric_str
-			    && get_one_int (NULL, NULL, gateway_str, G_MAXUINT32, &metric))
+			    && get_one_int (NULL, NULL, gateway_str, G_MAXUINT32, &u32)) {
+				metric = u32;
 				gateway_str = NULL;
-			else {
+			} else {
 				if (!info->error) {
 					handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
 					             _("ignoring invalid gateway '%s' for %s route"),
@@ -219,14 +220,15 @@ build_route (KeyfileReaderInfo *info,
 	} else
 		gateway_str = NULL;
 
-	/* parse metric, default to 0 */
+	/* parse metric, default to -1 */
 	if (metric_str) {
-		if (!get_one_int (info, property_name, metric_str, G_MAXUINT32, &metric))
+		if (!get_one_int (info, property_name, metric_str, G_MAXUINT32, &u32))
 			return NULL;
+		metric = u32;
 	}
 
 	route = nm_ip_route_new (family, dest_str, plen, gateway_str,
-	                         metric ? (gint64) metric : -1,
+	                         metric,
 	                         &error);
 	if (!route) {
 		handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
@@ -250,17 +252,17 @@ build_route (KeyfileReaderInfo *info,
  * When @current target is %NULL, gracefully fail returning %NULL while
  * leaving the @current target %NULL end setting @error to %NULL;
  */
-static char *
-read_field (char **current, char **error, const char *characters, const char *delimiters)
+static const char *
+read_field (char **current, const char **out_err_str, const char *characters, const char *delimiters)
 {
-	char *start;
+	const char *start;
 
-	g_return_val_if_fail (current, NULL);
-	g_return_val_if_fail (error, NULL);
-	g_return_val_if_fail (characters, NULL);
-	g_return_val_if_fail (delimiters, NULL);
+	nm_assert (current);
+	nm_assert (out_err_str);
+	nm_assert (characters);
+	nm_assert (delimiters);
 
-	*error = NULL;
+	*out_err_str = NULL;
 
 	if (!*current) {
 		/* graceful failure, leave '*current' NULL */
@@ -283,8 +285,8 @@ read_field (char **current, char **error, const char *characters, const char *de
 			return start;
 		} else {
 			/* error, bad character */
-			*error = *current;
-			*current = start;
+			*out_err_str = *current;
+			*current = (char *) start;
 			return NULL;
 		}
 	else {
@@ -333,42 +335,50 @@ read_one_ip_address_or_route (KeyfileReaderInfo *info,
                               char **out_gateway,
                               NMSetting *setting)
 {
-	guint32 plen = G_MAXUINT32;
+	guint plen;
 	gpointer result;
-	char *address_str, *plen_str, *gateway_str, *metric_str, *current, *error;
-	gs_free char *value = NULL, *value_orig = NULL;
+	const char *address_str;
+	const char *plen_str;
+	const char *gateway_str;
+	const char *metric_str;
+	const char *err_str = NULL;
+	char *current;
+	gs_free char *value = NULL;
+	gs_free char *value_orig = NULL;
 
 #define VALUE_ORIG()   (value_orig ? value_orig : (value_orig = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key_name, NULL)))
 
-	current = value = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key_name, NULL);
+	value = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key_name, NULL);
 	if (!value)
 		return NULL;
 
+	current = value;
+
 	/* get address field */
-	address_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
-	if (error) {
+	address_str = read_field (&current, &err_str, IP_ADDRESS_CHARS, DELIMITERS);
+	if (err_str) {
 		handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
 		             _("unexpected character '%c' for address %s: '%s' (position %td)"),
-		             *error, key_name, VALUE_ORIG (), error - current);
+		             *err_str, key_name, VALUE_ORIG (), err_str - current);
 		return NULL;
 	}
 	/* get prefix length field (skippable) */
-	plen_str = read_field (&current, &error, DIGITS, DELIMITERS);
+	plen_str = read_field (&current, &err_str, DIGITS, DELIMITERS);
 	/* get gateway field */
-	gateway_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
-	if (error) {
+	gateway_str = read_field (&current, &err_str, IP_ADDRESS_CHARS, DELIMITERS);
+	if (err_str) {
 		handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
 		             _("unexpected character '%c' for %s: '%s' (position %td)"),
-		             *error, key_name, VALUE_ORIG (), error - current);
+		             *err_str, key_name, VALUE_ORIG (), err_str - current);
 		return NULL;
 	}
 	/* for routes, get metric */
 	if (route) {
-		metric_str = read_field (&current, &error, DIGITS, DELIMITERS);
-		if (error) {
+		metric_str = read_field (&current, &err_str, DIGITS, DELIMITERS);
+		if (err_str) {
 			handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
 			             _("unexpected character '%c' in prefix length for %s: '%s' (position %td)"),
-			             *error, key_name, VALUE_ORIG (), error - current);
+			             *err_str, key_name, VALUE_ORIG (), err_str - current);
 			return NULL;
 		}
 	} else
@@ -394,7 +404,7 @@ read_one_ip_address_or_route (KeyfileReaderInfo *info,
 
 	/* parse plen, fallback to defaults */
 	if (plen_str) {
-		if (!get_one_int (info, property_name, plen_str, ipv6 ? 128 : 32, &plen)
+		if (   !get_one_int (info, property_name, plen_str, ipv6 ? 128 : 32, &plen)
 		    || (route && plen == 0)) {
 			plen = DEFAULT_PREFIX (route, ipv6);
 			if (   info->error
diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c
index 6a3d9a9f..30d519c4 100644
--- a/libnm-core/nm-keyfile-writer.c
+++ b/libnm-core/nm-keyfile-writer.c
@@ -47,7 +47,7 @@ typedef struct {
  * NMSettingConnection's 'type' property (which specifies the base type of the
  * connection, eg ethernet or wifi) or the 802-11-wireless setting's
  * 'security' property which specifies whether or not the AP requires
- * encrpytion.  This function handles translating those properties' values
+ * encryption.  This function handles translating those properties' values
  * from the real setting name to the more-readable alias.
  */
 static void
@@ -137,7 +137,7 @@ write_ip_values (GKeyFile *file,
 	GString *output;
 	int family, i;
 	const char *addr, *gw;
-	guint32 plen, metric;
+	guint32 plen;
 	char key_name[64], *key_name_idx;
 
 	if (!array->len)
@@ -150,25 +150,27 @@ write_ip_values (GKeyFile *file,
 
 	output = g_string_sized_new (2*INET_ADDRSTRLEN + 10);
 	for (i = 0; i < array->len; i++) {
+		gint64 metric = -1;
+
 		if (is_route) {
 			NMIPRoute *route = array->pdata[i];
 
 			addr = nm_ip_route_get_dest (route);
 			plen = nm_ip_route_get_prefix (route);
 			gw = nm_ip_route_get_next_hop (route);
-			metric = MAX (0, nm_ip_route_get_metric (route));
+			metric = nm_ip_route_get_metric (route);
 		} else {
 			NMIPAddress *address = array->pdata[i];
 
 			addr = nm_ip_address_get_address (address);
 			plen = nm_ip_address_get_prefix (address);
 			gw = i == 0 ? gateway : NULL;
-			metric = 0;
 		}
 
 		g_string_set_size (output, 0);
 		g_string_append_printf (output, "%s/%u", addr, plen);
-		if (metric || gw) {
+		if (   metric != -1
+		    || gw) {
 			/* Older versions of the plugin do not support the form
 			 * "a.b.c.d/plen,,metric", so, we always have to write the
 			 * gateway, even if there isn't one.
@@ -182,7 +184,7 @@ write_ip_values (GKeyFile *file,
 			}
 
 			g_string_append_printf (output, ",%s", gw);
-			if (metric)
+			if (is_route && metric != -1)
 				g_string_append_printf (output, ",%lu", (unsigned long) metric);
 		}
 
@@ -191,14 +193,9 @@ write_ip_values (GKeyFile *file,
 
 		if (is_route) {
 			gs_free char *attributes = NULL;
-			gs_strfreev char **names = NULL;
-			gs_unref_hashtable GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal);
-			int j;
-
-			names = nm_ip_route_get_attribute_names (array->pdata[i]);
-			for (j = 0; names && names[j]; j++)
-				g_hash_table_insert (hash, names[j], nm_ip_route_get_attribute (array->pdata[i], names[j]));
+			GHashTable *hash;
 
+			hash = _nm_ip_route_get_attributes_direct (array->pdata[i]);
 			attributes = nm_utils_format_variant_attributes (hash, ',', '=');
 			if (attributes) {
 				g_strlcat (key_name, "_options", sizeof (key_name));
diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c
index ab0f5c66..af195211 100644
--- a/libnm-core/nm-setting-8021x.c
+++ b/libnm-core/nm-setting-8021x.c
@@ -61,7 +61,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING,
-                         _nm_register_setting (802_1X, 2))
+                         _nm_register_setting (802_1X, NM_SETTING_PRIORITY_HW_AUX))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_802_1X)
 
 #define NM_SETTING_802_1X_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_802_1X, NMSetting8021xPrivate))
@@ -2772,6 +2772,7 @@ need_secrets_sim (NMSetting8021x *self,
 
 static gboolean
 need_private_key_password (GBytes *blob,
+                           NMSetting8021xCKScheme scheme,
                            const char *path,
                            const char *password,
                            NMSettingSecretFlags flags)
@@ -2781,6 +2782,10 @@ need_private_key_password (GBytes *blob,
 	if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
 		return FALSE;
 
+	if (   scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
+	    && flags == NM_SETTING_SECRET_FLAG_NONE)
+		return FALSE;
+
 	/* Private key password is required */
 	if (password) {
 		if (path)
@@ -2815,20 +2820,22 @@ need_secrets_tls (NMSetting8021x *self,
 		else if (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11)
 			g_warning ("%s: unknown phase2 private key scheme %d", __func__, scheme);
 
-		if (need_private_key_password (blob, path,
+		if (need_private_key_password (blob, scheme, path,
 		                               priv->phase2_private_key_password,
 		                               priv->phase2_private_key_password_flags))
 			g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
 
 		scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (self);
 		if (    scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
-		    && !(priv->phase2_ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
+		    && !(   priv->phase2_ca_cert_password_flags == NM_SETTING_SECRET_FLAG_NONE
+		         || priv->phase2_ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
 		    && !priv->phase2_ca_cert_password)
 			g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD);
 
 		scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (self);
 		if (    scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
-		    && !(priv->phase2_client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
+		    && !(   priv->phase2_client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED
+		         || priv->phase2_client_cert_password_flags == NM_SETTING_SECRET_FLAG_NONE)
 		    && !priv->phase2_client_cert_password)
 			g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD);
 	} else {
@@ -2840,20 +2847,22 @@ need_secrets_tls (NMSetting8021x *self,
 		else if (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11)
 			g_warning ("%s: unknown private key scheme %d", __func__, scheme);
 
-		if (need_private_key_password (blob, path,
+		if (need_private_key_password (blob, scheme, path,
 		                               priv->private_key_password,
 		                               priv->private_key_password_flags))
 			g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
 
 		scheme = nm_setting_802_1x_get_ca_cert_scheme (self);
 		if (    scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
-		    && !(priv->ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
+		    && !(   priv->ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED
+		         || priv->ca_cert_password_flags == NM_SETTING_SECRET_FLAG_NONE)
 		    && !priv->ca_cert_password)
 			g_ptr_array_add (secrets, NM_SETTING_802_1X_CA_CERT_PASSWORD);
 
 		scheme = nm_setting_802_1x_get_client_cert_scheme (self);
 		if (    scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
-		    && !(priv->client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
+		    && !(   priv->client_cert_password_flags == NM_SETTING_SECRET_FLAG_NONE
+		         || priv->client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
 		    && !priv->client_cert_password)
 			g_ptr_array_add (secrets, NM_SETTING_802_1X_CLIENT_CERT_PASSWORD);
 	}
@@ -4170,7 +4179,7 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 *
 	 * Specifies authentication flags to use in "phase 1" outer
 	 * authentication using #NMSetting8021xAuthFlags options.
-	 * The invidual TLS versions can be explicitly disabled. If a certain
+	 * The individual TLS versions can be explicitly disabled. If a certain
 	 * TLS disable flag is not set, it is up to the supplicant to allow
 	 * or forbid it. The TLS options map to tls_disable_tlsv1_x settings.
 	 * See the wpa_supplicant documentation for more details.
@@ -4518,8 +4527,10 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 **/
 	/* ---ifcfg-rh---
 	 * property: password-raw
-	 * variable: (none)
-	 * description: The property is not handled by ifcfg-rh plugin.
+	 * variable: IEEE_8021X_PASSWORD_RAW(+)
+	 * description: password used for EAP, encoded as a hexadecimal string. It
+	 *   can also go to "key-" lookaside file.
+	 * example: IEEE_8021X_PASSWORD_RAW=041c8320083aa4bf
 	 * ---end---
 	 */
 	g_object_class_install_property
@@ -4813,5 +4824,4 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 		                   G_PARAM_READWRITE |
 		                   NM_SETTING_PARAM_FUZZY_IGNORE |
 		                   G_PARAM_STATIC_STRINGS));
-
 }
diff --git a/libnm-core/nm-setting-adsl.c b/libnm-core/nm-setting-adsl.c
index 8198a72a..8be288b6 100644
--- a/libnm-core/nm-setting-adsl.c
+++ b/libnm-core/nm-setting-adsl.c
@@ -38,7 +38,7 @@
  */
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingAdsl, nm_setting_adsl, NM_TYPE_SETTING,
-                         _nm_register_setting (ADSL, 1))
+                         _nm_register_setting (ADSL, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_ADSL)
 
 #define NM_SETTING_ADSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_ADSL, NMSettingAdslPrivate))
diff --git a/libnm-core/nm-setting-bluetooth.c b/libnm-core/nm-setting-bluetooth.c
index 837b06c0..5b6ef3eb 100644
--- a/libnm-core/nm-setting-bluetooth.c
+++ b/libnm-core/nm-setting-bluetooth.c
@@ -25,6 +25,7 @@
 #include <string.h>
 #include <net/ethernet.h>
 
+#include "nm-connection-private.h"
 #include "nm-setting-bluetooth.h"
 #include "nm-setting-cdma.h"
 #include "nm-setting-gsm.h"
@@ -43,7 +44,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingBluetooth, nm_setting_bluetooth, NM_TYPE_SETTING,
-                         _nm_register_setting (BLUETOOTH, 1))
+                         _nm_register_setting (BLUETOOTH, NM_SETTING_PRIORITY_HW_NON_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BLUETOOTH)
 
 #define NM_SETTING_BLUETOOTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothPrivate))
@@ -80,8 +81,8 @@ NMSetting *nm_setting_bluetooth_new (void)
  * Returns the connection method for communicating with the remote device (i.e.
  * either DUN to a DUN-capable device or PANU to a NAP-capable device).
  *
- * Returns: the type, either %NM_SETTING_BLUETOOTH_TYPE_PANU or
- * %NM_SETTING_BLUETOOTH_TYPE_DUN
+ * Returns: the type, either %NM_SETTING_BLUETOOTH_TYPE_PANU,
+ * %NM_SETTING_BLUETOOTH_TYPE_NAP or %NM_SETTING_BLUETOOTH_TYPE_DUN
  **/
 const char *
 nm_setting_bluetooth_get_connection_type (NMSettingBluetooth *setting)
@@ -112,17 +113,10 @@ static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (setting);
+	const char *type;
+	gboolean missing_nap_bridge = FALSE;
 
-	if (!priv->bdaddr) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_MISSING_PROPERTY,
-		                     _("property is missing"));
-		g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_BDADDR);
-		return FALSE;
-	}
-
-	if (!nm_utils_hwaddr_valid (priv->bdaddr, ETH_ALEN)) {
+	if (priv->bdaddr && !nm_utils_hwaddr_valid (priv->bdaddr, ETH_ALEN)) {
 		g_set_error_literal (error,
 		                     NM_CONNECTION_ERROR,
 		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -131,27 +125,38 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (!priv->type) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_MISSING_PROPERTY,
-		                     _("property is missing"));
-		g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE);
-		return FALSE;
-	} else if (!g_str_equal (priv->type, NM_SETTING_BLUETOOTH_TYPE_DUN) &&
-	           !g_str_equal (priv->type, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
+	type = priv->type;
+	if (!type) {
+		if (connection) {
+			/* We may infer the type from the (non-)existence of gsm/cdma/bridge settings. */
+			type = _nm_connection_detect_bluetooth_type (connection);
+		}
+		if (!type) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_MISSING_PROPERTY,
+			                     _("property is missing"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE);
+			return FALSE;
+		}
+	}
+
+	if (!NM_IN_STRSET (type, NM_SETTING_BLUETOOTH_TYPE_DUN,
+	                         NM_SETTING_BLUETOOTH_TYPE_NAP,
+	                         NM_SETTING_BLUETOOTH_TYPE_PANU)) {
+		nm_assert (priv->type == type);
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
 		             _("'%s' is not a valid value for the property"),
-		             priv->type);
+		             type);
 		g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE);
 		return FALSE;
 	}
 
 	/* Make sure the corresponding 'type' setting is present */
 	if (   connection
-	    && !strcmp (priv->type, NM_SETTING_BLUETOOTH_TYPE_DUN)) {
+	    && nm_streq (type, NM_SETTING_BLUETOOTH_TYPE_DUN)) {
 		gboolean gsm = FALSE, cdma = FALSE;
 
 		gsm = !!nm_connection_get_setting_gsm (connection);
@@ -176,6 +181,48 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	 * is required at the interface level.
 	 */
 
+	/* NAP mode needs a bridge setting, and a bridge needs a name. */
+	if (nm_streq (type, NM_SETTING_BLUETOOTH_TYPE_NAP)) {
+		if (!_nm_connection_verify_required_interface_name (connection, error))
+			return FALSE;
+		if (   connection
+		    && !nm_connection_get_setting_bridge (connection))
+			missing_nap_bridge = TRUE;
+	} else {
+		if (!priv->bdaddr) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_MISSING_PROPERTY,
+			                     _("property is missing"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_BDADDR);
+			return FALSE;
+		}
+	}
+
+	/* errors form here are normalizable. */
+
+	if (!priv->type) {
+		/* as determined above, we can detect the bluetooth type. */
+		nm_assert (!missing_nap_bridge);
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_MISSING_PROPERTY,
+		                     _("property is missing"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE);
+		return NM_SETTING_VERIFY_NORMALIZABLE;
+	}
+
+	if (missing_nap_bridge) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_SETTING,
+		             _("'%s' connection requires '%s' setting"),
+		             NM_SETTING_BLUETOOTH_TYPE_NAP,
+		             NM_SETTING_BRIDGE_SETTING_NAME);
+		g_prefix_error (error, "%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME);
+		return NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+	}
+
 	return TRUE;
 }
 
diff --git a/libnm-core/nm-setting-bluetooth.h b/libnm-core/nm-setting-bluetooth.h
index 312aab14..58326702 100644
--- a/libnm-core/nm-setting-bluetooth.h
+++ b/libnm-core/nm-setting-bluetooth.h
@@ -54,12 +54,20 @@ G_BEGIN_DECLS
 /**
  * NM_SETTING_BLUETOOTH_TYPE_PANU:
  *
- * Connection type describing a connection to devices that support the Bluetooth
- * NAP (Network Access Point) protocol, which accepts connections via PANU.
+ * Connection type describing PANU connection to a Bluetooth NAP (Network
+ * Access Point).
  */
 #define NM_SETTING_BLUETOOTH_TYPE_PANU "panu"
 
 /**
+ * NM_SETTING_BLUETOOTH_TYPE_NAP:
+ *
+ * Connection type describing a Bluetooth NAP (Network Access Point),
+ * which accepts PANU clients.
+ */
+#define NM_SETTING_BLUETOOTH_TYPE_NAP "nap"
+
+/**
  * NMSettingBluetooth:
  *
  * Bluetooth Settings
diff --git a/libnm-core/nm-setting-bond.c b/libnm-core/nm-setting-bond.c
index 165001b0..d12940f6 100644
--- a/libnm-core/nm-setting-bond.c
+++ b/libnm-core/nm-setting-bond.c
@@ -43,7 +43,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingBond, nm_setting_bond, NM_TYPE_SETTING,
-                         _nm_register_setting (BOND, 1))
+                         _nm_register_setting (BOND, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BOND)
 
 #define NM_SETTING_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BOND, NMSettingBondPrivate))
@@ -224,19 +224,34 @@ validate_list (const char *name, const char *value, const BondDefault *def)
 static gboolean
 validate_ip (const char *name, const char *value)
 {
-	char **ips, **iter;
-	gboolean success = TRUE;
+	gs_free char *value_clone = NULL;
 	struct in_addr addr;
 
 	if (!value || !value[0])
 		return FALSE;
 
-	ips = g_strsplit_set (value, ",", 0);
-	for (iter = ips; iter && *iter && success; iter++)
-		success = !!inet_aton (*iter, &addr);
-	g_strfreev (ips);
+	value_clone = g_strdup (value);
+	value = value_clone;
+	for (;;) {
+		char *eow;
 
-	return success;
+		/* we do not skip over empty words. E.g
+		 * "192.168.1.1," is an error.
+		 *
+		 * ... for no particular reason. */
+
+		eow = strchr (value, ',');
+		if (eow)
+			*eow = '\0';
+
+		if (inet_pton (AF_INET, value, &addr) != 1)
+			return FALSE;
+
+		if (!eow)
+			break;
+		value = eow + 1;
+	}
+	return TRUE;
 }
 
 static gboolean
@@ -627,7 +642,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			g_set_error (error,
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			             _("'%s' is not a valid for '%s' option: %s"),
+			             _("'%s' is not valid for the '%s' option: %s"),
 			             primary, NM_SETTING_BOND_OPTION_PRIMARY, tmp_error->message);
 			g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
 			g_error_free (tmp_error);
@@ -646,7 +661,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		}
 	}
 
-	if (nm_connection_get_setting_infiniband (connection)) {
+	if (connection && nm_connection_get_setting_infiniband (connection)) {
 		if (strcmp (mode_new, "active-backup") != 0) {
 			g_set_error (error,
 			             NM_CONNECTION_ERROR,
diff --git a/libnm-core/nm-setting-bridge-port.c b/libnm-core/nm-setting-bridge-port.c
index 0116a836..3fc0e873 100644
--- a/libnm-core/nm-setting-bridge-port.c
+++ b/libnm-core/nm-setting-bridge-port.c
@@ -41,7 +41,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingBridgePort, nm_setting_bridge_port, NM_TYPE_SETTING,
-                         _nm_register_setting (BRIDGE_PORT, 3))
+                         _nm_register_setting (BRIDGE_PORT, NM_SETTING_PRIORITY_AUX))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BRIDGE_PORT)
 
 #define NM_SETTING_BRIDGE_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortPrivate))
@@ -273,7 +273,7 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *setting_class)
 	/**
 	 * NMSettingBridgePort:hairpin-mode:
 	 *
-	 * Enables or disabled "hairpin mode" for the port, which allows frames to
+	 * Enables or disables "hairpin mode" for the port, which allows frames to
 	 * be sent back out through the port the frame was received on.
 	 **/
 	/* ---ifcfg-rh---
diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c
index 7cfa7eb8..53300384 100644
--- a/libnm-core/nm-setting-bridge.c
+++ b/libnm-core/nm-setting-bridge.c
@@ -16,7 +16,7 @@
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301 USA.
  *
- * Copyright 2011 - 2013 Red Hat, Inc.
+ * Copyright 2011 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -39,7 +39,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING,
-                         _nm_register_setting (BRIDGE, 1))
+                         _nm_register_setting (BRIDGE, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BRIDGE)
 
 #define NM_SETTING_BRIDGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE, NMSettingBridgePrivate))
@@ -52,6 +52,7 @@ typedef struct {
 	guint16  hello_time;
 	guint16  max_age;
 	guint32  ageing_time;
+	guint16  group_forward_mask;
 	gboolean multicast_snooping;
 } NMSettingBridgePrivate;
 
@@ -64,6 +65,7 @@ enum {
 	PROP_HELLO_TIME,
 	PROP_MAX_AGE,
 	PROP_AGEING_TIME,
+	PROP_GROUP_FORWARD_MASK,
 	PROP_MULTICAST_SNOOPING,
 	LAST_PROP
 };
@@ -180,6 +182,22 @@ nm_setting_bridge_get_ageing_time (NMSettingBridge *setting)
 }
 
 /**
+ * nm_setting_bridge_get_group_forward_mask:
+ * @setting: the #NMSettingBridge
+ *
+ * Returns: the #NMSettingBridge:group-forward-mask property of the setting
+ *
+ * Since: 1.10
+ **/
+guint16
+nm_setting_bridge_get_group_forward_mask (NMSettingBridge *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0);
+
+	return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->group_forward_mask;
+}
+
+/**
  * nm_setting_bridge_get_multicast_snooping:
  * @setting: the #NMSettingBridge
  *
@@ -278,6 +296,15 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	                  error))
 		return FALSE;
 
+	if (priv->group_forward_mask & 7) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("the mask can't contain bits 0 (STP), 1 (MAC) or 2 (LACP)"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_GROUP_FORWARD_MASK);
+		return FALSE;
+	}
+
 	return _nm_connection_verify_required_interface_name (connection, error);
 }
 
@@ -326,6 +353,9 @@ set_property (GObject *object, guint prop_id,
 	case PROP_AGEING_TIME:
 		priv->ageing_time = g_value_get_uint (value);
 		break;
+	case PROP_GROUP_FORWARD_MASK:
+		priv->group_forward_mask = (guint16) g_value_get_uint (value);
+		break;
 	case PROP_MULTICAST_SNOOPING:
 		priv->multicast_snooping = g_value_get_boolean (value);
 		break;
@@ -364,6 +394,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_AGEING_TIME:
 		g_value_set_uint (value, priv->ageing_time);
 		break;
+	case PROP_GROUP_FORWARD_MASK:
+		g_value_set_uint (value, priv->group_forward_mask);
+		break;
 	case PROP_MULTICAST_SNOOPING:
 		g_value_set_boolean (value, priv->multicast_snooping);
 		break;
@@ -392,8 +425,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
 	 * NMSettingBridge:mac-address:
 	 *
 	 * If specified, the MAC address of bridge. When creating a new bridge, this
-	 * MAC address will be set. When matching an existing (outside
-	 * NetworkManager created) bridge, this MAC address must match.
+	 * MAC address will be set.
+	 *
+	 * If this field is left unspecified, the "ethernet.cloned-mac-address" is
+	 * referred instead to generate the initial MAC address. Note that setting
+	 * "ethernet.cloned-mac-address" anyway overwrites the MAC address of
+	 * the bridge later while activating the bridge. Hence, this property
+	 * is deprecated.
 	 **/
 	/* ---keyfile---
 	 * property: mac-address
@@ -557,6 +595,27 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
 		                    G_PARAM_STATIC_STRINGS));
 
 	/**
+	 * NMSettingBridge:group-forward-mask:
+	 *
+	 * A mask of group addresses to forward. Usually, group addresses in
+	 * the range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not
+	 * forwarded according to standards. This property is a mask of 16 bits,
+	 * each corresponding to a group address in that range that must be
+	 * forwarded. The mask can't have bits 0, 1 or 2 set because they are
+	 * used for STP, MAC pause frames and LACP.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_GROUP_FORWARD_MASK,
+		 g_param_spec_uint (NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, "", "",
+		                    0, 0xFFFF, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_CONSTRUCT |
+		                    NM_SETTING_PARAM_INFERRABLE |
+		                    G_PARAM_STATIC_STRINGS));
+
+	/**
 	 * NMSettingBridge:multicast-snooping:
 	 *
 	 * Controls whether IGMP snooping is enabled for this bridge.
diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h
index 81e4c644..d640dde3 100644
--- a/libnm-core/nm-setting-bridge.h
+++ b/libnm-core/nm-setting-bridge.h
@@ -46,6 +46,7 @@ G_BEGIN_DECLS
 #define NM_SETTING_BRIDGE_HELLO_TIME     "hello-time"
 #define NM_SETTING_BRIDGE_MAX_AGE        "max-age"
 #define NM_SETTING_BRIDGE_AGEING_TIME    "ageing-time"
+#define NM_SETTING_BRIDGE_GROUP_FORWARD_MASK "group-forward-mask"
 #define NM_SETTING_BRIDGE_MULTICAST_SNOOPING "multicast-snooping"
 
 /**
@@ -81,6 +82,8 @@ guint16      nm_setting_bridge_get_hello_time     (NMSettingBridge *setting);
 guint16      nm_setting_bridge_get_max_age        (NMSettingBridge *setting);
 
 guint32      nm_setting_bridge_get_ageing_time    (NMSettingBridge *setting);
+NM_AVAILABLE_IN_1_10
+guint16      nm_setting_bridge_get_group_forward_mask (NMSettingBridge *setting);
 
 gboolean     nm_setting_bridge_get_multicast_snooping (NMSettingBridge *setting);
 
diff --git a/libnm-core/nm-setting-cdma.c b/libnm-core/nm-setting-cdma.c
index b267ba63..63178951 100644
--- a/libnm-core/nm-setting-cdma.c
+++ b/libnm-core/nm-setting-cdma.c
@@ -38,7 +38,7 @@
  */
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingCdma, nm_setting_cdma, NM_TYPE_SETTING,
-                         _nm_register_setting (CDMA, 1))
+                         _nm_register_setting (CDMA, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_CDMA)
 
 #define NM_SETTING_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CDMA, NMSettingCdmaPrivate))
diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c
index bf205e9a..24c65e7c 100644
--- a/libnm-core/nm-setting-connection.c
+++ b/libnm-core/nm-setting-connection.c
@@ -46,7 +46,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingConnection, nm_setting_connection, NM_TYPE_SETTING,
-                         _nm_register_setting (CONNECTION, 0))
+                         _nm_register_setting (CONNECTION, NM_SETTING_PRIORITY_CONNECTION))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_CONNECTION)
 
 #define NM_SETTING_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionPrivate))
@@ -80,6 +80,7 @@ typedef struct {
 	guint gateway_ping_timeout;
 	NMMetered metered;
 	NMSettingConnectionLldp lldp;
+	gint auth_retries;
 } NMSettingConnectionPrivate;
 
 enum {
@@ -103,6 +104,7 @@ enum {
 	PROP_METERED,
 	PROP_LLDP,
 	PROP_STABLE_ID,
+	PROP_AUTH_RETRIES,
 
 	LAST_PROP
 };
@@ -553,6 +555,25 @@ nm_setting_connection_get_autoconnect_retries (NMSettingConnection *setting)
 }
 
 /**
+ * nm_setting_connection_get_auth_retries:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the value contained in the #NMSettingConnection:auth-retries property.
+ *
+ * Returns: the configured authentication retries. Zero means
+ * infinity and -1 means a global default value.
+ *
+ * Since: 1.10
+ **/
+gint
+nm_setting_connection_get_auth_retries (NMSettingConnection *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), -1);
+
+	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->auth_retries;
+}
+
+/**
  * nm_setting_connection_get_timestamp:
  * @setting: the #NMSettingConnection
  *
@@ -859,6 +880,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	gboolean is_slave;
 	const char *slave_setting_type;
 	NMSetting *normerr_base_type = NULL;
+	const char *type;
+	const char *slave_type;
 	const char *normerr_slave_setting_type = NULL;
 	const char *normerr_missing_slave_type = NULL;
 	const char *normerr_missing_slave_type_port = NULL;
@@ -904,8 +927,10 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		}
 	}
 
-	if (!priv->type) {
-		if (!connection || !(normerr_base_type = _nm_connection_find_base_type_setting (connection))) {
+	type = priv->type;
+	if (!type) {
+		if (   !connection
+		    || !(normerr_base_type = _nm_connection_find_base_type_setting (connection))) {
 			g_set_error_literal (error,
 			                     NM_CONNECTION_ERROR,
 			                     NM_CONNECTION_ERROR_MISSING_PROPERTY,
@@ -913,10 +938,11 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
 			return FALSE;
 		}
+		type = nm_setting_get_name (normerr_base_type);
 	} else {
 		GType base_type;
 
-		if (!priv->type[0]) {
+		if (!type[0]) {
 			g_set_error_literal (error,
 			                     NM_CONNECTION_ERROR,
 			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -925,20 +951,21 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			return FALSE;
 		}
 
-		base_type = nm_setting_lookup_type (priv->type);
-		if (base_type == G_TYPE_INVALID || !_nm_setting_type_is_base_type (base_type)) {
+		base_type = nm_setting_lookup_type (type);
+		if (   base_type == G_TYPE_INVALID
+		    || _nm_setting_type_get_base_type_priority (base_type) == NM_SETTING_PRIORITY_INVALID) {
 			g_set_error (error,
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
 			             _("connection type '%s' is not valid"),
-			             priv->type);
+			             type);
 			g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
 			return FALSE;
 		}
 
 		/* Make sure the corresponding 'type' item is present */
 		if (   connection
-		    && !nm_connection_get_setting_by_name (connection, priv->type)) {
+		    && !nm_connection_get_setting_by_name (connection, type)) {
 			NMSetting *s_base;
 			NMConnection *connection2;
 
@@ -951,7 +978,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			g_object_unref (connection2);
 
 			if (!normerr_base_setting) {
-				_set_error_missing_base_setting (error, priv->type);
+				_set_error_missing_base_setting (error, type);
 				return FALSE;
 			}
 		}
@@ -959,13 +986,14 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 
 	is_slave = FALSE;
 	slave_setting_type = NULL;
-	if (priv->slave_type) {
-		is_slave = _nm_setting_slave_type_is_valid (priv->slave_type, &slave_setting_type);
+	slave_type = priv->slave_type;
+	if (slave_type) {
+		is_slave = _nm_setting_slave_type_is_valid (slave_type, &slave_setting_type);
 		if (!is_slave) {
 			g_set_error (error,
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			             _("Unknown slave type '%s'"), priv->slave_type);
+			             _("Unknown slave type '%s'"), slave_type);
 			g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE);
 			return FALSE;
 		}
@@ -985,8 +1013,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		    && !nm_connection_get_setting_by_name (connection, slave_setting_type))
 			normerr_slave_setting_type = slave_setting_type;
 	} else {
+		nm_assert (!slave_type);
 		if (priv->master) {
-			const char *slave_type;
 			NMSetting *s_port;
 
 			if (   connection
@@ -1005,6 +1033,18 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		}
 	}
 
+	if (   nm_streq0 (type, NM_SETTING_OVS_PORT_SETTING_NAME)
+	    && !nm_streq0 (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_MISSING_PROPERTY,
+		             _("Only '%s' connections can be enslaved to '%s'"),
+		             NM_SETTING_OVS_PORT_SETTING_NAME,
+		             NM_SETTING_OVS_BRIDGE_SETTING_NAME);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
+		return FALSE;
+	}
+
 	if (priv->metered != NM_METERED_UNKNOWN &&
 	    priv->metered != NM_METERED_YES &&
 	    priv->metered != NM_METERED_NO) {
@@ -1289,6 +1329,9 @@ set_property (GObject *object, guint prop_id,
 	case PROP_LLDP:
 		priv->lldp = g_value_get_int (value);
 		break;
+	case PROP_AUTH_RETRIES:
+		priv->auth_retries = g_value_get_int (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1374,6 +1417,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_LLDP:
 		g_value_set_int (value, priv->lldp);
 		break;
+	case PROP_AUTH_RETRIES:
+		g_value_set_int (value, priv->auth_retries);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1462,7 +1508,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 	 * hosts/devices yields different addresses.
 	 *
 	 * If the value is unset, an ID unique for the connection is used.
-	 * Specifing a stable-id allows multiple connections to generate the
+	 * Specifying a stable-id allows multiple connections to generate the
 	 * same addresses. Another use is to generate IDs at runtime via
 	 * dynamic substitutions.
 	 *
@@ -1644,9 +1690,11 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 	/**
 	 * NMSettingConnection:autoconnect-retries:
 	 *
-	 * The number of times a connection should be tried when autoctivating before
+	 * The number of times a connection should be tried when autoactivating before
 	 * giving up. Zero means forever, -1 means the global default (4 times if not
-	 * overridden).
+	 * overridden). Setting this to 1 means to try activation only once before
+	 * blocking autoconnect. Note that after a timeout, NetworkManager will try
+	 * to autoconnect again.
 	 */
 	/* ---ifcfg-rh---
 	 * property: autoconnect-retries
@@ -1907,4 +1955,31 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 		                   G_PARAM_READWRITE |
 		                   G_PARAM_CONSTRUCT |
 		                   G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingConnection:auth-retries:
+	 *
+	 * The number of retries for the authentication. Zero means to try indefinitely; -1 means
+	 * to use a global default. If the global default is not set, the authentication
+	 * retries for 3 times before failing the connection.
+	 *
+	 * Currently this only applies to 802-1x authentication.
+	 *
+	 * Since: 1.10
+	 **/
+	/* ---ifcfg-rh---
+	 * property: auth-retries
+	 * variable: AUTH_RETRIES(+)
+	 * default: 0
+	 * description: Number of retries for authentication.
+	 * ---end---
+	 */
+	g_object_class_install_property
+		(object_class, PROP_AUTH_RETRIES,
+		 g_param_spec_int (NM_SETTING_CONNECTION_AUTH_RETRIES, "", "",
+		                   -1, G_MAXINT32, -1,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_CONSTRUCT |
+		                   NM_SETTING_PARAM_FUZZY_IGNORE |
+		                   G_PARAM_STATIC_STRINGS));
 }
diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h
index bd5b98ff..02a7f5b1 100644
--- a/libnm-core/nm-setting-connection.h
+++ b/libnm-core/nm-setting-connection.h
@@ -63,6 +63,7 @@ G_BEGIN_DECLS
 #define NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT "gateway-ping-timeout"
 #define NM_SETTING_CONNECTION_METERED        "metered"
 #define NM_SETTING_CONNECTION_LLDP           "lldp"
+#define NM_SETTING_CONNECTION_AUTH_RETRIES   "auth-retries"
 
 /* Types for property values */
 /**
@@ -166,6 +167,9 @@ NMMetered   nm_setting_connection_get_metered (NMSettingConnection *setting);
 NM_AVAILABLE_IN_1_2
 NMSettingConnectionLldp nm_setting_connection_get_lldp (NMSettingConnection *setting);
 
+NM_AVAILABLE_IN_1_10
+gint        nm_setting_connection_get_auth_retries     (NMSettingConnection *setting);
+
 G_END_DECLS
 
 #endif /* __NM_SETTING_CONNECTION_H__ */
diff --git a/libnm-core/nm-setting-dcb.c b/libnm-core/nm-setting-dcb.c
index 140dc021..24d461a1 100644
--- a/libnm-core/nm-setting-dcb.c
+++ b/libnm-core/nm-setting-dcb.c
@@ -41,7 +41,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingDcb, nm_setting_dcb, NM_TYPE_SETTING,
-                         _nm_register_setting (DCB, 2))
+                         _nm_register_setting (DCB, NM_SETTING_PRIORITY_HW_AUX))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_DCB)
 
 #define NM_SETTING_DCB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_DCB, NMSettingDcbPrivate))
@@ -1182,7 +1182,7 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 	 * An array of 8 uint values, where the array index corresponds to the
 	 * Priority Group ID (0 - 7) and the value indicates the percentage of link
 	 * bandwidth allocated to that group.  Allowed values are 0 - 100, and the
-	 * sum of all values must total 100 percent.
+	 * sum of all values must total 100 percents.
 	 *
 	 * Element-type: guint
 	 **/
@@ -1211,7 +1211,7 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 	 * Priority (0 - 7) and the value indicates the percentage of bandwidth of
 	 * the priority's assigned group that the priority may use.  The sum of all
 	 * percentages for priorities which belong to the same group must total 100
-	 * percent.
+	 * percents.
 	 *
 	 * Element-type: guint
 	 **/
diff --git a/libnm-core/nm-setting-dummy.c b/libnm-core/nm-setting-dummy.c
index d23f0a30..4b3c41f0 100644
--- a/libnm-core/nm-setting-dummy.c
+++ b/libnm-core/nm-setting-dummy.c
@@ -35,7 +35,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingDummy, nm_setting_dummy, NM_TYPE_SETTING,
-                         _nm_register_setting (DUMMY, 1))
+                         _nm_register_setting (DUMMY, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_DUMMY)
 
 /**
diff --git a/libnm-core/nm-setting-generic.c b/libnm-core/nm-setting-generic.c
index 09edeb6e..96f26ac2 100644
--- a/libnm-core/nm-setting-generic.c
+++ b/libnm-core/nm-setting-generic.c
@@ -37,7 +37,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingGeneric, nm_setting_generic, NM_TYPE_SETTING,
-                         _nm_register_setting (GENERIC, 1))
+                         _nm_register_setting (GENERIC, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_GENERIC)
 
 #define NM_SETTING_GENERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GENERIC, NMSettingGenericPrivate))
diff --git a/libnm-core/nm-setting-gsm.c b/libnm-core/nm-setting-gsm.c
index be32b852..3011d917 100644
--- a/libnm-core/nm-setting-gsm.c
+++ b/libnm-core/nm-setting-gsm.c
@@ -39,7 +39,7 @@
  */
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingGsm, nm_setting_gsm, NM_TYPE_SETTING,
-                         _nm_register_setting (GSM, 1))
+                         _nm_register_setting (GSM, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_GSM)
 
 #define NM_SETTING_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GSM, NMSettingGsmPrivate))
@@ -793,7 +793,7 @@ nm_setting_gsm_class_init (NMSettingGsmClass *setting_class)
 	 * mobile network operator which this connection applies to.  If given,
 	 * the connection will apply to any device also allowed by
 	 * #NMSettingGsm:device-id and #NMSettingGsm:sim-id which contains a SIM
-	 * card provisioined by the given operator.
+	 * card provisioned by the given operator.
 	 *
 	 * Since: 1.2
 	 **/
diff --git a/libnm-core/nm-setting-infiniband.c b/libnm-core/nm-setting-infiniband.c
index 1bbe2b3f..8c49849a 100644
--- a/libnm-core/nm-setting-infiniband.c
+++ b/libnm-core/nm-setting-infiniband.c
@@ -38,7 +38,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingInfiniband, nm_setting_infiniband, NM_TYPE_SETTING,
-                         _nm_register_setting (INFINIBAND, 1))
+                         _nm_register_setting (INFINIBAND, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_INFINIBAND)
 
 #define NM_SETTING_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_INFINIBAND, NMSettingInfinibandPrivate))
@@ -181,7 +181,7 @@ nm_setting_infiniband_get_virtual_interface_name (NMSettingInfiniband *setting)
 static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
-	NMSettingConnection *s_con;
+	NMSettingConnection *s_con = NULL;
 	NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (setting);
 	guint32 normerr_max_mtu = 0;
 
@@ -241,7 +241,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		}
 	}
 
-	s_con = nm_connection_get_setting_connection (connection);
+	if (connection)
+		s_con = nm_connection_get_setting_connection (connection);
 	if (s_con) {
 		const char *interface_name = nm_setting_connection_get_interface_name (s_con);
 		GError *tmp_error = NULL;
diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c
index 139c2c3e..7265fdc5 100644
--- a/libnm-core/nm-setting-ip-config.c
+++ b/libnm-core/nm-setting-ip-config.c
@@ -753,18 +753,22 @@ nm_ip_route_unref (NMIPRoute *route)
 }
 
 /**
- * _nm_ip_route_equal:
+ * nm_ip_route_equal_full:
  * @route: the #NMIPRoute
  * @other: the #NMIPRoute to compare @route to.
- * @consider_attributes: whether to compare attributes too
+ * @cmp_flags: tune how to compare attributes. Currently only
+ *   NM_IP_ROUTE_EQUAL_CMP_FLAGS_NONE (0) and NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS (1)
+ *   is supported.
  *
  * Determines if two #NMIPRoute objects contain the same destination, prefix,
  * next hop, and metric.
  *
  * Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
+ *
+ * Since: 1.10
  **/
-static gboolean
-_nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other, gboolean consider_attributes)
+gboolean
+nm_ip_route_equal_full (NMIPRoute *route, NMIPRoute *other, guint cmp_flags)
 {
 	g_return_val_if_fail (route != NULL, FALSE);
 	g_return_val_if_fail (route->refcount > 0, FALSE);
@@ -772,12 +776,16 @@ _nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other, gboolean consider_attrib
 	g_return_val_if_fail (other != NULL, FALSE);
 	g_return_val_if_fail (other->refcount > 0, FALSE);
 
+	g_return_val_if_fail (NM_IN_SET (cmp_flags,
+	                                 NM_IP_ROUTE_EQUAL_CMP_FLAGS_NONE,
+	                                 NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS), FALSE);
+
 	if (   route->prefix != other->prefix
 	    || route->metric != other->metric
 	    || strcmp (route->dest, other->dest) != 0
 	    || g_strcmp0 (route->next_hop, other->next_hop) != 0)
 		return FALSE;
-	if (consider_attributes) {
+	if (cmp_flags == NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS) {
 		GHashTableIter iter;
 		const char *key;
 		GVariant *value, *value2;
@@ -813,7 +821,7 @@ _nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other, gboolean consider_attrib
 gboolean
 nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other)
 {
-	return _nm_ip_route_equal (route, other, FALSE);
+	return nm_ip_route_equal_full (route, other, NM_IP_ROUTE_EQUAL_CMP_FLAGS_NONE);
 }
 
 /**
@@ -1104,6 +1112,54 @@ nm_ip_route_set_metric (NMIPRoute *route,
 	route->metric = metric;
 }
 
+GHashTable *
+_nm_ip_route_get_attributes_direct (NMIPRoute *route)
+{
+	nm_assert (route);
+
+	return route->attributes;
+}
+
+/**
+ * _nm_ip_route_get_attribute_names:
+ * @route: the #NMIPRoute
+ * @sorted: whether to sort the names. Otherwise, their order is
+ *   undefined and unstable.
+ * @out_length: (allow-none): (out): the number of elements
+ *
+ * Gets an array of attribute names defined on @route.
+ *
+ * Returns: (array length=out_length) (transfer container): a %NULL-terminated array
+ *   of attribute names or %NULL if there are no attributes. The order of the returned
+ *   names is undefined.
+ **/
+const char **
+_nm_ip_route_get_attribute_names (const NMIPRoute *route, gboolean sorted, guint *out_length)
+{
+	const char **names;
+	guint length;
+
+	g_return_val_if_fail (route != NULL, NULL);
+
+	if (   !route->attributes
+	    || !g_hash_table_size (route->attributes)) {
+		NM_SET_OUT (out_length, 0);
+		return NULL;
+	}
+
+	names = (const char **) g_hash_table_get_keys_as_array (route->attributes, &length);
+	if (   sorted
+	    && length > 1) {
+		g_qsort_with_data (names,
+		                   length,
+		                   sizeof (char *),
+		                   nm_strcmp_p_with_data,
+		                   NULL);
+	}
+	NM_SET_OUT (out_length, length);
+	return names;
+}
+
 /**
  * nm_ip_route_get_attribute_names:
  * @route: the #NMIPRoute
@@ -1115,22 +1171,21 @@ nm_ip_route_set_metric (NMIPRoute *route,
 char **
 nm_ip_route_get_attribute_names (NMIPRoute *route)
 {
-	GHashTableIter iter;
-	const char *key;
-	GPtrArray *names;
+	char **names;
+	guint i, len;
 
 	g_return_val_if_fail (route != NULL, NULL);
 
-	names = g_ptr_array_new ();
+	names = (char **) _nm_ip_route_get_attribute_names (route, TRUE, &len);
+	if (!names)
+		return g_new0 (char *, 1);
 
-	if (route->attributes) {
-		g_hash_table_iter_init (&iter, route->attributes);
-		while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL))
-			g_ptr_array_add (names, g_strdup (key));
+	nm_assert (len > 0 && names && names[len] == NULL);
+	for (i = 0; i < len; i++) {
+		nm_assert (names[i]);
+		names[i] = g_strdup (names[i]);
 	}
-	g_ptr_array_add (names, NULL);
-
-	return (char **) g_ptr_array_free (names, FALSE);
+	return names;
 }
 
 /**
@@ -1186,9 +1241,10 @@ nm_ip_route_set_attribute (NMIPRoute *route, const char *name, GVariant *value)
 	&(NMVariantAttributeSpec) { name, type, v4, v6, str_type }
 
 static const NMVariantAttributeSpec * const ip_route_attribute_spec[] = {
+	ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_TABLE,           G_VARIANT_TYPE_UINT32,   TRUE,  TRUE,  0 ),
 	ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_SRC,             G_VARIANT_TYPE_STRING,   TRUE,  TRUE, 'a'),
 	ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_FROM,            G_VARIANT_TYPE_STRING,   FALSE, TRUE, 'p'),
-	ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_TOS,             G_VARIANT_TYPE_BYTE,     TRUE,  TRUE,  0 ),
+	ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_TOS,             G_VARIANT_TYPE_BYTE,     TRUE,  FALSE, 0 ),
 	ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW,          G_VARIANT_TYPE_UINT32,   TRUE,  TRUE,  0 ),
 	ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_CWND,            G_VARIANT_TYPE_UINT32,   TRUE,  TRUE,  0 ),
 	ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND,        G_VARIANT_TYPE_UINT32,   TRUE,  TRUE,  0 ),
@@ -1289,7 +1345,7 @@ nm_ip_route_attribute_validate  (const char *name,
 		char *sep;
 
 		switch (spec->str_type) {
-		case 'a':	/* IP address */
+		case 'a': /* IP address */
 			if (!nm_utils_ipaddr_valid (family, string)) {
 				g_set_error (error,
 				             NM_CONNECTION_ERROR,
@@ -1301,7 +1357,7 @@ nm_ip_route_attribute_validate  (const char *name,
 				return FALSE;
 			}
 			break;
-		case 'p':	/* IP address + optional prefix */
+		case 'p': /* IP address + optional prefix */
 			string_free = g_strdup (string);
 			sep = strchr (string_free, '/');
 			if (sep) {
@@ -1333,6 +1389,26 @@ nm_ip_route_attribute_validate  (const char *name,
 	return TRUE;
 }
 
+gboolean
+_nm_ip_route_attribute_validate_all (const NMIPRoute *route)
+{
+	GHashTableIter iter;
+	const char *key;
+	GVariant *val;
+
+	g_return_val_if_fail (route, FALSE);
+
+	if (!route->attributes)
+		return TRUE;
+
+	g_hash_table_iter_init (&iter, route->attributes);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
+		if (!nm_ip_route_attribute_validate (key, val, route->family, NULL, NULL))
+			return FALSE;
+	}
+	return TRUE;
+}
+
 /*****************************************************************************/
 
 G_DEFINE_ABSTRACT_TYPE (NMSettingIPConfig, nm_setting_ip_config, NM_TYPE_SETTING)
@@ -1348,6 +1424,7 @@ typedef struct {
 	GPtrArray *addresses;  /* array of NMIPAddress */
 	GPtrArray *routes;     /* array of NMIPRoute */
 	gint64 route_metric;
+	guint32 route_table;
 	char *gateway;
 	gboolean ignore_auto_routes;
 	gboolean ignore_auto_dns;
@@ -1370,6 +1447,7 @@ enum {
 	PROP_GATEWAY,
 	PROP_ROUTES,
 	PROP_ROUTE_METRIC,
+	PROP_ROUTE_TABLE,
 	PROP_IGNORE_AUTO_ROUTES,
 	PROP_IGNORE_AUTO_DNS,
 	PROP_DHCP_HOSTNAME,
@@ -1429,7 +1507,7 @@ nm_setting_ip_config_get_dns (NMSettingIPConfig *setting, int idx)
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), NULL);
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	g_return_val_if_fail (idx < priv->dns->len, NULL);
+	g_return_val_if_fail (idx >= 0 && idx < priv->dns->len, NULL);
 
 	return priv->dns->pdata[idx];
 }
@@ -1449,7 +1527,7 @@ nm_setting_ip_config_add_dns (NMSettingIPConfig *setting, const char *dns)
 {
 	NMSettingIPConfigPrivate *priv;
 	char *dns_canonical;
-	int i;
+	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), FALSE);
 	g_return_val_if_fail (dns != NULL, FALSE);
@@ -1485,7 +1563,7 @@ nm_setting_ip_config_remove_dns (NMSettingIPConfig *setting, int idx)
 	g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	g_return_if_fail (idx < priv->dns->len);
+	g_return_if_fail (idx >= 0 && idx < priv->dns->len);
 
 	g_ptr_array_remove_index (priv->dns, idx);
 	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS);
@@ -1505,7 +1583,7 @@ nm_setting_ip_config_remove_dns_by_value (NMSettingIPConfig *setting, const char
 {
 	NMSettingIPConfigPrivate *priv;
 	char *dns_canonical;
-	int i;
+	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), FALSE);
 	g_return_val_if_fail (dns != NULL, FALSE);
@@ -1573,7 +1651,7 @@ nm_setting_ip_config_get_dns_search (NMSettingIPConfig *setting, int idx)
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), NULL);
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	g_return_val_if_fail (idx < priv->dns_search->len, NULL);
+	g_return_val_if_fail (idx >= 0 && idx < priv->dns_search->len, NULL);
 
 	return priv->dns_search->pdata[idx];
 }
@@ -1593,7 +1671,7 @@ nm_setting_ip_config_add_dns_search (NMSettingIPConfig *setting,
                                      const char *dns_search)
 {
 	NMSettingIPConfigPrivate *priv;
-	int i;
+	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), FALSE);
 	g_return_val_if_fail (dns_search != NULL, FALSE);
@@ -1625,7 +1703,7 @@ nm_setting_ip_config_remove_dns_search (NMSettingIPConfig *setting, int idx)
 	g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	g_return_if_fail (idx < priv->dns_search->len);
+	g_return_if_fail (idx >= 0 && idx < priv->dns_search->len);
 
 	g_ptr_array_remove_index (priv->dns_search, idx);
 	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH);
@@ -1647,7 +1725,7 @@ nm_setting_ip_config_remove_dns_search_by_value (NMSettingIPConfig *setting,
                                                  const char *dns_search)
 {
 	NMSettingIPConfigPrivate *priv;
-	int i;
+	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), FALSE);
 	g_return_val_if_fail (dns_search != NULL, FALSE);
@@ -1832,7 +1910,7 @@ nm_setting_ip_config_remove_dns_option (NMSettingIPConfig *setting, int idx)
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
 	g_return_if_fail (priv->dns_options);
-	g_return_if_fail (idx < priv->dns_options->len);
+	g_return_if_fail (idx >= 0 && idx < priv->dns_options->len);
 
 	g_ptr_array_remove_index (priv->dns_options, idx);
 	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_OPTIONS);
@@ -1854,7 +1932,7 @@ nm_setting_ip_config_remove_dns_option_by_value (NMSettingIPConfig *setting,
                                                  const char *dns_option)
 {
 	NMSettingIPConfigPrivate *priv;
-	int i;
+	gssize i;
 
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), FALSE);
 	g_return_val_if_fail (dns_option != NULL, FALSE);
@@ -1954,7 +2032,7 @@ nm_setting_ip_config_get_address (NMSettingIPConfig *setting, int idx)
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), NULL);
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	g_return_val_if_fail (idx < priv->addresses->len, NULL);
+	g_return_val_if_fail (idx >= 0 && idx < priv->addresses->len, NULL);
 
 	return priv->addresses->pdata[idx];
 }
@@ -1975,7 +2053,7 @@ nm_setting_ip_config_add_address (NMSettingIPConfig *setting,
                                   NMIPAddress *address)
 {
 	NMSettingIPConfigPrivate *priv;
-	int i;
+	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), FALSE);
 	g_return_val_if_fail (address != NULL, FALSE);
@@ -2008,7 +2086,7 @@ nm_setting_ip_config_remove_address (NMSettingIPConfig *setting, int idx)
 	g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	g_return_if_fail (idx < priv->addresses->len);
+	g_return_if_fail (idx >= 0 && idx < priv->addresses->len);
 
 	g_ptr_array_remove_index (priv->addresses, idx);
 
@@ -2029,7 +2107,7 @@ nm_setting_ip_config_remove_address_by_value (NMSettingIPConfig *setting,
                                               NMIPAddress *address)
 {
 	NMSettingIPConfigPrivate *priv;
-	int i;
+	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), FALSE);
 	g_return_val_if_fail (address != NULL, FALSE);
@@ -2106,7 +2184,7 @@ nm_setting_ip_config_get_route (NMSettingIPConfig *setting, int idx)
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), NULL);
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	g_return_val_if_fail (idx < priv->routes->len, NULL);
+	g_return_val_if_fail (idx >= 0 && idx < priv->routes->len, NULL);
 
 	return priv->routes->pdata[idx];
 }
@@ -2116,8 +2194,14 @@ nm_setting_ip_config_get_route (NMSettingIPConfig *setting, int idx)
  * @setting: the #NMSettingIPConfig
  * @route: the route to add
  *
- * Adds a new route and associated information to the setting.  The
+ * Appends a new route and associated information to the setting.  The
  * given route is duplicated internally and is not changed by this function.
+ * If an identical route (considering attributes as well) already exists, the
+ * route is not added and the function returns %FALSE.
+ *
+ * Note that before 1.10, this function would not consider route attributes
+ * and not add a route that has an existing route with same dest/prefix,next_hop,metric
+ * parameters.
  *
  * Returns: %TRUE if the route was added; %FALSE if the route was already known.
  **/
@@ -2126,7 +2210,7 @@ nm_setting_ip_config_add_route (NMSettingIPConfig *setting,
                                 NMIPRoute *route)
 {
 	NMSettingIPConfigPrivate *priv;
-	int i;
+	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), FALSE);
 	g_return_val_if_fail (route != NULL, FALSE);
@@ -2134,7 +2218,7 @@ nm_setting_ip_config_add_route (NMSettingIPConfig *setting,
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
 	for (i = 0; i < priv->routes->len; i++) {
-		if (nm_ip_route_equal (priv->routes->pdata[i], route))
+		if (nm_ip_route_equal_full (priv->routes->pdata[i], route, NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS))
 			return FALSE;
 	}
 
@@ -2158,7 +2242,7 @@ nm_setting_ip_config_remove_route (NMSettingIPConfig *setting, int idx)
 	g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	g_return_if_fail (idx < priv->routes->len);
+	g_return_if_fail (idx >= 0 && idx < priv->routes->len);
 
 	g_ptr_array_remove_index (priv->routes, idx);
 	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES);
@@ -2169,23 +2253,25 @@ nm_setting_ip_config_remove_route (NMSettingIPConfig *setting, int idx)
  * @setting: the #NMSettingIPConfig
  * @route: the route to remove
  *
- * Removes the route @route.
+ * Removes the first matching route that matches @route.
+ * Note that before 1.10, this function would only compare dest/prefix,next_hop,metric
+ * and ignore route attributes. Now, @route must match exactly.
  *
  * Returns: %TRUE if the route was found and removed; %FALSE if it was not.
  **/
 gboolean
 nm_setting_ip_config_remove_route_by_value (NMSettingIPConfig *setting,
-                                             NMIPRoute *route)
+                                            NMIPRoute *route)
 {
 	NMSettingIPConfigPrivate *priv;
-	int i;
+	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), FALSE);
 	g_return_val_if_fail (route != NULL, FALSE);
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
 	for (i = 0; i < priv->routes->len; i++) {
-		if (nm_ip_route_equal (priv->routes->pdata[i], route)) {
+		if (nm_ip_route_equal_full (priv->routes->pdata[i], route, NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS)) {
 			g_ptr_array_remove_index (priv->routes, i);
 			g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES);
 			return TRUE;
@@ -2229,6 +2315,24 @@ nm_setting_ip_config_get_route_metric (NMSettingIPConfig *setting)
 	return NM_SETTING_IP_CONFIG_GET_PRIVATE (setting)->route_metric;
 }
 
+/**
+ * nm_setting_ip_config_get_route_table:
+ * @setting: the #NMSettingIPConfig
+ *
+ * Returns the value contained in the #NMSettingIPConfig:route-table
+ * property.
+ *
+ * Returns: the configured route-table.
+ *
+ * Since: 1.10
+ **/
+guint32
+nm_setting_ip_config_get_route_table (NMSettingIPConfig *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), 0);
+
+	return NM_SETTING_IP_CONFIG_GET_PRIVATE (setting)->route_table;
+}
 
 /**
  * nm_setting_ip_config_get_ignore_auto_routes:
@@ -2402,7 +2506,7 @@ static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	int i;
+	guint i;
 
 	if (!priv->method) {
 		g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY,
@@ -2427,7 +2531,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
 			             _("%d. DNS server address is invalid"),
-			             i+1);
+			             (int) (i + 1));
 			g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_IP_CONFIG_DNS);
 			return FALSE;
 		}
@@ -2443,7 +2547,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
 			             _("%d. IP address is invalid"),
-			             i+1);
+			             (int) (i + 1));
 			g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
 			return FALSE;
 		}
@@ -2455,7 +2559,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 				             NM_CONNECTION_ERROR,
 				             NM_CONNECTION_ERROR_INVALID_PROPERTY,
 				             _("%d. IP address has 'label' property with invalid type"),
-				             i+1);
+				             (int) (i + 1));
 				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
 				return FALSE;
 			}
@@ -2464,7 +2568,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 				             NM_CONNECTION_ERROR,
 				             NM_CONNECTION_ERROR_INVALID_PROPERTY,
 				             _("%d. IP address has invalid label '%s'"),
-				             i+1, g_variant_get_string (label, NULL));
+				             (int) (i + 1), g_variant_get_string (label, NULL));
 				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
 				return FALSE;
 			}
@@ -2501,7 +2605,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
 			             _("%d. route is invalid"),
-			             i+1);
+			             (int) (i + 1));
 			g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_IP_CONFIG_ROUTES);
 			return FALSE;
 		}
@@ -2510,7 +2614,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
 			             _("%d. route cannot be a default route"),
-			             i+1);
+			             (int) (i + 1));
 			g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_IP_CONFIG_ROUTES);
 			return FALSE;
 		}
@@ -2559,7 +2663,7 @@ compare_property (NMSetting *setting,
 		if (a_priv->routes->len != b_priv->routes->len)
 			return FALSE;
 		for (i = 0; i < a_priv->routes->len; i++) {
-			if (!_nm_ip_route_equal (a_priv->routes->pdata[i], b_priv->routes->pdata[i], TRUE))
+			if (!nm_ip_route_equal_full (a_priv->routes->pdata[i], b_priv->routes->pdata[i], NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS))
 				return FALSE;
 		}
 		return TRUE;
@@ -2612,7 +2716,7 @@ set_property (GObject *object, guint prop_id,
 	NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
 	const char *gateway;
 	char **strv;
-	int i;
+	guint i;
 
 	switch (prop_id) {
 	case PROP_METHOD:
@@ -2670,6 +2774,9 @@ set_property (GObject *object, guint prop_id,
 	case PROP_ROUTE_METRIC:
 		priv->route_metric = g_value_get_int64 (value);
 		break;
+	case PROP_ROUTE_TABLE:
+		priv->route_table = g_value_get_uint (value);
+		break;
 	case PROP_IGNORE_AUTO_ROUTES:
 		priv->ignore_auto_routes = g_value_get_boolean (value);
 		break;
@@ -2740,6 +2847,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_ROUTE_METRIC:
 		g_value_set_int64 (value, priv->route_metric);
 		break;
+	case PROP_ROUTE_TABLE:
+		g_value_set_uint (value, priv->route_table);
+		break;
 	case PROP_IGNORE_AUTO_ROUTES:
 		g_value_set_boolean (value, nm_setting_ip_config_get_ignore_auto_routes (setting));
 		break;
@@ -2970,7 +3080,7 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
 	 * NMSettingIPConfig:route-metric:
 	 *
 	 * The default metric for routes that don't explicitly specify a metric.
-	 * The default value -1 means that the metric is choosen automatically
+	 * The default value -1 means that the metric is chosen automatically
 	 * based on the device type.
 	 * The metric applies to dynamic routes, manual (static) routes that
 	 * don't have an explicit metric setting, address prefix routes, and
@@ -2989,6 +3099,34 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
 	                         G_PARAM_STATIC_STRINGS));
 
 	/**
+	 * NMSettingIPConfig:route-table:
+	 *
+	 * Enable policy routing (source routing) and set the routing table used when adding routes.
+	 *
+	 * This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes
+	 * and static routes. But note that static routes can individually overwrite the setting
+	 * by explicitly specifying a non-zero routing table.
+	 *
+	 * If the table setting is left at zero, it is eligible to be overwritten via global
+	 * configuration. If the property is zero even after applying the global configuration
+	 * value, policy routing is disabled for the address family of this connection.
+	 *
+	 * Policy routing disabled means that NetworkManager will add all routes to the main
+	 * table (except static routes that explicitly configure a different table). Additionally,
+	 * NetworkManager will not delete any extraneous routes from tables except the main table.
+	 * This is to preserve backward compatibility for users who manage routing tables outside
+	 * of NetworkManager.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_ROUTE_TABLE,
+		 g_param_spec_uint (NM_SETTING_IP_CONFIG_ROUTE_TABLE, "", "",
+		                    0, G_MAXUINT32, 0,
+		                    G_PARAM_READWRITE |
+		                    NM_SETTING_PARAM_FUZZY_IGNORE |
+		                    G_PARAM_STATIC_STRINGS));
+	/**
 	 * NMSettingIPConfig:ignore-auto-routes:
 	 *
 	 * When #NMSettingIPConfig:method is set to "auto" and this property to
@@ -3106,6 +3244,7 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
 		                    G_PARAM_CONSTRUCT |
 		                    NM_SETTING_PARAM_FUZZY_IGNORE |
 		                    G_PARAM_STATIC_STRINGS));
+
 	/**
 	 * NMSettingIPConfig:dhcp-timeout:
 	 *
diff --git a/libnm-core/nm-setting-ip-config.h b/libnm-core/nm-setting-ip-config.h
index b7bb0215..84ce8465 100644
--- a/libnm-core/nm-setting-ip-config.h
+++ b/libnm-core/nm-setting-ip-config.h
@@ -92,6 +92,17 @@ void         nm_ip_route_ref                 (NMIPRoute  *route);
 void         nm_ip_route_unref               (NMIPRoute  *route);
 gboolean     nm_ip_route_equal               (NMIPRoute  *route,
                                               NMIPRoute  *other);
+
+enum {
+	NM_IP_ROUTE_EQUAL_CMP_FLAGS_NONE         = 0,
+	NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS   = (1LL <<  0),
+};
+
+NM_AVAILABLE_IN_1_10
+gboolean     nm_ip_route_equal_full          (NMIPRoute  *route,
+                                              NMIPRoute  *other,
+                                              guint       cmp_flags);
+
 NMIPRoute   *nm_ip_route_dup                 (NMIPRoute  *route);
 
 int          nm_ip_route_get_family          (NMIPRoute  *route);
@@ -131,6 +142,7 @@ gboolean     nm_ip_route_attribute_validate  (const char *name,
                                               gboolean *known,
                                               GError **error);
 
+#define NM_IP_ROUTE_ATTRIBUTE_TABLE          "table"
 #define NM_IP_ROUTE_ATTRIBUTE_SRC            "src"
 #define NM_IP_ROUTE_ATTRIBUTE_FROM           "from"
 #define NM_IP_ROUTE_ATTRIBUTE_TOS            "tos"
@@ -163,6 +175,7 @@ gboolean     nm_ip_route_attribute_validate  (const char *name,
 #define NM_SETTING_IP_CONFIG_GATEWAY            "gateway"
 #define NM_SETTING_IP_CONFIG_ROUTES             "routes"
 #define NM_SETTING_IP_CONFIG_ROUTE_METRIC       "route-metric"
+#define NM_SETTING_IP_CONFIG_ROUTE_TABLE        "route-table"
 #define NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES "ignore-auto-routes"
 #define NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS    "ignore-auto-dns"
 #define NM_SETTING_IP_CONFIG_DHCP_HOSTNAME      "dhcp-hostname"
@@ -271,6 +284,9 @@ void          nm_setting_ip_config_clear_routes               (NMSettingIPConfig
 
 gint64        nm_setting_ip_config_get_route_metric           (NMSettingIPConfig *setting);
 
+NM_AVAILABLE_IN_1_10
+guint32       nm_setting_ip_config_get_route_table            (NMSettingIPConfig *setting);
+
 gboolean      nm_setting_ip_config_get_ignore_auto_routes     (NMSettingIPConfig *setting);
 gboolean      nm_setting_ip_config_get_ignore_auto_dns        (NMSettingIPConfig *setting);
 
diff --git a/libnm-core/nm-setting-ip-tunnel.c b/libnm-core/nm-setting-ip-tunnel.c
index 4589f766..fa8d4a7a 100644
--- a/libnm-core/nm-setting-ip-tunnel.c
+++ b/libnm-core/nm-setting-ip-tunnel.c
@@ -31,7 +31,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingIPTunnel, nm_setting_ip_tunnel, NM_TYPE_SETTING,
-                         _nm_register_setting (IP_TUNNEL, 1))
+                         _nm_register_setting (IP_TUNNEL, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP_TUNNEL)
 
 #define NM_SETTING_IP_TUNNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP_TUNNEL, NMSettingIPTunnelPrivate))
@@ -758,7 +758,7 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *setting_class)
 		                    G_PARAM_STATIC_STRINGS));
 
 	/**
-	 * NMSettingIPTunel:mtu:
+	 * NMSettingIPTunnel:mtu:
 	 *
 	 * If non-zero, only transmit packets of the specified size or smaller,
 	 * breaking larger packets up into multiple fragments.
diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c
index 3f396b75..db3a12be 100644
--- a/libnm-core/nm-setting-ip4-config.c
+++ b/libnm-core/nm-setting-ip4-config.c
@@ -51,7 +51,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingIP4Config, nm_setting_ip4_config, NM_TYPE_SETTING_IP_CONFIG,
-                         _nm_register_setting (IP4_CONFIG, 4))
+                         _nm_register_setting (IP4_CONFIG, NM_SETTING_PRIORITY_IP))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP4_CONFIG)
 
 #define NM_SETTING_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4ConfigPrivate))
@@ -681,6 +681,14 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
 	 */
 
 	/* ---ifcfg-rh---
+	 * property: route-table
+	 * variable: IPV4_ROUTE_TABLE(+)
+	 * default: 0
+	 * description: IPV4_ROUTE_TABLE enables policy-routing and sets the default routing table.
+	 * ---end---
+	 */
+
+	/* ---ifcfg-rh---
 	 * property: dns-priority
 	 * variable: IPV4_DNS_PRIORITY(+)
 	 * description: The priority for DNS servers of this connection. Lower values have higher priority.
diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c
index 429f27c9..cc6381f0 100644
--- a/libnm-core/nm-setting-ip6-config.c
+++ b/libnm-core/nm-setting-ip6-config.c
@@ -52,7 +52,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingIP6Config, nm_setting_ip6_config, NM_TYPE_SETTING_IP_CONFIG,
-                         _nm_register_setting (IP6_CONFIG, 4))
+                         _nm_register_setting (IP6_CONFIG, NM_SETTING_PRIORITY_IP))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP6_CONFIG)
 
 #define NM_SETTING_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6ConfigPrivate))
@@ -224,7 +224,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		                     NM_CONNECTION_ERROR,
 		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
 		                      _("property is invalid"));
-		g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_METHOD);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE);
 		return FALSE;
 	}
 
@@ -653,6 +653,14 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
 	 */
 
 	/* ---ifcfg-rh---
+	 * property: route-table
+	 * variable: IPV6_ROUTE_TABLE(+)
+	 * default: 0
+	 * description: IPV6_ROUTE_TABLE enables policy-routing and sets the default routing table.
+	 * ---end---
+	 */
+
+	/* ---ifcfg-rh---
 	 * property: dns-priority
 	 * variable: IPV6_DNS_PRIORITY(+)
 	 * description: The priority for DNS servers of this connection. Lower values have higher priority.
@@ -707,16 +715,17 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
 	 * NMSettingIP6Config:addr-gen-mode:
 	 *
 	 * Configure method for creating the address for use with RFC4862 IPv6
-	 * Stateless Address Autoconfiguration. The permitted values are: "eui64",
-	 * or "stable-privacy".
+	 * Stateless Address Autoconfiguration. The permitted values are:
+	 * %NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64 or
+	 * %NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY.
 	 *
-	 * If the property is set to "eui64", the addresses will be generated
-	 * using the interface tokens derived from  hardware address. This makes
+	 * If the property is set to EUI64, the addresses will be generated
+	 * using the interface tokens derived from hardware address. This makes
 	 * the host part of the address to stay constant, making it possible
 	 * to track host's presence when it changes networks. The address changes
 	 * when the interface hardware is replaced.
 	 *
-	 * The value of "stable-privacy" enables use of cryptographically
+	 * The value of stable-privacy enables use of cryptographically
 	 * secure hash of a secret host-specific key along with the connection's
 	 * stable-id and the network address as specified by RFC7217.
 	 * This makes it impossible to use the address track host's presence,
@@ -724,8 +733,8 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
 	 * replaced.
 	 *
 	 * On D-Bus, the absence of an addr-gen-mode setting equals enabling
-	 * "stable-privacy". For keyfile plugin, the absence of the setting
-	 * on disk means "eui64" so that the property doesn't change on upgrade
+	 * stable-privacy. For keyfile plugin, the absence of the setting
+	 * on disk means EUI64 so that the property doesn't change on upgrade
 	 * from older versions.
 	 *
 	 * Note that this setting is distinct from the Privacy Extensions as
diff --git a/libnm-core/nm-setting-macsec.c b/libnm-core/nm-setting-macsec.c
index c818f07f..7a8a5a34 100644
--- a/libnm-core/nm-setting-macsec.c
+++ b/libnm-core/nm-setting-macsec.c
@@ -41,7 +41,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingMacsec, nm_setting_macsec, NM_TYPE_SETTING,
-                         _nm_register_setting (MACSEC, 1))
+                         _nm_register_setting (MACSEC, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_MACSEC)
 
 #define NM_SETTING_MACSEC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_MACSEC, NMSettingMacsecPrivate))
diff --git a/libnm-core/nm-setting-macvlan.c b/libnm-core/nm-setting-macvlan.c
index f5edc6e8..d684e58d 100644
--- a/libnm-core/nm-setting-macvlan.c
+++ b/libnm-core/nm-setting-macvlan.c
@@ -40,7 +40,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingMacvlan, nm_setting_macvlan, NM_TYPE_SETTING,
-                         _nm_register_setting (MACVLAN, 1))
+                         _nm_register_setting (MACVLAN, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_MACVLAN)
 
 #define NM_SETTING_MACVLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_MACVLAN, NMSettingMacvlanPrivate))
diff --git a/libnm-core/nm-setting-olpc-mesh.c b/libnm-core/nm-setting-olpc-mesh.c
index eba29c92..783d143f 100644
--- a/libnm-core/nm-setting-olpc-mesh.c
+++ b/libnm-core/nm-setting-olpc-mesh.c
@@ -40,7 +40,7 @@
 static void nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting);
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingOlpcMesh, nm_setting_olpc_mesh, NM_TYPE_SETTING,
-                         _nm_register_setting (OLPC_MESH, 1))
+                         _nm_register_setting (OLPC_MESH, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OLPC_MESH)
 
 #define NM_SETTING_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshPrivate))
diff --git a/libnm-core/nm-setting-ovs-bridge.c b/libnm-core/nm-setting-ovs-bridge.c
new file mode 100644
index 00000000..fbae95d0
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-bridge.c
@@ -0,0 +1,337 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-ovs-bridge.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-ovs-bridge
+ * @short_description: Describes connection properties for OpenVSwitch bridges.
+ *
+ * The #NMSettingOvsBridge object is a #NMSetting subclass that describes properties
+ * necessary for OpenVSwitch bridges.
+ **/
+
+enum {
+	PROP_0,
+	PROP_FAIL_MODE,
+	PROP_MCAST_SNOOPING_ENABLE,
+	PROP_RSTP_ENABLE,
+	PROP_STP_ENABLE,
+	LAST_PROP
+};
+
+/**
+ * NMSettingOvsBridge:
+ *
+ * OvsBridge Link Settings
+ */
+struct _NMSettingOvsBridge {
+	NMSetting parent;
+
+	char *fail_mode;
+	gboolean mcast_snooping_enable;
+	gboolean rstp_enable;
+	gboolean stp_enable;
+};
+
+struct _NMSettingOvsBridgeClass {
+	NMSettingClass parent;
+};
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingOvsBridge, nm_setting_ovs_bridge, NM_TYPE_SETTING,
+                         _nm_register_setting (OVS_BRIDGE, NM_SETTING_PRIORITY_HW_BASE))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_BRIDGE)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_ovs_bridge_get_fail_mode:
+ * @self: the #NMSettingOvsBridge
+ *
+ * Returns: the #NMSettingOvsBridge:fail_mode property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_bridge_get_fail_mode (NMSettingOvsBridge *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), NULL);
+
+	return self->fail_mode;
+}
+
+/**
+ * nm_setting_ovs_bridge_get_mcast_snooping_enable:
+ * @self: the #NMSettingOvsBridge
+ *
+ * Returns: the #NMSettingOvsBridge:mcast_snooping_enable property of the setting
+ *
+ * Since: 1.10
+ **/
+gboolean
+nm_setting_ovs_bridge_get_mcast_snooping_enable (NMSettingOvsBridge *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), FALSE);
+
+	return self->mcast_snooping_enable;
+}
+
+/**
+ * nm_setting_ovs_bridge_get_rstp_enable:
+ * @self: the #NMSettingOvsBridge
+ *
+ * Returns: the #NMSettingOvsBridge:rstp_enable property of the setting
+ *
+ * Since: 1.10
+ **/
+gboolean
+nm_setting_ovs_bridge_get_rstp_enable (NMSettingOvsBridge *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), FALSE);
+
+	return self->rstp_enable;
+}
+
+/**
+ * nm_setting_ovs_bridge_get_stp_enable:
+ * @self: the #NMSettingOvsBridge
+ *
+ * Returns: the #NMSettingOvsBridge:stp_enable property of the setting
+ *
+ * Since: 1.10
+ **/
+gboolean
+nm_setting_ovs_bridge_get_stp_enable (NMSettingOvsBridge *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), FALSE);
+
+	return self->stp_enable;
+}
+
+/*****************************************************************************/
+
+static int
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (setting);
+
+	if (!_nm_connection_verify_required_interface_name (connection, error))
+		return FALSE;
+
+	if (connection) {
+		NMSettingConnection *s_con;
+
+		s_con = nm_connection_get_setting_connection (connection);
+		if (!s_con) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_MISSING_SETTING,
+			             _("missing setting"));
+			g_prefix_error (error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME);
+			return FALSE;
+		}
+
+		if (nm_setting_connection_get_master (s_con)) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("A connection with a '%s' setting must not have a master."),
+			             NM_SETTING_OVS_BRIDGE_SETTING_NAME);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER);
+			return FALSE;
+		}
+	}
+
+	if (!NM_IN_STRSET (self->fail_mode, "secure", "standalone", NULL)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' is not allowed in fail_mode"),
+		             self->fail_mode);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_BRIDGE_SETTING_NAME, NM_SETTING_OVS_BRIDGE_FAIL_MODE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (object);
+
+	switch (prop_id) {
+	case PROP_FAIL_MODE:
+		g_value_set_string (value, self->fail_mode);
+		break;
+	case PROP_MCAST_SNOOPING_ENABLE:
+		g_value_set_boolean (value, self->mcast_snooping_enable);
+		break;
+	case PROP_RSTP_ENABLE:
+		g_value_set_boolean (value, self->rstp_enable);
+		break;
+	case PROP_STP_ENABLE:
+		g_value_set_boolean (value, self->stp_enable);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (object);
+
+	switch (prop_id) {
+	case PROP_FAIL_MODE:
+		g_free (self->fail_mode);
+		self->fail_mode = g_value_dup_string (value);
+		break;
+	case PROP_MCAST_SNOOPING_ENABLE:
+		self->mcast_snooping_enable = g_value_get_boolean (value);
+		break;
+	case PROP_RSTP_ENABLE:
+		self->rstp_enable = g_value_get_boolean (value);
+		break;
+	case PROP_STP_ENABLE:
+		self->stp_enable = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ovs_bridge_init (NMSettingOvsBridge *self)
+{
+}
+
+/**
+ * nm_setting_ovs_bridge_new:
+ *
+ * Creates a new #NMSettingOvsBridge object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingOvsBridge object
+ *
+ * Since: 1.10
+ **/
+NMSetting *
+nm_setting_ovs_bridge_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_BRIDGE, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (object);
+
+	g_free (self->fail_mode);
+
+	G_OBJECT_CLASS (nm_setting_ovs_bridge_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+	parent_class->verify = verify;
+
+	/**
+	 * NMSettingOvsBridge:fail-mode:
+	 *
+	 * The bridge failure mode. One of "secure", "standalone" or empty.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_FAIL_MODE,
+	         g_param_spec_string (NM_SETTING_OVS_BRIDGE_FAIL_MODE, "", "",
+	                              NULL,
+	                              G_PARAM_READWRITE |
+	                              G_PARAM_CONSTRUCT |
+	                              NM_SETTING_PARAM_INFERRABLE |
+	                              G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingOvsBridge:mcast-snooping-enable:
+	 *
+	 * Enable or disable multicast snooping.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_MCAST_SNOOPING_ENABLE,
+	         g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE, "", "",
+	                               FALSE,
+	                               G_PARAM_READWRITE |
+	                               G_PARAM_CONSTRUCT |
+	                               G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingOvsBridge:rstp-enable:
+	 *
+	 * Enable or disable RSTP.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_RSTP_ENABLE,
+	         g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_RSTP_ENABLE, "", "",
+	                               FALSE,
+	                               G_PARAM_READWRITE |
+	                               G_PARAM_CONSTRUCT |
+	                               G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingOvsBridge:stp-enable:
+	 *
+	 * Enable or disable STP.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_STP_ENABLE,
+	         g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_STP_ENABLE, "", "",
+	                               FALSE,
+	                               G_PARAM_READWRITE |
+	                               G_PARAM_CONSTRUCT |
+	                               G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-ovs-bridge.h b/libnm-core/nm-setting-ovs-bridge.h
new file mode 100644
index 00000000..d4837e58
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-bridge.h
@@ -0,0 +1,63 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_OVS_BRIDGE_H__
+#define __NM_SETTING_OVS_BRIDGE_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_OVS_BRIDGE            (nm_setting_ovs_bridge_get_type ())
+#define NM_SETTING_OVS_BRIDGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_BRIDGE, NMSettingOvsBridge))
+#define NM_SETTING_OVS_BRIDGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_BRIDGECONFIG, NMSettingOvsBridgeClass))
+#define NM_IS_SETTING_OVS_BRIDGE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_BRIDGE))
+#define NM_IS_SETTING_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_BRIDGE))
+#define NM_SETTING_OVS_BRIDGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_BRIDGE, NMSettingOvsBridgeClass))
+
+#define NM_SETTING_OVS_BRIDGE_SETTING_NAME          "ovs-bridge"
+
+#define NM_SETTING_OVS_BRIDGE_FAIL_MODE             "fail-mode"
+#define NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE "mcast-snooping-enable"
+#define NM_SETTING_OVS_BRIDGE_RSTP_ENABLE           "rstp-enable"
+#define NM_SETTING_OVS_BRIDGE_STP_ENABLE            "stp-enable"
+
+typedef struct _NMSettingOvsBridgeClass NMSettingOvsBridgeClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_setting_ovs_bridge_get_type (void);
+NM_AVAILABLE_IN_1_10
+NMSetting *nm_setting_ovs_bridge_new (void);
+
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_bridge_get_fail_mode             (NMSettingOvsBridge *self);
+NM_AVAILABLE_IN_1_10
+gboolean    nm_setting_ovs_bridge_get_mcast_snooping_enable (NMSettingOvsBridge *self);
+NM_AVAILABLE_IN_1_10
+gboolean    nm_setting_ovs_bridge_get_rstp_enable           (NMSettingOvsBridge *self);
+NM_AVAILABLE_IN_1_10
+gboolean    nm_setting_ovs_bridge_get_stp_enable            (NMSettingOvsBridge *self);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_OVS_BRIDGE_H__ */
diff --git a/libnm-core/nm-setting-ovs-interface.c b/libnm-core/nm-setting-ovs-interface.c
new file mode 100644
index 00000000..f06db32a
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-interface.c
@@ -0,0 +1,392 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-ovs-interface.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-ovs-interface
+ * @short_description: Describes connection properties for OpenVSwitch interfaces.
+ *
+ * The #NMSettingOvsInterface object is a #NMSetting subclass that describes properties
+ * necessary for OpenVSwitch interfaces.
+ **/
+
+enum {
+	PROP_0,
+	PROP_TYPE,
+	LAST_PROP
+};
+
+/**
+ * NMSettingOvsInterface:
+ *
+ * OpenVSwitch Interface Settings
+ */
+struct _NMSettingOvsInterface {
+	NMSetting parent;
+
+	char *type;
+};
+
+struct _NMSettingOvsInterfaceClass {
+	NMSettingClass parent;
+};
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingOvsInterface, nm_setting_ovs_interface, NM_TYPE_SETTING,
+                         _nm_register_setting (OVS_INTERFACE, NM_SETTING_PRIORITY_HW_BASE))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_INTERFACE)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_ovs_interface_get_interface_type:
+ * @self: the #NMSettingOvsInterface
+ *
+ * Returns: the #NMSettingOvsInterface:type property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_interface_get_interface_type (NMSettingOvsInterface *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_INTERFACE (self), NULL);
+
+	return self->type;
+}
+
+/*****************************************************************************/
+
+int
+_nm_setting_ovs_interface_verify_interface_type (NMSettingOvsInterface *self,
+                                                 NMConnection *connection,
+                                                 gboolean normalize,
+                                                 gboolean *out_modified,
+                                                 GError **error)
+{
+	gboolean has_patch;
+	const char *type;
+	const char *connection_type;
+	gboolean is_ovs_connection_type;
+	gboolean missing_patch_setting = FALSE;
+
+	g_return_val_if_fail (NM_IS_SETTING_OVS_INTERFACE (self), FALSE);
+	if (normalize) {
+		g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+		nm_assert (self == nm_connection_get_setting_ovs_interface (connection));
+	} else
+		g_return_val_if_fail (!connection || NM_IS_CONNECTION (connection), FALSE);
+
+	NM_SET_OUT (out_modified, FALSE);
+
+	type = self ? self->type : NULL;
+
+	if (   type
+	    && !NM_IN_STRSET (type,
+	                      "internal",
+	                      "system",
+	                      "patch")) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' is not a valid interface type"),
+		             type);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+		return FALSE;
+	}
+
+	if (!connection)
+		return TRUE;
+
+	connection_type = nm_connection_get_connection_type (connection);
+	if (!connection_type) {
+		/* if we have an ovs-interface, then the connection type must be either
+		 * "ovs-interface" (for non "system" type) or anything else (for "system" type).
+		 *
+		 * The connection type usually can be normalized based on the presence of a
+		 * base setting. However, in this case, if the connection type is missing,
+		 * that is too complicate to guess what the user wanted.
+		 *
+		 * Require the use to be explicit and fail. */
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("A connection with a '%s' setting needs connection.type explicitly set"),
+		             NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
+		return FALSE;
+	}
+
+	if (nm_streq (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME)) {
+		if (   type
+		    && nm_streq (type, "system")) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("A connection of type '%s' cannot have ovs-interface.type \"system\""),
+			             NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+			return FALSE;
+		}
+		is_ovs_connection_type = TRUE;
+	} else {
+		if (   type
+		    && !nm_streq (type, "system")) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("A connection of type '%s' cannot have an ovs-interface.type \"%s\""),
+			             connection_type,
+			             type);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+			return FALSE;
+		}
+		is_ovs_connection_type = FALSE;
+	}
+
+	has_patch = !!nm_connection_get_setting_by_name (connection, NM_SETTING_OVS_PATCH_SETTING_NAME);
+
+	if (has_patch) {
+		if (!is_ovs_connection_type) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("A connection with '%s' setting must be of connection.type \"ovs-interface\" but is \"%s\""),
+			             NM_SETTING_OVS_PATCH_SETTING_NAME,
+			             connection_type);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+			return FALSE;
+		}
+		if (type) {
+			if (!nm_streq (type, "patch")) {
+				g_set_error (error,
+				             NM_CONNECTION_ERROR,
+				             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				             _("A connection with '%s' setting needs to be of 'patch' interface type, not '%s'"),
+				             NM_SETTING_OVS_PATCH_SETTING_NAME,
+				             type);
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+				return FALSE;
+			}
+			return TRUE;
+		}
+		type = "patch";
+		goto normalize;
+	} else {
+		if (nm_streq0 (type, "patch")) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_MISSING_SETTING,
+			             _("A connection with ovs-interface.type '%s' setting a 'ovs-patch' setting"),
+			             type);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+			return FALSE;
+		}
+	}
+
+	if (type)
+		return TRUE;
+
+	if (is_ovs_connection_type)
+		type = "internal";
+	else
+		type = "system";
+normalize:
+	if (!normalize) {
+		if (!self) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_MISSING_SETTING,
+			             _("Missing ovs interface setting"));
+			g_prefix_error (error, "%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		} else {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_MISSING_PROPERTY,
+			             _("Missing ovs interface type"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE);
+		}
+		if (missing_patch_setting) {
+		}
+		return NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+	}
+
+	if (!self) {
+		self = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+		nm_connection_add_setting (connection, NM_SETTING (self));
+	}
+	g_object_set (self,
+	              NM_SETTING_OVS_INTERFACE_TYPE, type,
+	              NULL);
+	NM_SET_OUT (out_modified, TRUE);
+
+	return TRUE;
+}
+
+
+static int
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (setting);
+
+	if (connection) {
+		NMSettingConnection *s_con;
+		const char *slave_type;
+
+		s_con = nm_connection_get_setting_connection (connection);
+		if (!s_con) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_MISSING_SETTING,
+			             _("missing setting"));
+			g_prefix_error (error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME);
+			return FALSE;
+		}
+
+		if (!nm_setting_connection_get_master (s_con)) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("A connection with a '%s' setting must have a master."),
+			             NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER);
+			return FALSE;
+		}
+
+		slave_type = nm_setting_connection_get_slave_type (s_con);
+		if (   slave_type
+		    && !nm_streq (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME)) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("A connection with a '%s' setting must have the slave-type set to '%s'. Instead it is '%s'"),
+			             NM_SETTING_OVS_INTERFACE_SETTING_NAME,
+			             NM_SETTING_OVS_PORT_SETTING_NAME,
+			             slave_type);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE);
+			return FALSE;
+		}
+	}
+
+	return _nm_setting_ovs_interface_verify_interface_type (self,
+	                                                        connection,
+	                                                        FALSE,
+	                                                        NULL,
+	                                                        error);
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (object);
+
+	switch (prop_id) {
+	case PROP_TYPE:
+		g_value_set_string (value, self->type);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (object);
+
+	switch (prop_id) {
+	case PROP_TYPE:
+		g_free (self->type);
+		self->type = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ovs_interface_init (NMSettingOvsInterface *self)
+{
+}
+
+/**
+ * nm_setting_ovs_interface_new:
+ *
+ * Creates a new #NMSettingOvsInterface object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingOvsInterface object
+ *
+ * Since: 1.10
+ **/
+NMSetting *
+nm_setting_ovs_interface_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_INTERFACE, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (object);
+
+	g_free (self->type);
+
+	G_OBJECT_CLASS (nm_setting_ovs_interface_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_ovs_interface_class_init (NMSettingOvsInterfaceClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+	parent_class->verify = verify;
+
+	/**
+	 * NMSettingOvsInterface:type:
+	 *
+	 * The interface type. Either "internal", or empty.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_TYPE,
+	         g_param_spec_string (NM_SETTING_OVS_INTERFACE_TYPE, "", "",
+	                              NULL,
+	                              G_PARAM_READWRITE |
+	                              G_PARAM_CONSTRUCT |
+	                              NM_SETTING_PARAM_INFERRABLE |
+	                              G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-ovs-interface.h b/libnm-core/nm-setting-ovs-interface.h
new file mode 100644
index 00000000..7261cfd8
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-interface.h
@@ -0,0 +1,54 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_OVS_INTERFACE_H__
+#define __NM_SETTING_OVS_INTERFACE_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_OVS_INTERFACE            (nm_setting_ovs_interface_get_type ())
+#define NM_SETTING_OVS_INTERFACE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_INTERFACE, NMSettingOvsInterface))
+#define NM_SETTING_OVS_INTERFACE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_INTERFACECONFIG, NMSettingOvsInterfaceClass))
+#define NM_IS_SETTING_OVS_INTERFACE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_INTERFACE))
+#define NM_IS_SETTING_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_INTERFACE))
+#define NM_SETTING_OVS_INTERFACE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_INTERFACE, NMSettingOvsInterfaceClass))
+
+#define NM_SETTING_OVS_INTERFACE_SETTING_NAME "ovs-interface"
+
+#define NM_SETTING_OVS_INTERFACE_TYPE         "type"
+
+typedef struct _NMSettingOvsInterfaceClass NMSettingOvsInterfaceClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_setting_ovs_interface_get_type (void);
+NM_AVAILABLE_IN_1_10
+NMSetting *nm_setting_ovs_interface_new (void);
+
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_interface_get_interface_type (NMSettingOvsInterface *self);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_OVS_INTERFACE_H__ */
diff --git a/libnm-core/nm-setting-ovs-patch.c b/libnm-core/nm-setting-ovs-patch.c
new file mode 100644
index 00000000..9e380914
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-patch.c
@@ -0,0 +1,215 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-ovs-patch.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-ovs-patch
+ * @short_description: Describes connection properties for OpenVSwitch patch interfaces.
+ *
+ * The #NMSettingOvsPatch object is a #NMSetting subclass that describes properties
+ * necessary for OpenVSwitch interfaces of type "patch".
+ **/
+
+enum {
+	PROP_0,
+	PROP_PEER,
+	LAST_PROP
+};
+
+/**
+ * NMSettingOvsPatch:
+ *
+ * OvsPatch Link Settings
+ */
+struct _NMSettingOvsPatch {
+	NMSetting parent;
+
+	char *peer;
+};
+
+struct _NMSettingOvsPatchClass {
+	NMSettingClass parent;
+};
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingOvsPatch, nm_setting_ovs_patch, NM_TYPE_SETTING,
+                         _nm_register_setting (OVS_PATCH, NM_SETTING_PRIORITY_HW_BASE))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_PATCH)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_ovs_patch_get_peer:
+ * @self: the #NMSettingOvsPatch
+ *
+ * Returns: the #NMSettingOvsPatch:peer property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_patch_get_peer (NMSettingOvsPatch *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_PATCH (self), NULL);
+
+	return self->peer;
+}
+
+/*****************************************************************************/
+
+static int
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (setting);
+	int family = AF_UNSPEC;
+
+	if (!_nm_connection_verify_required_interface_name (connection, error))
+		return FALSE;
+
+	if (!self->peer) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_MISSING_PROPERTY,
+		                     _("property is missing"));
+		g_prefix_error (error, "%s.%s: ",
+		                NM_SETTING_OVS_PATCH_SETTING_NAME,
+		                NM_SETTING_OVS_PATCH_PEER);
+		return FALSE;
+	}
+
+	if (nm_utils_ipaddr_valid (AF_INET, self->peer))
+		family = AF_INET;
+	else if (nm_utils_ipaddr_valid (AF_INET6, self->peer))
+		family = AF_INET6;
+	else {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' is not a valid IP address"),
+		             self->peer);
+		g_prefix_error (error, "%s.%s: ",
+		                NM_SETTING_OVS_PATCH_SETTING_NAME,
+		                NM_SETTING_OVS_PATCH_PEER);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (object);
+
+	switch (prop_id) {
+	case PROP_PEER:
+		g_value_set_string (value, self->peer);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (object);
+
+	switch (prop_id) {
+	case PROP_PEER:
+		g_free (self->peer);
+		self->peer = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ovs_patch_init (NMSettingOvsPatch *self)
+{
+}
+
+/**
+ * nm_setting_ovs_patch_new:
+ *
+ * Creates a new #NMSettingOvsPatch object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingOvsPatch object
+ *
+ * Since: 1.10
+ **/
+NMSetting *
+nm_setting_ovs_patch_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_PATCH, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (object);
+
+	g_free (self->peer);
+
+	G_OBJECT_CLASS (nm_setting_ovs_patch_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_ovs_patch_class_init (NMSettingOvsPatchClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+	parent_class->verify = verify;
+
+	/**
+	 * NMSettingOvsPatch:peer:
+	 *
+	 * Specifies the unicast destination IP address of a remote OpenVSwitch
+	 * bridge port to connect to.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_PEER,
+	         g_param_spec_string (NM_SETTING_OVS_PATCH_PEER, "", "",
+	                              NULL,
+	                              G_PARAM_READWRITE |
+	                              G_PARAM_CONSTRUCT |
+	                              NM_SETTING_PARAM_INFERRABLE |
+	                              G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-ovs-patch.h b/libnm-core/nm-setting-ovs-patch.h
new file mode 100644
index 00000000..091f0d43
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-patch.h
@@ -0,0 +1,54 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_OVS_PATCH_H__
+#define __NM_SETTING_OVS_PATCH_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_OVS_PATCH            (nm_setting_ovs_patch_get_type ())
+#define NM_SETTING_OVS_PATCH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_PATCH, NMSettingOvsPatch))
+#define NM_SETTING_OVS_PATCH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_PATCHCONFIG, NMSettingOvsPatchClass))
+#define NM_IS_SETTING_OVS_PATCH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_PATCH))
+#define NM_IS_SETTING_OVS_PATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_PATCH))
+#define NM_SETTING_OVS_PATCH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_PATCH, NMSettingOvsPatchClass))
+
+#define NM_SETTING_OVS_PATCH_SETTING_NAME        "ovs-patch"
+
+#define NM_SETTING_OVS_PATCH_PEER                "peer"
+
+typedef struct _NMSettingOvsPatchClass NMSettingOvsPatchClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_setting_ovs_patch_get_type (void);
+NM_AVAILABLE_IN_1_10
+NMSetting *nm_setting_ovs_patch_new (void);
+
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_patch_get_peer (NMSettingOvsPatch *self);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_OVS_PATCH_H__ */
diff --git a/libnm-core/nm-setting-ovs-port.c b/libnm-core/nm-setting-ovs-port.c
new file mode 100644
index 00000000..bd05f2e7
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-port.c
@@ -0,0 +1,471 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-ovs-port.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-ovs-port
+ * @short_description: Describes connection properties for OpenVSwitch ports.
+ *
+ * The #NMSettingOvsPort object is a #NMSetting subclass that describes properties
+ * necessary for OpenVSwitch ports.
+ **/
+
+enum {
+	PROP_0,
+	PROP_VLAN_MODE,
+	PROP_TAG,
+	PROP_LACP,
+	PROP_BOND_MODE,
+	PROP_BOND_UPDELAY,
+	PROP_BOND_DOWNDELAY,
+	LAST_PROP
+};
+
+/**
+ * NMSettingOvsPort:
+ *
+ * OvsPort Link Settings
+ */
+struct _NMSettingOvsPort {
+	NMSetting parent;
+
+	char *vlan_mode;
+	guint tag;
+	char *lacp;
+	char *bond_mode;
+	guint bond_updelay;
+	guint bond_downdelay;
+};
+
+struct _NMSettingOvsPortClass {
+	NMSettingClass parent;
+};
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingOvsPort, nm_setting_ovs_port, NM_TYPE_SETTING,
+                         _nm_register_setting (OVS_PORT, NM_SETTING_PRIORITY_HW_BASE))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_PORT)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_ovs_port_get_vlan_mode:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:vlan-mode property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_port_get_vlan_mode (NMSettingOvsPort *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), NULL);
+
+	return self->vlan_mode;
+}
+
+/**
+ * nm_setting_ovs_port_get_tag:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:tag property of the setting
+ *
+ * Since: 1.10
+ **/
+guint
+nm_setting_ovs_port_get_tag (NMSettingOvsPort *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), 0);
+
+	return self->tag;
+}
+
+/**
+ * nm_setting_ovs_port_get_lacp:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:lacp property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_port_get_lacp (NMSettingOvsPort *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), NULL);
+
+	return self->lacp;
+}
+
+/**
+ * nm_setting_ovs_port_get_bond_mode:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:bond-mode property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_ovs_port_get_bond_mode (NMSettingOvsPort *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), NULL);
+
+	return self->bond_mode;
+}
+
+/**
+ * nm_setting_ovs_port_get_bond_updelay:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:bond-updelay property of the setting
+ *
+ * Since: 1.10
+ **/
+guint
+nm_setting_ovs_port_get_bond_updelay (NMSettingOvsPort *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), 0);
+
+	return self->bond_updelay;
+}
+
+/**
+ * nm_setting_ovs_port_get_bond_downdelay:
+ * @self: the #NMSettingOvsPort
+ *
+ * Returns: the #NMSettingOvsPort:bond-downdelay property of the setting
+ *
+ * Since: 1.10
+ **/
+guint
+nm_setting_ovs_port_get_bond_downdelay (NMSettingOvsPort *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), 0);
+
+	return self->bond_downdelay;
+}
+
+/*****************************************************************************/
+
+static int
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	NMSettingOvsPort *self = NM_SETTING_OVS_PORT (setting);
+
+	if (!_nm_connection_verify_required_interface_name (connection, error))
+		return FALSE;
+
+	if (connection) {
+		NMSettingConnection *s_con;
+		const char *slave_type;
+
+		s_con = nm_connection_get_setting_connection (connection);
+		if (!s_con) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_MISSING_SETTING,
+			             _("missing setting"));
+			g_prefix_error (error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME);
+			return FALSE;
+		}
+
+		if (!nm_setting_connection_get_master (s_con)) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("A connection with a '%s' setting must have a master."),
+			             NM_SETTING_OVS_PORT_SETTING_NAME);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER);
+			return FALSE;
+		}
+
+		slave_type = nm_setting_connection_get_slave_type (s_con);
+		if (   slave_type
+		    && strcmp (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("A connection with a '%s' setting must have the slave-type set to '%s'. Instead it is '%s'"),
+			             NM_SETTING_OVS_PORT_SETTING_NAME,
+			             NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+			             slave_type);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE);
+			return FALSE;
+		}
+	}
+
+	if (!NM_IN_STRSET (self->vlan_mode, "access", "native-tagged", "native-untagged", "trunk", NULL)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' is not allowed in vlan_mode"),
+		             self->vlan_mode);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_VLAN_MODE);
+		return FALSE;
+	}
+
+	if (self->tag >= 4095) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("the tag id must be in range 0-4094 but is %u"),
+		             self->tag);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_TAG);
+		return FALSE;
+	}
+
+	if (!NM_IN_STRSET (self->lacp, "active", "off", "passive", NULL)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' is not allowed in lacp"),
+		             self->lacp);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_LACP);
+		return FALSE;
+	}
+
+	if (!NM_IN_STRSET (self->bond_mode, "active-backup", "balance-slb", "balance-tcp", NULL)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' is not allowed in bond_mode"),
+		             self->bond_mode);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_BOND_MODE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingOvsPort *self = NM_SETTING_OVS_PORT (object);
+
+	switch (prop_id) {
+	case PROP_VLAN_MODE:
+		g_value_set_string (value, self->vlan_mode);
+		break;
+	case PROP_TAG:
+		g_value_set_uint (value, self->tag);
+		break;
+	case PROP_LACP:
+		g_value_set_string (value, self->lacp);
+		break;
+	case PROP_BOND_MODE:
+		g_value_set_string (value, self->bond_mode);
+		break;
+	case PROP_BOND_UPDELAY:
+		g_value_set_uint (value, self->bond_updelay);
+		break;
+	case PROP_BOND_DOWNDELAY:
+		g_value_set_uint (value, self->bond_downdelay);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettingOvsPort *self = NM_SETTING_OVS_PORT (object);
+
+	switch (prop_id) {
+	case PROP_VLAN_MODE:
+		g_free (self->vlan_mode);
+		self->vlan_mode = g_value_dup_string (value);
+		break;
+	case PROP_TAG:
+		self->tag = g_value_get_uint (value);
+		break;
+	case PROP_LACP:
+		g_free (self->lacp);
+		self->lacp = g_value_dup_string (value);
+		break;
+	case PROP_BOND_MODE:
+		g_free (self->bond_mode);
+		self->bond_mode = g_value_dup_string (value);
+		break;
+	case PROP_BOND_UPDELAY:
+		self->bond_updelay = g_value_get_uint (value);
+		break;
+	case PROP_BOND_DOWNDELAY:
+		self->bond_downdelay = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ovs_port_init (NMSettingOvsPort *self)
+{
+}
+
+/**
+ * nm_setting_ovs_port_new:
+ *
+ * Creates a new #NMSettingOvsPort object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingOvsPort object
+ *
+ * Since: 1.10
+ **/
+NMSetting *
+nm_setting_ovs_port_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_PORT, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingOvsPort *self = NM_SETTING_OVS_PORT (object);
+
+	g_free (self->vlan_mode);
+	g_free (self->lacp);
+	g_free (self->bond_mode);
+
+	G_OBJECT_CLASS (nm_setting_ovs_port_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_ovs_port_class_init (NMSettingOvsPortClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+	parent_class->verify = verify;
+
+	/**
+	 * NMSettingOvsPort:vlan-mode:
+	 *
+	 * The VLAN mode. One of "access", "native-tagged", "native-untagged",
+	 * "trunk" or unset.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_VLAN_MODE,
+	         g_param_spec_string (NM_SETTING_OVS_PORT_VLAN_MODE, "", "",
+	                              NULL,
+	                              G_PARAM_READWRITE |
+	                              G_PARAM_CONSTRUCT |
+	                              NM_SETTING_PARAM_INFERRABLE |
+	                              G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingOvsPort:tag:
+	 *
+	 * The VLAN tag in the range 0-4095.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_TAG,
+	         g_param_spec_uint (NM_SETTING_OVS_PORT_TAG, "", "",
+	                            0, 4095, 0,
+	                            G_PARAM_READWRITE |
+	                            G_PARAM_CONSTRUCT |
+	                            NM_SETTING_PARAM_INFERRABLE |
+	                            G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingOvsPort:lacp:
+	 *
+	 * LACP mode. One of "active", "off", or "passive".
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_LACP,
+	         g_param_spec_string (NM_SETTING_OVS_PORT_LACP, "", "",
+	                              NULL,
+	                              G_PARAM_READWRITE |
+	                              G_PARAM_CONSTRUCT |
+	                              NM_SETTING_PARAM_INFERRABLE |
+	                              G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingOvsPort:bond-mode:
+	 *
+	 * Bonding mode. One of "active-backup", "balance-slb", or "balance-tcp".
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_BOND_MODE,
+	         g_param_spec_string (NM_SETTING_OVS_PORT_BOND_MODE, "", "",
+	                              NULL,
+	                              G_PARAM_READWRITE |
+	                              G_PARAM_CONSTRUCT |
+	                              NM_SETTING_PARAM_INFERRABLE |
+	                              G_PARAM_STATIC_STRINGS));
+
+
+	/**
+	 * NMSettingOvsPort:bond-updelay:
+	 *
+	 * The time port must be active befor it starts forwarding traffic.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_BOND_UPDELAY,
+	         g_param_spec_uint (NM_SETTING_OVS_PORT_BOND_UPDELAY, "", "",
+	                            0, G_MAXUINT, 0,
+	                            G_PARAM_READWRITE |
+	                            G_PARAM_CONSTRUCT |
+	                            NM_SETTING_PARAM_INFERRABLE |
+	                            G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingOvsPort:bond-downdelay:
+	 *
+	 * The time port must be inactive in order to be considered down.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_BOND_DOWNDELAY,
+	         g_param_spec_uint (NM_SETTING_OVS_PORT_BOND_DOWNDELAY, "", "",
+	                            0, G_MAXUINT, 0,
+	                            G_PARAM_READWRITE |
+	                            G_PARAM_CONSTRUCT |
+	                            NM_SETTING_PARAM_INFERRABLE |
+	                            G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-ovs-port.h b/libnm-core/nm-setting-ovs-port.h
new file mode 100644
index 00000000..4c14f123
--- /dev/null
+++ b/libnm-core/nm-setting-ovs-port.h
@@ -0,0 +1,69 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_OVS_PORT_H__
+#define __NM_SETTING_OVS_PORT_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_OVS_PORT            (nm_setting_ovs_port_get_type ())
+#define NM_SETTING_OVS_PORT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_PORT, NMSettingOvsPort))
+#define NM_SETTING_OVS_PORT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_PORTCONFIG, NMSettingOvsPortClass))
+#define NM_IS_SETTING_OVS_PORT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_PORT))
+#define NM_IS_SETTING_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_PORT))
+#define NM_SETTING_OVS_PORT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_PORT, NMSettingOvsPortClass))
+
+#define NM_SETTING_OVS_PORT_SETTING_NAME        "ovs-port"
+
+#define NM_SETTING_OVS_PORT_VLAN_MODE           "vlan-mode"
+#define NM_SETTING_OVS_PORT_TAG                 "tag"
+#define NM_SETTING_OVS_PORT_LACP                "lacp"
+#define NM_SETTING_OVS_PORT_BOND_MODE           "bond-mode"
+#define NM_SETTING_OVS_PORT_BOND_UPDELAY        "bond-updelay"
+#define NM_SETTING_OVS_PORT_BOND_DOWNDELAY      "bond-downdelay"
+
+typedef struct _NMSettingOvsPortClass NMSettingOvsPortClass;
+
+NM_AVAILABLE_IN_1_10
+GType nm_setting_ovs_port_get_type (void);
+NM_AVAILABLE_IN_1_10
+NMSetting *nm_setting_ovs_port_new (void);
+
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_port_get_vlan_mode      (NMSettingOvsPort *self);
+NM_AVAILABLE_IN_1_10
+guint       nm_setting_ovs_port_get_tag            (NMSettingOvsPort *self);
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_port_get_lacp           (NMSettingOvsPort *self);
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_ovs_port_get_bond_mode      (NMSettingOvsPort *self);
+NM_AVAILABLE_IN_1_10
+guint       nm_setting_ovs_port_get_bond_updelay   (NMSettingOvsPort *self);
+NM_AVAILABLE_IN_1_10
+guint       nm_setting_ovs_port_get_bond_downdelay (NMSettingOvsPort *self);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_OVS_PORT_H__ */
diff --git a/libnm-core/nm-setting-ppp.c b/libnm-core/nm-setting-ppp.c
index 3519117c..32900c84 100644
--- a/libnm-core/nm-setting-ppp.c
+++ b/libnm-core/nm-setting-ppp.c
@@ -36,7 +36,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingPpp, nm_setting_ppp, NM_TYPE_SETTING,
-                         _nm_register_setting (PPP, 3))
+                         _nm_register_setting (PPP, NM_SETTING_PRIORITY_AUX))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_PPP)
 
 #define NM_SETTING_PPP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPP, NMSettingPppPrivate))
@@ -663,7 +663,7 @@ nm_setting_ppp_class_init (NMSettingPppClass *setting_class)
 	/**
 	 * NMSettingPpp:require-mppe:
 	 *
-	 * If %TRUE, MPPE (Microsoft Point-to-Point Encrpytion) will be required for
+	 * If %TRUE, MPPE (Microsoft Point-to-Point Encryption) will be required for
 	 * the PPP session.  If either 64-bit or 128-bit MPPE is not available the
 	 * session will fail.  Note that MPPE is not used on mobile broadband
 	 * connections.
@@ -679,7 +679,7 @@ nm_setting_ppp_class_init (NMSettingPppClass *setting_class)
 	/**
 	 * NMSettingPpp:require-mppe-128:
 	 *
-	 * If %TRUE, 128-bit MPPE (Microsoft Point-to-Point Encrpytion) will be
+	 * If %TRUE, 128-bit MPPE (Microsoft Point-to-Point Encryption) will be
 	 * required for the PPP session, and the "require-mppe" property must also
 	 * be set to %TRUE.  If 128-bit MPPE is not available the session will fail.
 	 **/
diff --git a/libnm-core/nm-setting-pppoe.c b/libnm-core/nm-setting-pppoe.c
index c7fbd56d..d7a1a0db 100644
--- a/libnm-core/nm-setting-pppoe.c
+++ b/libnm-core/nm-setting-pppoe.c
@@ -39,12 +39,13 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingPppoe, nm_setting_pppoe, NM_TYPE_SETTING,
-                         _nm_register_setting (PPPOE, 3))
+                         _nm_register_setting (PPPOE, NM_SETTING_PRIORITY_AUX))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_PPPOE)
 
 #define NM_SETTING_PPPOE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPPOE, NMSettingPppoePrivate))
 
 typedef struct {
+	char *parent;
 	char *service;
 	char *username;
 	char *password;
@@ -53,6 +54,7 @@ typedef struct {
 
 enum {
 	PROP_0,
+	PROP_PARENT,
 	PROP_SERVICE,
 	PROP_USERNAME,
 	PROP_PASSWORD,
@@ -75,6 +77,22 @@ nm_setting_pppoe_new (void)
 }
 
 /**
+ * nm_setting_pppoe_get_parent:
+ * @setting: the #NMSettingPppoe
+ *
+ * Returns: the #NMSettingPppoe:parent property of the setting
+ *
+ * Since: 1.10
+ **/
+const char *
+nm_setting_pppoe_get_parent (NMSettingPppoe *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NULL);
+
+	return NM_SETTING_PPPOE_GET_PRIVATE (setting)->parent;
+}
+
+/**
  * nm_setting_pppoe_get_service:
  * @setting: the #NMSettingPppoe
  *
@@ -134,6 +152,7 @@ static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingPppoePrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (setting);
+	gs_free_error GError *local_error = NULL;
 
 	if (!priv->username) {
 		g_set_error_literal (error,
@@ -160,6 +179,16 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
+	if (   priv->parent
+	    && !nm_utils_is_valid_iface_name (priv->parent, &local_error)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             "'%s': %s", priv->parent, local_error->message);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_PARENT);
+		return FALSE;
+	}
+
 	return TRUE;
 }
 
@@ -192,6 +221,10 @@ set_property (GObject *object, guint prop_id,
 	NMSettingPppoePrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (object);
 
 	switch (prop_id) {
+	case PROP_PARENT:
+		g_free (priv->parent);
+		priv->parent = g_value_dup_string (value);
+		break;
 	case PROP_SERVICE:
 		g_free (priv->service);
 		priv->service = g_value_dup_string (value);
@@ -220,6 +253,9 @@ get_property (GObject *object, guint prop_id,
 	NMSettingPppoe *setting = NM_SETTING_PPPOE (object);
 
 	switch (prop_id) {
+	case PROP_PARENT:
+		g_value_set_string (value, nm_setting_pppoe_get_parent (setting));
+		break;
 	case PROP_SERVICE:
 		g_value_set_string (value, nm_setting_pppoe_get_service (setting));
 		break;
@@ -243,6 +279,7 @@ finalize (GObject *object)
 {
 	NMSettingPppoePrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (object);
 
+	g_free (priv->parent);
 	g_free (priv->username);
 	g_free (priv->password);
 	g_free (priv->service);
@@ -267,6 +304,25 @@ nm_setting_pppoe_class_init (NMSettingPppoeClass *setting_class)
 
 	/* Properties */
 	/**
+	 * NMSettingPppoe:parent:
+	 *
+	 * If given, specifies the parent interface name on which this PPPoE
+	 * connection should be created.  If this property is not specified,
+	 * the connection is activated on the interface specified in
+	 * #NMSettingConnection:interface-name of #NMSettingConnection.
+	 *
+	 * Since: 1.10
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PARENT,
+		 g_param_spec_string (NM_SETTING_PPPOE_PARENT, "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_CONSTRUCT |
+		                      NM_SETTING_PARAM_INFERRABLE |
+		                      G_PARAM_STATIC_STRINGS));
+
+	/**
 	 * NMSettingPppoe:service:
 	 *
 	 * If specified, instruct PPPoE to only initiate sessions with access
diff --git a/libnm-core/nm-setting-pppoe.h b/libnm-core/nm-setting-pppoe.h
index b6717cde..cab96e3b 100644
--- a/libnm-core/nm-setting-pppoe.h
+++ b/libnm-core/nm-setting-pppoe.h
@@ -40,6 +40,7 @@ G_BEGIN_DECLS
 
 #define NM_SETTING_PPPOE_SETTING_NAME "pppoe"
 
+#define NM_SETTING_PPPOE_PARENT         "parent"
 #define NM_SETTING_PPPOE_SERVICE        "service"
 #define NM_SETTING_PPPOE_USERNAME       "username"
 #define NM_SETTING_PPPOE_PASSWORD       "password"
@@ -64,6 +65,8 @@ typedef struct {
 GType nm_setting_pppoe_get_type (void);
 
 NMSetting  *nm_setting_pppoe_new          (void);
+NM_AVAILABLE_IN_1_10
+const char *nm_setting_pppoe_get_parent   (NMSettingPppoe *setting);
 const char *nm_setting_pppoe_get_service  (NMSettingPppoe *setting);
 const char *nm_setting_pppoe_get_username (NMSettingPppoe *setting);
 const char *nm_setting_pppoe_get_password (NMSettingPppoe *setting);
diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h
index 79b6ac87..30e2a160 100644
--- a/libnm-core/nm-setting-private.h
+++ b/libnm-core/nm-setting-private.h
@@ -27,17 +27,17 @@
 
 #include "nm-core-internal.h"
 
-void _nm_register_setting (const char *name,
-                           const GType type,
-                           const guint32 priority);
+void _nm_register_setting_impl (const char *name,
+                                GType type,
+                                NMSettingPriority priority);
 
 #define _nm_register_setting(name, priority) \
 	G_STMT_START { \
-		_nm_register_setting (NM_SETTING_ ## name ## _SETTING_NAME "", g_define_type_id, priority); \
+		_nm_register_setting_impl ("" NM_SETTING_ ## name ## _SETTING_NAME "", g_define_type_id, priority); \
 	} G_STMT_END
 
-gboolean _nm_setting_is_base_type (NMSetting *setting);
-gboolean _nm_setting_type_is_base_type (GType type);
+NMSettingPriority _nm_setting_get_base_type_priority (NMSetting *setting);
+NMSettingPriority _nm_setting_type_get_base_type_priority (GType type);
 gint _nm_setting_compare_priority (gconstpointer a, gconstpointer b);
 
 typedef enum NMSettingUpdateSecretResult {
diff --git a/libnm-core/nm-setting-proxy.c b/libnm-core/nm-setting-proxy.c
index 1492892c..58a22080 100644
--- a/libnm-core/nm-setting-proxy.c
+++ b/libnm-core/nm-setting-proxy.c
@@ -40,7 +40,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingProxy, nm_setting_proxy, NM_TYPE_SETTING,
-                         _nm_register_setting (PROXY, 4))
+                         _nm_register_setting (PROXY, NM_SETTING_PRIORITY_IP))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_PROXY)
 
 #define NM_SETTING_PROXY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PROXY, NMSettingProxyPrivate))
diff --git a/libnm-core/nm-setting-serial.c b/libnm-core/nm-setting-serial.c
index fd251b7d..e0da89aa 100644
--- a/libnm-core/nm-setting-serial.c
+++ b/libnm-core/nm-setting-serial.c
@@ -38,7 +38,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingSerial, nm_setting_serial, NM_TYPE_SETTING,
-                         _nm_register_setting (SERIAL, 2))
+                         _nm_register_setting (SERIAL, NM_SETTING_PRIORITY_HW_AUX))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_SERIAL)
 
 #define NM_SETTING_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_SERIAL, NMSettingSerialPrivate))
diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c
index f64aa5f9..c0309665 100644
--- a/libnm-core/nm-setting-team-port.c
+++ b/libnm-core/nm-setting-team-port.c
@@ -40,7 +40,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingTeamPort, nm_setting_team_port, NM_TYPE_SETTING,
-                         _nm_register_setting (TEAM_PORT, 3))
+                         _nm_register_setting (TEAM_PORT, NM_SETTING_PRIORITY_AUX))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM_PORT)
 
 #define NM_SETTING_TEAM_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPortPrivate))
diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c
index e83ce309..0a09c4a6 100644
--- a/libnm-core/nm-setting-team.c
+++ b/libnm-core/nm-setting-team.c
@@ -27,7 +27,6 @@
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-connection-private.h"
-#include "nm-utils-private.h"
 
 /**
  * SECTION:nm-setting-team
@@ -38,7 +37,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING,
-                         _nm_register_setting (TEAM, 1))
+                         _nm_register_setting (TEAM, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM)
 
 #define NM_SETTING_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM, NMSettingTeamPrivate))
diff --git a/libnm-core/nm-setting-tun.c b/libnm-core/nm-setting-tun.c
index 62e9ef14..dab407bd 100644
--- a/libnm-core/nm-setting-tun.c
+++ b/libnm-core/nm-setting-tun.c
@@ -39,7 +39,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingTun, nm_setting_tun, NM_TYPE_SETTING,
-                         _nm_register_setting (TUN, 1))
+                         _nm_register_setting (TUN, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TUN)
 
 #define NM_SETTING_TUN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TUN, NMSettingTunPrivate))
diff --git a/libnm-core/nm-setting-user.c b/libnm-core/nm-setting-user.c
index 71d73891..049607e1 100644
--- a/libnm-core/nm-setting-user.c
+++ b/libnm-core/nm-setting-user.c
@@ -64,7 +64,7 @@ struct _NMSettingUserClass {
 };
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingUser, nm_setting_user, NM_TYPE_SETTING,
-                         _nm_register_setting (USER, 10))
+                         _nm_register_setting (USER, NM_SETTING_PRIORITY_USER))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_USER)
 
 #define NM_SETTING_USER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMSettingUser, NM_IS_SETTING_USER)
@@ -597,6 +597,18 @@ nm_setting_user_class_init (NMSettingUserClass *klass)
 	 *
 	 * Since: 1.8
 	 **/
+	/* ---ifcfg-rh---
+	 * property: data
+	 * variable: NM_USER_*
+	 * description: each key/value pair is stored as a separate variable with
+	 *   name composed by concatenating NM_USER_ with the encoded key. The key is
+	 *   encoded by substituting lowercase letters with uppercase and prepending
+	 *   uppercase letters with an underscore. A dot is encoded as a double
+	 *   underscore. Remaining characters are encoded as underscore followed by a
+	 *   3 digit octal representation of the character.
+	 * example: NM_USER_FOO__BAR=something
+	 * ---end---
+	 */
 	obj_properties[PROP_DATA] =
 	    g_param_spec_boxed (NM_SETTING_USER_DATA, "", "",
 	                        G_TYPE_HASH_TABLE,
diff --git a/libnm-core/nm-setting-vlan.c b/libnm-core/nm-setting-vlan.c
index ab1c5465..f9c6e02d 100644
--- a/libnm-core/nm-setting-vlan.c
+++ b/libnm-core/nm-setting-vlan.c
@@ -42,7 +42,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingVlan, nm_setting_vlan, NM_TYPE_SETTING,
-                         _nm_register_setting (VLAN, 1))
+                         _nm_register_setting (VLAN, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VLAN)
 
 #define NM_SETTING_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VLAN, NMSettingVlanPrivate))
diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c
index aa4ddc3a..6b42e0c7 100644
--- a/libnm-core/nm-setting-vpn.c
+++ b/libnm-core/nm-setting-vpn.c
@@ -44,7 +44,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingVpn, nm_setting_vpn, NM_TYPE_SETTING,
-                         _nm_register_setting (VPN, 1))
+                         _nm_register_setting (VPN, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VPN)
 
 #define NM_SETTING_VPN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VPN, NMSettingVpnPrivate))
@@ -933,7 +933,7 @@ nm_setting_vpn_class_init (NMSettingVpnClass *setting_class)
 	 *
 	 * Timeout for the VPN service to establish the connection. Some services
 	 * may take quite a long time to connect.
-	 * Value of 0 means a default timeout, which is 60 seconds (unless overriden
+	 * Value of 0 means a default timeout, which is 60 seconds (unless overridden
 	 * by vpn.timeout in configuration file). Values greater than zero mean
 	 * timeout in seconds.
 	 *
diff --git a/libnm-core/nm-setting-vxlan.c b/libnm-core/nm-setting-vxlan.c
index f6717a2f..4be6a236 100644
--- a/libnm-core/nm-setting-vxlan.c
+++ b/libnm-core/nm-setting-vxlan.c
@@ -37,7 +37,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingVxlan, nm_setting_vxlan, NM_TYPE_SETTING,
-                         _nm_register_setting (VXLAN, 1))
+                         _nm_register_setting (VXLAN, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VXLAN)
 
 #define NM_SETTING_VXLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VXLAN, NMSettingVxlanPrivate))
@@ -598,7 +598,7 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *setting_class)
 	/**
 	 * NMSettingVxlan:id:
 	 *
-	 * Specifies the VXLAN Network Identifer (or VXLAN Segment Identifier) to
+	 * Specifies the VXLAN Network Identifier (or VXLAN Segment Identifier) to
 	 * use.
 	 *
 	 * Since: 1.2
diff --git a/libnm-core/nm-setting-wimax.c b/libnm-core/nm-setting-wimax.c
index f893cbdc..dbfb46e4 100644
--- a/libnm-core/nm-setting-wimax.c
+++ b/libnm-core/nm-setting-wimax.c
@@ -43,7 +43,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingWimax, nm_setting_wimax, NM_TYPE_SETTING,
-                         _nm_register_setting (WIMAX, 1))
+                         _nm_register_setting (WIMAX, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIMAX)
 
 #define NM_SETTING_WIMAX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIMAX, NMSettingWimaxPrivate))
diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c
index 5476e357..9d255e1d 100644
--- a/libnm-core/nm-setting-wired.c
+++ b/libnm-core/nm-setting-wired.c
@@ -41,7 +41,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingWired, nm_setting_wired, NM_TYPE_SETTING,
-                         _nm_register_setting (WIRED, 1))
+                         _nm_register_setting (WIRED, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRED)
 
 #define NM_SETTING_WIRED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRED, NMSettingWiredPrivate))
@@ -1012,9 +1012,9 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 	/**
 	 * NMSettingWired:port:
 	 *
-	 * Specific port type to use if multiple the device supports multiple
+	 * Specific port type to use if the device supports multiple
 	 * attachment methods.  One of "tp" (Twisted Pair), "aui" (Attachment Unit
-	 * Interface), "bnc" (Thin Ethernet) or "mii" (Media Independent Interface.
+	 * Interface), "bnc" (Thin Ethernet) or "mii" (Media Independent Interface).
 	 * If the device supports only one port type, this setting is ignored.
 	 **/
 	/* ---ifcfg-rh---
@@ -1033,7 +1033,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 	/**
 	 * NMSettingWired:speed:
 	 *
-	 * Can be set to a value grater than zero only when "auto-negotiate" is "off".
+	 * Can be set to a value greater than zero only when "auto-negotiate" is "off".
 	 * In that case, statically configures the device to use that specified speed.
 	 * In Mbit/s, ie 100 == 100Mbit/s.
 	 * Must be set together with the "duplex" property when non-zero.
@@ -1147,7 +1147,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 	 * If specified, request that the device use this MAC address instead.
 	 * This is known as MAC cloning or spoofing.
 	 *
-	 * Beside explicitly specifing a MAC address, the special values "preserve", "permanent",
+	 * Beside explicitly specifying a MAC address, the special values "preserve", "permanent",
 	 * "random" and "stable" are supported.
 	 * "preserve" means not to touch the MAC address on activation.
 	 * "permanent" means to use the permanent hardware address if the device
@@ -1243,7 +1243,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 	 * "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled
 	 * globally-administered, burned-in MAC address.
 	 *
-	 * If the value contains more then one additional MAC addresses, one of
+	 * If the value contains more than one additional MAC addresses, one of
 	 * them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00"
 	 * will create a fully scrambled MAC address, randomly locally or globally
 	 * administered.
diff --git a/libnm-core/nm-setting-wireless-security.c b/libnm-core/nm-setting-wireless-security.c
index 70bdf754..91bcbe7a 100644
--- a/libnm-core/nm-setting-wireless-security.c
+++ b/libnm-core/nm-setting-wireless-security.c
@@ -16,7 +16,7 @@
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301 USA.
  *
- * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2017 Red Hat, Inc.
  * Copyright 2007 - 2008 Novell, Inc.
  */
 
@@ -54,7 +54,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingWirelessSecurity, nm_setting_wireless_security, NM_TYPE_SETTING,
-                         _nm_register_setting (WIRELESS_SECURITY, 2))
+                         _nm_register_setting (WIRELESS_SECURITY, NM_SETTING_PRIORITY_HW_AUX))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRELESS_SECURITY)
 
 #define NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityPrivate))
@@ -65,6 +65,7 @@ typedef struct {
 	GSList *proto; /* GSList of strings */
 	GSList *pairwise; /* GSList of strings */
 	GSList *group; /* GSList of strings */
+	NMSettingWirelessSecurityPmf pmf;
 
 	/* LEAP */
 	char *leap_username;
@@ -83,6 +84,9 @@ typedef struct {
 	/* WPA-PSK */
 	char *psk;
 	NMSettingSecretFlags psk_flags;
+
+	/* WPS */
+	NMSettingWirelessSecurityWpsMethod wps_method;
 } NMSettingWirelessSecurityPrivate;
 
 enum {
@@ -93,6 +97,7 @@ enum {
 	PROP_PROTO,
 	PROP_PAIRWISE,
 	PROP_GROUP,
+	PROP_PMF,
 	PROP_LEAP_USERNAME,
 	PROP_WEP_KEY0,
 	PROP_WEP_KEY1,
@@ -104,6 +109,7 @@ enum {
 	PROP_PSK_FLAGS,
 	PROP_LEAP_PASSWORD,
 	PROP_LEAP_PASSWORD_FLAGS,
+	PROP_WPS_METHOD,
 
 	LAST_PROP
 };
@@ -573,6 +579,22 @@ nm_setting_wireless_security_clear_groups (NMSettingWirelessSecurity *setting)
 	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP);
 }
 
+/*
+ * nm_setting_wireless_security_get_pmf:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingWirelessSecurity:pmf property of the setting
+ *
+ * Since: 1.10
+ **/
+NMSettingWirelessSecurityPmf
+nm_setting_wireless_security_get_pmf (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->pmf;
+}
+
 /**
  * nm_setting_wireless_security_get_psk:
  * @setting: the #NMSettingWirelessSecurity
@@ -775,6 +797,23 @@ nm_setting_wireless_security_get_wep_key_type (NMSettingWirelessSecurity *settin
 	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wep_key_type;
 }
 
+/**
+ * nm_setting_wireless_security_get_wps_method:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingWirelessSecurity:wps-method property of the setting
+ *
+ * Since: 1.10
+ **/
+NMSettingWirelessSecurityWpsMethod
+nm_setting_wireless_security_get_wps_method (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting),
+	                      NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED);
+
+	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wps_method;
+}
+
 static GPtrArray *
 need_secrets (NMSetting *setting)
 {
@@ -1013,6 +1052,50 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		}
 	}
 
+	G_STATIC_ASSERT_EXPR (((NMSettingWirelessSecurityPmf) -1) > 0);
+	if (priv->pmf > NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("property is invalid"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PMF);
+		return FALSE;
+	}
+
+	if (   NM_IN_SET (priv->pmf,
+	                  NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL,
+	                  NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)
+	    && !NM_IN_STRSET (priv->key_mgmt, "wpa-eap", "wpa-psk")) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' can only be used with '%s=%s' or '%s=%s'"),
+		             priv->pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL ? "optional" : "required",
+		             NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap",
+		             NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk");
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PMF);
+		return FALSE;
+	}
+
+	/* WPS */
+	if (priv->wps_method > NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("property is invalid"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WPS_METHOD);
+		return FALSE;
+	}
+
+	if (priv->wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED && priv->wps_method != NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("can't be simultaneously disabled and enabled"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WPS_METHOD);
+		return FALSE;
+	}
+
 	return TRUE;
 }
 
@@ -1198,6 +1281,9 @@ set_property (GObject *object, guint prop_id,
 		g_slist_free_full (priv->group, g_free);
 		priv->group = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
 		break;
+	case PROP_PMF:
+		priv->pmf = g_value_get_int (value);
+		break;
 	case PROP_LEAP_USERNAME:
 		g_free (priv->leap_username);
 		priv->leap_username = g_value_dup_string (value);
@@ -1238,6 +1324,9 @@ set_property (GObject *object, guint prop_id,
 	case PROP_WEP_KEY_TYPE:
 		priv->wep_key_type = g_value_get_enum (value);
 		break;
+	case PROP_WPS_METHOD:
+		priv->wps_method = g_value_get_uint (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1270,6 +1359,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_GROUP:
 		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->group, TRUE));
 		break;
+	case PROP_PMF:
+		g_value_set_int (value, nm_setting_wireless_security_get_pmf (setting));
+		break;
 	case PROP_LEAP_USERNAME:
 		g_value_set_string (value, priv->leap_username);
 		break;
@@ -1303,6 +1395,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_WEP_KEY_TYPE:
 		g_value_set_enum (value, priv->wep_key_type);
 		break;
+	case PROP_WPS_METHOD:
+		g_value_set_uint (value, priv->wps_method);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1468,6 +1563,37 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *setting
 		                     G_PARAM_STATIC_STRINGS));
 
 	/**
+	 * NMSettingWirelessSecurity:pmf:
+	 *
+	 * Indicates whether Protected Management Frames (802.11w) must be enabled
+	 * for the connection.  One of %NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT
+	 * (use global default value), %NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE
+	 * (disable PMF), %NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL (enable PMF if
+	 * the supplicant and the access point support it) or
+	 * %NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED (enable PMF and fail if not
+	 * supported).  When set to %NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT and no
+	 * global default is set, PMF will be optionally enabled.
+	 *
+	 * Since: 1.10
+	 **/
+	/* ---ifcfg-rh---
+	 * property: pmf
+	 * variable: PMF(+)
+	 * values: default, disable, optional, required
+	 * description: Enables or disables PMF (802.11w)
+	 * example: PMF=required
+	 * ---end---
+	 */
+	g_object_class_install_property
+		(object_class, PROP_PMF,
+		 g_param_spec_int (NM_SETTING_WIRELESS_SECURITY_PMF, "", "",
+		                   G_MININT32, G_MAXINT32, 0,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_CONSTRUCT |
+		                   NM_SETTING_PARAM_FUZZY_IGNORE |
+		                   G_PARAM_STATIC_STRINGS));
+
+	/**
 	 * NMSettingWirelessSecurity:leap-username:
 	 *
 	 * The login username for legacy LEAP connections (ie, key-mgmt =
@@ -1708,4 +1834,34 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *setting
 	                                      G_VARIANT_TYPE_UINT32,
 	                                      wep_key_type_to_dbus,
 	                                      NULL);
+	/**
+	 * NMSettingWirelessSecurity:wps-method:
+	 *
+	 * Flags indicating which mode of WPS is to be used if any.
+	 *
+	 * There's little point in changing the default setting as NetworkManager will
+	 * automatically determine whether it's feasible to start WPS enrollment from
+	 * the Access Point capabilities.
+	 *
+	 * WPS can be disabled by setting this property to a value of 1.
+	 *
+	 * Since: 1.10
+	 **/
+	/* ---ifcfg-rh---
+	 * property: wps-method
+	 * variable: WPS_METHOD
+	 * description: Used to control the WPS methods to be used
+	 *    Valid values are "default", "auto", "disabled", "pin" and "pbc".
+	 *    If omitted, whatver the AP announces is used.
+	 * example: WPS_METHOD=disabled, WPS_METHOD="pin pbc"
+	 * ---end---
+	 */
+	g_object_class_install_property
+		(object_class, PROP_WPS_METHOD,
+		 g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WPS_METHOD, "", "",
+		                    0, G_MAXUINT32, NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_CONSTRUCT |
+		                    NM_SETTING_PARAM_FUZZY_IGNORE |
+		                    G_PARAM_STATIC_STRINGS));
 }
diff --git a/libnm-core/nm-setting-wireless-security.h b/libnm-core/nm-setting-wireless-security.h
index ae252f11..e7641b31 100644
--- a/libnm-core/nm-setting-wireless-security.h
+++ b/libnm-core/nm-setting-wireless-security.h
@@ -16,7 +16,7 @@
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301 USA.
  *
- * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2017 Red Hat, Inc.
  * Copyright 2007 - 2008 Novell, Inc.
  */
 
@@ -68,15 +68,54 @@ typedef enum {
 	NM_WEP_KEY_TYPE_KEY = 1,          /* Hex or ASCII */
 	NM_WEP_KEY_TYPE_PASSPHRASE = 2,   /* 104/128-bit Passphrase */
 
-	NM_WEP_KEY_TYPE_LAST = NM_WEP_KEY_TYPE_PASSPHRASE
+	NM_WEP_KEY_TYPE_LAST = NM_WEP_KEY_TYPE_PASSPHRASE, /*< skip >*/
 } NMWepKeyType;
 
+/**
+ * NMSettingWirelessSecurityPmf:
+ * @NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT: use the default value
+ * @NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE: disable PMF
+ * @NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL: enable PMF if the supplicant and the AP support it
+ * @NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED: require PMF and fail if not available
+ *
+ * These flags indicate whether PMF must be enabled.
+ **/
+typedef enum {
+	NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT       = 0,
+	NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE       = 1,
+	NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL      = 2,
+	NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED      = 3,
+	_NM_SETTING_WIRELESS_SECURITY_PMF_NUM, /*< skip >*/
+	NM_SETTING_WIRELESS_SECURITY_PMF_LAST          =  _NM_SETTING_WIRELESS_SECURITY_PMF_NUM - 1, /*< skip >*/
+} NMSettingWirelessSecurityPmf;
+
+/**
+ * NMSettingWirelessSecurityWpsMethod:
+ * @NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT: Attempt whichever method AP supports
+ * @NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED: WPS can not be used.
+ * @NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO: Use WPS, any method
+ * @NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC: use WPS push-buthon method
+ * @NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN: use PIN method
+ *
+ * Configure the use of WPS by a connection while it activates.
+ *
+ * Since: 1.10
+ **/
+typedef enum {
+	NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT     = 0x00000000,
+	NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED    = 0x00000001,
+	NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO        = 0x00000002,
+	NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC         = 0x00000004,
+	NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN         = 0x00000008,
+} NMSettingWirelessSecurityWpsMethod;
+
 #define NM_SETTING_WIRELESS_SECURITY_KEY_MGMT "key-mgmt"
 #define NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX "wep-tx-keyidx"
 #define NM_SETTING_WIRELESS_SECURITY_AUTH_ALG "auth-alg"
 #define NM_SETTING_WIRELESS_SECURITY_PROTO "proto"
 #define NM_SETTING_WIRELESS_SECURITY_PAIRWISE "pairwise"
 #define NM_SETTING_WIRELESS_SECURITY_GROUP "group"
+#define NM_SETTING_WIRELESS_SECURITY_PMF "pmf"
 #define NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME "leap-username"
 #define NM_SETTING_WIRELESS_SECURITY_WEP_KEY0 "wep-key0"
 #define NM_SETTING_WIRELESS_SECURITY_WEP_KEY1 "wep-key1"
@@ -88,6 +127,7 @@ typedef enum {
 #define NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS "psk-flags"
 #define NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD "leap-password"
 #define NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS "leap-password-flags"
+#define NM_SETTING_WIRELESS_SECURITY_WPS_METHOD "wps-method"
 
 /**
  * NMSettingWirelessSecurity:
@@ -132,6 +172,9 @@ void        nm_setting_wireless_security_remove_group          (NMSettingWireles
 gboolean    nm_setting_wireless_security_remove_group_by_value (NMSettingWirelessSecurity *setting, const char *group);
 void        nm_setting_wireless_security_clear_groups          (NMSettingWirelessSecurity *setting);
 
+NM_AVAILABLE_IN_1_10
+NMSettingWirelessSecurityPmf nm_setting_wireless_security_get_pmf (NMSettingWirelessSecurity *setting);
+
 const char *nm_setting_wireless_security_get_psk           (NMSettingWirelessSecurity *setting);
 NMSettingSecretFlags nm_setting_wireless_security_get_psk_flags (NMSettingWirelessSecurity *setting);
 
@@ -147,6 +190,9 @@ const char *nm_setting_wireless_security_get_auth_alg      (NMSettingWirelessSec
 NMSettingSecretFlags nm_setting_wireless_security_get_wep_key_flags (NMSettingWirelessSecurity *setting);
 NMWepKeyType nm_setting_wireless_security_get_wep_key_type (NMSettingWirelessSecurity *setting);
 
+NM_AVAILABLE_IN_1_10
+NMSettingWirelessSecurityWpsMethod nm_setting_wireless_security_get_wps_method (NMSettingWirelessSecurity *setting);
+
 G_END_DECLS
 
 #endif /* __NM_SETTING_WIRELESS_SECURITY_H__ */
diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c
index 8ae84d4c..0a3915bf 100644
--- a/libnm-core/nm-setting-wireless.c
+++ b/libnm-core/nm-setting-wireless.c
@@ -41,7 +41,7 @@
  **/
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingWireless, nm_setting_wireless, NM_TYPE_SETTING,
-                         _nm_register_setting (WIRELESS, 1))
+                         _nm_register_setting (WIRELESS, NM_SETTING_PRIORITY_HW_BASE))
 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRELESS)
 
 #define NM_SETTING_WIRELESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessPrivate))
@@ -1353,7 +1353,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class)
 	 * If specified, request that the device use this MAC address instead.
 	 * This is known as MAC cloning or spoofing.
 	 *
-	 * Beside explicitly specifing a MAC address, the special values "preserve", "permanent",
+	 * Beside explicitly specifying a MAC address, the special values "preserve", "permanent",
 	 * "random" and "stable" are supported.
 	 * "preserve" means not to touch the MAC address on activation.
 	 * "permanent" means to use the permanent hardware address of the device.
@@ -1447,7 +1447,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class)
 	 * "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled
 	 * globally-administered, burned-in MAC address.
 	 *
-	 * If the value contains more then one additional MAC addresses, one of
+	 * If the value contains more than one additional MAC addresses, one of
 	 * them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00"
 	 * will create a fully scrambled MAC address, randomly locally or globally
 	 * administered.
diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c
index e4be8706..9c8e53ae 100644
--- a/libnm-core/nm-setting.c
+++ b/libnm-core/nm-setting.c
@@ -60,7 +60,7 @@ G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)
 typedef struct {
 	const char *name;
 	GType type;
-	guint32 priority;
+	NMSettingPriority priority;
 } SettingInfo;
 
 typedef struct {
@@ -118,61 +118,31 @@ _ensure_registered_constructor (void)
 /*****************************************************************************/
 
 /*
- * _nm_register_setting:
+ * _nm_register_setting_impl:
  * @name: the name of the #NMSetting object to register
  * @type: the #GType of the #NMSetting
- * @priority: the sort priority of the setting, see below
+ * @priority: the sort priority of the setting, see #NMSettingPriority
  *
  * INTERNAL ONLY: registers a setting's internal properties with libnm.
- *
- * A setting's priority should roughly follow the OSI layer model, but it also
- * controls which settings get asked for secrets first.  Thus settings which
- * relate to things that must be working first, like hardware, should get a
- * higher priority than things which layer on top of the hardware.  For example,
- * the GSM/CDMA settings should provide secrets before the PPP setting does,
- * because a PIN is required to unlock the device before PPP can even start.
- * Even settings without secrets should be assigned the right priority.
- *
- * 0: reserved for the Connection setting
- *
- * 1: hardware-related settings like Ethernet, Wi-Fi, InfiniBand, Bridge, etc.
- * These priority 1 settings are also "base types", which means that at least
- * one of them is required for the connection to be valid, and their name is
- * valid in the 'type' property of the Connection setting.
- *
- * 2: hardware-related auxiliary settings that require a base setting to be
- * successful first, like Wi-Fi security, 802.1x, etc.
- *
- * 3: hardware-independent settings that are required before IP connectivity
- * can be established, like PPP, PPPoE, etc.
- *
- * 4: IP-level stuff
- *
- * 10: NMSettingUser
  */
 void
-(_nm_register_setting) (const char *name,
-                        const GType type,
-                        const guint32 priority)
+_nm_register_setting_impl (const char *name,
+                           GType type,
+                           NMSettingPriority priority)
 {
 	SettingInfo *info;
 
-	g_return_if_fail (name != NULL && *name);
-	g_return_if_fail (type != G_TYPE_INVALID);
-	g_return_if_fail (type != G_TYPE_NONE);
+	nm_assert (name && *name);
+	nm_assert (!NM_IN_SET (type, G_TYPE_INVALID, G_TYPE_NONE));
+	nm_assert (priority != NM_SETTING_PRIORITY_INVALID);
 
 	_ensure_registered ();
 
-	if (G_LIKELY ((info = g_hash_table_lookup (registered_settings, name)))) {
-		g_return_if_fail (info->type == type);
-		g_return_if_fail (info->priority == priority);
-		g_return_if_fail (g_strcmp0 (info->name, name) == 0);
-		return;
-	}
-	g_return_if_fail (g_hash_table_lookup (registered_settings_by_type, &type) == NULL);
+	nm_assert (!g_hash_table_lookup (registered_settings, name));
+	nm_assert (!g_hash_table_lookup (registered_settings_by_type, &type));
 
-	if (priority == 0)
-		g_assert_cmpstr (name, ==, NM_SETTING_CONNECTION_SETTING_NAME);
+	nm_assert (   priority != NM_SETTING_PRIORITY_CONNECTION
+	           || nm_streq (name, NM_SETTING_CONNECTION_SETTING_NAME));
 
 	info = g_slice_new0 (SettingInfo);
 	info->type = type;
@@ -189,7 +159,7 @@ _nm_setting_lookup_setting_by_type (GType type)
 	return g_hash_table_lookup (registered_settings_by_type, &type);
 }
 
-static guint32
+static NMSettingPriority
 _get_setting_type_priority (GType type)
 {
 	const SettingInfo *info;
@@ -200,7 +170,7 @@ _get_setting_type_priority (GType type)
 	return info->priority;
 }
 
-guint32
+NMSettingPriority
 _nm_setting_get_setting_priority (NMSetting *setting)
 {
 	NMSettingPrivate *priv;
@@ -211,21 +181,30 @@ _nm_setting_get_setting_priority (NMSetting *setting)
 	return priv->info->priority;
 }
 
-gboolean
-_nm_setting_type_is_base_type (GType type)
+NMSettingPriority
+_nm_setting_type_get_base_type_priority (GType type)
 {
+	NMSettingPriority priority;
+
 	/* Historical oddity: PPPoE is a base-type even though it's not
 	 * priority 1.  It needs to be sorted *after* lower-level stuff like
 	 * Wi-Fi security or 802.1x for secrets, but it's still allowed as a
 	 * base type.
 	 */
-	return _get_setting_type_priority (type) == 1 || (type == NM_TYPE_SETTING_PPPOE);
+	priority = _get_setting_type_priority (type);
+	if (   NM_IN_SET (priority,
+	                  NM_SETTING_PRIORITY_HW_BASE,
+	                  NM_SETTING_PRIORITY_HW_NON_BASE)
+	    || type == NM_TYPE_SETTING_PPPOE)
+		return priority;
+	else
+		return NM_SETTING_PRIORITY_INVALID;
 }
 
-gboolean
-_nm_setting_is_base_type (NMSetting *setting)
+NMSettingPriority
+_nm_setting_get_base_type_priority (NMSetting *setting)
 {
-	return _nm_setting_type_is_base_type (G_OBJECT_TYPE (setting));
+	return _nm_setting_type_get_base_type_priority (G_OBJECT_TYPE (setting));
 }
 
 /**
@@ -240,9 +219,9 @@ _nm_setting_is_base_type (NMSetting *setting)
 GType
 nm_setting_lookup_type (const char *name)
 {
-	SettingInfo *info;
+	const SettingInfo *info;
 
-	g_return_val_if_fail (name != NULL, G_TYPE_INVALID);
+	g_return_val_if_fail (name, G_TYPE_INVALID);
 
 	_ensure_registered ();
 
@@ -253,7 +232,7 @@ nm_setting_lookup_type (const char *name)
 gint
 _nm_setting_compare_priority (gconstpointer a, gconstpointer b)
 {
-	guint32 prio_a, prio_b;
+	NMSettingPriority prio_a, prio_b;
 
 	prio_a = _nm_setting_get_setting_priority ((NMSetting *) a);
 	prio_b = _nm_setting_get_setting_priority ((NMSetting *) b);
@@ -279,6 +258,10 @@ _nm_setting_slave_type_is_valid (const char *slave_type, const char **out_port_t
 		;
 	else if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME))
 		port_type = NM_SETTING_BRIDGE_PORT_SETTING_NAME;
+	else if (!strcmp (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME))
+		port_type = NM_SETTING_OVS_PORT_SETTING_NAME;
+	else if (!strcmp (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
+		port_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
 	else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME))
 		port_type = NM_SETTING_TEAM_PORT_SETTING_NAME;
 	else
@@ -1343,6 +1326,8 @@ nm_setting_diff (NMSetting *a,
 	NMSettingDiffResult a_result_default = NM_SETTING_DIFF_RESULT_IN_A_DEFAULT;
 	NMSettingDiffResult b_result_default = NM_SETTING_DIFF_RESULT_IN_B_DEFAULT;
 	gboolean results_created = FALSE;
+	gboolean compared_any = FALSE;
+	gboolean diff_found = FALSE;
 
 	g_return_val_if_fail (results != NULL, FALSE);
 	g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
@@ -1391,6 +1376,8 @@ nm_setting_diff (NMSetting *a,
 		if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
 			continue;
 
+		compared_any = TRUE;
+
 		if (b) {
 			gboolean different;
 
@@ -1439,6 +1426,7 @@ nm_setting_diff (NMSetting *a,
 		if (r != NM_SETTING_DIFF_RESULT_UNKNOWN) {
 			void *p;
 
+			diff_found = TRUE;
 			if (g_hash_table_lookup_extended (*results, prop_spec->name, NULL, &p)) {
 				if ((r & GPOINTER_TO_UINT (p)) != r)
 					g_hash_table_insert (*results, g_strdup (prop_spec->name), GUINT_TO_POINTER (r | GPOINTER_TO_UINT (p)));
@@ -1448,13 +1436,28 @@ nm_setting_diff (NMSetting *a,
 	}
 	g_free (property_specs);
 
-	/* Don't return an empty hash table */
-	if (results_created && !g_hash_table_size (*results)) {
-		g_hash_table_destroy (*results);
-		*results = NULL;
+	if (!compared_any && !b) {
+		/* special case: the setting has no properties, and the opposite
+		 * setting @b is not given. The settings differ, and we signal that
+		 * by returning an empty results hash. */
+		diff_found = TRUE;
 	}
 
-	return !(*results);
+	if (diff_found) {
+		/* if there is a difference, we always return FALSE. It also means, we might
+		 * have allocated a new @results hash, and return if to the caller. */
+		return FALSE;
+	} else {
+		if (results_created) {
+			/* the allocated hash is unused. Clear it again. */
+			g_hash_table_destroy (*results);
+			*results = NULL;
+		} else {
+			/* we found no diff, and return false. However, the input
+			 * @result is returned unmodified. */
+		}
+		return TRUE;
+	}
 }
 
 #define CMP_AND_RETURN(n_a, n_b, name) \
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 8a80d2d3..bc955834 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -32,11 +32,14 @@
 #include <libintl.h>
 #include <gmodule.h>
 #include <sys/stat.h>
+#include <net/if.h>
 
 #if WITH_JANSSON
 #include <jansson.h>
 #endif
 
+#include "nm-utils/nm-enum-utils.h"
+#include "nm-utils/nm-hash-utils.h"
 #include "nm-common-macros.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
@@ -58,85 +61,79 @@
  * access points and devices, among other things.
  */
 
-struct EncodingTriplet
-{
-	const char *encoding1;
-	const char *encoding2;
-	const char *encoding3;
-};
-
 struct IsoLangToEncodings
 {
-	const char *	lang;
-	struct EncodingTriplet encodings;
+	const char *lang;
+	const char *const *encodings;
 };
 
+#define LANG_ENCODINGS(l, ...) { .lang = l, .encodings = (const char *[]) { __VA_ARGS__, NULL }}
+
 /* 5-letter language codes */
 static const struct IsoLangToEncodings isoLangEntries5[] =
 {
 	/* Simplified Chinese */
-	{ "zh_cn",	{"euc-cn",	"gb2312",			"gb18030"} },	/* PRC */
-	{ "zh_sg",	{"euc-cn",	"gb2312",			"gb18030"} },	/* Singapore */
+	LANG_ENCODINGS ("zh_cn",   "euc-cn", "gb2312", "gb18030"),         /* PRC */
+	LANG_ENCODINGS ("zh_sg",   "euc-cn", "gb2312", "gb18030"),         /* Singapore */
 
 	/* Traditional Chinese */
-	{ "zh_tw",	{"big5",		"euc-tw",			NULL} },		/* Taiwan */
-	{ "zh_hk",	{"big5",		"euc-tw",			"big5-hkcs"} },/* Hong Kong */
-	{ "zh_mo",	{"big5",		"euc-tw",			NULL} },		/* Macau */
+	LANG_ENCODINGS ("zh_tw",   "big5", "euc-tw"),                      /* Taiwan */
+	LANG_ENCODINGS ("zh_hk",   "big5", "euc-tw", "big5-hkcs"),         /* Hong Kong */
+	LANG_ENCODINGS ("zh_mo",   "big5", "euc-tw"),                      /* Macau */
 
-	/* Table end */
-	{ NULL, {NULL, NULL, NULL} }
+	LANG_ENCODINGS (NULL, NULL)
 };
 
 /* 2-letter language codes; we don't care about the other 3 in this table */
 static const struct IsoLangToEncodings isoLangEntries2[] =
 {
 	/* Japanese */
-	{ "ja",		{"euc-jp",	"shift_jis",		"iso-2022-jp"} },
+	LANG_ENCODINGS ("ja",      "euc-jp", "shift_jis", "iso-2022-jp"),
 
 	/* Korean */
-	{ "ko",		{"euc-kr",	"iso-2022-kr",		"johab"} },
+	LANG_ENCODINGS ("ko",      "euc-kr", "iso-2022-kr", "johab"),
 
 	/* Thai */
-	{ "th",		{"iso-8859-11","windows-874",		NULL} },
+	LANG_ENCODINGS ("th",      "iso-8859-11", "windows-874"),
 
 	/* Central European */
-	{ "hu",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Hungarian */
-	{ "cs",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Czech */
-	{ "hr",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Croatian */
-	{ "pl",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Polish */
-	{ "ro",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Romanian */
-	{ "sk",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Slovakian */
-	{ "sl",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Slovenian */
-	{ "sh",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Serbo-Croatian */
+	LANG_ENCODINGS ("hu",      "iso-8859-2", "windows-1250"),          /* Hungarian */
+	LANG_ENCODINGS ("cs",      "iso-8859-2", "windows-1250"),          /* Czech */
+	LANG_ENCODINGS ("hr",      "iso-8859-2", "windows-1250"),          /* Croatian */
+	LANG_ENCODINGS ("pl",      "iso-8859-2", "windows-1250"),          /* Polish */
+	LANG_ENCODINGS ("ro",      "iso-8859-2", "windows-1250"),          /* Romanian */
+	LANG_ENCODINGS ("sk",      "iso-8859-2", "windows-1250"),          /* Slovakian */
+	LANG_ENCODINGS ("sl",      "iso-8859-2", "windows-1250"),          /* Slovenian */
+	LANG_ENCODINGS ("sh",      "iso-8859-2", "windows-1250"),          /* Serbo-Croatian */
 
 	/* Cyrillic */
-	{ "ru",		{"koi8-r",	"windows-1251",	"iso-8859-5"} },	/* Russian */
-	{ "be",		{"koi8-r",	"windows-1251",	"iso-8859-5"} },	/* Belorussian */
-	{ "bg",		{"windows-1251","koi8-r",		"iso-8859-5"} },	/* Bulgarian */
-	{ "mk",		{"koi8-r",	"windows-1251",	"iso-8859-5"} },	/* Macedonian */
-	{ "sr",		{"koi8-r",	"windows-1251",	"iso-8859-5"} },	/* Serbian */
-	{ "uk",		{"koi8-u",	"koi8-r",			"windows-1251"} },	/* Ukranian */
+	LANG_ENCODINGS ("ru",      "koi8-r", "windows-1251","iso-8859-5"), /* Russian */
+	LANG_ENCODINGS ("be",      "koi8-r", "windows-1251","iso-8859-5"), /* Belorussian */
+	LANG_ENCODINGS ("bg",      "windows-1251","koi8-r", "iso-8859-5"), /* Bulgarian */
+	LANG_ENCODINGS ("mk",      "koi8-r", "windows-1251", "iso-8859-5"),/* Macedonian */
+	LANG_ENCODINGS ("sr",      "koi8-r", "windows-1251", "iso-8859-5"),/* Serbian */
+	LANG_ENCODINGS ("uk",      "koi8-u", "koi8-r", "windows-1251"),    /* Ukranian */
 
 	/* Arabic */
-	{ "ar",		{"iso-8859-6",	"windows-1256",	NULL} },
+	LANG_ENCODINGS ("ar",      "iso-8859-6","windows-1256"),
 
 	/* Baltic */
-	{ "et",		{"iso-8859-4",	"windows-1257",	NULL} },	/* Estonian */
-	{ "lt",		{"iso-8859-4",	"windows-1257",	NULL} },	/* Lithuanian */
-	{ "lv",		{"iso-8859-4",	"windows-1257",	NULL} },	/* Latvian */
+	LANG_ENCODINGS ("et",      "iso-8859-4", "windows-1257"),          /* Estonian */
+	LANG_ENCODINGS ("lt",      "iso-8859-4", "windows-1257"),          /* Lithuanian */
+	LANG_ENCODINGS ("lv",      "iso-8859-4", "windows-1257"),          /* Latvian */
 
 	/* Greek */
-	{ "el",		{"iso-8859-7",	"windows-1253",	NULL} },
+	LANG_ENCODINGS ("el",      "iso-8859-7","windows-1253"),
 
 	/* Hebrew */
-	{ "he",		{"iso-8859-8",	"windows-1255",	NULL} },
-	{ "iw",		{"iso-8859-8",	"windows-1255",	NULL} },
+	LANG_ENCODINGS ("he",      "iso-8859-8", "windows-1255"),
+	LANG_ENCODINGS ("iw",      "iso-8859-8", "windows-1255"),
 
 	/* Turkish */
-	{ "tr",		{"iso-8859-9",	"windows-1254",	NULL} },
+	LANG_ENCODINGS ("tr",      "iso-8859-9", "windows-1254"),
 
 	/* Table end */
-	{ NULL, {NULL, NULL, NULL} }
+	LANG_ENCODINGS (NULL, NULL)
 };
 
 
@@ -154,7 +151,7 @@ init_lang_to_encodings_hash (void)
 		langToEncodings5 = g_hash_table_new (g_str_hash, g_str_equal);
 		while (enc->lang) {
 			g_hash_table_insert (langToEncodings5, (gpointer) enc->lang,
-			                     (gpointer) &enc->encodings);
+			                     (gpointer) enc->encodings);
 			enc++;
 		}
 	}
@@ -165,54 +162,73 @@ init_lang_to_encodings_hash (void)
 		langToEncodings2 = g_hash_table_new (g_str_hash, g_str_equal);
 		while (enc->lang) {
 			g_hash_table_insert (langToEncodings2, (gpointer) enc->lang,
-			                     (gpointer) &enc->encodings);
+			                     (gpointer) enc->encodings);
 			enc++;
 		}
 	}
 }
 
-
 static gboolean
-get_encodings_for_lang (const char *lang,
-                        char **encoding1,
-                        char **encoding2,
-                        char **encoding3)
+get_encodings_for_lang (const char *lang, const char *const **encodings)
 {
-	struct EncodingTriplet *	encodings;
-	gboolean				success = FALSE;
-	char *				tmp_lang;
-
-	g_return_val_if_fail (lang != NULL, FALSE);
-	g_return_val_if_fail (encoding1 != NULL, FALSE);
-	g_return_val_if_fail (encoding2 != NULL, FALSE);
-	g_return_val_if_fail (encoding3 != NULL, FALSE);
+	gs_free char *tmp_lang = NULL;
 
-	*encoding1 = "iso-8859-1";
-	*encoding2 = "windows-1251";
-	*encoding3 = NULL;
+	g_return_val_if_fail (lang, FALSE);
+	g_return_val_if_fail (encodings, FALSE);
 
 	init_lang_to_encodings_hash ();
 
-	tmp_lang = g_strdup (lang);
-	if ((encodings = g_hash_table_lookup (langToEncodings5, tmp_lang))) {
-		*encoding1 = (char *) encodings->encoding1;
-		*encoding2 = (char *) encodings->encoding2;
-		*encoding3 = (char *) encodings->encoding3;
-		success = TRUE;
-	}
+	if ((*encodings = g_hash_table_lookup (langToEncodings5, lang)))
+		return TRUE;
 
 	/* Truncate tmp_lang to length of 2 */
-	if (strlen (tmp_lang) > 2)
+	if (strlen (lang) > 2) {
+		tmp_lang = g_strdup (lang);
 		tmp_lang[2] = '\0';
-	if (!success && (encodings = g_hash_table_lookup (langToEncodings2, tmp_lang))) {
-		*encoding1 = (char *) encodings->encoding1;
-		*encoding2 = (char *) encodings->encoding2;
-		*encoding3 = (char *) encodings->encoding3;
-		success = TRUE;
+		if ((*encodings = g_hash_table_lookup (langToEncodings2, tmp_lang)))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static const char *const *
+get_system_encodings (void)
+{
+	static const char *const *cached_encodings;
+	static char *default_encodings[4];
+	const char *const *encodings = NULL;
+	char *lang;
+
+	if (cached_encodings)
+		return cached_encodings;
+
+	/* Use environment variables as encoding hint */
+	lang = getenv ("LC_ALL");
+	if (!lang)
+		lang = getenv ("LC_CTYPE");
+	if (!lang)
+		lang = getenv ("LANG");
+	if (lang) {
+		char *dot;
+
+		lang = g_ascii_strdown (lang, -1);
+		if ((dot = strchr (lang, '.')))
+			*dot = '\0';
+
+		get_encodings_for_lang (lang, &encodings);
+		g_free (lang);
+	}
+	if (!encodings) {
+		g_get_charset ((const char **) &default_encodings[0]);
+		default_encodings[1] = "iso-8859-1";
+		default_encodings[2] = "windows-1251";
+		default_encodings[3] = NULL;
+		encodings = (const char *const *) default_encodings;
 	}
 
-	g_free (tmp_lang);
-	return success;
+	cached_encodings = encodings;
+	return cached_encodings;
 }
 
 /* init libnm */
@@ -281,37 +297,26 @@ gboolean _nm_utils_is_manager_process;
 char *
 nm_utils_ssid_to_utf8 (const guint8 *ssid, gsize len)
 {
+	const char *const *encodings;
+	const char *const *e;
 	char *converted = NULL;
-	char *lang, *e1 = NULL, *e2 = NULL, *e3 = NULL;
 
 	g_return_val_if_fail (ssid != NULL, NULL);
 
 	if (g_utf8_validate ((const gchar *) ssid, len, NULL))
 		return g_strndup ((const gchar *) ssid, len);
 
-	/* LANG may be a good encoding hint */
-	g_get_charset ((const char **)(&e1));
-	if ((lang = getenv ("LANG"))) {
-		char * dot;
+	encodings = get_system_encodings ();
 
-		lang = g_ascii_strdown (lang, -1);
-		if ((dot = strchr (lang, '.')))
-			*dot = '\0';
-
-		get_encodings_for_lang (lang, &e1, &e2, &e3);
-		g_free (lang);
+	for (e = encodings; *e; e++) {
+		converted = g_convert ((const gchar *) ssid, len, "UTF-8", *e, NULL, NULL, NULL);
+		if (converted)
+			break;
 	}
 
-	converted = g_convert ((const gchar *) ssid, len, "UTF-8", e1, NULL, NULL, NULL);
-	if (!converted && e2)
-		converted = g_convert ((const gchar *) ssid, len, "UTF-8", e2, NULL, NULL, NULL);
-
-	if (!converted && e3)
-		converted = g_convert ((const gchar *) ssid, len, "UTF-8", e3, NULL, NULL, NULL);
-
 	if (!converted) {
 		converted = g_convert_with_fallback ((const gchar *) ssid, len,
-		                                     "UTF-8", e1, "?", NULL, NULL, NULL);
+		                                     "UTF-8", encodings[0], "?", NULL, NULL, NULL);
 	}
 
 	if (!converted) {
@@ -325,7 +330,7 @@ nm_utils_ssid_to_utf8 (const guint8 *ssid, gsize len)
 		                     "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
 		                     "abcdefghijklmnopqrstuvwxyz{|}~";
 
-		converted = g_strndup ((const gchar *)ssid, len);
+		converted = g_strndup ((const char *) ssid, len);
 		g_strcanon (converted, valid_chars, '?');
 	}
 
@@ -443,36 +448,6 @@ nm_utils_same_ssid (const guint8 *ssid1, gsize len1,
 	return memcmp (ssid1, ssid2, len1) == 0 ? TRUE : FALSE;
 }
 
-char **
-_nm_utils_strv_cleanup (char **strv,
-                        gboolean strip_whitespace,
-                        gboolean skip_empty,
-                        gboolean skip_repeated)
-{
-	guint i, j;
-
-	if (!strv || !*strv)
-		return strv;
-
-	if (strip_whitespace) {
-		for (i = 0; strv[i]; i++)
-			g_strstrip (strv[i]);
-	}
-	if (!skip_empty && !skip_repeated)
-		return strv;
-	j = 0;
-	for (i = 0; strv[i]; i++) {
-		if (   (skip_empty && !*strv[i])
-		    || (skip_repeated && nm_utils_strv_find_first (strv, j, strv[i]) >= 0))
-			g_free (strv[i]);
-		else
-			strv[j++] = strv[i];
-	}
-	strv[j] = NULL;
-	return strv;
-}
-
-
 gboolean
 _nm_utils_string_slist_validate (GSList *list, const char **valid_values)
 {
@@ -1519,10 +1494,9 @@ nm_utils_ip4_netmask_to_prefix (guint32 netmask)
 guint32
 nm_utils_ip4_prefix_to_netmask (guint32 prefix)
 {
-	return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF;
+	return _nm_utils_ip4_prefix_to_netmask (prefix);
 }
 
-
 /**
  * nm_utils_ip4_get_default_prefix:
  * @ip: an IPv4 address (in network byte order)
@@ -1538,12 +1512,7 @@ nm_utils_ip4_prefix_to_netmask (guint32 prefix)
 guint32
 nm_utils_ip4_get_default_prefix (guint32 ip)
 {
-	if (((ntohl (ip) & 0xFF000000) >> 24) <= 127)
-		return 8;  /* Class A - 255.0.0.0 */
-	else if (((ntohl (ip) & 0xFF000000) >> 24) <= 191)
-		return 16;  /* Class B - 255.255.0.0 */
-
-	return 24;  /* Class C - 255.255.255.0 */
+	return _nm_utils_ip4_get_default_prefix (ip);
 }
 
 /**
@@ -1993,8 +1962,8 @@ nm_utils_ip_routes_to_variant (GPtrArray *routes)
 		for (i = 0; i < routes->len; i++) {
 			NMIPRoute *route = routes->pdata[i];
 			GVariantBuilder route_builder;
-			char **names;
-			int n;
+			gs_free const char **names = NULL;
+			guint j, len;
 
 			g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}"));
 			g_variant_builder_add (&route_builder, "{sv}",
@@ -2014,13 +1983,12 @@ nm_utils_ip_routes_to_variant (GPtrArray *routes)
 				                       g_variant_new_uint32 ((guint32) nm_ip_route_get_metric (route)));
 			}
 
-			names = nm_ip_route_get_attribute_names (route);
-			for (n = 0; names[n]; n++) {
+			names = _nm_ip_route_get_attribute_names (route, TRUE, &len);
+			for (j = 0; j < len; j++) {
 				g_variant_builder_add (&route_builder, "{sv}",
-				                       names[n],
-				                       nm_ip_route_get_attribute (route, names[n]));
+				                       names[j],
+				                       nm_ip_route_get_attribute (route, names[j]));
 			}
-			g_strfreev (names);
 
 			g_variant_builder_add (&builder, "a{sv}", &route_builder);
 		}
@@ -3707,36 +3675,41 @@ _nm_utils_generate_mac_address_mask_parse (const char *value,
 gboolean
 nm_utils_is_valid_iface_name (const char *name, GError **error)
 {
-	g_return_val_if_fail (name != NULL, FALSE);
+	int i;
 
-	if (*name == '\0') {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     _("interface name is too short"));
-		return FALSE;
-	}
+	g_return_val_if_fail (name, FALSE);
 
-	if (strlen (name) >= 16) {
+	if (name[0] == '\0') {
 		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     _("interface name is longer than 15 characters"));
+		                     _("interface name is too short"));
 		return FALSE;
 	}
 
-	if (!strcmp (name, ".") || !strcmp (name, "..")) {
+	if (   name[0] == '.'
+	    && (   name[1] == '\0'
+	        || (   name[1] == '.'
+	            && name[2] == '\0'))) {
 		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
 		                     _("interface name is reserved"));
 		return FALSE;
 	}
 
-	while (*name) {
-		if (*name == '/' || g_ascii_isspace (*name)) {
+	for (i = 0; i < IFNAMSIZ; i++) {
+		char ch = name[i];
+
+		if (ch == '\0')
+			return TRUE;
+		if (   NM_IN_SET (ch, '/', ':')
+		    || g_ascii_isspace (ch)) {
 			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
 			                     _("interface name contains an invalid character"));
 			return FALSE;
 		}
-		name++;
 	}
 
-	return TRUE;
+	g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+	                     _("interface name is longer than 15 characters"));
+	return FALSE;
 }
 
 /**
@@ -3925,8 +3898,8 @@ _nm_utils_inet6_is_token (const struct in6_addr *in6addr)
 gboolean
 nm_utils_check_virtual_device_compatibility (GType virtual_type, GType other_type)
 {
-	g_return_val_if_fail (_nm_setting_type_is_base_type (virtual_type), FALSE);
-	g_return_val_if_fail (_nm_setting_type_is_base_type (other_type), FALSE);
+	g_return_val_if_fail (_nm_setting_type_get_base_type_priority (virtual_type) != NM_SETTING_PRIORITY_INVALID, FALSE);
+	g_return_val_if_fail (_nm_setting_type_get_base_type_priority (other_type)   != NM_SETTING_PRIORITY_INVALID, FALSE);
 
 	if (virtual_type == NM_TYPE_SETTING_BOND) {
 		return (   other_type == NM_TYPE_SETTING_INFINIBAND
@@ -4038,28 +4011,29 @@ guint
 _nm_utils_strstrdictkey_hash (gconstpointer a)
 {
 	const NMUtilsStrStrDictKey *k = a;
-	const signed char *p;
-	guint32 h = 5381;
+	const char *p;
+	NMHashState h;
 
+	nm_hash_init (&h, 76642997u);
 	if (k) {
 		if (((int) k->type) & ~STRSTRDICTKEY_ALL_SET)
 			g_return_val_if_reached (0);
 
-		h = (h << 5) + h + k->type;
+		nm_hash_update_val (&h, k->type);
 		if (k->type & STRSTRDICTKEY_ALL_SET) {
-			p = (void *) k->data;
-			for (; *p != '\0'; p++)
-				h = (h << 5) + h + *p;
+			gsize n;
+
+			n = 0;
+			p = strchr (k->data, '\0');
 			if (k->type == STRSTRDICTKEY_ALL_SET) {
 				/* the key contains two strings. Continue... */
-				h = (h << 5) + h + '\0';
-				for (p++; *p != '\0'; p++)
-					h = (h << 5) + h + *p;
+				p = strchr (p + 1, '\0');
 			}
+			if (p != k->data)
+				nm_hash_update (&h, k->data, p - k->data);
 		}
 	}
-
-	return h;
+	return nm_hash_complete (&h);
 }
 
 gboolean
@@ -4224,11 +4198,11 @@ out:
  * Returns: the index of the option in the array or -1 if was not
  * found.
  */
-int _nm_utils_dns_option_find_idx (GPtrArray *array, const char *option)
+gssize _nm_utils_dns_option_find_idx (GPtrArray *array, const char *option)
 {
 	gboolean ret;
 	char *option_name, *tmp_name;
-	int i;
+	guint i;
 
 	if (!_nm_utils_dns_option_validate (option, &option_name, NULL, FALSE, NULL))
 		return -1;
@@ -4249,82 +4223,7 @@ int _nm_utils_dns_option_find_idx (GPtrArray *array, const char *option)
 	return -1;
 }
 
-#define IS_FLAGS_SEPARATOR(ch)  (NM_IN_SET ((ch), ' ', '\t', ',', '\n', '\r'))
-
-static gboolean
-_is_hex_string (const char *str)
-{
-	return    str[0] == '0'
-	       && str[1] == 'x'
-	       && str[2]
-	       && NM_STRCHAR_ALL (&str[2], ch, g_ascii_isxdigit (ch));
-}
-
-static gboolean
-_enum_is_valid_enum_nick (const char *str)
-{
-	return    str[0]
-	       && !NM_STRCHAR_ANY (str, ch, g_ascii_isspace (ch))
-	       && !NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch));
-}
-
-static gboolean
-_enum_is_valid_flags_nick (const char *str)
-{
-	return    str[0]
-	       && !NM_STRCHAR_ANY (str, ch, IS_FLAGS_SEPARATOR (ch))
-	       && !_is_hex_string (str);
-}
-
-char *
-_nm_utils_enum_to_str_full (GType type,
-                            int value,
-                            const char *flags_separator)
-{
-	GTypeClass *class;
-	char *ret;
-
-	if (   flags_separator
-	    && (   !flags_separator[0]
-	        || NM_STRCHAR_ANY (flags_separator, ch, !IS_FLAGS_SEPARATOR (ch))))
-		g_return_val_if_reached (NULL);
-
-	class = g_type_class_ref (type);
-
-	if (G_IS_ENUM_CLASS (class)) {
-		GEnumValue *enum_value;
-
-		enum_value = g_enum_get_value (G_ENUM_CLASS (class), value);
-		if (   !enum_value
-		    || !_enum_is_valid_enum_nick (enum_value->value_nick))
-			ret = g_strdup_printf ("%d", value);
-		else
-			ret = strdup (enum_value->value_nick);
-	} else if (G_IS_FLAGS_CLASS (class)) {
-		GFlagsValue *flags_value;
-		GString *str = g_string_new ("");
-
-		flags_separator = flags_separator ?: " ";
-
-		while (value) {
-			flags_value = g_flags_get_first_value (G_FLAGS_CLASS (class), value);
-			if (str->len)
-				g_string_append (str, flags_separator);
-			if (   !flags_value
-			    || !_enum_is_valid_flags_nick (flags_value->value_nick)) {
-				g_string_append_printf (str, "0x%x", (unsigned) value);
-				break;
-			}
-			g_string_append (str, flags_value->value_nick);
-			value &= ~flags_value->value;
-		}
-		ret = g_string_free (str, FALSE);
-	} else
-		g_return_val_if_reached (NULL);
-
-	g_type_class_unref (class);
-	return ret;
-}
+/*****************************************************************************/
 
 /**
  * nm_utils_enum_to_str:
@@ -4333,8 +4232,9 @@ _nm_utils_enum_to_str_full (GType type,
  *
  * Converts an enum value to its string representation. If the enum is a
  * %G_TYPE_FLAGS the function returns a comma-separated list of matching values.
- * If the enum is a %G_TYPE_ENUM and the given value is not valid the
- * function returns %NULL.
+ * If the value has no corresponding string representation, it is converted
+ * to a number. For enums it is converted to a decimal number, for flags
+ * to an (unsigned) hex number.
  *
  * Returns: a newly allocated string or %NULL
  *
@@ -4368,81 +4268,7 @@ gboolean
 nm_utils_enum_from_str (GType type, const char *str,
                         int *out_value, char **err_token)
 {
-	GTypeClass *class;
-	gboolean ret = FALSE;
-	int value = 0;
-	gs_free char *str_clone = NULL;
-	char *s;
-	gint64 v64;
-
-	g_return_val_if_fail (str, FALSE);
-
-	str_clone = strdup (str);
-	s = nm_str_skip_leading_spaces (str_clone);
-	g_strchomp (s);
-
-	class = g_type_class_ref (type);
-
-	if (G_IS_ENUM_CLASS (class)) {
-		GEnumValue *enum_value;
-
-		if (s[0]) {
-			if (NM_STRCHAR_ALL (s, ch, g_ascii_isdigit (ch))) {
-				v64 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXINT, -1);
-				if (v64 != -1) {
-					value = (int) v64;
-					ret = TRUE;
-				}
-			} else {
-				enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (class), s);
-				if (enum_value) {
-					value = enum_value->value;
-					ret = TRUE;
-				}
-			}
-		}
-	} else if (G_IS_FLAGS_CLASS (class)) {
-		GFlagsValue *flags_value;
-
-		ret = TRUE;
-		while (s[0]) {
-			char *s_end;
-
-			for (s_end = s; s_end[0]; s_end++) {
-				if (IS_FLAGS_SEPARATOR (s_end[0])) {
-					s_end[0] = '\0';
-					s_end++;
-					break;
-				}
-			}
-
-			if (s[0]) {
-				if (_is_hex_string (s)) {
-					v64 = _nm_utils_ascii_str_to_int64 (&s[2], 16, 0, G_MAXUINT, -1);
-					if (v64 == -1) {
-						ret = FALSE;
-						break;
-					}
-					value |= (int) v64;
-				} else {
-					flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (class), s);
-					if (!flags_value) {
-						ret = FALSE;
-						break;
-					}
-					value |= flags_value->value;
-				}
-			}
-
-			s = s_end;
-		}
-	} else
-		g_return_val_if_reached (FALSE);
-
-	NM_SET_OUT (err_token, !ret && s[0] ? g_strdup (s) : NULL);
-	NM_SET_OUT (out_value, ret ? value : 0);
-	g_type_class_unref (class);
-	return ret;
+	return _nm_utils_enum_from_str_full (type, str, out_value, err_token, NULL);
 }
 
 /**
@@ -4460,52 +4286,11 @@ nm_utils_enum_from_str (GType type, const char *str,
  */
 const char **nm_utils_enum_get_values (GType type, gint from, gint to)
 {
-	GTypeClass *class;
-	GPtrArray *array;
-	gint i;
-	char sbuf[64];
-
-	class = g_type_class_ref (type);
-	array = g_ptr_array_new ();
-
-	if (G_IS_ENUM_CLASS (class)) {
-		GEnumClass *enum_class = G_ENUM_CLASS (class);
-		GEnumValue *enum_value;
-
-		for (i = 0; i < enum_class->n_values; i++) {
-			enum_value = &enum_class->values[i];
-			if (enum_value->value >= from && enum_value->value <= to) {
-				if (_enum_is_valid_enum_nick (enum_value->value_nick))
-					g_ptr_array_add (array, (gpointer) enum_value->value_nick);
-				else
-					g_ptr_array_add (array, (gpointer) g_intern_string (nm_sprintf_buf (sbuf, "%d", enum_value->value)));
-			}
-		}
-	} else if (G_IS_FLAGS_CLASS (class)) {
-		GFlagsClass *flags_class = G_FLAGS_CLASS (class);
-		GFlagsValue *flags_value;
-
-		for (i = 0; i < flags_class->n_values; i++) {
-			flags_value = &flags_class->values[i];
-			if (flags_value->value >= from && flags_value->value <= to) {
-				if (_enum_is_valid_flags_nick (flags_value->value_nick))
-					g_ptr_array_add (array, (gpointer) flags_value->value_nick);
-				else
-					g_ptr_array_add (array, (gpointer) g_intern_string (nm_sprintf_buf (sbuf, "0x%x", (unsigned) flags_value->value)));
-			}
-		}
-	} else {
-		g_type_class_unref (class);
-		g_ptr_array_free (array, TRUE);
-		g_return_val_if_reached (NULL);
-	}
-
-	g_type_class_unref (class);
-	g_ptr_array_add (array, NULL);
-
-	return (const char **) g_ptr_array_free (array, FALSE);
+	return _nm_utils_enum_get_values (type, from, to);
 }
 
+/*****************************************************************************/
+
 #if WITH_JANSSON
 /**
  * nm_utils_is_json_object:
@@ -4535,7 +4320,7 @@ nm_utils_is_json_object (const char *str, GError **error)
 		return FALSE;
 	}
 
-	json = json_loads (str, 0, &jerror);
+	json = json_loads (str, JSON_REJECT_DUPLICATES, &jerror);
 	if (!json) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
@@ -4575,6 +4360,7 @@ _nm_utils_team_config_equal (const char *conf1,
                              gboolean port_config)
 {
 	json_t *json1 = NULL, *json2 = NULL, *json;
+	json_t *array, *name;
 	gs_free char *dump1 = NULL, *dump2 = NULL;
 	json_t *value, *property;
 	json_error_t jerror;
@@ -4587,9 +4373,9 @@ _nm_utils_team_config_equal (const char *conf1,
 		return TRUE;
 
 	/* A NULL configuration is equivalent to default value '{}' */
-	json1 = json_loads (conf1 ?: "{}", 0, &jerror);
+	json1 = json_loads (conf1 ?: "{}", JSON_REJECT_DUPLICATES, &jerror);
 	if (json1)
-		json2 = json_loads (conf2 ?: "{}", 0, &jerror);
+		json2 = json_loads (conf2 ?: "{}", JSON_REJECT_DUPLICATES, &jerror);
 
 	if (!json1 || !json2) {
 		ret = FALSE;
@@ -4614,6 +4400,16 @@ _nm_utils_team_config_equal (const char *conf1,
 				property = json_object ();
 				json_object_set_new (property, "name", json_string ("roundrobin"));
 				json_object_set_new (json, "runner", property);
+			} else if (   (name = json_object_get (property, "name"))
+			           && NM_IN_STRSET (json_string_value (name), "lacp", "loadbalance")) {
+				/* Add default tx_hash when missing */
+				if (!json_object_get (property, "tx_hash")) {
+					array = json_array ();
+					json_array_append_new (array, json_string ("eth"));
+					json_array_append_new (array, json_string ("ipv4"));
+					json_array_append_new (array, json_string ("ipv6"));
+					json_object_set_new (property, "tx_hash", array);
+				}
 			}
 		}
 	}
diff --git a/libnm-core/nm-version.h b/libnm-core/nm-version.h
index c6cd2892..f6bfaf72 100644
--- a/libnm-core/nm-version.h
+++ b/libnm-core/nm-version.h
@@ -132,4 +132,18 @@
 # define NM_AVAILABLE_IN_1_8
 #endif
 
+#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_10
+# define NM_DEPRECATED_IN_1_10           G_DEPRECATED
+# define NM_DEPRECATED_IN_1_10_FOR(f)    G_DEPRECATED_FOR(f)
+#else
+# define NM_DEPRECATED_IN_1_10
+# define NM_DEPRECATED_IN_1_10_FOR(f)
+#endif
+
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_10
+# define NM_AVAILABLE_IN_1_10            G_UNAVAILABLE(1,10)
+#else
+# define NM_AVAILABLE_IN_1_10
+#endif
+
 #endif  /* NM_VERSION_H */
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index ccde24f8..188cb28e 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -25,6 +25,9 @@
 
 #include <string.h>
 
+#include "nm-utils/c-list-util.h"
+#include "nm-utils/nm-hash-utils.h"
+
 #include "nm-utils.h"
 #include "nm-setting-private.h"
 #include "nm-utils.h"
@@ -60,6 +63,7 @@
 #include "nm-setting-wireless-security.h"
 #include "nm-simple-connection.h"
 #include "nm-keyfile-internal.h"
+#include "nm-utils/nm-dedup-multi.h"
 
 #include "test-general-enums.h"
 
@@ -75,6 +79,598 @@ G_STATIC_ASSERT (sizeof (bool) <= sizeof (int));
 
 /*****************************************************************************/
 
+typedef struct _nm_packed {
+	int v0;
+	char v1;
+	double v2;
+	guint8 v3;
+} TestHashStruct;
+
+static void
+_test_hash_struct (int v0, char v1, double v2, guint8 v3)
+{
+	const TestHashStruct s = {
+		.v0 = v0,
+		.v1 = v1,
+		.v2 = v2,
+		.v3 = v3,
+	};
+	NMHashState h;
+	guint hh;
+
+	nm_hash_init (&h, 100);
+	nm_hash_update (&h, &s, sizeof (s));
+	hh = nm_hash_complete (&h);
+
+	nm_hash_init (&h, 100);
+	nm_hash_update_val (&h, v0);
+	nm_hash_update_val (&h, v1);
+	nm_hash_update_val (&h, v2);
+	nm_hash_update_val (&h, v3);
+	g_assert_cmpint (hh, ==, nm_hash_complete (&h));
+
+	nm_hash_init (&h, 100);
+	nm_hash_update_vals (&h, v0, v1, v2, v3);
+	g_assert_cmpint (hh, ==, nm_hash_complete (&h));
+}
+
+static guint
+_test_hash_str (const char *str)
+{
+	NMHashState h;
+	guint v, v2;
+	const guint SEED = 10;
+
+	nm_hash_init (&h, SEED);
+	nm_hash_update_str0 (&h, str);
+	v = nm_hash_complete (&h);
+
+	/* assert that hashing a string and a buffer yields the
+	 * same result.
+	 *
+	 * I think that is a desirable property. */
+	nm_hash_init (&h, SEED);
+	nm_hash_update_mem (&h, str, strlen (str));
+	v2 = nm_hash_complete (&h);
+
+	g_assert (v == v2);
+	return v;
+}
+
+#define _test_hash_vals(type, ...) \
+	G_STMT_START { \
+		NMHashState h0, h1, h2, h3; \
+		const type v[] = { __VA_ARGS__ }; \
+		guint h; \
+		guint i; \
+		\
+		nm_hash_init (&h0, 10); \
+		nm_hash_init (&h1, 10); \
+		nm_hash_init (&h2, 10); \
+		nm_hash_init (&h3, 10); \
+		\
+		/* assert that it doesn't matter, whether we hash the values individually,
+		 * or all at once, or via the convenience macros nm_hash_update_val()
+		 * and nm_hash_update_vals(). */ \
+		for (i = 0; i < G_N_ELEMENTS (v); i++) { \
+			nm_hash_update (&h0, &v[i], sizeof (type)); \
+			nm_hash_update_val (&h1, v[i]); \
+		} \
+		nm_hash_update_vals (&h2, __VA_ARGS__); \
+		nm_hash_update (&h3, v, sizeof (v)); \
+		\
+		h = nm_hash_complete (&h0); \
+		g_assert_cmpint (h, ==, nm_hash_complete (&h1)); \
+		g_assert_cmpint (h, ==, nm_hash_complete (&h2)); \
+		g_assert_cmpint (h, ==, nm_hash_complete (&h3)); \
+	} G_STMT_END
+
+static void
+test_nm_hash (void)
+{
+	_test_hash_str ("");
+	_test_hash_str ("a");
+	_test_hash_str ("aa");
+	_test_hash_str ("diceros bicornis longipes");
+
+	/* assert that nm_hash_update_vals() is the same as calling nm_hash_update_val() multiple times. */
+	_test_hash_vals (int, 1);
+	_test_hash_vals (int, 1, 2);
+	_test_hash_vals (int, 1, 2, 3);
+	_test_hash_vals (int, 1, 2, 3, 4);
+	_test_hash_vals (long, 1l);
+	_test_hash_vals (long, 1l, 2l, 3l, 4l, 5l);
+
+	_test_hash_struct (10, 'a', 5.4, 7);
+	_test_hash_struct (-10, '\0', -5.4e49, 255);
+
+	g_assert_cmpint (NM_HASH_COMBINE_BOOLS (guint8,                       1, 0), ==, 0x002);
+	g_assert_cmpint (NM_HASH_COMBINE_BOOLS (guint8,                       1, 1), ==, 0x003);
+	g_assert_cmpint (NM_HASH_COMBINE_BOOLS (guint8,           1, 1, 0, 0, 0, 0), ==, 0x030);
+	g_assert_cmpint (NM_HASH_COMBINE_BOOLS (guint8,           1, 1, 0, 0, 0, 1), ==, 0x031);
+	g_assert_cmpint (NM_HASH_COMBINE_BOOLS (guint8,     0, 0, 1, 1, 0, 0, 0, 1), ==, 0x031);
+	g_assert_cmpint (NM_HASH_COMBINE_BOOLS (guint16,    0, 0, 1, 1, 0, 0, 0, 1), ==, 0x031);
+	g_assert_cmpint (NM_HASH_COMBINE_BOOLS (guint16, 0, 0, 0, 1, 1, 0, 0, 0, 1), ==, 0x031);
+	g_assert_cmpint (NM_HASH_COMBINE_BOOLS (guint16, 1, 0, 0, 1, 1, 0, 0, 0, 1), ==, 0x131);
+}
+
+/*****************************************************************************/
+
+static void
+test_nm_g_slice_free_fcn (void)
+{
+	gpointer p;
+
+	p = g_slice_new (gint64);
+	(nm_g_slice_free_fcn (gint64)) (p);
+
+	p = g_slice_new (gint32);
+	(nm_g_slice_free_fcn (gint32)) (p);
+
+	p = g_slice_new (gint);
+	(nm_g_slice_free_fcn (gint)) (p);
+
+	p = g_slice_new (gint64);
+	nm_g_slice_free_fcn_gint64 (p);
+}
+
+/*****************************************************************************/
+
+static void
+_do_test_nm_utils_strsplit_set (const char *str, ...)
+{
+	gs_unref_ptrarray GPtrArray *args_array = g_ptr_array_new ();
+	const char *const*args;
+	gs_free const char **words = NULL;
+	const char *arg;
+	gsize i;
+	va_list ap;
+
+	va_start (ap, str);
+	while ((arg = va_arg (ap, const char *)))
+		g_ptr_array_add (args_array, (gpointer) arg);
+	va_end (ap);
+	g_ptr_array_add (args_array, NULL);
+
+	args = (const char *const*) args_array->pdata;
+
+	words = nm_utils_strsplit_set (str, " \t\n");
+
+	if (!args[0]) {
+		g_assert (!words);
+		g_assert (   !str
+		          || NM_STRCHAR_ALL (str, ch, NM_IN_SET (ch, ' ', '\t', '\n')));
+		return;
+	}
+	g_assert (words);
+	for (i = 0; args[i] || words[i]; i++) {
+		g_assert (args[i]);
+		g_assert (words[i]);
+		g_assert (args[i][0]);
+		g_assert (NM_STRCHAR_ALL (args[i], ch, !NM_IN_SET (ch, ' ', '\t', '\n')));
+		g_assert_cmpstr (args[i], ==, words[i]);
+	}
+}
+
+#define do_test_nm_utils_strsplit_set(str, ...) \
+	_do_test_nm_utils_strsplit_set (str, ##__VA_ARGS__, NULL)
+
+static void
+test_nm_utils_strsplit_set (void)
+{
+	do_test_nm_utils_strsplit_set (NULL);
+	do_test_nm_utils_strsplit_set ("");
+	do_test_nm_utils_strsplit_set ("\t");
+	do_test_nm_utils_strsplit_set (" \t\n");
+	do_test_nm_utils_strsplit_set ("a", "a");
+	do_test_nm_utils_strsplit_set ("a b", "a", "b");
+	do_test_nm_utils_strsplit_set ("a\rb", "a\rb");
+	do_test_nm_utils_strsplit_set ("  a\rb  ", "a\rb");
+	do_test_nm_utils_strsplit_set ("  a bbbd afds ere", "a", "bbbd", "afds", "ere");
+	do_test_nm_utils_strsplit_set ("1 2 3 4 5 6 7 8 9 0 "
+	                               "1 2 3 4 5 6 7 8 9 0 "
+	                               "1 2 3 4 5 6 7 8 9 0",
+	                               "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
+	                               "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
+	                               "1", "2", "3", "4", "5", "6", "7", "8", "9", "0");
+}
+
+/*****************************************************************************/
+
+typedef struct {
+	int val;
+	int idx;
+	CList lst;
+} CListSort;
+
+static int
+_c_list_sort_cmp (const CList *lst_a, const CList *lst_b, const void *user_data)
+{
+	const CListSort *a, *b;
+
+	g_assert (lst_a);
+	g_assert (lst_b);
+	g_assert (lst_a != lst_b);
+
+	a = c_list_entry (lst_a, CListSort, lst);
+	b = c_list_entry (lst_b, CListSort, lst);
+
+	if (a->val < b->val)
+		return -1;
+	if (a->val > b->val)
+		return 1;
+	return 0;
+}
+
+static void
+test_c_list_sort (void)
+{
+	guint i, n_list, repeat, headless;
+	CList head, *iter, *iter_prev, *lst;
+	CListSort elements[30];
+	const CListSort *el_prev;
+
+	c_list_init (&head);
+	c_list_sort (&head, _c_list_sort_cmp, NULL);
+	g_assert (c_list_length (&head) == 0);
+	g_assert (c_list_is_empty (&head));
+
+	for (repeat = 0; repeat < 10; repeat++) {
+		for (n_list = 1; n_list < G_N_ELEMENTS (elements); n_list++) {
+			for (headless = 0; headless < 2; headless++) {
+				c_list_init (&head);
+				for (i = 0; i < n_list; i++) {
+					CListSort *el;
+
+					el = &elements[i];
+					el->val = nmtst_get_rand_int () % (2*n_list);
+					el->idx = i;
+					c_list_link_tail (&head, &el->lst);
+				}
+
+				if (headless) {
+					lst = head.next;
+					c_list_unlink (&head);
+					lst = c_list_sort_headless (lst, _c_list_sort_cmp, NULL);
+					g_assert (lst);
+					g_assert (lst->next);
+					g_assert (lst->prev);
+					g_assert (c_list_length (lst) == n_list - 1);
+					iter_prev = lst->prev;
+					for (iter = lst; iter != lst; iter = iter->next) {
+						g_assert (iter);
+						g_assert (iter->next);
+						g_assert (iter->prev == iter_prev);
+					}
+					c_list_link_before (lst, &head);
+				} else {
+					c_list_sort (&head, _c_list_sort_cmp, NULL);
+				}
+
+				g_assert (!c_list_is_empty (&head));
+				g_assert (c_list_length (&head) == n_list);
+
+				el_prev = NULL;
+				c_list_for_each (iter, &head) {
+					CListSort *el;
+
+					el = c_list_entry (iter, CListSort, lst);
+					g_assert (el->idx >= 0 && el->idx < n_list);
+					g_assert (el == &elements[el->idx]);
+					if (el_prev) {
+						g_assert (el_prev->val <= el->val);
+						if (el_prev->val == el->val)
+							g_assert (el_prev->idx < el->idx);
+						g_assert (iter->prev == &el_prev->lst);
+						g_assert (el_prev->lst.next == iter);
+					}
+					el_prev = el;
+				}
+				g_assert (head.prev == &el_prev->lst);
+			}
+		}
+	}
+}
+
+/*****************************************************************************/
+
+typedef struct {
+	NMDedupMultiObj parent;
+	guint val;
+	guint other;
+} DedupObj;
+
+static const NMDedupMultiObjClass dedup_obj_class;
+
+static DedupObj *
+_dedup_obj_assert (const NMDedupMultiObj *obj)
+{
+	DedupObj *o;
+
+	g_assert (obj);
+	o = (DedupObj *) obj;
+	g_assert (o->parent.klass == &dedup_obj_class);
+	g_assert (o->parent._ref_count > 0);
+	g_assert (o->val > 0);
+	return o;
+}
+
+static const NMDedupMultiObj *
+_dedup_obj_clone (const NMDedupMultiObj *obj)
+{
+	DedupObj *o, *o2;
+
+	o = _dedup_obj_assert (obj);
+	o2 = g_slice_new0 (DedupObj);
+	o2->parent.klass = &dedup_obj_class;
+	o2->parent._ref_count = 1;
+	o2->val = o->val;
+	o2->other = o->other;
+	return (NMDedupMultiObj *) o2;
+}
+
+static void
+_dedup_obj_destroy (NMDedupMultiObj *obj)
+{
+	DedupObj *o = (DedupObj *) obj;
+
+	nm_assert (o->parent._ref_count == 0);
+	o->parent._ref_count = 1;
+	o = _dedup_obj_assert (obj);
+	g_slice_free (DedupObj, o);
+}
+
+static void
+_dedup_obj_full_hash_update (const NMDedupMultiObj *obj, NMHashState *h)
+{
+	const DedupObj *o;
+
+	o = _dedup_obj_assert (obj);
+	nm_hash_update_vals (h,
+	                     o->val,
+	                     o->other);
+}
+
+static gboolean
+_dedup_obj_full_equal (const NMDedupMultiObj *obj_a,
+                       const NMDedupMultiObj *obj_b)
+{
+	const DedupObj *o_a = _dedup_obj_assert (obj_a);
+	const DedupObj *o_b = _dedup_obj_assert (obj_b);
+
+	return    o_a->val == o_b->val
+	       && o_a->other == o_b->other;
+}
+
+static const NMDedupMultiObjClass dedup_obj_class = {
+	.obj_clone = _dedup_obj_clone,
+	.obj_destroy = _dedup_obj_destroy,
+	.obj_full_hash_update = _dedup_obj_full_hash_update,
+	.obj_full_equal = _dedup_obj_full_equal,
+};
+
+#define DEDUP_OBJ_INIT(val_val, other_other) \
+	(&((DedupObj) { \
+		.parent = { \
+			.klass = &dedup_obj_class, \
+			._ref_count = NM_OBJ_REF_COUNT_STACKINIT, \
+		}, \
+		.val = (val_val), \
+		.other = (other_other), \
+	}))
+
+typedef struct {
+	NMDedupMultiIdxType parent;
+	guint partition_size;
+	guint val_mod;
+} DedupIdxType;
+
+static const NMDedupMultiIdxTypeClass dedup_idx_type_class;
+
+static const DedupIdxType *
+_dedup_idx_assert (const NMDedupMultiIdxType *idx_type)
+{
+	DedupIdxType *t;
+
+	g_assert (idx_type);
+	t = (DedupIdxType *) idx_type;
+	g_assert (t->parent.klass == &dedup_idx_type_class);
+	g_assert (t->partition_size > 0);
+	g_assert (t->val_mod > 0);
+	return t;
+}
+
+static void
+_dedup_idx_obj_id_hash_update (const NMDedupMultiIdxType *idx_type,
+                               const NMDedupMultiObj *obj,
+                               NMHashState *h)
+{
+	const DedupIdxType *t;
+	const DedupObj *o;
+
+	t = _dedup_idx_assert (idx_type);
+	o = _dedup_obj_assert (obj);
+
+	nm_hash_update_val (h, o->val / t->partition_size);
+	nm_hash_update_val (h, o->val % t->val_mod);
+}
+
+static gboolean
+_dedup_idx_obj_id_equal (const NMDedupMultiIdxType *idx_type,
+                         const NMDedupMultiObj *obj_a,
+                         const NMDedupMultiObj *obj_b)
+{
+	const DedupIdxType *t;
+	const DedupObj *o_a;
+	const DedupObj *o_b;
+
+	t = _dedup_idx_assert (idx_type);
+	o_a = _dedup_obj_assert (obj_a);
+	o_b = _dedup_obj_assert (obj_b);
+
+	return    (o_a->val / t->partition_size) == (o_b->val / t->partition_size)
+	       && (o_a->val % t->val_mod) == (o_b->val % t->val_mod);
+}
+
+static void
+_dedup_idx_obj_partition_hash_update (const NMDedupMultiIdxType *idx_type,
+                                      const NMDedupMultiObj *obj,
+                                      NMHashState *h)
+{
+	const DedupIdxType *t;
+	const DedupObj *o;
+
+	t = _dedup_idx_assert (idx_type);
+	o = _dedup_obj_assert (obj);
+
+	nm_hash_update_val (h, o->val / t->partition_size);
+}
+
+static gboolean
+_dedup_idx_obj_partition_equal (const NMDedupMultiIdxType *idx_type,
+                                const NMDedupMultiObj *obj_a,
+                                const NMDedupMultiObj *obj_b)
+{
+	const DedupIdxType *t;
+	const DedupObj *o_a;
+	const DedupObj *o_b;
+
+	t = _dedup_idx_assert (idx_type);
+	o_a = _dedup_obj_assert (obj_a);
+	o_b = _dedup_obj_assert (obj_b);
+
+	return (o_a->val / t->partition_size) == (o_b->val / t->partition_size);
+}
+
+static const NMDedupMultiIdxTypeClass dedup_idx_type_class = {
+	.idx_obj_id_hash_update = _dedup_idx_obj_id_hash_update,
+	.idx_obj_id_equal = _dedup_idx_obj_id_equal,
+	.idx_obj_partition_hash_update = _dedup_idx_obj_partition_hash_update,
+	.idx_obj_partition_equal = _dedup_idx_obj_partition_equal,
+};
+
+static const DedupIdxType *
+DEDUP_IDX_TYPE_INIT (DedupIdxType *idx_type, guint partition_size, guint val_mod)
+{
+	nm_dedup_multi_idx_type_init ((NMDedupMultiIdxType *) idx_type, &dedup_idx_type_class);
+	idx_type->val_mod = val_mod;
+	idx_type->partition_size = partition_size;
+	return idx_type;
+}
+
+static gboolean
+_dedup_idx_add (NMDedupMultiIndex *idx, const DedupIdxType *idx_type, const DedupObj *obj, NMDedupMultiIdxMode mode, const NMDedupMultiEntry **out_entry)
+{
+	g_assert (idx);
+	_dedup_idx_assert ((NMDedupMultiIdxType *) idx_type);
+	if (obj)
+		_dedup_obj_assert ((NMDedupMultiObj *) obj);
+	return nm_dedup_multi_index_add (idx, (NMDedupMultiIdxType *) idx_type,
+	                                 obj, mode, out_entry, NULL);
+}
+
+static void
+_dedup_head_entry_assert (const NMDedupMultiHeadEntry *entry)
+{
+	g_assert (entry);
+	g_assert (entry->len > 0);
+	g_assert (entry->len == c_list_length (&entry->lst_entries_head));
+	g_assert (entry->idx_type);
+	g_assert (entry->is_head);
+}
+
+static const DedupObj *
+_dedup_entry_assert (const NMDedupMultiEntry *entry)
+{
+	g_assert (entry);
+	g_assert (!c_list_is_empty (&entry->lst_entries));
+	g_assert (entry->head);
+	g_assert (!entry->is_head);
+	g_assert (entry->head != (gpointer) entry);
+	_dedup_head_entry_assert (entry->head);
+	return _dedup_obj_assert (entry->obj);
+}
+
+static const DedupIdxType *
+_dedup_entry_get_idx_type (const NMDedupMultiEntry *entry)
+{
+	_dedup_entry_assert (entry);
+
+	g_assert (entry->head);
+	g_assert (entry->head->idx_type);
+	return _dedup_idx_assert (entry->head->idx_type);
+}
+
+static void
+_dedup_entry_assert_all (const NMDedupMultiEntry *entry, gssize expected_idx, const DedupObj *const*expected_obj)
+{
+	gsize n, i;
+	CList *iter;
+
+	g_assert (entry);
+	_dedup_entry_assert (entry);
+
+	g_assert (expected_obj);
+	n = NM_PTRARRAY_LEN (expected_obj);
+
+	g_assert (n == c_list_length (&entry->lst_entries));
+
+	g_assert (expected_idx >= -1 && expected_idx < n);
+	g_assert (entry->head);
+	if (expected_idx == -1)
+		g_assert (entry->head == (gpointer) entry);
+	else
+		g_assert (entry->head != (gpointer) entry);
+
+	i = 0;
+	c_list_for_each (iter, &entry->head->lst_entries_head) {
+		const NMDedupMultiEntry *entry_current = c_list_entry (iter, NMDedupMultiEntry, lst_entries);
+		const DedupObj *obj_current;
+		const DedupIdxType *idx_type = _dedup_entry_get_idx_type (entry_current);
+
+		obj_current = _dedup_entry_assert (entry_current);
+		g_assert (obj_current);
+		g_assert (i < n);
+		if (expected_idx == i)
+			g_assert (entry_current == entry);
+		g_assert (idx_type->parent.klass->idx_obj_partition_equal (&idx_type->parent,
+		                                                           entry_current->obj,
+		                                                           c_list_entry (entry->head->lst_entries_head.next, NMDedupMultiEntry, lst_entries)->obj));
+		i++;
+	}
+}
+#define _dedup_entry_assert_all(entry, expected_idx, ...) _dedup_entry_assert_all (entry, expected_idx, (const DedupObj *const[]) { __VA_ARGS__, NULL })
+
+static void
+test_dedup_multi (void)
+{
+	NMDedupMultiIndex *idx;
+	DedupIdxType IDX_20_3_a_stack;
+	const DedupIdxType *const IDX_20_3_a = DEDUP_IDX_TYPE_INIT (&IDX_20_3_a_stack, 20, 3);
+	const NMDedupMultiEntry *entry1;
+
+	idx = nm_dedup_multi_index_new ();
+
+	g_assert (_dedup_idx_add (idx, IDX_20_3_a, DEDUP_OBJ_INIT (1, 1), NM_DEDUP_MULTI_IDX_MODE_APPEND, &entry1));
+	_dedup_entry_assert_all (entry1, 0, DEDUP_OBJ_INIT (1, 1));
+
+	g_assert (nm_dedup_multi_index_obj_find (idx, (NMDedupMultiObj *) DEDUP_OBJ_INIT (1, 1)));
+	g_assert (!nm_dedup_multi_index_obj_find (idx, (NMDedupMultiObj *) DEDUP_OBJ_INIT (1, 2)));
+
+	g_assert (_dedup_idx_add (idx, IDX_20_3_a, DEDUP_OBJ_INIT (1, 2), NM_DEDUP_MULTI_IDX_MODE_APPEND, &entry1));
+	_dedup_entry_assert_all (entry1, 0, DEDUP_OBJ_INIT (1, 2));
+
+	g_assert (!nm_dedup_multi_index_obj_find (idx, (NMDedupMultiObj *) DEDUP_OBJ_INIT (1, 1)));
+	g_assert (nm_dedup_multi_index_obj_find (idx, (NMDedupMultiObj *) DEDUP_OBJ_INIT (1, 2)));
+
+	g_assert (_dedup_idx_add (idx, IDX_20_3_a, DEDUP_OBJ_INIT (2, 2), NM_DEDUP_MULTI_IDX_MODE_APPEND, &entry1));
+	_dedup_entry_assert_all (entry1, 1, DEDUP_OBJ_INIT (1, 2), DEDUP_OBJ_INIT (2, 2));
+
+	nm_dedup_multi_index_unref (idx);
+}
+
+/*****************************************************************************/
+
 static NMConnection *
 _connection_new_from_dbus (GVariant *dict, GError **error)
 {
@@ -1975,6 +2571,7 @@ test_connection_diff_a_only (void)
 			{ NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_METERED,              NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_LLDP,                 NM_SETTING_DIFF_RESULT_IN_A },
+			{ NM_SETTING_CONNECTION_AUTH_RETRIES,         NM_SETTING_DIFF_RESULT_IN_A },
 			{ NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }
 		} },
 		{ NM_SETTING_WIRED_SETTING_NAME, {
@@ -2003,6 +2600,7 @@ test_connection_diff_a_only (void)
 			{ NM_SETTING_IP_CONFIG_GATEWAY,            NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_IP_CONFIG_ROUTES,             NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_IP_CONFIG_ROUTE_METRIC,       NM_SETTING_DIFF_RESULT_IN_A },
+			{ NM_SETTING_IP_CONFIG_ROUTE_TABLE,        NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS,    NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID,    NM_SETTING_DIFF_RESULT_IN_A },
@@ -2834,7 +3432,7 @@ test_ip4_prefix_to_netmask (void)
 	int i;
 
 	for (i = 0; i<=32; i++) {
-		guint32 netmask = nm_utils_ip4_prefix_to_netmask (i);
+		guint32 netmask = _nm_utils_ip4_prefix_to_netmask (i);
 		int plen = nm_utils_ip4_netmask_to_prefix (netmask);
 
 		g_assert_cmpint (i, ==, plen);
@@ -2862,8 +3460,8 @@ test_ip4_netmask_to_prefix (void)
 	g_rand_set_seed (rand, 1);
 
 	for (i = 2; i<=32; i++) {
-		guint32 netmask = nm_utils_ip4_prefix_to_netmask (i);
-		guint32 netmask_lowest_bit = netmask & ~nm_utils_ip4_prefix_to_netmask (i-1);
+		guint32 netmask = _nm_utils_ip4_prefix_to_netmask (i);
+		guint32 netmask_lowest_bit = netmask & ~_nm_utils_ip4_prefix_to_netmask (i-1);
 
 		g_assert_cmpint (i, ==, nm_utils_ip4_netmask_to_prefix (netmask));
 
@@ -3008,7 +3606,7 @@ test_setting_ip4_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_dns (s_ip4, "11.22.0.0"));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_dns (s_ip4, 0));
 
-	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->dns->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns (s_ip4, 1));
 	g_test_assert_expected_messages ();
 
@@ -3018,7 +3616,7 @@ test_setting_ip4_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_dns_search (s_ip4, "foobar.com"));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_dns_search (s_ip4, 0));
 
-	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->dns_search->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns_search->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns_search (s_ip4, 1));
 	g_test_assert_expected_messages ();
 
@@ -3030,7 +3628,7 @@ test_setting_ip4_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_address (s_ip4, addr));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_address (s_ip4, 0));
 
-	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->addresses->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->addresses->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_address (s_ip4, 1));
 	g_test_assert_expected_messages ();
 
@@ -3043,7 +3641,7 @@ test_setting_ip4_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_route (s_ip4, route));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_route (s_ip4, 0));
 
-	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->routes->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->routes->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_route (s_ip4, 1));
 	g_test_assert_expected_messages ();
 
@@ -3053,7 +3651,7 @@ test_setting_ip4_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_dns_option (s_ip4, "debug"));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_dns_option (s_ip4, 0));
 
-	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->dns_options->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns_options->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns_option (s_ip4, 1));
 	g_test_assert_expected_messages ();
 
@@ -3084,7 +3682,7 @@ test_setting_ip6_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_dns (s_ip6, "1:2:3::4:5:6"));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_dns (s_ip6, 0));
 
-	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->dns->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns (s_ip6, 1));
 	g_test_assert_expected_messages ();
 
@@ -3094,7 +3692,7 @@ test_setting_ip6_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_dns_search (s_ip6, "foobar.com"));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_dns_search (s_ip6, 0));
 
-	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->dns_search->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns_search->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns_search (s_ip6, 1));
 	g_test_assert_expected_messages ();
 
@@ -3107,7 +3705,7 @@ test_setting_ip6_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_address (s_ip6, addr));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_address (s_ip6, 0));
 
-	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->addresses->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->addresses->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_address (s_ip6, 1));
 	g_test_assert_expected_messages ();
 
@@ -3120,7 +3718,7 @@ test_setting_ip6_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_route (s_ip6, route));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_route (s_ip6, 0));
 
-	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->routes->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->routes->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_route (s_ip6, 1));
 	g_test_assert_expected_messages ();
 
@@ -3492,7 +4090,7 @@ _test_connection_normalize_type_normalizable_setting (const char *type,
 
 	base_type = nm_setting_lookup_type (type);
 	g_assert (base_type != G_TYPE_INVALID);
-	g_assert (_nm_setting_type_is_base_type (base_type));
+	g_assert (_nm_setting_type_get_base_type_priority (base_type) != NM_SETTING_PRIORITY_INVALID);
 
 	con = nmtst_create_minimal_connection (id, NULL, NULL, &s_con);
 
@@ -3522,7 +4120,7 @@ _test_connection_normalize_type_unnormalizable_setting (const char *type)
 
 	base_type = nm_setting_lookup_type (type);
 	g_assert (base_type != G_TYPE_INVALID);
-	g_assert (_nm_setting_type_is_base_type (base_type));
+	g_assert (_nm_setting_type_get_base_type_priority (base_type) != NM_SETTING_PRIORITY_INVALID);
 
 	con = nmtst_create_minimal_connection (id, NULL, NULL, &s_con);
 
@@ -3545,7 +4143,7 @@ _test_connection_normalize_type_normalizable_type (const char *type,
 
 	base_type = nm_setting_lookup_type (type);
 	g_assert (base_type != G_TYPE_INVALID);
-	g_assert (_nm_setting_type_is_base_type (base_type));
+	g_assert (_nm_setting_type_get_base_type_priority (base_type) != NM_SETTING_PRIORITY_INVALID);
 
 	con = nmtst_create_minimal_connection (id, NULL, NULL, &s_con);
 
@@ -3558,7 +4156,7 @@ _test_connection_normalize_type_normalizable_type (const char *type,
 		nm_connection_add_setting (con, s_base);
 	}
 
-	g_assert (!nm_connection_get_connection_type (con));
+	g_assert (!nm_setting_connection_get_connection_type (s_con));
 	g_assert (nm_connection_get_setting_by_name (con, type) == s_base);
 
 	nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
@@ -4058,6 +4656,344 @@ test_connection_normalize_shared_addresses (void)
 }
 
 static void
+test_connection_normalize_ovs_interface_type_system (gconstpointer test_data)
+{
+	const guint TEST_CASE = GPOINTER_TO_UINT (test_data);
+	gs_unref_object NMConnection *con = NULL;
+	NMSettingConnection *s_con;
+	NMSettingOvsInterface *s_ovs_if;
+
+	con = nmtst_create_minimal_connection ("test_connection_normalize_ovs_interface_type_system",
+	                                       NULL,
+	                                       NM_SETTING_WIRED_SETTING_NAME, &s_con);
+
+	switch (TEST_CASE) {
+	case 1:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+
+		nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING);
+
+		nmtst_connection_normalize (con);
+		nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		                                           NM_SETTING_WIRED_SETTING_NAME,
+		                                           NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		s_ovs_if = nm_connection_get_setting_ovs_interface (con);
+		g_assert (s_ovs_if);
+		g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "system");
+		break;
+	case 2:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+
+		s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+		nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+		nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+
+		nmtst_connection_normalize (con);
+		nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		                                           NM_SETTING_WIRED_SETTING_NAME,
+		                                           NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+		g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "system");
+		break;
+	case 3:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+
+		s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+		nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "system",
+		              NULL);
+		nmtst_assert_connection_verifies_without_normalization (con);
+		nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		                                           NM_SETTING_WIRED_SETTING_NAME,
+		                                           NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		break;
+	case 4:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+
+		s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+		nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "internal",
+		              NULL);
+		/* the setting doesn't verify, because the interface-type must be "system". */
+		nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+		break;
+	case 5:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NULL);
+
+		s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+		nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "system",
+		              NULL);
+		nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+		nmtst_connection_normalize (con);
+		nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		                                           NM_SETTING_WIRED_SETTING_NAME,
+		                                           NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		g_assert (s_con == nm_connection_get_setting_connection (con));
+		g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+		break;
+	case 6:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
+		              NULL);
+
+		s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+		nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "system",
+		              NULL);
+		nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+		break;
+	case 7:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
+		              NULL);
+
+		nm_connection_add_setting (con, nm_setting_bridge_port_new ());
+
+		s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ());
+		nm_connection_add_setting (con, NM_SETTING (s_ovs_if));
+
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "system",
+		              NULL);
+		nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+static void
+test_connection_normalize_ovs_interface_type_ovs_interface (gconstpointer test_data)
+{
+	const guint TEST_CASE = GPOINTER_TO_UINT (test_data);
+	gs_unref_object NMConnection *con = NULL;
+	NMSettingConnection *s_con;
+	NMSettingOvsInterface *s_ovs_if;
+	NMSettingOvsPatch *s_ovs_patch;
+	NMSettingIP4Config *s_ip4;
+	NMSettingIP6Config *s_ip6;
+
+	con = nmtst_create_minimal_connection ("test_connection_normalize_ovs_interface_type_ovs_interface",
+	                                       NULL,
+	                                       NM_SETTING_OVS_INTERFACE_SETTING_NAME, &s_con);
+	s_ovs_if = nm_connection_get_setting_ovs_interface (con);
+	g_assert (s_ovs_if);
+
+	switch (TEST_CASE) {
+	case 1:
+		nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+		break;
+	case 2:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NULL);
+		nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+		nmtst_connection_normalize (con);
+		nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		                                           NM_SETTING_IP4_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_IP6_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_PROXY_SETTING_NAME,
+		                                           NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		g_assert (s_con == nm_connection_get_setting_connection (con));
+		g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+		g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+		g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal");
+		break;
+	case 3:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+		nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+		nmtst_connection_normalize (con);
+		nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		                                           NM_SETTING_IP4_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_IP6_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_PROXY_SETTING_NAME,
+		                                           NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		g_assert (s_con == nm_connection_get_setting_connection (con));
+		g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+		g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+		g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal");
+		break;
+	case 4:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NULL);
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "internal",
+		              NULL);
+		nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+		nmtst_connection_normalize (con);
+		nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		                                           NM_SETTING_IP4_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_IP6_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_PROXY_SETTING_NAME,
+		                                           NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		g_assert (s_con == nm_connection_get_setting_connection (con));
+		g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+		g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+		g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal");
+		break;
+	case 5:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "internal",
+		              NULL);
+		nm_connection_add_setting (con, nm_setting_ip4_config_new ());
+		nm_connection_add_setting (con, nm_setting_ip6_config_new ());
+		nm_connection_add_setting (con, nm_setting_proxy_new ());
+		s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting_ip4_config (con));
+		s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (con));
+		g_object_set (s_ip4,
+		              NM_SETTING_IP_CONFIG_METHOD, "auto",
+		              NULL);
+		g_object_set (s_ip6,
+		              NM_SETTING_IP_CONFIG_METHOD, "auto",
+		              NULL);
+		nmtst_assert_connection_verifies_without_normalization (con);
+		nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		                                           NM_SETTING_IP4_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_IP6_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_PROXY_SETTING_NAME,
+		                                           NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		break;
+	case 6:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "internal",
+		              NULL);
+		nmtst_assert_connection_verifies_and_normalizable (con);
+		nmtst_connection_normalize (con);
+		nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		                                           NM_SETTING_IP4_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_IP6_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_PROXY_SETTING_NAME,
+		                                           NM_SETTING_OVS_INTERFACE_SETTING_NAME);
+		g_assert (s_con == nm_connection_get_setting_connection (con));
+		g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+		g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+		g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal");
+		break;
+	case 7:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "system",
+		              NULL);
+		nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+		break;
+	case 8:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "bogus",
+		              NULL);
+		nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
+		break;
+	case 9:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "patch",
+		              NULL);
+		nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING);
+		break;
+	case 10:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "patch",
+		              NULL);
+		nm_connection_add_setting (con, nm_setting_ovs_patch_new ());
+		nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+		break;
+	case 11:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NM_SETTING_CONNECTION_INTERFACE_NAME, "adsf",
+		              NULL);
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "patch",
+		              NULL);
+		nm_connection_add_setting (con, nm_setting_ovs_patch_new ());
+		nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
+		break;
+	case 12:
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NM_SETTING_CONNECTION_INTERFACE_NAME, "adsf",
+		              NULL);
+		g_object_set (s_ovs_if,
+		              NM_SETTING_OVS_INTERFACE_TYPE, "patch",
+		              NULL);
+		s_ovs_patch = NM_SETTING_OVS_PATCH (nm_setting_ovs_patch_new ());
+		nm_connection_add_setting (con, NM_SETTING (s_ovs_patch));
+		g_object_set (s_ovs_patch,
+		              NM_SETTING_OVS_PATCH_PEER, "1.2.3.4",
+		              NULL);
+		nmtst_assert_connection_verifies_and_normalizable (con);
+		nmtst_connection_normalize (con);
+		nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		                                           NM_SETTING_IP4_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_IP6_CONFIG_SETTING_NAME,
+		                                           NM_SETTING_PROXY_SETTING_NAME,
+		                                           NM_SETTING_OVS_INTERFACE_SETTING_NAME,
+		                                           NM_SETTING_OVS_PATCH_SETTING_NAME);
+		g_assert (s_con == nm_connection_get_setting_connection (con));
+		g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con));
+		g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME);
+		g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "patch");
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+static void
 test_setting_ip4_gateway (void)
 {
 	NMConnection *conn;
@@ -4375,6 +5311,88 @@ test_hexstr2bin (void)
 
 /*****************************************************************************/
 
+static void
+_do_strquote (const char *str, gsize buf_len, const char *expected)
+{
+	char canary = (char) nmtst_get_rand_int ();
+	gs_free char *buf_full = g_malloc (buf_len + 2);
+	char *buf = &buf_full[1];
+	const char *b;
+
+	buf[-1] = canary;
+	buf[buf_len] = canary;
+
+	if (buf_len == 0) {
+		b = nm_strquote (NULL, 0, str);
+		g_assert (b == NULL);
+		g_assert (expected == NULL);
+		b = nm_strquote (buf, 0, str);
+		g_assert (b == buf);
+	} else {
+		b = nm_strquote (buf, buf_len, str);
+		g_assert (b == buf);
+		g_assert (strlen (b) < buf_len);
+		g_assert_cmpstr (expected, ==, b);
+	}
+
+	g_assert (buf[-1] == canary);
+	g_assert (buf[buf_len] == canary);
+}
+
+static void
+test_nm_strquote (void)
+{
+	_do_strquote (NULL, 0, NULL);
+	_do_strquote ("", 0, NULL);
+	_do_strquote ("a", 0, NULL);
+	_do_strquote ("ab", 0, NULL);
+
+	_do_strquote (NULL, 1, "");
+	_do_strquote (NULL, 2, "(");
+	_do_strquote (NULL, 3, "(n");
+	_do_strquote (NULL, 4, "(nu");
+	_do_strquote (NULL, 5, "(nul");
+	_do_strquote (NULL, 6, "(null");
+	_do_strquote (NULL, 7, "(null)");
+	_do_strquote (NULL, 8, "(null)");
+	_do_strquote (NULL, 100, "(null)");
+
+	_do_strquote ("", 1, "");
+	_do_strquote ("", 2, "^");
+	_do_strquote ("", 3, "\"\"");
+	_do_strquote ("", 4, "\"\"");
+	_do_strquote ("", 5, "\"\"");
+	_do_strquote ("", 100, "\"\"");
+
+	_do_strquote ("a", 1, "");
+	_do_strquote ("a", 2, "^");
+	_do_strquote ("a", 3, "\"^");
+	_do_strquote ("a", 4, "\"a\"");
+	_do_strquote ("a", 5, "\"a\"");
+	_do_strquote ("a", 6, "\"a\"");
+	_do_strquote ("a", 100, "\"a\"");
+
+	_do_strquote ("ab", 1, "");
+	_do_strquote ("ab", 2, "^");
+	_do_strquote ("ab", 3, "\"^");
+	_do_strquote ("ab", 4, "\"a^");
+	_do_strquote ("ab", 5, "\"ab\"");
+	_do_strquote ("ab", 6, "\"ab\"");
+	_do_strquote ("ab", 7, "\"ab\"");
+	_do_strquote ("ab", 100, "\"ab\"");
+
+	_do_strquote ("abc", 1, "");
+	_do_strquote ("abc", 2, "^");
+	_do_strquote ("abc", 3, "\"^");
+	_do_strquote ("abc", 4, "\"a^");
+	_do_strquote ("abc", 5, "\"ab^");
+	_do_strquote ("abc", 6, "\"abc\"");
+	_do_strquote ("abc", 7, "\"abc\"");
+	_do_strquote ("abc", 100, "\"abc\"");
+}
+
+/*****************************************************************************/
+
 #define UUID_NIL        "00000000-0000-0000-0000-000000000000"
 #define UUID_NS_DNS     "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
 
@@ -4655,6 +5673,81 @@ test_nm_utils_strstrdictkey (void)
 
 /*****************************************************************************/
 
+static guint
+_g_strv_length (gconstpointer arr)
+{
+	return arr ? g_strv_length ((char **) arr) : 0;
+}
+
+static void
+test_nm_ptrarray_len (void)
+{
+#define _PTRARRAY_cmp(len, arr) \
+	G_STMT_START { \
+		g_assert_cmpint (len, ==, NM_PTRARRAY_LEN (arr)); \
+		g_assert_cmpint (len, ==, _g_strv_length (arr)); \
+	} G_STMT_END
+#define _PTRARRAY_LEN0(T) \
+	G_STMT_START { \
+		T **vnull = NULL; \
+		T *const*vnull1 = NULL; \
+		T *const*const vnull2 = NULL; \
+		T *v0[] = { NULL }; \
+		T *const*v01 = v0; \
+		T *const*const v02 = v0; \
+		T **const v03 = v0; \
+		\
+		_PTRARRAY_cmp (0, vnull); \
+		_PTRARRAY_cmp (0, vnull1); \
+		_PTRARRAY_cmp (0, vnull2); \
+		_PTRARRAY_cmp (0, v0); \
+		_PTRARRAY_cmp (0, v01); \
+		_PTRARRAY_cmp (0, v02); \
+		_PTRARRAY_cmp (0, v03); \
+	} G_STMT_END
+
+	_PTRARRAY_LEN0 (char);
+	_PTRARRAY_LEN0 (const char);
+	_PTRARRAY_LEN0 (int);
+	_PTRARRAY_LEN0 (const int);
+	_PTRARRAY_LEN0 (void *);
+	_PTRARRAY_LEN0 (void);
+	_PTRARRAY_LEN0 (const void);
+
+#define _PTRARRAY_LENn(T) \
+	G_STMT_START { \
+		T x[5] = { 0 }; \
+		\
+		T *v1[] = { &x[0], NULL }; \
+		T *const*v11 = v1; \
+		T *const*const v12 = v1; \
+		T **const v13 = v1; \
+		\
+		T *v2[] = { &x[0], &x[1], NULL }; \
+		T *const*v21 = v2; \
+		T *const*const v22 = v2; \
+		T **const v23 = v2; \
+		\
+		_PTRARRAY_cmp (1, v1); \
+		_PTRARRAY_cmp (1, v11); \
+		_PTRARRAY_cmp (1, v12); \
+		_PTRARRAY_cmp (1, v13); \
+		\
+		_PTRARRAY_cmp (2, v2); \
+		_PTRARRAY_cmp (2, v21); \
+		_PTRARRAY_cmp (2, v22); \
+		_PTRARRAY_cmp (2, v23); \
+	} G_STMT_END
+
+	_PTRARRAY_LENn (char);
+	_PTRARRAY_LENn (const char);
+	_PTRARRAY_LENn (int);
+	_PTRARRAY_LENn (const int);
+	_PTRARRAY_LENn (void *);
+}
+
+/*****************************************************************************/
+
 static void
 test_nm_utils_dns_option_validate_do (char *option, gboolean ipv6, const NMUtilsDNSOptionDesc *descs,
                                       gboolean exp_result, char *exp_name, gboolean exp_value)
@@ -4808,10 +5901,26 @@ test_nm_utils_team_config_equal (void)
 	                          "{ \"runner\" :  { \"name\" : \"random\"} }",
 	                          FALSE,
 	                          TRUE);
+	_team_config_equal_check ("{ \"runner\" :  { \"name\" : \"loadbalance\"} }",
+	                          "{ \"runner\" :  { \"name\" : \"loadbalance\"} }",
+	                          FALSE,
+	                          TRUE);
 	_team_config_equal_check ("{ \"runner\" :  { \"name\" : \"random\"}, \"ports\" : { \"eth0\" : {} } }",
 	                          "{ \"runner\" :  { \"name\" : \"random\"}, \"ports\" : { \"eth1\" : {} } }",
 	                          FALSE,
 	                          TRUE);
+	_team_config_equal_check ("{ \"runner\" :  { \"name\" : \"lacp\"} }",
+	                          "{ \"runner\" :  { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }",
+	                          FALSE,
+	                          TRUE);
+	_team_config_equal_check ("{ \"runner\" :  { \"name\" : \"roundrobin\"} }",
+	                          "{ \"runner\" :  { \"name\" : \"roundrobin\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }",
+	                          FALSE,
+	                          FALSE);
+	_team_config_equal_check ("{ \"runner\" :  { \"name\" : \"lacp\"} }",
+	                          "{ \"runner\" :  { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\" ] } }",
+	                          FALSE,
+	                          FALSE);
 
 	/* team port config */
 	_team_config_equal_check ("{ }",
@@ -5209,7 +6318,7 @@ static void test_nm_utils_enum (void)
 	test_nm_utils_enum_to_str_do (bool_enum, NM_TEST_GENERAL_BOOL_ENUM_67, "67");
 	test_nm_utils_enum_to_str_do (bool_enum, NM_TEST_GENERAL_BOOL_ENUM_46, "64");
 
-	test_nm_utils_enum_to_str_do (meta_flags, NM_TEST_GENERAL_META_FLAGS_NONE, "");
+	test_nm_utils_enum_to_str_do (meta_flags, NM_TEST_GENERAL_META_FLAGS_NONE, "none");
 	test_nm_utils_enum_to_str_do (meta_flags, NM_TEST_GENERAL_META_FLAGS_BAZ, "baz");
 	test_nm_utils_enum_to_str_do (meta_flags, NM_TEST_GENERAL_META_FLAGS_FOO |
 	                                          NM_TEST_GENERAL_META_FLAGS_BAR |
@@ -5707,8 +6816,12 @@ int main (int argc, char **argv)
 {
 	nmtst_init (&argc, &argv, TRUE);
 
-	/* The tests */
+	g_test_add_func ("/core/general/test_nm_hash", test_nm_hash);
+	g_test_add_func ("/core/general/test_nm_g_slice_free_fcn", test_nm_g_slice_free_fcn);
+	g_test_add_func ("/core/general/test_c_list_sort", test_c_list_sort);
+	g_test_add_func ("/core/general/test_dedup_multi", test_dedup_multi);
 	g_test_add_func ("/core/general/test_utils_str_utf8safe", test_utils_str_utf8safe);
+	g_test_add_func ("/core/general/test_nm_utils_strsplit_set", test_nm_utils_strsplit_set);
 	g_test_add_func ("/core/general/test_nm_in_set", test_nm_in_set);
 	g_test_add_func ("/core/general/test_nm_in_strset", test_nm_in_strset);
 	g_test_add_func ("/core/general/test_setting_vpn_items", test_setting_vpn_items);
@@ -5766,6 +6879,25 @@ int main (int argc, char **argv)
 	g_test_add_func ("/core/general/test_connection_normalize_gateway_never_default", test_connection_normalize_gateway_never_default);
 	g_test_add_func ("/core/general/test_connection_normalize_may_fail", test_connection_normalize_may_fail);
 	g_test_add_func ("/core/general/test_connection_normalize_shared_addresses", test_connection_normalize_shared_addresses);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/1", GUINT_TO_POINTER (1), test_connection_normalize_ovs_interface_type_system);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/2", GUINT_TO_POINTER (2), test_connection_normalize_ovs_interface_type_system);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/3", GUINT_TO_POINTER (3), test_connection_normalize_ovs_interface_type_system);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/4", GUINT_TO_POINTER (4), test_connection_normalize_ovs_interface_type_system);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/5", GUINT_TO_POINTER (5), test_connection_normalize_ovs_interface_type_system);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/6", GUINT_TO_POINTER (6), test_connection_normalize_ovs_interface_type_system);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/7", GUINT_TO_POINTER (7), test_connection_normalize_ovs_interface_type_system);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/1",  GUINT_TO_POINTER (1),  test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/2",  GUINT_TO_POINTER (2),  test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/3",  GUINT_TO_POINTER (3),  test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/4",  GUINT_TO_POINTER (4),  test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/5",  GUINT_TO_POINTER (5),  test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/6",  GUINT_TO_POINTER (6),  test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/7",  GUINT_TO_POINTER (7),  test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/8",  GUINT_TO_POINTER (8),  test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/9",  GUINT_TO_POINTER (9),  test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/10", GUINT_TO_POINTER (10), test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/11", GUINT_TO_POINTER (11), test_connection_normalize_ovs_interface_type_ovs_interface);
+	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/12", GUINT_TO_POINTER (12), test_connection_normalize_ovs_interface_type_ovs_interface);
 
 	g_test_add_func ("/core/general/test_setting_connection_permissions_helpers", test_setting_connection_permissions_helpers);
 	g_test_add_func ("/core/general/test_setting_connection_permissions_property", test_setting_connection_permissions_property);
@@ -5811,6 +6943,7 @@ int main (int argc, char **argv)
 	g_test_add_func ("/core/general/test_setting_user_data", test_setting_user_data);
 
 	g_test_add_func ("/core/general/hexstr2bin", test_hexstr2bin);
+	g_test_add_func ("/core/general/nm_strquote", test_nm_strquote);
 	g_test_add_func ("/core/general/test_nm_utils_uuid_generate_from_string", test_nm_utils_uuid_generate_from_string);
 	g_test_add_func ("/core/general/_nm_utils_uuid_generate_from_strings", test_nm_utils_uuid_generate_from_strings);
 
@@ -5820,6 +6953,7 @@ int main (int argc, char **argv)
 	g_test_add_func ("/core/general/_glib_compat_g_hash_table_get_keys_as_array", test_g_hash_table_get_keys_as_array);
 	g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search", test_nm_utils_ptrarray_find_binary_search);
 	g_test_add_func ("/core/general/_nm_utils_strstrdictkey", test_nm_utils_strstrdictkey);
+	g_test_add_func ("/core/general/nm_ptrarray_len", test_nm_ptrarray_len);
 
 	g_test_add_func ("/core/general/_nm_utils_dns_option_validate", test_nm_utils_dns_option_validate);
 	g_test_add_func ("/core/general/_nm_utils_dns_option_find_idx", test_nm_utils_dns_option_find_idx);
diff --git a/libnm-core/tests/test-secrets.c b/libnm-core/tests/test-secrets.c
index 0149348d..3328e356 100644
--- a/libnm-core/tests/test-secrets.c
+++ b/libnm-core/tests/test-secrets.c
@@ -121,6 +121,12 @@ make_tls_connection (const char *detail, NMSetting8021xCKScheme scheme)
 	                                             &error);
 	nmtst_assert_success (success, error);
 
+	success = nm_setting_set_secret_flags (NM_SETTING (s_8021x),
+	                                       NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
+	                                       NM_SETTING_SECRET_FLAG_AGENT_OWNED,
+	                                       &error);
+	nmtst_assert_success (success, error);
+
 	/* IP4 setting */
 	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
 	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
@@ -247,6 +253,13 @@ make_tls_phase2_connection (const char *detail, NMSetting8021xCKScheme scheme)
 	                                                    &error);
 	nmtst_assert_success (success, error);
 
+	success = nm_setting_set_secret_flags (NM_SETTING (s_8021x),
+	                                       NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
+	                                       NM_SETTING_SECRET_FLAG_AGENT_OWNED,
+	                                       &error);
+	nmtst_assert_success (success, error);
+
+
 	/* IP4 setting */
 	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
 	nm_connection_add_setting (connection, NM_SETTING (s_ip4));