summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2017-01-23 23:34:56 +0100
committerMichael Biebl <biebl@debian.org>2017-01-23 23:34:56 +0100
commitd462f64d6044349b0a4e3581cf2c97bbab7aec97 (patch)
tree0743acb1a191ce83b5722d77aeb896c6c0cdd952 /src
parent58f8be580039b0575b197b9573a1c92745d96d30 (diff)
New upstream version 1.5.91 upstream/1.5.91
Diffstat (limited to 'src')
-rw-r--r--src/devices/nm-device-ethernet.c17
-rw-r--r--src/devices/nm-device-veth.c1
-rw-r--r--src/devices/nm-device-vlan.c20
-rw-r--r--src/devices/nm-device.c82
-rw-r--r--src/devices/nm-device.h2
-rw-r--r--src/nm-act-request.c30
-rw-r--r--src/nm-config.c6
-rw-r--r--src/nm-core-utils.c390
-rw-r--r--src/nm-core-utils.h13
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-invalid2
-rw-r--r--src/supplicant/nm-supplicant-interface.c38
-rw-r--r--src/supplicant/nm-supplicant-interface.h13
-rw-r--r--src/supplicant/nm-supplicant-manager.c30
-rw-r--r--src/tests/test-general.c322
14 files changed, 507 insertions, 459 deletions
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index f9d753a5..327e54a8 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -1543,19 +1543,12 @@ new_default_connection (NMDevice *self)
 	return connection;
 }
 
-static NMMatchSpecMatchType
-spec_match_list (NMDevice *device, const GSList *specs)
+static const char *
+get_s390_subchannels (NMDevice *device)
 {
-	NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m;
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE ((NMDeviceEthernet *) device);
+	nm_assert (NM_IS_DEVICE_ETHERNET (device));
 
-	if (priv->subchannels)
-		matched = nm_match_spec_s390_subchannels (specs, priv->subchannels);
-	if (matched != NM_MATCH_SPEC_NEG_MATCH) {
-		m = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->spec_match_list (device, specs);
-		matched = MAX (matched, m);
-	}
-	return matched;
+	return NM_DEVICE_ETHERNET_GET_PRIVATE ((NMDeviceEthernet *) device)->subchannels;
 }
 
 static void
@@ -1746,7 +1739,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
 	parent_class->get_configured_mtu = get_configured_mtu;
 	parent_class->deactivate = deactivate;
-	parent_class->spec_match_list = spec_match_list;
+	parent_class->get_s390_subchannels = get_s390_subchannels;
 	parent_class->update_connection = update_connection;
 	parent_class->carrier_changed = carrier_changed;
 	parent_class->link_changed = link_changed;
diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c
index a5a87a2d..1971aeeb 100644
--- a/src/devices/nm-device-veth.c
+++ b/src/devices/nm-device-veth.c
@@ -111,6 +111,7 @@ notify (GObject *object, GParamSpec *pspec)
 {
 	if (nm_streq (pspec->name, NM_DEVICE_PARENT))
 		_notify (NM_DEVICE_VETH (object), PROP_PEER);
+	G_OBJECT_CLASS (nm_device_veth_parent_class)->notify (object, pspec);
 }
 
 static void
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index 3979c95d..45d7cf67 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -572,6 +572,24 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 	return ret;
 }
 
+static guint32
+get_configured_mtu (NMDevice *self, gboolean *out_is_user_config)
+{
+	guint32 mtu = 0;
+	int ifindex;
+
+	mtu = nm_device_get_configured_mtu_for_wired (self, out_is_user_config);
+	if (*out_is_user_config)
+		return mtu;
+
+	/* Inherit the MTU from parent device, if any */
+	ifindex = nm_device_parent_get_ifindex (self);
+	if (ifindex > 0)
+		mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex);
+
+	return mtu ?: NM_DEVICE_DEFAULT_MTU_WIRED;
+}
+
 /*****************************************************************************/
 
 static void
@@ -612,7 +630,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 	parent_class->unrealize_notify = unrealize_notify;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
+	parent_class->get_configured_mtu = get_configured_mtu;
 	parent_class->is_available = is_available;
 	parent_class->parent_changed_notify = parent_changed_notify;
 
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index bc532ff9..b614894e 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -2140,8 +2140,7 @@ device_link_changed (NMDevice *self)
 		_notify (self, PROP_UDI);
 	}
 
-	if (g_strcmp0 (info.driver, priv->driver)) {
-		/* Update driver to what udev gives us */
+	if (!nm_streq0 (info.driver, priv->driver)) {
 		g_free (priv->driver);
 		priv->driver = g_strdup (info.driver);
 		_notify (self, PROP_DRIVER);
@@ -2152,12 +2151,6 @@ device_link_changed (NMDevice *self)
 		_notify (self, PROP_MTU);
 	}
 
-	if (info.driver && g_strcmp0 (priv->driver, info.driver) != 0) {
-		g_free (priv->driver);
-		priv->driver = g_strdup (info.driver);
-		_notify (self, PROP_DRIVER);
-	}
-
 	if (ifindex == nm_device_get_ip_ifindex (self))
 		_stats_update_counters_from_pllink (self, &info);
 
@@ -5698,10 +5691,9 @@ act_stage3_ip4_config_start (NMDevice *self,
 				ret = NM_ACT_STAGE_RETURN_FAILURE;
 		} else
 			g_return_val_if_reached (NM_ACT_STAGE_RETURN_FAILURE);
-	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) {
-		_commit_mtu (self, priv->ip4_config);
+	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
 		ret = NM_ACT_STAGE_RETURN_SUCCESS;
-	} else
+	else
 		_LOGW (LOGD_IP4, "unhandled IPv4 config method '%s'; will fail", method);
 
 	return ret;
@@ -6630,7 +6622,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 	struct {
 		gboolean initialized;
 		guint32 value;
-	} ip6_mtu_sysctl;
+	} ip6_mtu_sysctl = { 0, };
 	int ifindex;
 	char sbuf[64], sbuf1[64], sbuf2[64];
 
@@ -6729,7 +6721,6 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 	       ip6_mtu == ip6_mtu_orig ? "" : nm_sprintf_buf (sbuf2, " (was %u)", (guint) ip6_mtu_orig),
 	       ifindex);
 
-	ip6_mtu_sysctl.initialized = FALSE;
 #define _IP6_MTU_SYS() \
 	({ \
 		if (!ip6_mtu_sysctl.initialized) { \
@@ -8317,7 +8308,7 @@ _hash_check_invalid_keys_impl (GHashTable *hash, const char *setting_name, GErro
 		gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
 
 		for (i = 0; argv[i]; i++) {
-			if (!g_hash_table_add (check_dups, (char *) argv[i]))
+			if (!nm_g_hash_table_add (check_dups, (char *) argv[i]))
 				nm_assert (FALSE);
 		}
 		nm_assert (g_hash_table_size (check_dups) > 0);
@@ -12870,42 +12861,19 @@ nm_device_get_initial_hw_address (NMDevice *self)
 gboolean
 nm_device_spec_match_list (NMDevice *self, const GSList *specs)
 {
-	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
-
-	if (!specs)
-		return FALSE;
-
-	return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs) == NM_MATCH_SPEC_MATCH;
-}
+	NMDeviceClass *klass;
+	NMMatchSpecMatchType m;
 
-static NMMatchSpecMatchType
-spec_match_list (NMDevice *self, const GSList *specs)
-{
-	NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m;
-	const GSList *iter;
-	const char *hw_addr_perm;
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		if (!strcmp ((const char *) iter->data, "*")) {
-			matched = NM_MATCH_SPEC_MATCH;
-			break;
-		}
-	}
+	klass = NM_DEVICE_GET_CLASS (self);
 
-	hw_addr_perm = nm_device_get_permanent_hw_address (self);
-	if (hw_addr_perm) {
-		m = nm_match_spec_hwaddr (specs, hw_addr_perm);
-		matched = MAX (matched, m);
-	}
-	if (matched != NM_MATCH_SPEC_NEG_MATCH) {
-		m = nm_match_spec_interface_name (specs, nm_device_get_iface (self));
-		matched = MAX (matched, m);
-	}
-	if (matched != NM_MATCH_SPEC_NEG_MATCH) {
-		m = nm_match_spec_device_type (specs, nm_device_get_type_description (self));
-		matched = MAX (matched, m);
-	}
-	return matched;
+	m = nm_match_spec_device (specs,
+	                          nm_device_get_iface (self),
+	                          nm_device_get_type_description (self),
+	                          nm_device_get_permanent_hw_address (self),
+	                          klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL);
+	return m == NM_MATCH_SPEC_MATCH;
 }
 
 /*****************************************************************************/
@@ -13186,17 +13154,15 @@ set_property (GObject *object, guint prop_id,
 		priv->iface = g_value_dup_string (value);
 		break;
 	case PROP_DRIVER:
-		if (g_value_get_string (value)) {
-			g_free (priv->driver);
-			priv->driver = g_value_dup_string (value);
-		}
+		/* construct-only */
+		priv->driver = g_value_dup_string (value);
 		break;
 	case PROP_DRIVER_VERSION:
-		g_free (priv->driver_version);
+		/* construct-only */
 		priv->driver_version = g_value_dup_string (value);
 		break;
 	case PROP_FIRMWARE_VERSION:
-		g_free (priv->firmware_version);
+		/* construct-only */
 		priv->firmware_version = g_value_dup_string (value);
 		break;
 	case PROP_IP4_ADDRESS:
@@ -13230,22 +13196,25 @@ set_property (GObject *object, guint prop_id,
 		priv->nm_plugin_missing = g_value_get_boolean (value);
 		break;
 	case PROP_DEVICE_TYPE:
-		g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN);
+		/* construct-only */
+		nm_assert (priv->type == NM_DEVICE_TYPE_UNKNOWN);
 		priv->type = g_value_get_uint (value);
 		break;
 	case PROP_LINK_TYPE:
 		/* construct-only */
-		g_return_if_fail (priv->link_type == NM_LINK_TYPE_NONE);
+		nm_assert (priv->link_type == NM_LINK_TYPE_NONE);
 		priv->link_type = g_value_get_uint (value);
 		break;
 	case PROP_TYPE_DESC:
-		g_free (priv->type_desc);
+		/* construct-only */
 		priv->type_desc = g_value_dup_string (value);
 		break;
 	case PROP_RFKILL_TYPE:
+		/* construct-only */
 		priv->rfkill_type = g_value_get_uint (value);
 		break;
 	case PROP_IS_MASTER:
+		/* construct-only */
 		priv->is_master = g_value_get_boolean (value);
 		break;
 	case PROP_PERM_HW_ADDRESS:
@@ -13469,7 +13438,6 @@ nm_device_class_init (NMDeviceClass *klass)
 	klass->have_any_ready_slaves = have_any_ready_slaves;
 
 	klass->get_type_description = get_type_description;
-	klass->spec_match_list = spec_match_list;
 	klass->can_auto_connect = can_auto_connect;
 	klass->check_connection_compatible = check_connection_compatible;
 	klass->check_connection_available = check_connection_available;
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 52a74d26..311b5a2c 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -295,7 +295,7 @@ typedef struct {
 
 	const char *(*get_type_description) (NMDevice *self);
 
-	NMMatchSpecMatchType (* spec_match_list)   (NMDevice *self, const GSList *specs);
+	const char *(*get_s390_subchannels) (NMDevice *self);
 
 	/* Update the connection with currently configured L2 settings */
 	void            (* update_connection) (NMDevice *device, NMConnection *connection);
diff --git a/src/nm-act-request.c b/src/nm-act-request.c
index 3a04667b..03964896 100644
--- a/src/nm-act-request.c
+++ b/src/nm-act-request.c
@@ -495,31 +495,39 @@ static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
+	NMActiveConnection *active;
 	NMDevice *device;
-
-	device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (object));
-	if (!device) {
-		g_value_set_string (value, "/");
-		return;
-	}
+	char *name;
 
 	switch (prop_id) {
 	case PROP_IP4_CONFIG:
-		g_object_get_property (G_OBJECT (device), NM_DEVICE_IP4_CONFIG, value);
+		name = NM_DEVICE_IP4_CONFIG;
 		break;
 	case PROP_DHCP4_CONFIG:
-		g_object_get_property (G_OBJECT (device), NM_DEVICE_DHCP4_CONFIG, value);
+		name = NM_DEVICE_DHCP4_CONFIG;
 		break;
 	case PROP_IP6_CONFIG:
-		g_object_get_property (G_OBJECT (device), NM_DEVICE_IP6_CONFIG, value);
+		name = NM_DEVICE_IP6_CONFIG;
 		break;
 	case PROP_DHCP6_CONFIG:
-		g_object_get_property (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG, value);
+		name = NM_DEVICE_DHCP6_CONFIG;
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
+		return;
 	}
+
+	active = NM_ACTIVE_CONNECTION (object);
+	device = nm_active_connection_get_device (active);
+	if (   !device
+	    || !NM_IN_SET (nm_active_connection_get_state (active),
+	                   NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+	                   NM_ACTIVE_CONNECTION_STATE_DEACTIVATING)) {
+		g_value_set_string (value, "/");
+		return;
+	}
+
+	g_object_get_property (G_OBJECT (device), name, value);
 }
 
 static void
diff --git a/src/nm-config.c b/src/nm-config.c
index 87d0c8a7..bc8a3ea5 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -529,9 +529,9 @@ ignore_config_snippet (GKeyFile *keyfile, gboolean is_base_config)
 
 	/* second, interpret the value as match-spec. */
 	specs = nm_config_get_match_spec (keyfile, NM_CONFIG_KEYFILE_GROUP_CONFIG, NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE, NULL);
-	match_type = nm_match_spec_match_config (specs,
-	                                         _nm_config_match_nm_version,
-	                                         _nm_config_match_env);
+	match_type = nm_match_spec_config (specs,
+	                                   _nm_config_match_nm_version,
+	                                   _nm_config_match_env);
 	g_slist_free_full (specs, g_free);
 
 	return match_type != NM_MATCH_SPEC_MATCH;
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index 219f309b..79f9e117 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -1213,169 +1213,44 @@ nm_utils_read_link_absolute (const char *link_file, GError **error)
 #define MATCH_TAG_CONFIG_NM_VERSION_MAX         "nm-version-max:"
 #define MATCH_TAG_CONFIG_ENV                    "env:"
 
-#define _spec_has_prefix(pspec, tag) \
-	({ \
-		const char **_spec = (pspec); \
-		gboolean _has = FALSE; \
-		\
-		if (!g_ascii_strncasecmp (*_spec, (""tag), NM_STRLEN (tag))) { \
-			*_spec += NM_STRLEN (tag); \
-			_has = TRUE; \
-		} \
-		_has; \
-	})
-
-static const char *
-_match_except (const char *spec_str, gboolean *out_except)
-{
-	if (!g_ascii_strncasecmp (spec_str, EXCEPT_TAG, NM_STRLEN (EXCEPT_TAG))) {
-		spec_str += NM_STRLEN (EXCEPT_TAG);
-		*out_except = TRUE;
-	} else
-		*out_except = FALSE;
-	return spec_str;
-}
-
-NMMatchSpecMatchType
-nm_match_spec_device_type (const GSList *specs, const char *device_type)
-{
-	const GSList *iter;
-	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
-
-	if (!device_type || !*device_type)
-		return NM_MATCH_SPEC_NO_MATCH;
-
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		const char *spec_str = iter->data;
-		gboolean except;
-
-		if (!spec_str || !*spec_str)
-			continue;
-
-		spec_str = _match_except (spec_str, &except);
-
-		if (g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, NM_STRLEN (DEVICE_TYPE_TAG)) != 0)
-			continue;
-
-		spec_str += NM_STRLEN (DEVICE_TYPE_TAG);
-		if (strcmp (spec_str, device_type) == 0) {
-			if (except)
-				return NM_MATCH_SPEC_NEG_MATCH;
-			match = NM_MATCH_SPEC_MATCH;
-		}
-	}
-	return match;
-}
-
-NMMatchSpecMatchType
-nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
-{
-	const GSList *iter;
-	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
-	gsize hwaddr_len = 0;
-	guint8 hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX];
-
-	nm_assert (nm_utils_hwaddr_valid (hwaddr, -1));
-
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		const char *spec_str = iter->data;
-		gboolean except;
-
-		if (!spec_str || !*spec_str)
-			continue;
-
-		spec_str = _match_except (spec_str, &except);
-
-		if (   !g_ascii_strncasecmp (spec_str, INTERFACE_NAME_TAG, NM_STRLEN (INTERFACE_NAME_TAG))
-		    || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, NM_STRLEN (SUBCHAN_TAG))
-		    || !g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, NM_STRLEN (DEVICE_TYPE_TAG)))
-			continue;
-
-		if (!g_ascii_strncasecmp (spec_str, MAC_TAG, NM_STRLEN (MAC_TAG)))
-			spec_str += NM_STRLEN (MAC_TAG);
-		else if (except)
-			continue;
-
-		if (G_UNLIKELY (hwaddr_len == 0)) {
-			if (!_nm_utils_hwaddr_aton (hwaddr, hwaddr_bin, sizeof (hwaddr_bin), &hwaddr_len))
-				g_return_val_if_reached (NM_MATCH_SPEC_NO_MATCH);
-		}
-
-		if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr_bin, hwaddr_len)) {
-			if (except)
-				return NM_MATCH_SPEC_NEG_MATCH;
-			match = NM_MATCH_SPEC_MATCH;
-		}
-	}
-	return match;
-}
-
-NMMatchSpecMatchType
-nm_match_spec_interface_name (const GSList *specs, const char *interface_name)
-{
-	const GSList *iter;
-	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
-
-	g_return_val_if_fail (interface_name != NULL, NM_MATCH_SPEC_NO_MATCH);
-
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		const char *spec_str = iter->data;
-		gboolean use_pattern = FALSE;
-		gboolean except;
-
-		if (!spec_str || !*spec_str)
-			continue;
-
-		spec_str = _match_except (spec_str, &except);
-
-		if (   !g_ascii_strncasecmp (spec_str, MAC_TAG, NM_STRLEN (MAC_TAG))
-		    || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, NM_STRLEN (SUBCHAN_TAG))
-		    || !g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, NM_STRLEN (DEVICE_TYPE_TAG)))
-			continue;
-
-		if (!g_ascii_strncasecmp (spec_str, INTERFACE_NAME_TAG, NM_STRLEN (INTERFACE_NAME_TAG))) {
-			spec_str += NM_STRLEN (INTERFACE_NAME_TAG);
-			if (spec_str[0] == '=')
-				spec_str += 1;
-			else {
-				if (spec_str[0] == '~')
-					spec_str += 1;
-				use_pattern=TRUE;
-			}
-		} else if (except)
-			continue;
-
-		if (   !strcmp (spec_str, interface_name)
-		    || (use_pattern && g_pattern_match_simple (spec_str, interface_name))) {
-			if (except)
-				return NM_MATCH_SPEC_NEG_MATCH;
-			match = NM_MATCH_SPEC_MATCH;
-		}
-	}
-	return match;
-}
-
-#define BUFSIZE 10
+typedef struct {
+	const char *interface_name;
+	const char *device_type;
+	struct {
+		const char *value;
+		gboolean is_parsed;
+		guint len;
+		guint8 bin[NM_UTILS_HWADDR_LEN_MAX];
+	} hwaddr;
+	struct {
+		const char *value;
+		gboolean is_parsed;
+		guint32 a;
+		guint32 b;
+		guint32 c;
+	} s390_subchannels;
+} MatchDeviceData;
 
 static gboolean
-parse_subchannels (const char *subchannels, guint32 *a, guint32 *b, guint32 *c)
+match_device_s390_subchannels_parse (const char *s390_subchannels, guint32 *a, guint32 *b, guint32 *c)
 {
+	const int BUFSIZE = 10;
 	long unsigned int tmp;
 	char buf[BUFSIZE + 1];
-	const char *p = subchannels;
+	const char *p = s390_subchannels;
 	int i = 0;
 	char *pa = NULL, *pb = NULL, *pc = NULL;
 
-	g_return_val_if_fail (subchannels != NULL, FALSE);
-	g_return_val_if_fail (a != NULL, FALSE);
-	g_return_val_if_fail (*a == 0, FALSE);
-	g_return_val_if_fail (b != NULL, FALSE);
-	g_return_val_if_fail (*b == 0, FALSE);
-	g_return_val_if_fail (c != NULL, FALSE);
-	g_return_val_if_fail (*c == 0, FALSE);
+	nm_assert (s390_subchannels);
+	nm_assert (a != NULL);
+	nm_assert (*a == 0);
+	nm_assert (b != NULL);
+	nm_assert (*b == 0);
+	nm_assert (c != NULL);
+	nm_assert (*c == 0);
 
 	/* sanity check */
-	if (!g_ascii_isxdigit (subchannels[0]))
+	if (!g_ascii_isxdigit (s390_subchannels[0]))
 		return FALSE;
 
 	/* Get the first channel */
@@ -1421,47 +1296,194 @@ parse_subchannels (const char *subchannels, guint32 *a, guint32 *b, guint32 *c)
 	return TRUE;
 }
 
+static gboolean
+match_data_s390_subchannels_eval (const char *spec_str,
+                                  MatchDeviceData *match_data)
+{
+	guint32 a, b, c;
+
+	if (G_UNLIKELY (!match_data->s390_subchannels.is_parsed)) {
+		match_data->s390_subchannels.is_parsed = TRUE;
+
+		if (   !match_data->s390_subchannels.value
+		    || !match_device_s390_subchannels_parse (match_data->s390_subchannels.value,
+		                                             &match_data->s390_subchannels.a,
+		                                             &match_data->s390_subchannels.b,
+		                                             &match_data->s390_subchannels.c)) {
+			match_data->s390_subchannels.value = NULL;
+			return FALSE;
+		}
+	} else if (!match_data->s390_subchannels.value)
+		return FALSE;
+
+	if (!match_device_s390_subchannels_parse (spec_str, &a, &b, &c))
+		return FALSE;
+	return    match_data->s390_subchannels.a == a
+	       && match_data->s390_subchannels.b == b
+	       && match_data->s390_subchannels.c == c;
+}
+
+static gboolean
+match_device_hwaddr_eval (const char *spec_str,
+                          MatchDeviceData *match_data)
+{
+	if (G_UNLIKELY (!match_data->hwaddr.is_parsed)) {
+		match_data->hwaddr.is_parsed = TRUE;
+
+		if (match_data->hwaddr.value) {
+			gsize l;
+
+			if (!_nm_utils_hwaddr_aton (match_data->hwaddr.value, match_data->hwaddr.bin, sizeof (match_data->hwaddr.bin), &l))
+				g_return_val_if_reached (FALSE);
+			match_data->hwaddr.len = l;
+		} else
+			return FALSE;
+	} else if (!match_data->hwaddr.len)
+		return FALSE;
+
+	return nm_utils_hwaddr_matches (spec_str, -1, match_data->hwaddr.bin, match_data->hwaddr.len);
+}
+
+#define _MATCH_CHECK(spec_str, tag) \
+	({ \
+		gboolean _has = FALSE; \
+		\
+		if (!g_ascii_strncasecmp (spec_str, (""tag""), NM_STRLEN (tag))) { \
+			spec_str += NM_STRLEN (tag); \
+			_has = TRUE; \
+		} \
+		_has; \
+	})
+
+static const char *
+match_except (const char *spec_str, gboolean *out_except)
+{
+	if (_MATCH_CHECK (spec_str, EXCEPT_TAG))
+		*out_except = TRUE;
+	else
+		*out_except = FALSE;
+	return spec_str;
+}
+
+static gboolean
+match_device_eval (const char *spec_str,
+                   gboolean allow_fuzzy,
+                   MatchDeviceData *match_data)
+{
+	if (spec_str[0] == '*' && spec_str[1] == '\0')
+		return TRUE;
+
+	if (_MATCH_CHECK (spec_str, DEVICE_TYPE_TAG)) {
+		return    match_data->device_type
+		       && nm_streq (spec_str, match_data->device_type);
+	}
+
+	if (_MATCH_CHECK (spec_str, MAC_TAG))
+		return match_device_hwaddr_eval (spec_str, match_data);
+
+	if (_MATCH_CHECK (spec_str, INTERFACE_NAME_TAG)) {
+		gboolean use_pattern = FALSE;
+
+		if (spec_str[0] == '=')
+			spec_str += 1;
+		else {
+			if (spec_str[0] == '~')
+				spec_str += 1;
+			use_pattern = TRUE;
+		}
+
+		if (match_data->interface_name) {
+			if (nm_streq (spec_str, match_data->interface_name))
+				return TRUE;
+			if (use_pattern && g_pattern_match_simple (spec_str, match_data->interface_name))
+				return TRUE;
+		}
+		return FALSE;
+	}
+
+	if (_MATCH_CHECK (spec_str, SUBCHAN_TAG))
+		return match_data_s390_subchannels_eval (spec_str, match_data);
+
+	if (allow_fuzzy) {
+		if (match_device_hwaddr_eval (spec_str, match_data))
+			return TRUE;
+		if (   match_data->interface_name
+		    && nm_streq (spec_str, match_data->interface_name))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
 NMMatchSpecMatchType
-nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels)
+nm_match_spec_device (const GSList *specs,
+                      const char *interface_name,
+                      const char *device_type,
+                      const char *hwaddr,
+                      const char *s390_subchannels)
 {
 	const GSList *iter;
-	guint32 a = 0, b = 0, c = 0;
-	guint32 spec_a = 0, spec_b = 0, spec_c = 0;
-	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
+	NMMatchSpecMatchType match;
+	const char *spec_str;
+	gboolean except;
+	MatchDeviceData match_data = {
+	    .interface_name = interface_name,
+	    .device_type = nm_str_not_empty (device_type),
+	    .hwaddr = {
+	        .value = hwaddr,
+	    },
+	    .s390_subchannels = {
+	        .value = s390_subchannels,
+	    },
+	};
 
-	g_return_val_if_fail (subchannels != NULL, NM_MATCH_SPEC_NO_MATCH);
+	nm_assert (!hwaddr || nm_utils_hwaddr_valid (hwaddr, -1));
 
 	if (!specs)
 		return NM_MATCH_SPEC_NO_MATCH;
 
-	if (!parse_subchannels (subchannels, &a, &b, &c))
-		return NM_MATCH_SPEC_NO_MATCH;
+	match = NM_MATCH_SPEC_NO_MATCH;
 
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		const char *spec_str = iter->data;
-		gboolean except;
+	/* pre-search for "*" */
+	for (iter = specs; iter; iter = iter->next) {
+		spec_str = iter->data;
+
+		if (spec_str && spec_str[0] == '*' && spec_str[1] == '\0') {
+			match = NM_MATCH_SPEC_MATCH;
+			break;
+		}
+	}
+
+	for (iter = specs; iter; iter = iter->next) {
+		spec_str = iter->data;
 
 		if (!spec_str || !*spec_str)
 			continue;
 
-		spec_str = _match_except (spec_str, &except);
+		spec_str = match_except (spec_str, &except);
 
-		if (!g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, NM_STRLEN (SUBCHAN_TAG))) {
-			spec_str += NM_STRLEN (SUBCHAN_TAG);
-			if (parse_subchannels (spec_str, &spec_a, &spec_b, &spec_c)) {
-				if (a == spec_a && b == spec_b && c == spec_c) {
-					if (except)
-						return NM_MATCH_SPEC_NEG_MATCH;
-					match = NM_MATCH_SPEC_MATCH;
-				}
-			}
+		if (   !except
+		    && match == NM_MATCH_SPEC_MATCH) {
+			/* we have no "except-match" but already match. No need to evaluate
+			 * the match, we cannot match stronger. */
+			continue;
 		}
+
+		if (!match_device_eval (spec_str,
+		                        !except,
+		                        &match_data))
+			continue;
+
+		if (except)
+			return NM_MATCH_SPEC_NEG_MATCH;
+		match = NM_MATCH_SPEC_MATCH;
 	}
+
 	return match;
 }
 
 static gboolean
-_match_config_nm_version (const char *str, const char *tag, guint cur_nm_version)
+match_config_eval (const char *str, const char *tag, guint cur_nm_version)
 {
 	gs_free char *s_ver = NULL;
 	gs_strfreev char **s_ver_tokens = NULL;
@@ -1524,7 +1546,7 @@ _match_config_nm_version (const char *str, const char *tag, guint cur_nm_version
 }
 
 NMMatchSpecMatchType
-nm_match_spec_match_config (const GSList *specs, guint cur_nm_version, const char *env)
+nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env)
 {
 	const GSList *iter;
 	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
@@ -1540,15 +1562,15 @@ nm_match_spec_match_config (const GSList *specs, guint cur_nm_version, const cha
 		if (!spec_str || !*spec_str)
 			continue;
 
-		spec_str = _match_except (spec_str, &except);
+		spec_str = match_except (spec_str, &except);
 
-		if (_spec_has_prefix (&spec_str, MATCH_TAG_CONFIG_NM_VERSION))
-			v_match = _match_config_nm_version (spec_str, MATCH_TAG_CONFIG_NM_VERSION, cur_nm_version);
-		else if (_spec_has_prefix (&spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN))
-			v_match = _match_config_nm_version (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN, cur_nm_version);
-		else if (_spec_has_prefix (&spec_str, MATCH_TAG_CONFIG_NM_VERSION_MAX))
-			v_match = _match_config_nm_version (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MAX, cur_nm_version);
-		else if (_spec_has_prefix (&spec_str, MATCH_TAG_CONFIG_ENV))
+		if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION))
+			v_match = match_config_eval (spec_str, MATCH_TAG_CONFIG_NM_VERSION, cur_nm_version);
+		else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN))
+			v_match = match_config_eval (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN, cur_nm_version);
+		else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MAX))
+			v_match = match_config_eval (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MAX, cur_nm_version);
+		else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_ENV))
 			v_match = env && env[0] && !strcmp (spec_str, env);
 		else
 			continue;
@@ -1562,6 +1584,8 @@ nm_match_spec_match_config (const GSList *specs, guint cur_nm_version, const cha
 	return match;
 }
 
+#undef _MATCH_CHECK
+
 /**
  * nm_match_spec_split:
  * @value: the string of device specs
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index e3aa257f..ada3f8ef 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -142,11 +142,14 @@ typedef enum {
 	NM_MATCH_SPEC_NEG_MATCH = 2,
 } NMMatchSpecMatchType;
 
-NMMatchSpecMatchType nm_match_spec_device_type (const GSList *specs, const char *device_type);
-NMMatchSpecMatchType nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr);
-NMMatchSpecMatchType nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels);
-NMMatchSpecMatchType nm_match_spec_interface_name (const GSList *specs, const char *interface_name);
-NMMatchSpecMatchType nm_match_spec_match_config (const GSList *specs, guint nm_version, const char *env);
+NMMatchSpecMatchType nm_match_spec_device (const GSList *specs,
+                                           const char *interface_name,
+                                           const char *device_type,
+                                           const char *hwaddr,
+                                           const char *s390_subchannels);
+NMMatchSpecMatchType nm_match_spec_config (const GSList *specs,
+                                           guint nm_version,
+                                           const char *env);
 GSList *nm_match_spec_split (const char *value);
 char *nm_match_spec_join (GSList *specs);
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-invalid b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-invalid
index 4534882e..41c0f25c 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-invalid
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-invalid
@@ -1,4 +1,4 @@
 DEVICE=team0
 ONBOOT=no
 BOOTPROTO=dhcp
-TEAM_CONFIG="{ foobar }"
+TEAM_CONFIG="{ foobar }x"
diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
index 6fc85535..deaac779 100644
--- a/src/supplicant/nm-supplicant-interface.c
+++ b/src/supplicant/nm-supplicant-interface.c
@@ -57,15 +57,15 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface,
 	PROP_SCANNING,
 	PROP_CURRENT_BSS,
 	PROP_DRIVER,
-	PROP_FAST_SUPPORTED,
+	PROP_FAST_SUPPORT,
 	PROP_AP_SUPPORT,
 );
 
 typedef struct {
 	char *         dev;
 	NMSupplicantDriver driver;
-	bool           fast_supported;
 	gboolean       has_credreq;  /* Whether querying 802.1x credentials is supported */
+	NMSupplicantFeature fast_support;
 	NMSupplicantFeature ap_support;   /* Lightweight AP mode support */
 	NMSupplicantFeature mac_randomization_support;
 	guint32        max_scan_ssids;
@@ -510,6 +510,15 @@ nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
 		priv->ap_support = ap_support;
 }
 
+void
+nm_supplicant_interface_set_fast_support (NMSupplicantInterface *self,
+                                          NMSupplicantFeature fast_support)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	priv->fast_support = fast_support;
+}
+
 static void
 set_preassoc_scan_mac_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
@@ -1302,7 +1311,8 @@ nm_supplicant_interface_set_config (NMSupplicantInterface *self,
 	/* Make sure the supplicant supports EAP-FAST before trying to send
 	 * it an EAP-FAST configuration.
 	 */
-	if (nm_supplicant_config_fast_required (cfg) && !priv->fast_supported) {
+	if (   priv->fast_support == NM_SUPPLICANT_FEATURE_NO
+	    && nm_supplicant_config_fast_required (cfg)) {
 		g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
 		             "EAP-FAST is not supported by the supplicant");
 		return FALSE;
@@ -1464,7 +1474,7 @@ nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self)
 NMSupplicantInterface *
 nm_supplicant_interface_new (const char *ifname,
                              NMSupplicantDriver driver,
-                             gboolean fast_supported,
+                             NMSupplicantFeature fast_support,
                              NMSupplicantFeature ap_support)
 {
 	g_return_val_if_fail (ifname != NULL, NULL);
@@ -1472,7 +1482,7 @@ nm_supplicant_interface_new (const char *ifname,
 	return g_object_new (NM_TYPE_SUPPLICANT_INTERFACE,
 	                     NM_SUPPLICANT_INTERFACE_IFACE, ifname,
 	                     NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver,
-	                     NM_SUPPLICANT_INTERFACE_FAST_SUPPORTED, fast_supported,
+	                     NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support,
 	                     NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support,
 	                     NULL);
 }
@@ -1504,9 +1514,9 @@ set_property (GObject *object,
 		/* construct-only */
 		priv->driver = g_value_get_uint (value);
 		break;
-	case PROP_FAST_SUPPORTED:
+	case PROP_FAST_SUPPORT:
 		/* construct-only */
-		priv->fast_supported = g_value_get_boolean (value);
+		priv->fast_support = g_value_get_int (value);
 		break;
 	case PROP_AP_SUPPORT:
 		/* construct-only */
@@ -1597,12 +1607,14 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
 	                       G_PARAM_WRITABLE |
 	                       G_PARAM_CONSTRUCT_ONLY |
 	                       G_PARAM_STATIC_STRINGS);
-	obj_properties[PROP_FAST_SUPPORTED] =
-	    g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_FAST_SUPPORTED, "", "",
-	                          TRUE,
-	                          G_PARAM_WRITABLE |
-	                          G_PARAM_CONSTRUCT_ONLY |
-	                          G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_FAST_SUPPORT] =
+	    g_param_spec_int (NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, "", "",
+	                      NM_SUPPLICANT_FEATURE_UNKNOWN,
+	                      NM_SUPPLICANT_FEATURE_YES,
+	                      NM_SUPPLICANT_FEATURE_UNKNOWN,
+	                      G_PARAM_WRITABLE |
+	                      G_PARAM_CONSTRUCT_ONLY |
+	                      G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_AP_SUPPORT] =
 	    g_param_spec_int (NM_SUPPLICANT_INTERFACE_AP_SUPPORT, "", "",
 	                      NM_SUPPLICANT_FEATURE_UNKNOWN,
diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h
index 5ab66d5d..2ef63d1e 100644
--- a/src/supplicant/nm-supplicant-interface.h
+++ b/src/supplicant/nm-supplicant-interface.h
@@ -19,8 +19,8 @@
  * Copyright (C) 2007 - 2008 Novell, Inc.
  */
 
-#ifndef __NETWORKMANAGER_SUPPLICANT_INTERFACE_H__
-#define __NETWORKMANAGER_SUPPLICANT_INTERFACE_H__
+#ifndef __NM_SUPPLICANT_INTERFACE_H__
+#define __NM_SUPPLICANT_INTERFACE_H__
 
 #include "nm-supplicant-types.h"
 
@@ -58,7 +58,7 @@ enum {
 #define NM_SUPPLICANT_INTERFACE_SCANNING         "scanning"
 #define NM_SUPPLICANT_INTERFACE_CURRENT_BSS      "current-bss"
 #define NM_SUPPLICANT_INTERFACE_DRIVER           "driver"
-#define NM_SUPPLICANT_INTERFACE_FAST_SUPPORTED   "fast-supported"
+#define NM_SUPPLICANT_INTERFACE_FAST_SUPPORT     "fast-support"
 #define NM_SUPPLICANT_INTERFACE_AP_SUPPORT       "ap-support"
 
 /* Signals */
@@ -77,7 +77,7 @@ GType nm_supplicant_interface_get_type (void);
 
 NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname,
                                                      NMSupplicantDriver driver,
-                                                     gboolean fast_supported,
+                                                     NMSupplicantFeature fast_support,
                                                      NMSupplicantFeature ap_support);
 
 void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self,
@@ -119,4 +119,7 @@ NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterfac
 void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
                                              NMSupplicantFeature apmode);
 
-#endif	/* NM_SUPPLICANT_INTERFACE_H */
+void nm_supplicant_interface_set_fast_support (NMSupplicantInterface *self,
+                                               NMSupplicantFeature fast_support);
+
+#endif /* __NM_SUPPLICANT_INTERFACE_H__ */
diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c
index 2fbfa391..cf53fb41 100644
--- a/src/supplicant/nm-supplicant-manager.c
+++ b/src/supplicant/nm-supplicant-manager.c
@@ -37,7 +37,7 @@ typedef struct {
 	gboolean         running;
 
 	GSList          *ifaces;
-	gboolean          fast_supported;
+	NMSupplicantFeature fast_support;
 	NMSupplicantFeature ap_support;
 	guint             die_count_reset_id;
 	guint             die_count;
@@ -158,7 +158,7 @@ nm_supplicant_manager_create_interface (NMSupplicantManager *self,
 
 	iface = nm_supplicant_interface_new (ifname,
 	                                     driver,
-	                                     priv->fast_supported,
+	                                     priv->fast_support,
 	                                     priv->ap_support);
 
 	priv->ifaces = g_slist_prepend (priv->ifaces, iface);
@@ -217,21 +217,32 @@ update_capabilities (NMSupplicantManager *self)
 	           (priv->ap_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
 
 	/* EAP-FAST */
-	priv->fast_supported = FALSE;
+	priv->fast_support = NM_SUPPLICANT_FEATURE_NO;
 	value = g_dbus_proxy_get_cached_property (priv->proxy, "EapMethods");
 	if (value) {
 		if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) {
 			array = g_variant_get_strv (value, NULL);
 			if (array) {
-				if (g_strv_contains (array, "fast"))
-					priv->fast_supported = TRUE;
+				const char **a;
+
+				for (a = array; *a; a++) {
+					if (g_ascii_strcasecmp (*a, "FAST") == 0) {
+						priv->fast_support = NM_SUPPLICANT_FEATURE_YES;
+						break;
+					}
+				}
 				g_free (array);
 			}
 		}
 		g_variant_unref (value);
 	}
 
-	_LOGD ("EAP-FAST is %ssupported", priv->fast_supported ? "" : "not ");
+	for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next)
+		nm_supplicant_interface_set_fast_support (ifaces->data, priv->fast_support);
+
+	_LOGD ("EAP-FAST is %ssupported",
+	       (priv->fast_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
+	           (priv->fast_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
 }
 
 static void
@@ -306,8 +317,8 @@ name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data)
 	_LOGI ("wpa_supplicant %s", owner ? "running" : "stopped");
 
 	if (owner) {
-		set_running (self, TRUE);
 		update_capabilities (self);
+		set_running (self, TRUE);
 	} else if (priv->running) {
 		/* Reschedule the die count reset timeout.  Every time the supplicant
 		 * dies we wait 10 seconds before resetting the counter.  If the
@@ -324,9 +335,10 @@ name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data)
 			       priv->die_count);
 		}
 
-		set_running (self, FALSE);
+		priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
+		priv->fast_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
 
-		priv->fast_supported = FALSE;
+		set_running (self, FALSE);
 	}
 
 	g_free (owner);
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index ed2f4e51..add12233 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -955,6 +955,12 @@ test_connection_sort_autoconnect_priority (void)
 
 /*****************************************************************************/
 
+static NMMatchSpecMatchType
+_test_match_device_interface (const GSList *specs, const char *interface_name)
+{
+	return nm_match_spec_device (specs, interface_name, NULL, NULL, NULL);
+}
+
 static const char *_test_match_spec_all[] = {
 	"e",
 	"em",
@@ -984,10 +990,10 @@ _test_match_spec_contains (const char **matches, const char *match)
 }
 
 static void
-test_match_spec_ifname (const char *spec_str, const char **matches, const char **neg_matches)
+_do_test_match_spec_device (const char *spec_str, const char **matches, const char **neg_matches)
 {
 	const char *m;
-	GSList *specs, *specs_reverse = NULL, *specs_resplit, *specs_i, *specs_j;
+	GSList *specs, *specs_randperm = NULL, *specs_resplit, *specs_i, *specs_j;
 	guint i;
 	gs_free char *specs_joined = NULL;
 
@@ -1008,87 +1014,87 @@ test_match_spec_ifname (const char *spec_str, const char **matches, const char *
 	g_assert (!specs_j);
 	g_slist_free_full (specs_resplit, g_free);
 
-	/* also check the matches in the reverse order. They must yield the same result because
+	/* also check the matches in the random order. They must yield the same result because
 	 * matches are inclusive -- except "except:" which always wins. */
-	specs_reverse = g_slist_reverse (g_slist_copy (specs));
+	specs_randperm = nmtst_rand_perm_gslist (NULL, g_slist_copy (specs));
 
 	for (i = 0; matches && matches[i]; i++) {
-		g_assert (nm_match_spec_interface_name (specs, matches[i]) == NM_MATCH_SPEC_MATCH);
-		g_assert (nm_match_spec_interface_name (specs_reverse, matches[i]) == NM_MATCH_SPEC_MATCH);
+		g_assert (_test_match_device_interface (specs, matches[i]) == NM_MATCH_SPEC_MATCH);
+		g_assert (_test_match_device_interface (specs_randperm, matches[i]) == NM_MATCH_SPEC_MATCH);
 	}
 	for (i = 0; neg_matches && neg_matches[i]; i++) {
-		g_assert (nm_match_spec_interface_name (specs, neg_matches[i]) == NM_MATCH_SPEC_NEG_MATCH);
-		g_assert (nm_match_spec_interface_name (specs_reverse, neg_matches[i]) == NM_MATCH_SPEC_NEG_MATCH);
+		g_assert (_test_match_device_interface (specs, neg_matches[i]) == NM_MATCH_SPEC_NEG_MATCH);
+		g_assert (_test_match_device_interface (specs_randperm, neg_matches[i]) == NM_MATCH_SPEC_NEG_MATCH);
 	}
 	for (i = 0; (m = _test_match_spec_all[i]); i++) {
 		if (_test_match_spec_contains (matches, m))
 			continue;
 		if (_test_match_spec_contains (neg_matches, m))
 			continue;
-		g_assert (nm_match_spec_interface_name (specs, m) == NM_MATCH_SPEC_NO_MATCH);
-		g_assert (nm_match_spec_interface_name (specs_reverse, m) == NM_MATCH_SPEC_NO_MATCH);
+		g_assert (_test_match_device_interface (specs, m) == NM_MATCH_SPEC_NO_MATCH);
+		g_assert (_test_match_device_interface (specs_randperm, m) == NM_MATCH_SPEC_NO_MATCH);
 	}
 
-	g_slist_free (specs_reverse);
+	g_slist_free (specs_randperm);
 	g_slist_free_full (specs, g_free);
 }
 
 static void
-test_nm_match_spec_interface_name (void)
+test_match_spec_device (void)
 {
 #define S(...) ((const char *[]) { __VA_ARGS__, NULL } )
-	test_match_spec_ifname ("em1",
-	                        S ("em1"),
-	                        NULL);
-	test_match_spec_ifname ("em1,em2",
-	                        S ("em1", "em2"),
-	                        NULL);
-	test_match_spec_ifname ("em1,em2,interface-name:em2",
-	                        S ("em1", "em2"),
-	                        NULL);
-	test_match_spec_ifname ("interface-name:em1",
-	                        S ("em1"),
-	                        NULL);
-	test_match_spec_ifname ("interface-name:em*",
-	                        S ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"),
-	                        NULL);
-	test_match_spec_ifname ("interface-name:em\\*",
-	                        S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"),
-	                        NULL);
-	test_match_spec_ifname ("interface-name:~em\\*",
-	                        S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"),
-	                        NULL);
-	test_match_spec_ifname ("interface-name:=em*",
-	                        S ("em*"),
-	                        NULL);
-	test_match_spec_ifname ("interface-name:em*,except:interface-name:em1*",
-	                        S ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em2", "em3"),
-	                        S ("em1", "em11"));
-	test_match_spec_ifname ("interface-name:em*,except:interface-name:=em*",
-	                        S ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"),
-	                        S ("em*"));
-	test_match_spec_ifname ("aa,bb,cc\\,dd,e,,",
-	                        S ("aa", "bb", "cc,dd", "e"),
-	                        NULL);
-	test_match_spec_ifname ("aa;bb;cc\\;dd;e,;",
-	                        S ("aa", "bb", "cc;dd", "e"),
-	                        NULL);
-	test_match_spec_ifname ("interface-name:em\\;1,em\\,2,\\,,\\\\,,em\\\\x",
-	                        S ("em;1", "em,2", ",", "\\", "em\\x"),
-	                        NULL);
-	test_match_spec_ifname ("\\s\\s,\\sinterface-name:a,\\s,",
-	                        S ("  ", " ", " interface-name:a"),
-	                        NULL);
-	test_match_spec_ifname (" aa ;  bb   ; cc\\;dd  ;e , ; \t\\t  , ",
-	                        S ("aa", "bb", "cc;dd", "e", "\t"),
-	                        NULL);
+	_do_test_match_spec_device ("em1",
+	                            S ("em1"),
+	                            NULL);
+	_do_test_match_spec_device ("em1,em2",
+	                            S ("em1", "em2"),
+	                            NULL);
+	_do_test_match_spec_device ("em1,em2,interface-name:em2",
+	                            S ("em1", "em2"),
+	                            NULL);
+	_do_test_match_spec_device ("interface-name:em1",
+	                            S ("em1"),
+	                            NULL);
+	_do_test_match_spec_device ("interface-name:em*",
+	                            S ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"),
+	                            NULL);
+	_do_test_match_spec_device ("interface-name:em\\*",
+	                            S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"),
+	                            NULL);
+	_do_test_match_spec_device ("interface-name:~em\\*",
+	                            S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"),
+	                            NULL);
+	_do_test_match_spec_device ("interface-name:=em*",
+	                            S ("em*"),
+	                            NULL);
+	_do_test_match_spec_device ("interface-name:em*,except:interface-name:em1*",
+	                            S ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em2", "em3"),
+	                            S ("em1", "em11"));
+	_do_test_match_spec_device ("interface-name:em*,except:interface-name:=em*",
+	                            S ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"),
+	                            S ("em*"));
+	_do_test_match_spec_device ("aa,bb,cc\\,dd,e,,",
+	                            S ("aa", "bb", "cc,dd", "e"),
+	                            NULL);
+	_do_test_match_spec_device ("aa;bb;cc\\;dd;e,;",
+	                            S ("aa", "bb", "cc;dd", "e"),
+	                            NULL);
+	_do_test_match_spec_device ("interface-name:em\\;1,em\\,2,\\,,\\\\,,em\\\\x",
+	                            S ("em;1", "em,2", ",", "\\", "em\\x"),
+	                            NULL);
+	_do_test_match_spec_device ("\\s\\s,\\sinterface-name:a,\\s,",
+	                            S ("  ", " ", " interface-name:a"),
+	                            NULL);
+	_do_test_match_spec_device (" aa ;  bb   ; cc\\;dd  ;e , ; \t\\t  , ",
+	                            S ("aa", "bb", "cc;dd", "e", "\t"),
+	                            NULL);
 #undef S
 }
 
 /*****************************************************************************/
 
 static void
-_do_test_match_spec_match_config (const char *file, gint line, const char *spec_str, guint version, guint v_maj, guint v_min, guint v_mic, NMMatchSpecMatchType expected)
+_do_test_match_spec_config (const char *file, gint line, const char *spec_str, guint version, guint v_maj, guint v_min, guint v_mic, NMMatchSpecMatchType expected)
 {
 	GSList *specs;
 	NMMatchSpecMatchType match_result;
@@ -1103,7 +1109,7 @@ _do_test_match_spec_match_config (const char *file, gint line, const char *spec_
 
 	specs = nm_match_spec_split (spec_str);
 
-	match_result = nm_match_spec_match_config (specs, version, NULL);
+	match_result = nm_match_spec_config (specs, version, NULL);
 
 	if (expected != match_result)
 		g_error ("%s:%d: faild comparing \"%s\" with %u.%u.%u. Expected %d, but got %d", file, line, spec_str, v_maj, v_min, v_mic, (int) expected, (int) match_result);
@@ -1115,7 +1121,7 @@ _do_test_match_spec_match_config (const char *file, gint line, const char *spec_
 		NMMatchSpecMatchType match_result2;
 
 
-		match_result2 = nm_match_spec_match_config (specs2, version, NULL);
+		match_result2 = nm_match_spec_config (specs2, version, NULL);
 		if (match_result == NM_MATCH_SPEC_NO_MATCH)
 			g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_NO_MATCH);
 		else
@@ -1126,105 +1132,105 @@ _do_test_match_spec_match_config (const char *file, gint line, const char *spec_
 
 	g_slist_free_full (specs, g_free);
 }
-#define do_test_match_spec_match_config(spec, v_maj, v_min, v_mic, expected) \
-	_do_test_match_spec_match_config (__FILE__, __LINE__, (""spec), NM_ENCODE_VERSION ((v_maj), (v_min), (v_mic)), (v_maj), (v_min), (v_mic), (expected))
+#define do_test_match_spec_config(spec, v_maj, v_min, v_mic, expected) \
+	_do_test_match_spec_config (__FILE__, __LINE__, (""spec), NM_ENCODE_VERSION ((v_maj), (v_min), (v_mic)), (v_maj), (v_min), (v_mic), (expected))
 
 static void
-test_nm_match_spec_match_config (void)
+test_match_spec_config (void)
 {
-	do_test_match_spec_match_config ("", 1, 2, 3, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version:1.2.3", 1, 2, 2, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version:1.2.3", 1, 2, 3, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version:1.2.3", 1, 2, 4, NM_MATCH_SPEC_NO_MATCH);
-
-	do_test_match_spec_match_config ("nm-version:1.2", 1, 1, 2, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version:1.2", 1, 2, 0, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version:1.2", 1, 2, 2, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version:1.2", 1, 2, 3, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version:1.2", 1, 2, 4, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version:1.2", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH);
-
-	do_test_match_spec_match_config ("nm-version-min:1.2.3", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2.3", 1, 1, 1, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2.3", 1, 2, 2, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2.3", 1, 2, 3, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2.3", 1, 2, 5, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2.3", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2.3", 1, 3, 30, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2.3", 1, 4, 30, NM_MATCH_SPEC_NO_MATCH);
-
-	do_test_match_spec_match_config ("nm-version-min:1.2", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2", 1, 1, 1, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2", 1, 2, 0, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2", 1, 2, 3, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2", 1, 2, 5, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2", 1, 3, 0, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2", 1, 3, 30, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.2", 1, 4, 30, NM_MATCH_SPEC_MATCH);
-
-	do_test_match_spec_match_config ("nm-version-min:1", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1", 1, 1, 1, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1", 1, 2, 0, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1", 1, 2, 3, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1", 1, 2, 5, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1", 1, 3, 0, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1", 1, 3, 30, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1", 1, 4, 30, NM_MATCH_SPEC_MATCH);
-
-
-	do_test_match_spec_match_config ("nm-version-max:1.2.3", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2.3", 1, 1, 1, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2.3", 1, 2, 0, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2.3", 1, 2, 1, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2.3", 1, 2, 2, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2.3", 1, 2, 3, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2.3", 1, 2, 5, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2.3", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2.3", 1, 3, 30, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2.3", 1, 4, 30, NM_MATCH_SPEC_NO_MATCH);
-
-	do_test_match_spec_match_config ("nm-version-max:1.2", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2", 1, 1, 1, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2", 1, 2, 0, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2", 1, 2, 3, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2", 1, 2, 5, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2", 1, 3, 30, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1.2", 1, 4, 30, NM_MATCH_SPEC_NO_MATCH);
-
-	do_test_match_spec_match_config ("nm-version-max:1", 0, 2, 30, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1", 1, 1, 1, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1", 1, 2, 0, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1", 1, 2, 3, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1", 1, 2, 5, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1", 1, 3, 0, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1", 1, 3, 30, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1", 1, 4, 30, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-max:1", 2, 4, 30, NM_MATCH_SPEC_NO_MATCH);
-
-	do_test_match_spec_match_config ("except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH);
-
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 0, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 0, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 15, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 16, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 17, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 20, NM_MATCH_SPEC_MATCH);
-
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 0, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 5, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 6, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 7, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 8, NM_MATCH_SPEC_NEG_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 9, NM_MATCH_SPEC_MATCH);
-
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 5, 0, NM_MATCH_SPEC_NO_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 6, 5, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 7, 7, NM_MATCH_SPEC_MATCH);
-	do_test_match_spec_match_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 8, 8, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("", 1, 2, 3, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version:1.2.3", 1, 2, 2, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version:1.2.3", 1, 2, 3, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version:1.2.3", 1, 2, 4, NM_MATCH_SPEC_NO_MATCH);
+
+	do_test_match_spec_config ("nm-version:1.2", 1, 1, 2, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version:1.2", 1, 2, 0, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version:1.2", 1, 2, 2, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version:1.2", 1, 2, 3, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version:1.2", 1, 2, 4, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version:1.2", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH);
+
+	do_test_match_spec_config ("nm-version-min:1.2.3", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2.3", 1, 1, 1, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2.3", 1, 2, 2, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2.3", 1, 2, 3, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2.3", 1, 2, 5, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2.3", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2.3", 1, 3, 30, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2.3", 1, 4, 30, NM_MATCH_SPEC_NO_MATCH);
+
+	do_test_match_spec_config ("nm-version-min:1.2", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2", 1, 1, 1, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2", 1, 2, 0, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2", 1, 2, 3, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2", 1, 2, 5, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2", 1, 3, 0, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2", 1, 3, 30, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.2", 1, 4, 30, NM_MATCH_SPEC_MATCH);
+
+	do_test_match_spec_config ("nm-version-min:1", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1", 1, 1, 1, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1", 1, 2, 0, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1", 1, 2, 3, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1", 1, 2, 5, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1", 1, 3, 0, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1", 1, 3, 30, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1", 1, 4, 30, NM_MATCH_SPEC_MATCH);
+
+
+	do_test_match_spec_config ("nm-version-max:1.2.3", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2.3", 1, 1, 1, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2.3", 1, 2, 0, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2.3", 1, 2, 1, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2.3", 1, 2, 2, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2.3", 1, 2, 3, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2.3", 1, 2, 5, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2.3", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2.3", 1, 3, 30, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2.3", 1, 4, 30, NM_MATCH_SPEC_NO_MATCH);
+
+	do_test_match_spec_config ("nm-version-max:1.2", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2", 1, 1, 1, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2", 1, 2, 0, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2", 1, 2, 3, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2", 1, 2, 5, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2", 1, 3, 30, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-max:1.2", 1, 4, 30, NM_MATCH_SPEC_NO_MATCH);
+
+	do_test_match_spec_config ("nm-version-max:1", 0, 2, 30, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1", 1, 1, 1, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1", 1, 2, 0, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1", 1, 2, 3, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1", 1, 2, 5, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1", 1, 3, 0, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1", 1, 3, 30, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1", 1, 4, 30, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-max:1", 2, 4, 30, NM_MATCH_SPEC_NO_MATCH);
+
+	do_test_match_spec_config ("except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH);
+
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 0, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 0, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 15, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 16, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 17, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 20, NM_MATCH_SPEC_MATCH);
+
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 0, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 5, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 6, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 7, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 8, NM_MATCH_SPEC_NEG_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 4, 9, NM_MATCH_SPEC_MATCH);
+
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 5, 0, NM_MATCH_SPEC_NO_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 6, 5, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 7, 7, NM_MATCH_SPEC_MATCH);
+	do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 8, 8, NM_MATCH_SPEC_MATCH);
 }
 
 /*****************************************************************************/
@@ -1594,8 +1600,8 @@ main (int argc, char **argv)
 
 	g_test_add_func ("/general/connection-sort/autoconnect-priority", test_connection_sort_autoconnect_priority);
 
-	g_test_add_func ("/general/nm_match_spec_interface_name", test_nm_match_spec_interface_name);
-	g_test_add_func ("/general/nm_match_spec_match_config", test_nm_match_spec_match_config);
+	g_test_add_func ("/general/match-spec/device", test_match_spec_device);
+	g_test_add_func ("/general/match-spec/config", test_match_spec_config);
 	g_test_add_func ("/general/duplicate_decl_specifier", test_duplicate_decl_specifier);
 
 	g_test_add_func ("/general/reverse_dns/ip4", test_reverse_dns_ip4);