summary refs log tree commit diff
path: root/libnm-core
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-09-08 17:44:06 +0200
committerMichael Biebl <biebl@debian.org>2018-09-08 17:44:06 +0200
commit8f7a3cbbdd0c0a48277c341dd3a8ec8743ae9735 (patch)
tree4353551fcb59cc822c3cadf2f4888f70601e8fbf /libnm-core
parentcaf1db9d6fbc056cc6c76a24574890f6c7895f3d (diff)
New upstream version 1.13.90 upstream/1.13.90
Diffstat (limited to 'libnm-core')
-rw-r--r--libnm-core/crypto.c816
-rw-r--r--libnm-core/crypto.h135
-rw-r--r--libnm-core/meson.build51
-rw-r--r--libnm-core/nm-connection.c305
-rw-r--r--libnm-core/nm-connection.h8
-rw-r--r--libnm-core/nm-core-enum-types.c.template1
-rw-r--r--libnm-core/nm-core-internal.h212
-rw-r--r--libnm-core/nm-core-types.h7
-rw-r--r--libnm-core/nm-crypto-gnutls.c (renamed from libnm-core/crypto_gnutls.c)307
-rw-r--r--libnm-core/nm-crypto-impl.h74
-rw-r--r--libnm-core/nm-crypto-nss.c (renamed from libnm-core/crypto_nss.c)363
-rw-r--r--libnm-core/nm-crypto.c1035
-rw-r--r--libnm-core/nm-crypto.h125
-rw-r--r--libnm-core/nm-dbus-interface.h77
-rw-r--r--libnm-core/nm-dbus-types.xml105
-rw-r--r--libnm-core/nm-dbus-utils.c6
-rw-r--r--libnm-core/nm-keyfile-utils.c14
-rw-r--r--libnm-core/nm-keyfile-utils.h12
-rw-r--r--libnm-core/nm-keyfile.c455
-rw-r--r--libnm-core/nm-property-compare.c8
-rw-r--r--libnm-core/nm-setting-6lowpan.c230
-rw-r--r--libnm-core/nm-setting-6lowpan.h54
-rw-r--r--libnm-core/nm-setting-8021x.c2766
-rw-r--r--libnm-core/nm-setting-8021x.h2
-rw-r--r--libnm-core/nm-setting-adsl.c22
-rw-r--r--libnm-core/nm-setting-bluetooth.c31
-rw-r--r--libnm-core/nm-setting-bond.c52
-rw-r--r--libnm-core/nm-setting-bridge-port.c19
-rw-r--r--libnm-core/nm-setting-bridge.c41
-rw-r--r--libnm-core/nm-setting-cdma.c21
-rw-r--r--libnm-core/nm-setting-connection.c192
-rw-r--r--libnm-core/nm-setting-connection.h31
-rw-r--r--libnm-core/nm-setting-dcb.c101
-rw-r--r--libnm-core/nm-setting-dcb.h6
-rw-r--r--libnm-core/nm-setting-dummy.c12
-rw-r--r--libnm-core/nm-setting-ethtool.c342
-rw-r--r--libnm-core/nm-setting-ethtool.h124
-rw-r--r--libnm-core/nm-setting-generic.c12
-rw-r--r--libnm-core/nm-setting-gsm.c41
-rw-r--r--libnm-core/nm-setting-infiniband.c30
-rw-r--r--libnm-core/nm-setting-ip-config.c91
-rw-r--r--libnm-core/nm-setting-ip-config.h8
-rw-r--r--libnm-core/nm-setting-ip-tunnel.c45
-rw-r--r--libnm-core/nm-setting-ip4-config.c92
-rw-r--r--libnm-core/nm-setting-ip6-config.c81
-rw-r--r--libnm-core/nm-setting-macsec.c19
-rw-r--r--libnm-core/nm-setting-macvlan.c18
-rw-r--r--libnm-core/nm-setting-match.c295
-rw-r--r--libnm-core/nm-setting-match.h63
-rw-r--r--libnm-core/nm-setting-olpc-mesh.c32
-rw-r--r--libnm-core/nm-setting-ovs-bridge.c17
-rw-r--r--libnm-core/nm-setting-ovs-interface.c17
-rw-r--r--libnm-core/nm-setting-ovs-patch.c17
-rw-r--r--libnm-core/nm-setting-ovs-port.c17
-rw-r--r--libnm-core/nm-setting-ppp.c19
-rw-r--r--libnm-core/nm-setting-pppoe.c21
-rw-r--r--libnm-core/nm-setting-private.h130
-rw-r--r--libnm-core/nm-setting-proxy.c20
-rw-r--r--libnm-core/nm-setting-serial.c40
-rw-r--r--libnm-core/nm-setting-sriov.c1357
-rw-r--r--libnm-core/nm-setting-sriov.h123
-rw-r--r--libnm-core/nm-setting-tc-config.c77
-rw-r--r--libnm-core/nm-setting-team-port.c55
-rw-r--r--libnm-core/nm-setting-team-port.h8
-rw-r--r--libnm-core/nm-setting-team.c101
-rw-r--r--libnm-core/nm-setting-team.h30
-rw-r--r--libnm-core/nm-setting-tun.c19
-rw-r--r--libnm-core/nm-setting-user.c48
-rw-r--r--libnm-core/nm-setting-vlan.c46
-rw-r--r--libnm-core/nm-setting-vpn.c67
-rw-r--r--libnm-core/nm-setting-vxlan.c18
-rw-r--r--libnm-core/nm-setting-wimax.c32
-rw-r--r--libnm-core/nm-setting-wired.c83
-rw-r--r--libnm-core/nm-setting-wireless-security.c40
-rw-r--r--libnm-core/nm-setting-wireless.c81
-rw-r--r--libnm-core/nm-setting-wpan.c274
-rw-r--r--libnm-core/nm-setting-wpan.h59
-rw-r--r--libnm-core/nm-setting.c1303
-rw-r--r--libnm-core/nm-setting.h17
-rw-r--r--libnm-core/nm-utils-private.h8
-rw-r--r--libnm-core/nm-utils.c737
-rw-r--r--libnm-core/nm-utils.h13
-rw-r--r--libnm-core/nm-version.h14
-rw-r--r--libnm-core/tests/certs/test-aes-128-key.pem (renamed from libnm-core/tests/certs/test-aes-key.pem)0
-rw-r--r--libnm-core/tests/certs/test-aes-256-key.pem54
-rw-r--r--libnm-core/tests/meson.build3
-rw-r--r--libnm-core/tests/test-crypto.c116
-rw-r--r--libnm-core/tests/test-general.c383
-rw-r--r--libnm-core/tests/test-keyfile.c14
-rw-r--r--libnm-core/tests/test-setting.c325
90 files changed, 9836 insertions, 4956 deletions
diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c
deleted file mode 100644
index c4e48475..00000000
--- a/libnm-core/crypto.c
+++ /dev/null
@@ -1,816 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-
-/*
- * Dan Williams <dcbw@redhat.com>
- *
- * 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 2007 - 2011 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include <string.h>
-#include <strings.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#include "crypto.h"
-#include "nm-errors.h"
-
-#define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
-#define PEM_RSA_KEY_END   "-----END RSA PRIVATE KEY-----"
-
-#define PEM_DSA_KEY_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
-#define PEM_DSA_KEY_END   "-----END DSA PRIVATE KEY-----"
-
-#define PEM_CERT_BEGIN    "-----BEGIN CERTIFICATE-----"
-#define PEM_CERT_END      "-----END CERTIFICATE-----"
-
-#define PEM_PKCS8_ENC_KEY_BEGIN "-----BEGIN ENCRYPTED PRIVATE KEY-----"
-#define PEM_PKCS8_ENC_KEY_END   "-----END ENCRYPTED PRIVATE KEY-----"
-
-#define PEM_PKCS8_DEC_KEY_BEGIN "-----BEGIN PRIVATE KEY-----"
-#define PEM_PKCS8_DEC_KEY_END   "-----END PRIVATE KEY-----"
-
-static gboolean
-find_tag (const char *tag,
-          const guint8 *data,
-          gsize data_len,
-          gsize start_at,
-          gsize *out_pos)
-{
-	gsize i, taglen;
-	gsize len = data_len - start_at;
-
-	g_return_val_if_fail (out_pos != NULL, FALSE);
-
-	taglen = strlen (tag);
-	if (len >= taglen) {
-		for (i = 0; i < len - taglen + 1; i++) {
-			if (memcmp (data + start_at + i, tag, taglen) == 0) {
-				*out_pos = start_at + i;
-				return TRUE;
-			}
-		}
-	}
-	return FALSE;
-}
-
-#define DEK_INFO_TAG "DEK-Info: "
-#define PROC_TYPE_TAG "Proc-Type: "
-
-static GByteArray *
-parse_old_openssl_key_file (const guint8 *data,
-                            gsize data_len,
-                            NMCryptoKeyType *out_key_type,
-                            char **out_cipher,
-                            char **out_iv,
-                            GError **error)
-{
-	GByteArray *bindata = NULL;
-	char **lines = NULL;
-	char **ln = NULL;
-	gsize start = 0, end = 0;
-	GString *str = NULL;
-	int enc_tags = 0;
-	NMCryptoKeyType key_type;
-	char *iv = NULL;
-	char *cipher = NULL;
-	unsigned char *tmp = NULL;
-	gsize tmp_len = 0;
-	const char *start_tag;
-	const char *end_tag;
-	guint8 save_end = 0;
-
-	*out_key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
-	*out_iv = NULL;
-	*out_cipher = NULL;
-
-	if (find_tag (PEM_RSA_KEY_BEGIN, data, data_len, 0, &start)) {
-		key_type = NM_CRYPTO_KEY_TYPE_RSA;
-		start_tag = PEM_RSA_KEY_BEGIN;
-		end_tag = PEM_RSA_KEY_END;
-	} else if (find_tag (PEM_DSA_KEY_BEGIN, data, data_len, 0, &start)) {
-		key_type = NM_CRYPTO_KEY_TYPE_DSA;
-		start_tag = PEM_DSA_KEY_BEGIN;
-		end_tag = PEM_DSA_KEY_END;
-	} else
-		goto parse_error;
-
-	start += strlen (start_tag);
-	if (!find_tag (end_tag, data, data_len, start, &end)) {
-		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("PEM key file had no end tag '%s'."),
-		             end_tag);
-		goto parse_error;
-	}
-
-	save_end = data[end];
-	((guint8 *)data)[end] = '\0';
-	lines = g_strsplit ((const char *) (data + start), "\n", 0);
-	((guint8 *)data)[end] = save_end;
-
-	if (!lines || g_strv_length (lines) <= 1) {
-		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("Doesn't look like a PEM private key file."));
-		goto parse_error;
-	}
-
-	str = g_string_new_len (NULL, end - start);
-	for (ln = lines; *ln; ln++) {
-		char *p = *ln;
-
-		/* Chug leading spaces */
-		p = g_strstrip (p);
-		if (!*p)
-			continue;
-
-		if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) {
-			if (enc_tags++ != 0 || str->len != 0) {
-				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERROR_INVALID_DATA,
-				             _("Malformed PEM file: Proc-Type was not first tag."));
-				goto parse_error;
-			}
-
-			p += strlen (PROC_TYPE_TAG);
-			if (strcmp (p, "4,ENCRYPTED")) {
-				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERROR_INVALID_DATA,
-				             _("Malformed PEM file: unknown Proc-Type tag '%s'."),
-				             p);
-				goto parse_error;
-			}
-		} else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) {
-			char *comma;
-
-			if (enc_tags++ != 1 || str->len != 0) {
-				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERROR_INVALID_DATA,
-				             _("Malformed PEM file: DEK-Info was not the second tag."));
-				goto parse_error;
-			}
-
-			p += strlen (DEK_INFO_TAG);
-
-			/* Grab the IV first */
-			comma = strchr (p, ',');
-			if (!comma || (*(comma + 1) == '\0')) {
-				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERROR_INVALID_DATA,
-				             _("Malformed PEM file: no IV found in DEK-Info tag."));
-				goto parse_error;
-			}
-			*comma++ = '\0';
-			if (!g_ascii_isxdigit (*comma)) {
-				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERROR_INVALID_DATA,
-				             _("Malformed PEM file: invalid format of IV in DEK-Info tag."));
-				goto parse_error;
-			}
-			iv = g_strdup (comma);
-
-			/* Get the private key cipher */
-			if (!strcasecmp (p, "DES-EDE3-CBC")) {
-				cipher = g_strdup (p);
-			} else if (!strcasecmp (p, "DES-CBC")) {
-				cipher = g_strdup (p);
-			} else if (!strcasecmp (p, "AES-128-CBC")) {
-				cipher = g_strdup (p);
-			} else {
-				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERROR_INVALID_DATA,
-				             _("Malformed PEM file: unknown private key cipher '%s'."),
-				             p);
-				goto parse_error;
-			}
-		} else {
-			if (enc_tags == 1) {
-				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERROR_INVALID_DATA,
-				             "Malformed PEM file: both Proc-Type and DEK-Info tags are required.");
-				goto parse_error;
-			}
-			g_string_append (str, p);
-		}
-	}
-
-	tmp = g_base64_decode (str->str, &tmp_len);
-	if (tmp == NULL || !tmp_len) {
-		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("Could not decode private key."));
-		goto parse_error;
-	}
-	g_string_free (str, TRUE);
-
-	if (lines)
-		g_strfreev (lines);
-
-	bindata = g_byte_array_sized_new (tmp_len);
-	g_byte_array_append (bindata, tmp, tmp_len);
-	g_free (tmp);
-
-	*out_key_type = key_type;
-	*out_iv = iv;
-	*out_cipher = cipher;
-	return bindata;
-
-parse_error:
-	g_free (tmp);
-	g_free (cipher);
-	g_free (iv);
-	if (str)
-		g_string_free (str, TRUE);
-	if (lines)
-		g_strfreev (lines);
-	return NULL;
-}
-
-static GByteArray *
-parse_pkcs8_key_file (const guint8 *data,
-                      gsize data_len,
-                      gboolean *out_encrypted,
-                      GError **error)
-{
-	GByteArray *key = NULL;
-	gsize start = 0, end = 0;
-	unsigned char *der = NULL;
-	guint8 save_end;
-	gsize length = 0;
-	const char *start_tag = NULL, *end_tag = NULL;
-	gboolean encrypted = FALSE;
-
-	/* Try encrypted first, decrypted next */
-	if (find_tag (PEM_PKCS8_ENC_KEY_BEGIN, data, data_len, 0, &start)) {
-		start_tag = PEM_PKCS8_ENC_KEY_BEGIN;
-		end_tag = PEM_PKCS8_ENC_KEY_END;
-		encrypted = TRUE;
-	} else if (find_tag (PEM_PKCS8_DEC_KEY_BEGIN, data, data_len, 0, &start)) {
-		start_tag = PEM_PKCS8_DEC_KEY_BEGIN;
-		end_tag = PEM_PKCS8_DEC_KEY_END;
-		encrypted = FALSE;
-	} else {
-		g_set_error_literal (error, NM_CRYPTO_ERROR,
-		                     NM_CRYPTO_ERROR_INVALID_DATA,
-		                     _("Failed to find expected PKCS#8 start tag."));
-		return NULL;
-	}
-
-	start += strlen (start_tag);
-	if (!find_tag (end_tag, data, data_len, start, &end)) {
-		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("Failed to find expected PKCS#8 end tag '%s'."),
-		             end_tag);
-		return NULL;
-	}
-
-	/* g_base64_decode() wants a NULL-terminated string */
-	save_end = data[end];
-	((guint8 *)data)[end] = '\0';
-	der = g_base64_decode ((const char *) (data + start), &length);
-	((guint8 *)data)[end] = save_end;
-
-	if (der && length) {
-		key = g_byte_array_sized_new (length);
-		g_byte_array_append (key, der, length);
-		g_assert (key->len == length);
-		*out_encrypted = encrypted;
-	} else {
-		g_set_error_literal (error, NM_CRYPTO_ERROR,
-		                     NM_CRYPTO_ERROR_INVALID_DATA,
-		                     _("Failed to decode PKCS#8 private key."));
-	}
-
-	g_free (der);
-	return key;
-}
-
-static GByteArray *
-file_to_g_byte_array (const char *filename, GError **error)
-{
-	char *contents;
-	GByteArray *array = NULL;
-	gsize length = 0;
-
-	if (g_file_get_contents (filename, &contents, &length, error)) {
-		array = g_byte_array_sized_new (length);
-		g_byte_array_append (array, (guint8 *) contents, length);
-		g_assert (array->len == length);
-		g_free (contents);
-	}
-	return array;
-}
-
-/*
- * Convert a hex string into bytes.
- */
-static char *
-convert_iv (const char *src,
-            gsize *out_len,
-            GError **error)
-{
-	int num;
-	int i;
-	char conv[3];
-	char *c;
-
-	g_return_val_if_fail (src != NULL, NULL);
-
-	num = strlen (src);
-	if (num % 2) {
-		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("IV must be an even number of bytes in length."));
-		return NULL;
-	}
-
-	num /= 2;
-	c = g_malloc0 (num + 1);
-
-	conv[2] = '\0';
-	for (i = 0; i < num; i++) {
-		conv[0] = src[(i * 2)];
-		conv[1] = src[(i * 2) + 1];
-		if (!g_ascii_isxdigit (conv[0]) || !g_ascii_isxdigit (conv[1])) {
-			g_set_error (error, NM_CRYPTO_ERROR,
-			             NM_CRYPTO_ERROR_INVALID_DATA,
-			             _("IV contains non-hexadecimal digits."));
-			goto error;
-		}
-
-		c[i] = strtol(conv, NULL, 16);
-	}
-	*out_len = num;
-	return c;
-
-error:
-	g_free (c);
-	return NULL;
-}
-
-char *
-crypto_make_des_aes_key (const char *cipher,
-                         const char *salt,
-                         const gsize salt_len,
-                         const char *password,
-                         gsize *out_len,
-                         GError **error)
-{
-	char *key;
-	guint32 digest_len;
-
-	g_return_val_if_fail (cipher != NULL, NULL);
-	g_return_val_if_fail (salt != NULL, NULL);
-	g_return_val_if_fail (salt_len >= 8, NULL);
-	g_return_val_if_fail (password != NULL, NULL);
-	g_return_val_if_fail (out_len != NULL, NULL);
-
-	if (!strcmp (cipher, "DES-EDE3-CBC"))
-		digest_len = 24;
-	else if (!strcmp (cipher, "DES-CBC"))
-		digest_len = 8;
-	else if (!strcmp (cipher, "AES-128-CBC"))
-		digest_len = 16;
-	else {
-		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
-		             _("Private key cipher '%s' was unknown."),
-		             cipher);
-		return NULL;
-	}
-
-	if (password[0] == '\0')
-		return NULL;
-
-	key = g_malloc0 (digest_len + 1);
-
-	crypto_md5_hash (salt,
-	                 8,
-	                 password,
-	                 strlen (password),
-	                 key,
-	                 digest_len);
-
-	*out_len = digest_len;
-	return key;
-}
-
-static GByteArray *
-decrypt_key (const char *cipher,
-             int key_type,
-             const guint8 *data,
-             gsize data_len,
-             const char *iv,
-             const char *password,
-             GError **error)
-{
-	char *bin_iv = NULL;
-	gsize bin_iv_len = 0;
-	char *key = NULL;
-	gsize key_len = 0;
-	char *output = NULL;
-	gsize decrypted_len = 0;
-	GByteArray *decrypted = NULL;
-
-	g_return_val_if_fail (password != NULL, NULL);
-
-	bin_iv = convert_iv (iv, &bin_iv_len, error);
-	if (!bin_iv)
-		return NULL;
-
-	/* Convert the password and IV into a DES or AES key */
-	key = crypto_make_des_aes_key (cipher, bin_iv, bin_iv_len, password, &key_len, error);
-	if (!key || !key_len)
-		goto out;
-
-	output = crypto_decrypt (cipher, key_type,
-	                         data, data_len,
-	                         bin_iv, bin_iv_len,
-	                         key, key_len,
-	                         &decrypted_len,
-	                         error);
-	if (output && decrypted_len) {
-		decrypted = g_byte_array_sized_new (decrypted_len);
-		g_byte_array_append (decrypted, (guint8 *) output, decrypted_len);
-	}
-
-out:
-	/* Don't leak stale key material */
-	if (key)
-		memset (key, 0, key_len);
-	g_free (output);
-	g_free (key);
-	g_free (bin_iv);
-
-	return decrypted;
-}
-
-GByteArray *
-crypto_decrypt_openssl_private_key_data (const guint8 *data,
-                                         gsize data_len,
-                                         const char *password,
-                                         NMCryptoKeyType *out_key_type,
-                                         GError **error)
-{
-	GByteArray *decrypted = NULL;
-	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
-	GByteArray *parsed;
-	char *iv = NULL;
-	char *cipher = NULL;
-
-	g_return_val_if_fail (data != NULL, NULL);
-	if (out_key_type)
-		g_return_val_if_fail (*out_key_type == NM_CRYPTO_KEY_TYPE_UNKNOWN, NULL);
-
-	if (!crypto_init (error))
-		return NULL;
-
-	parsed = parse_old_openssl_key_file (data, data_len, &key_type, &cipher, &iv, NULL);
-	/* return the key type even if decryption failed */
-	if (out_key_type)
-		*out_key_type = key_type;
-
-	if (!parsed) {
-		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("Unable to determine private key type."));
-		return NULL;
-	}
-
-	if (password) {
-		if (!cipher || !iv) {
-			g_set_error (error, NM_CRYPTO_ERROR,
-			             NM_CRYPTO_ERROR_INVALID_PASSWORD,
-			             _("Password provided, but key was not encrypted."));
-		} else {
-			decrypted = decrypt_key (cipher,
-			                         key_type,
-			                         parsed->data,
-			                         parsed->len,
-			                         iv,
-			                         password,
-			                         error);
-		}
-	} else if (!cipher && !iv)
-		decrypted = g_byte_array_ref (parsed);
-
-	g_byte_array_unref (parsed);
-	g_free (cipher);
-	g_free (iv);
-
-	return decrypted;
-}
-
-GByteArray *
-crypto_decrypt_openssl_private_key (const char *file,
-                                    const char *password,
-                                    NMCryptoKeyType *out_key_type,
-                                    GError **error)
-{
-	GByteArray *contents;
-	GByteArray *key = NULL;
-
-	if (!crypto_init (error))
-		return NULL;
-
-	contents = file_to_g_byte_array (file, error);
-	if (contents) {
-		key = crypto_decrypt_openssl_private_key_data (contents->data, contents->len,
-		                                               password, out_key_type, error);
-		g_byte_array_free (contents, TRUE);
-	}
-	return key;
-}
-
-static GByteArray *
-extract_pem_cert_data (GByteArray *contents, GError **error)
-{
-	GByteArray *cert = NULL;
-	gsize start = 0, end = 0;
-	unsigned char *der = NULL;
-	guint8 save_end;
-	gsize length = 0;
-
-	if (!find_tag (PEM_CERT_BEGIN, contents->data, contents->len, 0, &start)) {
-		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("PEM certificate had no start tag '%s'."),
-		             PEM_CERT_BEGIN);
-		goto done;
-	}
-
-	start += strlen (PEM_CERT_BEGIN);
-	if (!find_tag (PEM_CERT_END, contents->data, contents->len, start, &end)) {
-		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("PEM certificate had no end tag '%s'."),
-		             PEM_CERT_END);
-		goto done;
-	}
-
-	/* g_base64_decode() wants a NULL-terminated string */
-	save_end = contents->data[end];
-	contents->data[end] = '\0';
-	der = g_base64_decode ((const char *) (contents->data + start), &length);
-	contents->data[end] = save_end;
-
-	if (der && length) {
-		cert = g_byte_array_sized_new (length);
-		g_byte_array_append (cert, der, length);
-		g_assert (cert->len == length);
-	} else {
-		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("Failed to decode certificate."));
-	}
-
-done:
-	g_free (der);
-	return cert;
-}
-
-GByteArray *
-crypto_load_and_verify_certificate (const char *file,
-                                    NMCryptoFileFormat *out_file_format,
-                                    GError **error)
-{
-	GByteArray *array, *contents;
-
-	g_return_val_if_fail (file != NULL, NULL);
-	g_return_val_if_fail (out_file_format != NULL, NULL);
-	g_return_val_if_fail (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL);
-
-	if (!crypto_init (error))
-		return NULL;
-
-	contents = file_to_g_byte_array (file, error);
-	if (!contents)
-		return NULL;
-
-	/* Check for PKCS#12 */
-	if (crypto_is_pkcs12_data (contents->data, contents->len, NULL)) {
-		*out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12;
-		return contents;
-	}
-
-	/* Check for plain DER format */
-	if (contents->len > 2 && contents->data[0] == 0x30 && contents->data[1] == 0x82) {
-		*out_file_format = crypto_verify_cert (contents->data, contents->len, error);
-	} else {
-		array = extract_pem_cert_data (contents, error);
-		if (!array) {
-			g_byte_array_free (contents, TRUE);
-			return NULL;
-		}
-
-		*out_file_format = crypto_verify_cert (array->data, array->len, error);
-		g_byte_array_free (array, TRUE);
-	}
-
-	if (*out_file_format != NM_CRYPTO_FILE_FORMAT_X509) {
-		g_byte_array_free (contents, TRUE);
-		contents = NULL;
-	}
-
-	return contents;
-}
-
-gboolean
-crypto_is_pkcs12_data (const guint8 *data,
-                       gsize data_len,
-                       GError **error)
-{
-	GError *local = NULL;
-	gboolean success;
-
-	if (!data_len)
-		return FALSE;
-
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	if (!crypto_init (error))
-		return FALSE;
-
-	success = crypto_verify_pkcs12 (data, data_len, NULL, &local);
-	if (success == FALSE) {
-		/* If the error was just a decryption error, then it's pkcs#12 */
-		if (local) {
-			if (g_error_matches (local, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED)) {
-				success = TRUE;
-				g_error_free (local);
-			} else
-				g_propagate_error (error, local);
-		}
-	}
-	return success;
-}
-
-gboolean
-crypto_is_pkcs12_file (const char *file, GError **error)
-{
-	GByteArray *contents;
-	gboolean success = FALSE;
-
-	g_return_val_if_fail (file != NULL, FALSE);
-
-	if (!crypto_init (error))
-		return FALSE;
-
-	contents = file_to_g_byte_array (file, error);
-	if (contents) {
-		success = crypto_is_pkcs12_data (contents->data, contents->len, error);
-		g_byte_array_free (contents, TRUE);
-	}
-	return success;
-}
-
-/* Verifies that a private key can be read, and if a password is given, that
- * the private key can be decrypted with that password.
- */
-NMCryptoFileFormat
-crypto_verify_private_key_data (const guint8 *data,
-                                gsize data_len,
-                                const char *password,
-                                gboolean *out_is_encrypted,
-                                GError **error)
-{
-	GByteArray *tmp;
-	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	NMCryptoKeyType ktype = NM_CRYPTO_KEY_TYPE_UNKNOWN;
-	gboolean is_encrypted = FALSE;
-
-	g_return_val_if_fail (data != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
-	g_return_val_if_fail (out_is_encrypted == NULL || *out_is_encrypted == FALSE, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
-
-	if (!crypto_init (error))
-		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-
-	/* Check for PKCS#12 first */
-	if (crypto_is_pkcs12_data (data, data_len, NULL)) {
-		is_encrypted = TRUE;
-		if (!password || crypto_verify_pkcs12 (data, data_len, password, error))
-			format = NM_CRYPTO_FILE_FORMAT_PKCS12;
-	} else {
-		/* Maybe it's PKCS#8 */
-		tmp = parse_pkcs8_key_file (data, data_len, &is_encrypted, NULL);
-		if (tmp) {
-			if (!password || crypto_verify_pkcs8 (tmp->data, tmp->len, is_encrypted, password, error))
-				format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
-		} else {
-			char *cipher, *iv;
-
-			/* Or it's old-style OpenSSL */
-			tmp = parse_old_openssl_key_file (data, data_len, &ktype,
-			                                  &cipher, &iv, NULL);
-			if (tmp) {
-				format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
-				is_encrypted = (cipher && iv);
-				g_free (cipher);
-				g_free (iv);
-			}
-		}
-
-		if (tmp) {
-			/* Don't leave key data around */
-			memset (tmp->data, 0, tmp->len);
-			g_byte_array_free (tmp, TRUE);
-		}
-	}
-
-	if (out_is_encrypted)
-		*out_is_encrypted = is_encrypted;
-	return format;
-}
-
-NMCryptoFileFormat
-crypto_verify_private_key (const char *filename,
-                           const char *password,
-                           gboolean *out_is_encrypted,
-                           GError **error)
-{
-	GByteArray *contents;
-	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-
-	g_return_val_if_fail (filename != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
-
-	if (!crypto_init (error))
-		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-
-	contents = file_to_g_byte_array (filename, error);
-	if (contents) {
-		format = crypto_verify_private_key_data (contents->data, contents->len, password, out_is_encrypted, error);
-		g_byte_array_free (contents, TRUE);
-	}
-	return format;
-}
-
-void
-crypto_md5_hash (const char *salt,
-                 gssize salt_len,
-                 const char *password,
-                 gssize password_len,
-                 char *buffer,
-                 gsize buflen)
-{
-	GChecksum *ctx;
-	gsize digest_len;
-	char digest[16];
-	gsize bufidx = 0;
-	int i;
-
-	nm_assert (g_checksum_type_get_length (G_CHECKSUM_MD5) == sizeof (digest));
-
-	g_return_if_fail (password_len == 0 || password);
-	g_return_if_fail (buffer != NULL);
-	g_return_if_fail (buflen > 0);
-	g_return_if_fail (salt_len == 0 || salt);
-
-	ctx = g_checksum_new (G_CHECKSUM_MD5);
-
-	if (salt_len < 0)
-		salt_len = strlen (salt);
-	if (password_len < 0)
-		password_len = strlen (password);
-
-	for (;;) {
-		if (password_len > 0)
-			g_checksum_update (ctx, (const guchar *) password, password_len);
-		if (salt_len > 0)
-			g_checksum_update (ctx, (const guchar *) salt, salt_len);
-
-		digest_len = sizeof (digest);
-		g_checksum_get_digest (ctx, (guchar *) digest, &digest_len);
-		nm_assert (digest_len == sizeof (digest));
-
-		for (i = 0; i < sizeof (digest); i++) {
-			if (bufidx >= buflen)
-				goto done;
-			buffer[bufidx++] = digest[i];
-		}
-
-		g_checksum_reset (ctx);
-		g_checksum_update (ctx, (const guchar *) digest, sizeof (digest));
-	}
-
-done:
-	memset (digest, 0, sizeof (digest));
-	g_checksum_free (ctx);
-}
diff --git a/libnm-core/crypto.h b/libnm-core/crypto.h
deleted file mode 100644
index e89f0919..00000000
--- a/libnm-core/crypto.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-
-/*
- * Dan Williams <dcbw@redhat.com>
- *
- * 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 2007 - 2014 Red Hat, Inc.
- */
-
-#ifndef __CRYPTO_H__
-#define __CRYPTO_H__
-
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-#error Cannot use this header.
-#endif
-
-#define MD5_HASH_LEN 20
-#define CIPHER_DES_EDE3_CBC "DES-EDE3-CBC"
-#define CIPHER_DES_CBC "DES-CBC"
-#define CIPHER_AES_CBC "AES-128-CBC"
-
-typedef enum {
-	NM_CRYPTO_KEY_TYPE_UNKNOWN = 0,
-	NM_CRYPTO_KEY_TYPE_RSA,
-	NM_CRYPTO_KEY_TYPE_DSA
-} NMCryptoKeyType;
-
-typedef enum {
-	NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0,
-	NM_CRYPTO_FILE_FORMAT_X509,
-	NM_CRYPTO_FILE_FORMAT_RAW_KEY,
-	NM_CRYPTO_FILE_FORMAT_PKCS12
-} NMCryptoFileFormat;
-
-gboolean crypto_init (GError **error);
-
-GByteArray *crypto_decrypt_openssl_private_key_data (const guint8 *data,
-                                                     gsize data_len,
-                                                     const char *password,
-                                                     NMCryptoKeyType *out_key_type,
-                                                     GError **error);
-
-GByteArray *crypto_decrypt_openssl_private_key (const char *file,
-                                                const char *password,
-                                                NMCryptoKeyType *out_key_type,
-                                                GError **error);
-
-GByteArray *crypto_load_and_verify_certificate (const char *file,
-                                                NMCryptoFileFormat *out_file_format,
-                                                GError **error);
-
-gboolean crypto_is_pkcs12_file (const char *file, GError **error);
-
-gboolean crypto_is_pkcs12_data (const guint8 *data, gsize len, GError **error);
-
-NMCryptoFileFormat crypto_verify_private_key_data (const guint8 *data,
-                                                   gsize data_len,
-                                                   const char *password,
-                                                   gboolean *out_is_encrypted,
-                                                   GError **error);
-
-NMCryptoFileFormat crypto_verify_private_key (const char *file,
-                                              const char *password,
-                                              gboolean *out_is_encrypted,
-                                              GError **error);
-
-/* Internal utils API bits for crypto providers */
-
-void crypto_md5_hash (const char *salt,
-                      gssize salt_len,
-                      const char *password,
-                      gssize password_len,
-                      char *buffer,
-                      gsize buflen);
-
-char *crypto_make_des_aes_key (const char *cipher,
-                               const char *salt,
-                               const gsize salt_len,
-                               const char *password,
-                               gsize *out_len,
-                               GError **error);
-
-char * crypto_decrypt (const char *cipher,
-                       int key_type,
-                       const guint8 *data,
-                       gsize data_len,
-                       const char *iv,
-                       const gsize iv_len,
-                       const char *key,
-                       const gsize key_len,
-                       gsize *out_len,
-                       GError **error);
-
-char * crypto_encrypt (const char *cipher,
-                       const guint8 *data,
-                       gsize data_len,
-                       const char *iv,
-                       gsize iv_len,
-                       const char *key,
-                       gsize key_len,
-                       gsize *out_len,
-                       GError **error);
-
-gboolean crypto_randomize (void *buffer, gsize buffer_len, GError **error);
-
-NMCryptoFileFormat crypto_verify_cert (const guint8 *data,
-                                       gsize len,
-                                       GError **error);
-
-gboolean crypto_verify_pkcs12 (const guint8 *data,
-                               gsize data_len,
-                               const char *password,
-                               GError **error);
-
-gboolean crypto_verify_pkcs8 (const guint8 *data,
-                              gsize data_len,
-                              gboolean is_encrypted,
-                              const char *password,
-                              GError **error);
-
-#endif  /* __CRYPTO_H__ */
diff --git a/libnm-core/meson.build b/libnm-core/meson.build
index 7b143706..eb6fcce9 100644
--- a/libnm-core/meson.build
+++ b/libnm-core/meson.build
@@ -5,6 +5,7 @@ libnm_core_headers = files(
   'nm-core-types.h',
   'nm-dbus-interface.h',
   'nm-errors.h',
+  'nm-setting-6lowpan.h',
   'nm-setting-8021x.h',
   'nm-setting-adsl.h',
   'nm-setting-bluetooth.h',
@@ -24,6 +25,7 @@ libnm_core_headers = files(
   'nm-setting-ip6-config.h',
   'nm-setting-macsec.h',
   'nm-setting-macvlan.h',
+  'nm-setting-match.h',
   'nm-setting-olpc-mesh.h',
   'nm-setting-ovs-bridge.h',
   'nm-setting-ovs-interface.h',
@@ -33,6 +35,7 @@ libnm_core_headers = files(
   'nm-setting-pppoe.h',
   'nm-setting-proxy.h',
   'nm-setting-serial.h',
+  'nm-setting-sriov.h',
   'nm-setting-tc-config.h',
   'nm-setting-team-port.h',
   'nm-setting-team.h',
@@ -45,6 +48,7 @@ libnm_core_headers = files(
   'nm-setting-wired.h',
   'nm-setting-wireless-security.h',
   'nm-setting-wireless.h',
+  'nm-setting-wpan.h',
   'nm-setting.h',
   'nm-simple-connection.h',
   'nm-utils.h',
@@ -55,6 +59,7 @@ libnm_core_headers = files(
 )
 
 libnm_core_settings_sources = files(
+  'nm-setting-6lowpan.c',
   'nm-setting-8021x.c',
   'nm-setting-adsl.c',
   'nm-setting-bluetooth.c',
@@ -65,6 +70,7 @@ libnm_core_settings_sources = files(
   'nm-setting-connection.c',
   'nm-setting-dcb.c',
   'nm-setting-dummy.c',
+  'nm-setting-ethtool.c',
   'nm-setting-generic.c',
   'nm-setting-gsm.c',
   'nm-setting-infiniband.c',
@@ -74,6 +80,7 @@ libnm_core_settings_sources = files(
   'nm-setting-ip6-config.c',
   'nm-setting-macsec.c',
   'nm-setting-macvlan.c',
+  'nm-setting-match.c',
   'nm-setting-olpc-mesh.c',
   'nm-setting-ovs-bridge.c',
   'nm-setting-ovs-interface.c',
@@ -83,6 +90,7 @@ libnm_core_settings_sources = files(
   'nm-setting-pppoe.c',
   'nm-setting-proxy.c',
   'nm-setting-serial.c',
+  'nm-setting-sriov.c',
   'nm-setting-tc-config.c',
   'nm-setting-team-port.c',
   'nm-setting-team.c',
@@ -94,12 +102,12 @@ libnm_core_settings_sources = files(
   'nm-setting-wimax.c',
   'nm-setting-wired.c',
   'nm-setting-wireless-security.c',
-  'nm-setting-wireless.c'
+  'nm-setting-wireless.c',
+  'nm-setting-wpan.c'
 )
 
 libnm_core_sources = libnm_core_settings_sources + files(
-  'crypto.c',
-  'crypto_' + crypto + '.c',
+  'nm-crypto.c',
   'nm-connection.c',
   'nm-dbus-utils.c',
   'nm-errors.c',
@@ -127,7 +135,6 @@ libnm_core_enum = gnome.mkenums(
 )
 
 deps = [
-  crypto_dep,
   dl_dep,
   libudev_dep,
   shared_dep,
@@ -138,12 +145,6 @@ deps = [
 cflags = [
   '-DG_LOG_DOMAIN="@0@"'.format(libnm_name),
   '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE',
-  '-DLIBEXECDIR="@0@"'.format(nm_libexecdir),
-  '-DNMLOCALEDIR="@0@"'.format(nm_localedir),
-  '-DNMCONFDIR="@0@"'.format(nm_pkgconfdir),
-  '-DNMLIBDIR="@0@"'.format(nm_pkglibdir),
-  '-DNMPLUGINDIR="@0@"'.format(nm_plugindir),
-  '-DNMVPNDIR="@0@"'.format(nm_vpndir)
 ]
 
 if enable_json_validation
@@ -151,9 +152,36 @@ if enable_json_validation
   deps += jansson_dep
 endif
 
+if (crypto_gnutls_dep.found())
+  libnm_crypto_gnutls = static_library(
+    'nm-crypto-gnutls',
+    sources: [ 'nm-crypto-gnutls.c' ],
+    dependencies: deps + [ crypto_gnutls_dep ],
+    c_args: cflags
+  )
+endif
+
+if (crypto_nss_dep.found())
+  libnm_crypto_nss = static_library(
+    'nm-crypto-nss',
+    sources: [ 'nm-crypto-nss.c' ],
+    dependencies: deps + [ crypto_nss_dep ],
+    c_args: cflags
+  )
+endif
+
+if crypto == 'gnutls'
+  libnm_crypto = libnm_crypto_gnutls
+elif crypto == 'nss'
+  libnm_crypto = libnm_crypto_nss
+else
+  error('bug')
+endif
+
 libnm_core_sources_all =  libnm_core_sources
 libnm_core_sources_all += libnm_core_enum
-libnm_core_sources_all += shared_nm_utils_nm_meta_setting_c
+libnm_core_sources_all += shared_nm_meta_setting_c
+libnm_core_sources_all += shared_nm_ethtool_utils_c
 libnm_core_sources_all += shared_files_libnm_core
 libnm_core_sources_all += [version_header]
 
@@ -161,6 +189,7 @@ libnm_core = static_library(
   'nm-core',
   sources: libnm_core_sources_all,
   dependencies: deps,
+  link_with: libnm_crypto,
   c_args: cflags
 )
 
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index 0c4d7ce1..a5df0814 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -16,7 +16,7 @@
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301 USA.
  *
- * Copyright 2007 - 2017 Red Hat, Inc.
+ * Copyright 2007 - 2018 Red Hat, Inc.
  * Copyright 2007 - 2008 Novell, Inc.
  */
 
@@ -225,6 +225,40 @@ nm_connection_get_setting_by_name (NMConnection *connection, const char *name)
 	return type ? _connection_get_setting (connection, type) : NULL;
 }
 
+/*****************************************************************************/
+
+gpointer /* (NMSetting *) */
+_nm_connection_check_main_setting (NMConnection *connection,
+                                   const char *setting_name,
+                                   GError **error)
+{
+	NMSetting *setting;
+
+	nm_assert (NM_IS_CONNECTION (connection));
+	nm_assert (setting_name);
+
+	if (!nm_connection_is_type (connection, setting_name)) {
+		nm_utils_error_set (error,
+		                    NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
+		                    "connection type is not \"%s\"",
+		                    setting_name);
+		return NULL;
+	}
+
+	setting = nm_connection_get_setting_by_name (connection, setting_name);
+	if (!setting) {
+		nm_utils_error_set (error,
+		                    NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
+		                    "connection misses \"%s\" settings",
+		                    setting_name);
+		return NULL;
+	}
+
+	return setting;
+}
+
+/*****************************************************************************/
+
 static gboolean
 validate_permissions_type (GVariant *variant, GError **error)
 {
@@ -809,16 +843,34 @@ _normalize_ethernet_link_neg (NMConnection *self)
 	return FALSE;
 }
 
+/**
+ * _supports_addr_family:
+ * @self: a #NMConnection
+ * @family: AF_*
+ *
+ * Check whether the connection supports certain L3 address family,
+ * in order to be able to tell whether is should have the corresponding
+ * setting ("ipv4" for AF_INET and "ipv6" for AF_INET6).
+ *
+ * If AF_UNSPEC is given, then the function checks whether the connection
+ * supports any L3 configuration at all.
+ *
+ * Returns: %TRUE if the AF is supported, %FALSE otherwise
+ **/
 static gboolean
-_without_ip_config (NMConnection *self)
+_supports_addr_family (NMConnection *self, int family)
 {
 	const char *connection_type = nm_connection_get_connection_type (self);
 
-	g_return_val_if_fail (connection_type, FALSE);
+	g_return_val_if_fail (connection_type, TRUE);
 	if (strcmp (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) == 0)
+		return TRUE;
+	if (strcmp (connection_type, NM_SETTING_WPAN_SETTING_NAME) == 0)
 		return FALSE;
+	if (strcmp (connection_type, NM_SETTING_6LOWPAN_SETTING_NAME) == 0)
+		return family == AF_INET6 || family == AF_UNSPEC;
 
-	return !!nm_setting_connection_get_master (nm_connection_get_setting_connection (self));
+	return !nm_setting_connection_get_master (nm_connection_get_setting_connection (self));
 }
 
 static gboolean
@@ -841,32 +893,18 @@ _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 (_without_ip_config (self)) {
-		/* Slave connections don't have IP configuration. */
-
-		if (s_ip4)
-			nm_connection_remove_setting (self, NM_TYPE_SETTING_IP4_CONFIG);
-
-		if (s_ip6)
-			nm_connection_remove_setting (self, NM_TYPE_SETTING_IP6_CONFIG);
-
-		if (s_proxy)
-			nm_connection_remove_setting (self, NM_TYPE_SETTING_PROXY);
-
-		return s_ip4 || s_ip6 || s_proxy;
-	} else {
-		/* Ensure all non-slave connections have IP4 and IP6 settings objects. If no
-		 * IP6 setting was specified, then assume that means IP6 config is allowed
-		 * to fail. But if no IP4 setting was specified, assume the caller was just
-		 * being lazy.
-		 */
+	if (_supports_addr_family (self, AF_INET)) {
 		if (!s_ip4) {
+			 /* But if no IP4 setting was specified, assume the caller was just
+			  * being lazy and use the default method.
+			  */
 			setting = nm_setting_ip4_config_new ();
 
 			g_object_set (setting,
 			              NM_SETTING_IP_CONFIG_METHOD, default_ip4_method,
 			              NULL);
 			nm_connection_add_setting (self, setting);
+			changed = TRUE;
 		} else {
 			if (   nm_setting_ip_config_get_gateway (s_ip4)
 			    && nm_setting_ip_config_get_never_default (s_ip4)) {
@@ -890,7 +928,18 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters)
 				changed = TRUE;
 			}
 		}
+	} else {
+		if (s_ip4) {
+			nm_connection_remove_setting (self, NM_TYPE_SETTING_IP4_CONFIG);
+			changed = TRUE;
+		}
+	}
+
+	if (_supports_addr_family (self, AF_INET6)) {
 		if (!s_ip6) {
+			/* If no IP6 setting was specified, then assume that means IP6 config is
+			 * allowed to fail.
+			 */
 			setting = nm_setting_ip6_config_new ();
 
 			g_object_set (setting,
@@ -898,6 +947,7 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters)
 			              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
 			              NULL);
 			nm_connection_add_setting (self, setting);
+			changed = TRUE;
 		} else {
 			const char *token;
 
@@ -930,14 +980,27 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters)
 				changed = TRUE;
 			}
 		}
+	} else {
+		if (s_ip6) {
+			nm_connection_remove_setting (self, NM_TYPE_SETTING_IP6_CONFIG);
+			changed = TRUE;
+		}
+	}
 
+	if (_supports_addr_family (self, AF_UNSPEC)) {
 		if (!s_proxy) {
 			setting = nm_setting_proxy_new ();
 			nm_connection_add_setting (self, setting);
+			changed = TRUE;
+		}
+	} else {
+		if (s_proxy) {
+			nm_connection_remove_setting (self, NM_TYPE_SETTING_PROXY);
+			changed = TRUE;
 		}
-
-		return !s_ip4 || !s_ip6 || !s_proxy || changed;
 	}
+
+	return changed;
 }
 
 static gboolean
@@ -1129,6 +1192,38 @@ _normalize_ovs_interface_type (NMConnection *self, GHashTable *parameters)
 }
 
 static gboolean
+_normalize_ip_tunnel_wired_setting (NMConnection *self, GHashTable *parameters)
+{
+	NMSettingIPTunnel *s_ip_tunnel;
+
+	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (self);
+	if (!s_ip_tunnel)
+		return FALSE;
+
+	if (   nm_connection_get_setting_wired (self)
+	    && !NM_IN_SET (nm_setting_ip_tunnel_get_mode (s_ip_tunnel),
+	                   NM_IP_TUNNEL_MODE_GRETAP,
+	                   NM_IP_TUNNEL_MODE_IP6GRETAP)) {
+		nm_connection_remove_setting (self, NM_TYPE_SETTING_WIRED);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+_normalize_sriov_vf_order (NMConnection *self, GHashTable *parameters)
+{
+	NMSettingSriov *s_sriov;
+
+	s_sriov = nm_connection_get_setting_sriov (self);
+	if (!s_sriov)
+		return FALSE;
+
+	return _nm_setting_sriov_sort_vfs (s_sriov);
+}
+
+static gboolean
 _normalize_required_settings (NMConnection *self, GHashTable *parameters)
 {
 	NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (self);
@@ -1288,39 +1383,79 @@ _nm_connection_verify (NMConnection *connection, GError **error)
 	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) {
+		if (_supports_addr_family (connection, AF_INET)) {
+			if (!s_ip4 && normalizable_error_type == NM_SETTING_VERIFY_SUCCESS) {
+				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: ", NM_SETTING_IP4_CONFIG_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;
+			}
+		} else {
+			if (s_ip4) {
 				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));
+				g_prefix_error (&normalizable_error, "%s: ", NM_SETTING_IP4_CONFIG_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)) {
+		}
+
+		if (_supports_addr_family (connection, AF_INET6)) {
+			if (!s_ip6 && normalizable_error_type == NM_SETTING_VERIFY_SUCCESS) {
 				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));
+				g_prefix_error (&normalizable_error, "%s: ", NM_SETTING_IP6_CONFIG_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;
 			}
+		} else {
+			if (s_ip6) {
+				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: ", NM_SETTING_IP6_CONFIG_SETTING_NAME);
+				/* having a slave with IP config *was* and is a verify() error. */
+				normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+			}
+		}
+
+		if (_supports_addr_family (connection, AF_UNSPEC)) {
+			if (!s_proxy && normalizable_error_type == NM_SETTING_VERIFY_SUCCESS) {
+				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: ", NM_SETTING_PROXY_SETTING_NAME);
+
+				/* having a master without proxy config was not a verify() error, accept
+				 * it for backward compatibility. */
+				normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE;
+			}
+		} else {
+			if (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: ", NM_SETTING_PROXY_SETTING_NAME);
+				/* having a slave with proxy config *was* and is a verify() error. */
+				normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+			}
 		}
 	}
 
@@ -1433,6 +1568,8 @@ nm_connection_normalize (NMConnection *connection,
 	was_modified |= _normalize_team_port_config (connection, parameters);
 	was_modified |= _normalize_bluetooth_type (connection, parameters);
 	was_modified |= _normalize_ovs_interface_type (connection, parameters);
+	was_modified |= _normalize_ip_tunnel_wired_setting (connection, parameters);
+	was_modified |= _normalize_sriov_vf_order (connection, parameters);
 
 	/* Verify anew. */
 	success = _nm_connection_verify (connection, error);
@@ -1858,9 +1995,10 @@ nm_connection_for_each_setting_value (NMConnection *connection,
  * nm_connection_dump:
  * @connection: the #NMConnection
  *
- * Print the connection to stdout.  For debugging purposes ONLY, should NOT
- * be used for serialization of the connection or machine-parsed in any way. The
- * output format is not guaranteed to be stable and may change at any time.
+ * Print the connection (including secrets!) to stdout. For debugging
+ * purposes ONLY, should NOT be used for serialization of the setting,
+ * or machine-parsed in any way. The output format is not guaranteed to
+ * be stable and may change at any time.
  **/
 void
 nm_connection_dump (NMConnection *connection)
@@ -1943,6 +2081,32 @@ nm_connection_get_interface_name (NMConnection *connection)
 	return s_con ? nm_setting_connection_get_interface_name (s_con) : NULL;
 }
 
+NMConnectionMultiConnect
+_nm_connection_get_multi_connect (NMConnection *connection)
+{
+	NMSettingConnection *s_con;
+	NMConnectionMultiConnect multi_connect;
+	const NMConnectionMultiConnect DEFAULT = NM_CONNECTION_MULTI_CONNECT_SINGLE;
+
+	/* connection.multi_connect property cannot be specified via regular
+	 * connection defaults in NetworkManager.conf, because those are per-device,
+	 * and we need to determine the multi_connect independent of a particular
+	 * device.
+	 *
+	 * There is however still a default-value, so theoretically, the default
+	 * value could be specified in NetworkManager.conf. Just not as [connection*]
+	 * and indepdented of a device. */
+
+	s_con = nm_connection_get_setting_connection (connection);
+	if (!s_con)
+		return DEFAULT;
+
+	multi_connect = nm_setting_connection_get_multi_connect (s_con);
+	return multi_connect == NM_CONNECTION_MULTI_CONNECT_DEFAULT
+	       ? DEFAULT
+	       : multi_connect;
+}
+
 gboolean
 _nm_connection_verify_required_interface_name (NMConnection *connection,
                                                GError **error)
@@ -2033,7 +2197,8 @@ nm_connection_is_virtual (NMConnection *connection)
 	if (!type)
 		return FALSE;
 
-	if (   !strcmp (type, NM_SETTING_BOND_SETTING_NAME)
+	if (   !strcmp (type, NM_SETTING_6LOWPAN_SETTING_NAME)
+	    || !strcmp (type, NM_SETTING_BOND_SETTING_NAME)
 	    || !strcmp (type, NM_SETTING_DUMMY_SETTING_NAME)
 	    || !strcmp (type, NM_SETTING_TEAM_SETTING_NAME)
 	    || !strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)
@@ -2114,6 +2279,22 @@ nm_connection_get_virtual_device_description (NMConnection *connection)
 /*****************************************************************************/
 
 /**
+ * nm_connection_get_setting_6lowpan:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSetting6Lowpan the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSetting6Lowpan if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.14
+ **/
+NMSetting6Lowpan *
+nm_connection_get_setting_6lowpan (NMConnection *connection)
+{
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_6LOWPAN);
+}
+
+/**
  * nm_connection_get_setting_802_1x:
  * @connection: the #NMConnection
  *
@@ -2520,6 +2701,22 @@ nm_connection_get_setting_serial (NMConnection *connection)
 }
 
 /**
+ * nm_connection_get_setting_sriov:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingSriov the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingSriov if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.14
+ **/
+NMSettingSriov *
+nm_connection_get_setting_sriov (NMConnection *connection)
+{
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_SRIOV);
+}
+
+/**
  * nm_connection_get_setting_tc_config:
  * @connection: the #NMConnection
  *
@@ -2679,6 +2876,22 @@ nm_connection_get_setting_vlan (NMConnection *connection)
 	return _connection_get_setting_check (connection, NM_TYPE_SETTING_VLAN);
 }
 
+/**
+ * nm_connection_get_setting_wpan:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingWpan the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingWpan if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.14
+ **/
+NMSettingWpan *
+nm_connection_get_setting_wpan (NMConnection *connection)
+{
+	return _connection_get_setting_check (connection, NM_TYPE_SETTING_WPAN);
+}
+
 NMSettingBluetooth *
 _nm_connection_get_setting_bluetooth_for_nap (NMConnection *connection)
 {
diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h
index 6d23cfdb..312760f1 100644
--- a/libnm-core/nm-connection.h
+++ b/libnm-core/nm-connection.h
@@ -16,7 +16,7 @@
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301 USA.
  *
- * Copyright 2007 - 2017 Red Hat, Inc.
+ * Copyright 2007 - 2018 Red Hat, Inc.
  * Copyright 2007 - 2008 Novell, Inc.
  */
 
@@ -193,6 +193,8 @@ const char *  nm_connection_get_connection_type (NMConnection *connection);
 gboolean      nm_connection_is_virtual          (NMConnection *connection);
 char *        nm_connection_get_virtual_device_description (NMConnection *connection);
 
+NM_AVAILABLE_IN_1_14
+NMSetting6Lowpan *         nm_connection_get_setting_6lowpan           (NMConnection *connection);
 NMSetting8021x *           nm_connection_get_setting_802_1x            (NMConnection *connection);
 NMSettingBluetooth *       nm_connection_get_setting_bluetooth         (NMConnection *connection);
 NMSettingBond *            nm_connection_get_setting_bond              (NMConnection *connection);
@@ -229,6 +231,8 @@ NMSettingPppoe *           nm_connection_get_setting_pppoe             (NMConnec
 NM_AVAILABLE_IN_1_6
 NMSettingProxy *           nm_connection_get_setting_proxy             (NMConnection *connection);
 NMSettingSerial *          nm_connection_get_setting_serial            (NMConnection *connection);
+NM_AVAILABLE_IN_1_14
+NMSettingSriov *           nm_connection_get_setting_sriov             (NMConnection *connection);
 NM_AVAILABLE_IN_1_12
 NMSettingTCConfig *        nm_connection_get_setting_tc_config         (NMConnection *connection);
 NMSettingTun *             nm_connection_get_setting_tun               (NMConnection *connection);
@@ -241,6 +245,8 @@ NMSettingWirelessSecurity *nm_connection_get_setting_wireless_security (NMConnec
 NMSettingVlan *            nm_connection_get_setting_vlan              (NMConnection *connection);
 NM_AVAILABLE_IN_1_2
 NMSettingVxlan *           nm_connection_get_setting_vxlan             (NMConnection *connection);
+NM_AVAILABLE_IN_1_14
+NMSettingWpan *            nm_connection_get_setting_wpan              (NMConnection *connection);
 
 G_END_DECLS
 
diff --git a/libnm-core/nm-core-enum-types.c.template b/libnm-core/nm-core-enum-types.c.template
index 9c08f526..605fa34a 100644
--- a/libnm-core/nm-core-enum-types.c.template
+++ b/libnm-core/nm-core-enum-types.c.template
@@ -48,6 +48,7 @@
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-wireless.h"
+#include "nm-setting-wpan.h"
 #include "nm-setting.h"
 #include "nm-simple-connection.h"
 #include "nm-utils.h"
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 2a2045e6..f0d4e40f 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -15,7 +15,7 @@
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2014 - 2017 Red Hat, Inc.
+ * (C) Copyright 2014 - 2018 Red Hat, Inc.
  */
 
 #ifndef NM_CORE_NM_INTERNAL_H
@@ -37,6 +37,7 @@
 
 #include "nm-connection.h"
 #include "nm-core-enum-types.h"
+#include "nm-setting-6lowpan.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-adsl.h"
 #include "nm-setting-bluetooth.h"
@@ -56,6 +57,7 @@
 #include "nm-setting-ip6-config.h"
 #include "nm-setting-macsec.h"
 #include "nm-setting-macvlan.h"
+#include "nm-setting-match.h"
 #include "nm-setting-olpc-mesh.h"
 #include "nm-setting-ovs-bridge.h"
 #include "nm-setting-ovs-interface.h"
@@ -64,6 +66,7 @@
 #include "nm-setting-ppp.h"
 #include "nm-setting-pppoe.h"
 #include "nm-setting-serial.h"
+#include "nm-setting-sriov.h"
 #include "nm-setting-tc-config.h"
 #include "nm-setting-team-port.h"
 #include "nm-setting-team.h"
@@ -75,12 +78,14 @@
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-wireless.h"
+#include "nm-setting-wpan.h"
 #include "nm-setting.h"
 #include "nm-simple-connection.h"
 #include "nm-utils.h"
 #include "nm-vpn-dbus-interface.h"
 #include "nm-core-types-internal.h"
 #include "nm-vpn-editor-plugin.h"
+#include "nm-meta-setting.h"
 
 /* IEEE 802.1D-1998 timer values */
 #define NM_BR_MIN_HELLO_TIME    1
@@ -137,6 +142,10 @@ gboolean _nm_connection_replace_settings (NMConnection *connection,
                                           NMSettingParseFlags parse_flags,
                                           GError **error);
 
+gpointer _nm_connection_check_main_setting (NMConnection *connection,
+                                            const char *setting_name,
+                                            GError **error);
+
 /**
  * NMSettingVerifyResult:
  * @NM_SETTING_VERIFY_SUCCESS: the setting verifies successfully
@@ -161,49 +170,43 @@ NMConnection *_nm_simple_connection_new_from_dbus (GVariant      *dict,
                                                    NMSettingParseFlags parse_flags,
                                                    GError       **error);
 
-/*
- * 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);
 
+/*****************************************************************************/
+
+GHashTable *_nm_setting_gendata_hash (NMSetting *setting,
+                                      gboolean create_if_necessary);
+
+void _nm_setting_gendata_notify (NMSetting *setting,
+                                 gboolean keys_changed);
+
+guint _nm_setting_gendata_get_all (NMSetting *setting,
+                                   const char *const**out_names,
+                                   GVariant *const**out_values);
+
+gboolean _nm_setting_gendata_reset_from_hash (NMSetting *setting,
+                                              GHashTable *new);
+
+void _nm_setting_gendata_to_gvalue (NMSetting *setting,
+                                    GValue *value);
+
+GVariant *nm_setting_gendata_get (NMSetting *setting,
+                                  const char *name);
+
+const char *const*nm_setting_gendata_get_all_names (NMSetting *setting,
+                                                    guint *out_len);
+
+GVariant *const*nm_setting_gendata_get_all_values (NMSetting *setting);
+
+/*****************************************************************************/
+
+guint nm_setting_ethtool_init_features (NMSettingEthtool *setting,
+                                        NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */);
+
+/*****************************************************************************/
+
 #define NM_UTILS_HWADDR_LEN_MAX_STR (NM_UTILS_HWADDR_LEN_MAX * 3)
 
 guint8 *_nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize buffer_length, gsize *out_length);
@@ -212,6 +215,13 @@ const char *nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboole
 char *_nm_utils_bin2str (gconstpointer addr, gsize length, gboolean upper_case);
 void _nm_utils_bin2str_full (gconstpointer addr, gsize length, const char delimiter, gboolean upper_case, char *out);
 
+guint8 *_nm_utils_str2bin_full (const char *asc,
+                                gboolean delimiter_required,
+                                const char *delimiter_candidates,
+                                guint8 *buffer,
+                                gsize buffer_length,
+                                gsize *out_len);
+
 GSList *    _nm_utils_hash_values_to_slist (GHashTable *hash);
 
 GHashTable *_nm_utils_copy_strdict (GHashTable *strdict);
@@ -224,6 +234,9 @@ 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);
 
+NMSriovVF *_nm_utils_sriov_vf_from_strparts (const char *index, const char *detail, GError **error);
+gboolean _nm_sriov_vf_attribute_validate_all (const NMSriovVF *vf, GError **error);
+
 static inline void
 _nm_auto_ip_route_unref (NMIPRoute **v)
 {
@@ -232,12 +245,6 @@ _nm_auto_ip_route_unref (NMIPRoute **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);
-GSList    *_nm_utils_copy_array_to_slist (const GPtrArray *array,
-                                          NMUtilsCopyFunc copy_func);
-
 GPtrArray *_nm_utils_copy_array (const GPtrArray *array,
                                  NMUtilsCopyFunc copy_func,
                                  GDestroyNotify free_func);
@@ -245,15 +252,6 @@ GPtrArray *_nm_utils_copy_object_array (const GPtrArray *array);
 
 gssize _nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpointer needle);
 
-gssize _nm_utils_ptrarray_find_binary_search (gconstpointer *list,
-                                              gsize len,
-                                              gconstpointer needle,
-                                              GCompareDataFunc cmpfcn,
-                                              gpointer user_data,
-                                              gssize *out_idx_first,
-                                              gssize *out_idx_last);
-gssize _nm_utils_array_find_binary_search (gconstpointer list, gsize elem_size, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data);
-
 GSList *    _nm_utils_strv_to_slist (char **strv, gboolean deep_copy);
 char **     _nm_utils_slist_to_strv (GSList *slist, gboolean deep_copy);
 
@@ -296,12 +294,6 @@ void _nm_dbus_errors_init (void);
 
 extern gboolean _nm_utils_is_manager_process;
 
-GByteArray *nm_utils_rsa_key_encrypt (const guint8 *data,
-                                      gsize len,
-                                      const char *in_password,
-                                      char **out_password,
-                                      GError **error);
-
 gulong _nm_dbus_signal_connect_data (GDBusProxy *proxy,
                                      const char *signal_name,
                                      const GVariantType *signature,
@@ -318,11 +310,11 @@ GVariant *_nm_dbus_proxy_call_finish (GDBusProxy           *proxy,
                                       GError              **error);
 
 GVariant *_nm_dbus_proxy_call_sync   (GDBusProxy           *proxy,
-                                      const gchar          *method_name,
+                                      const char           *method_name,
                                       GVariant             *parameters,
                                       const GVariantType   *reply_type,
                                       GDBusCallFlags        flags,
-                                      gint                  timeout_msec,
+                                      int                   timeout_msec,
                                       GCancellable         *cancellable,
                                       GError              **error);
 
@@ -331,6 +323,13 @@ gboolean _nm_dbus_error_has_name (GError     *error,
 
 /*****************************************************************************/
 
+char *_nm_utils_ssid_to_string_arr (const guint8 *ssid, gsize len);
+char *_nm_utils_ssid_to_string (GBytes *ssid);
+char *_nm_utils_ssid_to_utf8 (GBytes *ssid);
+gboolean _nm_utils_is_empty_ssid (GBytes *ssid);
+
+/*****************************************************************************/
+
 gboolean _nm_vpn_plugin_info_check_file (const char *filename,
                                          gboolean check_absolute,
                                          gboolean do_validate_filename,
@@ -427,6 +426,8 @@ _nm_connection_get_uuid (NMConnection *connection)
 	return connection ? nm_connection_get_uuid (connection) : NULL;
 }
 
+NMConnectionMultiConnect _nm_connection_get_multi_connect (NMConnection *connection);
+
 /*****************************************************************************/
 
 typedef enum {
@@ -511,4 +512,89 @@ _nm_connection_type_is_master (const char *type)
 gboolean _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin);
 
 /*****************************************************************************/
+
+gboolean _nm_setting_sriov_sort_vfs (NMSettingSriov *setting);
+
+/*****************************************************************************/
+
+typedef struct _NMSettInfoSetting NMSettInfoSetting;
+
+typedef GVariant *(*NMSettingPropertyGetFunc)           (NMSetting     *setting,
+                                                         const char    *property);
+typedef GVariant *(*NMSettingPropertySynthFunc)         (NMSetting     *setting,
+                                                         NMConnection  *connection,
+                                                         const char    *property);
+typedef gboolean  (*NMSettingPropertySetFunc)           (NMSetting     *setting,
+                                                         GVariant      *connection_dict,
+                                                         const char    *property,
+                                                         GVariant      *value,
+                                                         NMSettingParseFlags parse_flags,
+                                                         GError       **error);
+typedef gboolean  (*NMSettingPropertyNotSetFunc)        (NMSetting     *setting,
+                                                         GVariant      *connection_dict,
+                                                         const char    *property,
+                                                         NMSettingParseFlags parse_flags,
+                                                         GError       **error);
+typedef GVariant *(*NMSettingPropertyTransformToFunc)   (const GValue *from);
+typedef void      (*NMSettingPropertyTransformFromFunc) (GVariant *from,
+                                                          GValue *to);
+
+typedef struct {
+	const char *name;
+	GParamSpec *param_spec;
+	const GVariantType *dbus_type;
+
+	NMSettingPropertyGetFunc           get_func;
+	NMSettingPropertySynthFunc         synth_func;
+	NMSettingPropertySetFunc           set_func;
+	NMSettingPropertyNotSetFunc        not_set_func;
+
+	NMSettingPropertyTransformToFunc   to_dbus;
+	NMSettingPropertyTransformFromFunc from_dbus;
+} NMSettInfoProperty;
+
+typedef struct {
+	const GVariantType *(*get_variant_type) (const struct _NMSettInfoSetting *sett_info,
+	                                         const char *name,
+	                                         GError **error);
+} NMSettInfoSettGendata;
+
+typedef struct {
+	/* if set, then this setting class has no own fields. Instead, its
+	 * data is entirely based on gendata. Meaning: it tracks all data
+	 * as native GVariants.
+	 * It might have some GObject properties, but these are merely accessors
+	 * to the underlying gendata.
+	 *
+	 * Note, that at the moment there are few hooks, to customize the behavior
+	 * of the setting further. They are currently unneeded. This is desired,
+	 * but could be added when there is a good reason.
+	 *
+	 * However, a few hooks there are... see NMSettInfoSettGendata. */
+	const NMSettInfoSettGendata *gendata_info;
+} NMSettInfoSettDetail;
+
+struct _NMSettInfoSetting {
+	NMSettingClass *setting_class;
+	const NMSettInfoProperty *property_infos;
+	guint property_infos_len;
+	NMSettInfoSettDetail detail;
+};
+
+const NMSettInfoSetting *_nm_sett_info_setting_get (NMSettingClass *setting_class);
+
+const NMSettInfoProperty *_nm_sett_info_property_get (NMSettingClass *setting_class,
+                                                      const char *property_name);
+
+/*****************************************************************************/
+
+NMSetting8021xCKScheme _nm_setting_802_1x_cert_get_scheme (GBytes *bytes, GError **error);
+
+GBytes *_nm_setting_802_1x_cert_value_to_bytes (NMSetting8021xCKScheme scheme,
+                                                const guint8 *val_bin,
+                                                gssize val_len,
+                                                GError **error);
+
+/*****************************************************************************/
+
 #endif
diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h
index 6a274333..89d99579 100644
--- a/libnm-core/nm-core-types.h
+++ b/libnm-core/nm-core-types.h
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2014 - 2017 Red Hat, Inc.
+ * Copyright 2014 - 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_CORE_TYPES_H__
@@ -29,6 +29,7 @@
 
 typedef struct _NMConnection              NMConnection;
 typedef struct _NMSetting                 NMSetting;
+typedef struct _NMSetting6Lowpan          NMSetting6Lowpan;
 typedef struct _NMSetting8021x            NMSetting8021x;
 typedef struct _NMSettingAdsl             NMSettingAdsl;
 typedef struct _NMSettingBluetooth        NMSettingBluetooth;
@@ -39,6 +40,7 @@ typedef struct _NMSettingCdma             NMSettingCdma;
 typedef struct _NMSettingConnection       NMSettingConnection;
 typedef struct _NMSettingDcb              NMSettingDcb;
 typedef struct _NMSettingDummy            NMSettingDummy;
+typedef struct _NMSettingEthtool          NMSettingEthtool;
 typedef struct _NMSettingGeneric          NMSettingGeneric;
 typedef struct _NMSettingGsm              NMSettingGsm;
 typedef struct _NMSettingInfiniband       NMSettingInfiniband;
@@ -49,6 +51,7 @@ typedef struct _NMSettingIP4Config        NMSettingIP4Config;
 typedef struct _NMSettingIP6Config        NMSettingIP6Config;
 typedef struct _NMSettingMacsec           NMSettingMacsec;
 typedef struct _NMSettingMacvlan          NMSettingMacvlan;
+typedef struct _NMSettingMatch            NMSettingMatch;
 typedef struct _NMSettingOlpcMesh         NMSettingOlpcMesh;
 typedef struct _NMSettingOvsBridge        NMSettingOvsBridge;
 typedef struct _NMSettingOvsInterface     NMSettingOvsInterface;
@@ -57,6 +60,7 @@ typedef struct _NMSettingOvsPort          NMSettingOvsPort;
 typedef struct _NMSettingPpp              NMSettingPpp;
 typedef struct _NMSettingPppoe            NMSettingPppoe;
 typedef struct _NMSettingSerial           NMSettingSerial;
+typedef struct _NMSettingSriov            NMSettingSriov;
 typedef struct _NMSettingTCConfig         NMSettingTCConfig;
 typedef struct _NMSettingTeam             NMSettingTeam;
 typedef struct _NMSettingTeamPort         NMSettingTeamPort;
@@ -69,6 +73,7 @@ typedef struct _NMSettingWimax            NMSettingWimax;
 typedef struct _NMSettingWired            NMSettingWired;
 typedef struct _NMSettingWireless         NMSettingWireless;
 typedef struct _NMSettingWirelessSecurity NMSettingWirelessSecurity;
+typedef struct _NMSettingWpan             NMSettingWpan;
 typedef struct _NMSimpleConnection        NMSimpleConnection;
 
 #endif  /* __NM_CORE_TYPES_H__ */
diff --git a/libnm-core/crypto_gnutls.c b/libnm-core/nm-crypto-gnutls.c
index 53a3ba4a..6c897e6d 100644
--- a/libnm-core/crypto_gnutls.c
+++ b/libnm-core/nm-crypto-gnutls.c
@@ -23,26 +23,53 @@
 
 #include "nm-default.h"
 
+#include "nm-crypto-impl.h"
+
 #include <gnutls/gnutls.h>
 #include <gnutls/crypto.h>
 #include <gnutls/x509.h>
 #include <gnutls/pkcs12.h>
 
-#include "crypto.h"
+#include "nm-utils/nm-secret-utils.h"
 #include "nm-errors.h"
 
-#define SALT_LEN 8
+/*****************************************************************************/
+
+static gboolean
+_get_cipher_info (NMCryptoCipherType cipher,
+                  int *out_cipher_mech,
+                  guint8 *out_real_iv_len)
+{
+	static const int cipher_mechs[] = {
+		[NM_CRYPTO_CIPHER_DES_EDE3_CBC] = GNUTLS_CIPHER_3DES_CBC,
+		[NM_CRYPTO_CIPHER_DES_CBC]      = GNUTLS_CIPHER_DES_CBC,
+		[NM_CRYPTO_CIPHER_AES_128_CBC]  = GNUTLS_CIPHER_AES_128_CBC,
+		[NM_CRYPTO_CIPHER_AES_192_CBC]  = GNUTLS_CIPHER_AES_192_CBC,
+		[NM_CRYPTO_CIPHER_AES_256_CBC]  = GNUTLS_CIPHER_AES_256_CBC,
+	};
+
+	g_return_val_if_fail (_NM_INT_NOT_NEGATIVE (cipher) && (gsize) cipher < G_N_ELEMENTS (cipher_mechs), FALSE);
+
+	if (cipher_mechs[cipher] == 0)
+		return FALSE;
 
-static gboolean initialized = FALSE;
+	NM_SET_OUT (out_cipher_mech, cipher_mechs[cipher]);
+	NM_SET_OUT (out_real_iv_len, nm_crypto_cipher_get_info (cipher)->real_iv_len);
+	return TRUE;
+}
+
+/*****************************************************************************/
 
 gboolean
-crypto_init (GError **error)
+_nm_crypto_init (GError **error)
 {
+	static gboolean initialized = FALSE;
+
 	if (initialized)
 		return TRUE;
 
-	if (gnutls_global_init() != 0) {
-		gnutls_global_deinit();
+	if (gnutls_global_init () != 0) {
+		gnutls_global_deinit ();
 		g_set_error_literal (error, NM_CRYPTO_ERROR,
 		                     NM_CRYPTO_ERROR_FAILED,
 		                     _("Failed to initialize the crypto engine."));
@@ -53,55 +80,47 @@ crypto_init (GError **error)
 	return TRUE;
 }
 
-char *
-crypto_decrypt (const char *cipher,
-                int key_type,
-                const guint8 *data,
-                gsize data_len,
-                const char *iv,
-                const gsize iv_len,
-                const char *key,
-                const gsize key_len,
-                gsize *out_len,
-                GError **error)
+/*****************************************************************************/
+
+guint8 *
+_nmtst_crypto_decrypt (NMCryptoCipherType cipher,
+                       const guint8 *data,
+                       gsize data_len,
+                       const guint8 *iv,
+                       gsize iv_len,
+                       const guint8 *key,
+                       gsize key_len,
+                       gsize *out_len,
+                       GError **error)
 {
 	gnutls_cipher_hd_t ctx;
 	gnutls_datum_t key_dt, iv_dt;
 	int err;
-	int cipher_mech, i;
-	char *output = NULL;
-	gboolean success = FALSE;
-	gsize pad_len, real_iv_len;
-
-	if (!crypto_init (error))
-		return NULL;
+	int cipher_mech;
+	nm_auto_clear_secret_ptr NMSecretPtr output = { 0 };
+	guint8 pad_i, pad_len;
+	guint8 real_iv_len;
 
-	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) {
-		cipher_mech = GNUTLS_CIPHER_3DES_CBC;
-		real_iv_len = SALT_LEN;
-	} else if (!strcmp (cipher, CIPHER_DES_CBC)) {
-		cipher_mech = GNUTLS_CIPHER_DES_CBC;
-		real_iv_len = SALT_LEN;
-	} else if (!strcmp (cipher, CIPHER_AES_CBC)) {
-		cipher_mech = GNUTLS_CIPHER_AES_128_CBC;
-		real_iv_len = 16;
-	} else {
+	if (!_get_cipher_info (cipher, &cipher_mech, &real_iv_len)) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
-		             _("Private key cipher '%s' was unknown."),
-		             cipher);
+		             _("Unsupported key cipher for decryption"));
 		return NULL;
 	}
 
+	if (!_nm_crypto_init (error))
+		return NULL;
+
 	if (iv_len < real_iv_len) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("Invalid IV length (must be at least %zd)."),
-		             real_iv_len);
+		             _("Invalid IV length (must be at least %u)."),
+		             (guint) real_iv_len);
 		return NULL;
 	}
 
-	output = g_malloc0 (data_len);
+	output.len = data_len;
+	output.bin = g_malloc (data_len);
 
 	key_dt.data = (unsigned char *) key;
 	key_dt.size = key_len;
@@ -114,103 +133,81 @@ crypto_decrypt (const char *cipher,
 		             NM_CRYPTO_ERROR_DECRYPTION_FAILED,
 		             _("Failed to initialize the decryption cipher context: %s (%s)"),
 		             gnutls_strerror_name (err), gnutls_strerror (err));
-		goto out;
+		return NULL;
 	}
 
-	err = gnutls_cipher_decrypt2 (ctx, data, data_len, output, data_len);
+	err = gnutls_cipher_decrypt2 (ctx, data, data_len, output.bin, output.len);
+
+	gnutls_cipher_deinit (ctx);
+
 	if (err < 0) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_DECRYPTION_FAILED,
 		             _("Failed to decrypt the private key: %s (%s)"),
 		             gnutls_strerror_name (err), gnutls_strerror (err));
-		goto out;
+		return NULL;
 	}
-	pad_len = output[data_len - 1];
+
+	pad_len = output.len > 0
+	          ? output.bin[output.len - 1]
+	          : 0;
 
 	/* Check if the padding at the end of the decrypted data is valid */
-	if (pad_len == 0 || pad_len > real_iv_len) {
+	if (   pad_len == 0
+	    || pad_len > real_iv_len) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_DECRYPTION_FAILED,
 		             _("Failed to decrypt the private key: unexpected padding length."));
-		goto out;
+		return NULL;
 	}
 
 	/* Validate tail padding; last byte is the padding size, and all pad bytes
 	 * should contain the padding size.
 	 */
-	for (i = 1; i <= pad_len; ++i) {
-		if (output[data_len - i] != pad_len) {
+	for (pad_i = 1; pad_i <= pad_len; ++pad_i) {
+		if (output.bin[data_len - pad_i] != pad_len) {
 			g_set_error (error, NM_CRYPTO_ERROR,
 			             NM_CRYPTO_ERROR_DECRYPTION_FAILED,
 			             _("Failed to decrypt the private key."));
-			goto out;
+			return NULL;
 		}
 	}
 
-	*out_len = data_len - pad_len;
-	success = TRUE;
-
-out:
-	if (!success) {
-		if (output) {
-			/* Don't expose key material */
-			memset (output, 0, data_len);
-			g_free (output);
-			output = NULL;
-		}
-	}
-	gnutls_cipher_deinit (ctx);
-	return output;
+	*out_len = output.len - pad_len;
+	return g_steal_pointer (&output.bin);
 }
 
-char *
-crypto_encrypt (const char *cipher,
-                const guint8 *data,
-                gsize data_len,
-                const char *iv,
-                const gsize iv_len,
-                const char *key,
-                gsize key_len,
-                gsize *out_len,
-                GError **error)
+guint8 *
+_nmtst_crypto_encrypt (NMCryptoCipherType cipher,
+                       const guint8 *data,
+                       gsize data_len,
+                       const guint8 *iv,
+                       gsize iv_len,
+                       const guint8 *key,
+                       gsize key_len,
+                       gsize *out_len,
+                       GError **error)
 {
 	gnutls_cipher_hd_t ctx;
 	gnutls_datum_t key_dt, iv_dt;
 	int err;
 	int cipher_mech;
-	char *output = NULL;
-	gboolean success = FALSE;
-	gsize padded_buf_len, pad_len, output_len;
-	char *padded_buf = NULL;
-	guint32 i;
+	nm_auto_clear_secret_ptr NMSecretPtr output = { 0 };
+	nm_auto_clear_secret_ptr NMSecretPtr padded_buf = { 0 };
+	gsize i, pad_len;
 
-	if (!crypto_init (error))
-		return NULL;
+	nm_assert (iv_len);
 
-	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
-		cipher_mech = GNUTLS_CIPHER_3DES_CBC;
-	else if (!strcmp (cipher, CIPHER_AES_CBC))
-		cipher_mech = GNUTLS_CIPHER_AES_128_CBC;
-	else {
+	if (   cipher == NM_CRYPTO_CIPHER_DES_CBC
+	    || !_get_cipher_info (cipher, &cipher_mech, NULL)) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
-		             _("Private key cipher '%s' was unknown."),
-		             cipher);
+		             _("Unsupported key cipher for encryption"));
 		return NULL;
 	}
 
-	/* If data_len % ivlen == 0, then we add another complete block
-	 * onto the end so that the decrypter knows there's padding.
-	 */
-	pad_len = iv_len - (data_len % iv_len);
-	output_len = padded_buf_len = data_len + pad_len;
-	padded_buf = g_malloc0 (padded_buf_len);
-
-	memcpy (padded_buf, data, data_len);
-	for (i = 0; i < pad_len; i++)
-		padded_buf[data_len + i] = (guint8) (pad_len & 0xFF);
-
-	output = g_malloc0 (output_len);
+	if (!_nm_crypto_init (error))
+		return NULL;
 
 	key_dt.data = (unsigned char *) key;
 	key_dt.size = key_len;
@@ -223,51 +220,50 @@ crypto_encrypt (const char *cipher,
 		             NM_CRYPTO_ERROR_ENCRYPTION_FAILED,
 		             _("Failed to initialize the encryption cipher context: %s (%s)"),
 		             gnutls_strerror_name (err), gnutls_strerror (err));
-		goto out;
+		return NULL;
 	}
 
-	err = gnutls_cipher_encrypt2 (ctx, padded_buf, padded_buf_len, output, output_len);
+	/* If data_len % ivlen == 0, then we add another complete block
+	 * onto the end so that the decrypter knows there's padding.
+	 */
+	pad_len = iv_len - (data_len % iv_len);
+
+	padded_buf.len = data_len + pad_len;
+	padded_buf.bin = g_malloc (padded_buf.len);
+	memcpy (padded_buf.bin, data, data_len);
+	for (i = 0; i < pad_len; i++)
+		padded_buf.bin[data_len + i] = (guint8) (pad_len & 0xFF);
+
+	output.len = padded_buf.len;
+	output.bin = g_malloc (output.len);
+
+	err = gnutls_cipher_encrypt2 (ctx, padded_buf.bin, padded_buf.len, output.bin, output.len);
+
+	gnutls_cipher_deinit (ctx);
+
 	if (err < 0) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_ENCRYPTION_FAILED,
 		             _("Failed to encrypt the data: %s (%s)"),
 		             gnutls_strerror_name (err), gnutls_strerror (err));
-		goto out;
-	}
-
-	*out_len = output_len;
-	success = TRUE;
-
-out:
-	if (padded_buf) {
-		memset (padded_buf, 0, padded_buf_len);
-		g_free (padded_buf);
-		padded_buf = NULL;
+		return NULL;
 	}
 
-	if (!success) {
-		if (output) {
-			/* Don't expose key material */
-			memset (output, 0, output_len);
-			g_free (output);
-			output = NULL;
-		}
-	}
-	gnutls_cipher_deinit (ctx);
-	return output;
+	*out_len = output.len;
+	return g_steal_pointer (&output.bin);
 }
 
-NMCryptoFileFormat
-crypto_verify_cert (const unsigned char *data,
-                    gsize len,
-                    GError **error)
+gboolean
+_nm_crypto_verify_x509 (const guint8 *data,
+                        gsize len,
+                        GError **error)
 {
 	gnutls_x509_crt_t der;
 	gnutls_datum_t dt;
 	int err;
 
-	if (!crypto_init (error))
-		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	if (!_nm_crypto_init (error))
+		return FALSE;
 
 	err = gnutls_x509_crt_init (&der);
 	if (err < 0) {
@@ -275,7 +271,7 @@ crypto_verify_cert (const unsigned char *data,
 		             NM_CRYPTO_ERROR_INVALID_DATA,
 		             _("Error initializing certificate data: %s"),
 		             gnutls_strerror (err));
-		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+		return FALSE;
 	}
 
 	/* Try DER first */
@@ -284,36 +280,35 @@ crypto_verify_cert (const unsigned char *data,
 	err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_DER);
 	if (err == GNUTLS_E_SUCCESS) {
 		gnutls_x509_crt_deinit (der);
-		return NM_CRYPTO_FILE_FORMAT_X509;
+		return TRUE;
 	}
 
 	/* And PEM next */
 	err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_PEM);
 	gnutls_x509_crt_deinit (der);
 	if (err == GNUTLS_E_SUCCESS)
-		return NM_CRYPTO_FILE_FORMAT_X509;
+		return TRUE;
 
 	g_set_error (error, NM_CRYPTO_ERROR,
 	             NM_CRYPTO_ERROR_INVALID_DATA,
 	             _("Couldn't decode certificate: %s"),
 	             gnutls_strerror (err));
-	return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	return FALSE;
 }
 
 gboolean
-crypto_verify_pkcs12 (const guint8 *data,
-                      gsize data_len,
-                      const char *password,
-                      GError **error)
+_nm_crypto_verify_pkcs12 (const guint8 *data,
+                          gsize data_len,
+                          const char *password,
+                          GError **error)
 {
 	gnutls_pkcs12_t p12;
 	gnutls_datum_t dt;
-	gboolean success = FALSE;
 	int err;
 
 	g_return_val_if_fail (data != NULL, FALSE);
 
-	if (!crypto_init (error))
+	if (!_nm_crypto_init (error))
 		return FALSE;
 
 	dt.data = (unsigned char *) data;
@@ -338,31 +333,32 @@ crypto_verify_pkcs12 (const guint8 *data,
 			             NM_CRYPTO_ERROR_INVALID_DATA,
 			             _("Couldn't decode PKCS#12 file: %s"),
 			             gnutls_strerror (err));
-			goto out;
+			gnutls_pkcs12_deinit (p12);
+			return FALSE;
 		}
 	}
 
 	err = gnutls_pkcs12_verify_mac (p12, password);
-	if (err == GNUTLS_E_SUCCESS)
-		success = TRUE;
-	else {
+
+	gnutls_pkcs12_deinit (p12);
+
+	if (err != GNUTLS_E_SUCCESS) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_DECRYPTION_FAILED,
 		             _("Couldn't verify PKCS#12 file: %s"),
 		             gnutls_strerror (err));
+		return FALSE;
 	}
 
-out:
-	gnutls_pkcs12_deinit (p12);
-	return success;
+	return TRUE;
 }
 
 gboolean
-crypto_verify_pkcs8 (const guint8 *data,
-                     gsize data_len,
-                     gboolean is_encrypted,
-                     const char *password,
-                     GError **error)
+_nm_crypto_verify_pkcs8 (const guint8 *data,
+                         gsize data_len,
+                         gboolean is_encrypted,
+                         const char *password,
+                         GError **error)
 {
 	gnutls_x509_privkey_t p8;
 	gnutls_datum_t dt;
@@ -370,12 +366,9 @@ crypto_verify_pkcs8 (const guint8 *data,
 
 	g_return_val_if_fail (data != NULL, FALSE);
 
-	if (!crypto_init (error))
+	if (!_nm_crypto_init (error))
 		return FALSE;
 
-	dt.data = (unsigned char *) data;
-	dt.size = data_len;
-
 	err = gnutls_x509_privkey_init (&p8);
 	if (err < 0) {
 		g_set_error (error, NM_CRYPTO_ERROR,
@@ -385,11 +378,15 @@ crypto_verify_pkcs8 (const guint8 *data,
 		return FALSE;
 	}
 
+	dt.data = (unsigned char *) data;
+	dt.size = data_len;
+
 	err = gnutls_x509_privkey_import_pkcs8 (p8,
 	                                        &dt,
 	                                        GNUTLS_X509_FMT_DER,
 	                                        is_encrypted ? password : NULL,
 	                                        is_encrypted ? 0 : GNUTLS_PKCS_PLAIN);
+
 	gnutls_x509_privkey_deinit (p8);
 
 	if (err < 0) {
@@ -413,9 +410,9 @@ crypto_verify_pkcs8 (const guint8 *data,
 }
 
 gboolean
-crypto_randomize (void *buffer, gsize buffer_len, GError **error)
+_nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error)
 {
-	if (!crypto_init (error))
+	if (!_nm_crypto_init (error))
 		return FALSE;
 
 	gnutls_rnd (GNUTLS_RND_RANDOM, buffer, buffer_len);
diff --git a/libnm-core/nm-crypto-impl.h b/libnm-core/nm-crypto-impl.h
new file mode 100644
index 00000000..91865152
--- /dev/null
+++ b/libnm-core/nm-crypto-impl.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * 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 2007 - 2018 Red Hat, Inc.
+ */
+
+#ifndef __NM_CRYPTO_IMPL_H__
+#define __NM_CRYPTO_IMPL_H__
+
+#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
+#error Cannot use this header.
+#endif
+
+#include "nm-crypto.h"
+
+gboolean _nm_crypto_init (GError **error);
+
+gboolean _nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error);
+
+gboolean _nm_crypto_verify_x509 (const guint8 *data,
+                                 gsize len,
+                                 GError **error);
+
+gboolean _nm_crypto_verify_pkcs12 (const guint8 *data,
+                                   gsize data_len,
+                                   const char *password,
+                                   GError **error);
+
+gboolean _nm_crypto_verify_pkcs8 (const guint8 *data,
+                                  gsize data_len,
+                                  gboolean is_encrypted,
+                                  const char *password,
+                                  GError **error);
+
+/*****************************************************************************/
+
+guint8 *_nmtst_crypto_encrypt (NMCryptoCipherType cipher,
+                               const guint8 *data,
+                               gsize data_len,
+                               const guint8 *iv,
+                               gsize iv_len,
+                               const guint8 *key,
+                               gsize key_len,
+                               gsize *out_len,
+                               GError **error);
+
+guint8 *_nmtst_crypto_decrypt (NMCryptoCipherType cipher,
+                               const guint8 *data,
+                               gsize data_len,
+                               const guint8 *iv,
+                               gsize iv_len,
+                               const guint8 *key,
+                               gsize key_len,
+                               gsize *out_len,
+                               GError **error);
+
+#endif  /* __NM_CRYPTO_IMPL_H__ */
diff --git a/libnm-core/crypto_nss.c b/libnm-core/nm-crypto-nss.c
index 56e91e26..711dde4b 100644
--- a/libnm-core/crypto_nss.c
+++ b/libnm-core/nm-crypto-nss.c
@@ -23,6 +23,8 @@
 
 #include "nm-default.h"
 
+#include "nm-crypto-impl.h"
+
 #include <prinit.h>
 #include <nss.h>
 #include <pk11pub.h>
@@ -33,20 +35,46 @@
 #include <ciferfam.h>
 #include <p12plcy.h>
 
-#include "crypto.h"
+#include "nm-utils/nm-secret-utils.h"
 #include "nm-errors.h"
 
-static gboolean initialized = FALSE;
+/*****************************************************************************/
+
+static gboolean
+_get_cipher_info (NMCryptoCipherType cipher,
+                  CK_MECHANISM_TYPE *out_cipher_mech,
+                  guint8 *out_real_iv_len)
+{
+	static const CK_MECHANISM_TYPE cipher_mechs[] = {
+		[NM_CRYPTO_CIPHER_DES_EDE3_CBC] = CKM_DES3_CBC_PAD,
+		[NM_CRYPTO_CIPHER_DES_CBC]      = CKM_DES_CBC_PAD,
+		[NM_CRYPTO_CIPHER_AES_128_CBC]  = CKM_AES_CBC_PAD,
+		[NM_CRYPTO_CIPHER_AES_192_CBC]  = CKM_AES_CBC_PAD,
+		[NM_CRYPTO_CIPHER_AES_256_CBC]  = CKM_AES_CBC_PAD,
+	};
+
+	g_return_val_if_fail (_NM_INT_NOT_NEGATIVE (cipher) && (gsize) cipher < G_N_ELEMENTS (cipher_mechs), FALSE);
+
+	if (!cipher_mechs[cipher])
+		return FALSE;
+
+	NM_SET_OUT (out_cipher_mech, cipher_mechs[cipher]);
+	NM_SET_OUT (out_real_iv_len, nm_crypto_cipher_get_info (cipher)->real_iv_len);
+	return TRUE;
+}
+
+/*****************************************************************************/
 
 gboolean
-crypto_init (GError **error)
+_nm_crypto_init (GError **error)
 {
+	static gboolean initialized = FALSE;
 	SECStatus ret;
 
 	if (initialized)
 		return TRUE;
 
-	PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 1);
+	PR_Init (PR_USER_THREAD, PR_PRIORITY_NORMAL, 1);
 	ret = NSS_NoDB_Init (NULL);
 	if (ret != SECSuccess) {
 		g_set_error (error, NM_CRYPTO_ERROR,
@@ -57,72 +85,61 @@ crypto_init (GError **error)
 		return FALSE;
 	}
 
-	SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
-	SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
-	SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
-	SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
-	SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
-	SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
-	SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
+	SEC_PKCS12EnableCipher (PKCS12_RC4_40, 1);
+	SEC_PKCS12EnableCipher (PKCS12_RC4_128, 1);
+	SEC_PKCS12EnableCipher (PKCS12_RC2_CBC_40, 1);
+	SEC_PKCS12EnableCipher (PKCS12_RC2_CBC_128, 1);
+	SEC_PKCS12EnableCipher (PKCS12_DES_56, 1);
+	SEC_PKCS12EnableCipher (PKCS12_DES_EDE3_168, 1);
+	SEC_PKCS12SetPreferredCipher (PKCS12_DES_EDE3_168, 1);
 
 	initialized = TRUE;
 	return TRUE;
 }
 
-char *
-crypto_decrypt (const char *cipher,
-                int key_type,
-                const guint8 *data,
-                gsize data_len,
-                const char *iv,
-                const gsize iv_len,
-                const char *key,
-                const gsize key_len,
-                gsize *out_len,
-                GError **error)
+guint8 *
+_nmtst_crypto_decrypt (NMCryptoCipherType cipher,
+                       const guint8 *data,
+                       gsize data_len,
+                       const guint8 *iv,
+                       gsize iv_len,
+                       const guint8 *key,
+                       gsize key_len,
+                       gsize *out_len,
+                       GError **error)
 {
-	char *output = NULL;
-	int decrypted_len = 0;
 	CK_MECHANISM_TYPE cipher_mech;
 	PK11SlotInfo *slot = NULL;
 	SECItem key_item;
 	PK11SymKey *sym_key = NULL;
 	SECItem *sec_param = NULL;
 	PK11Context *ctx = NULL;
+	nm_auto_clear_secret_ptr NMSecretPtr output = { 0 };
 	SECStatus s;
 	gboolean success = FALSE;
-	unsigned pad_len = 0, extra = 0;
-	guint32 i, real_iv_len = 0;
-
-	if (!crypto_init (error))
-		return NULL;
+	int decrypted_len = 0;
+	unsigned extra = 0;
+	unsigned pad_len = 0;
+	guint32 i;
+	guint8 real_iv_len;
 
-	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) {
-		cipher_mech = CKM_DES3_CBC_PAD;
-		real_iv_len = 8;
-	} else if (!strcmp (cipher, CIPHER_DES_CBC)) {
-		cipher_mech = CKM_DES_CBC_PAD;
-		real_iv_len = 8;
-	} else if (!strcmp (cipher, CIPHER_AES_CBC)) {
-		cipher_mech = CKM_AES_CBC_PAD;
-		real_iv_len = 16;
-	} else {
+	if (!_get_cipher_info (cipher, &cipher_mech, &real_iv_len)) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
-		             _("Private key cipher '%s' was unknown."),
-		             cipher);
+		             _("Unsupported key cipher for decryption"));
 		return NULL;
 	}
 
 	if (iv_len < real_iv_len) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_INVALID_DATA,
-		             _("Invalid IV length (must be at least %d)."),
-		             real_iv_len);
+		             _("Invalid IV length (must be at least %u)."),
+		             (guint) real_iv_len);
 		return NULL;
 	}
 
-	output = g_malloc0 (data_len);
+	if (!_nm_crypto_init (error))
+		return NULL;
 
 	slot = PK11_GetBestSlot (cipher_mech, NULL);
 	if (!slot) {
@@ -160,10 +177,13 @@ crypto_decrypt (const char *cipher,
 		goto out;
 	}
 
+	output.len = data_len;
+	output.bin = g_malloc (data_len);
+
 	s = PK11_CipherOp (ctx,
-	                   (unsigned char *) output,
+	                   (unsigned char *) output.bin,
 	                   &decrypted_len,
-	                   data_len,
+	                   output.len,
 	                   data,
 	                   data_len);
 	if (s != SECSuccess) {
@@ -182,7 +202,7 @@ crypto_decrypt (const char *cipher,
 	}
 
 	s = PK11_DigestFinal (ctx,
-	                      (unsigned char *) (output + decrypted_len),
+	                      (unsigned char *) &output.bin[decrypted_len],
 	                      &extra,
 	                      data_len - decrypted_len);
 	if (s != SECSuccess) {
@@ -192,6 +212,7 @@ crypto_decrypt (const char *cipher,
 		             PORT_GetError ());
 		goto out;
 	}
+
 	decrypted_len += extra;
 	pad_len = data_len - decrypted_len;
 
@@ -207,7 +228,7 @@ crypto_decrypt (const char *cipher,
 	 * should contain the padding size.
 	 */
 	for (i = pad_len; i > 0; i--) {
-		if (output[data_len - i] != pad_len) {
+		if (output.bin[data_len - i] != pad_len) {
 			g_set_error (error, NM_CRYPTO_ERROR,
 			             NM_CRYPTO_ERROR_DECRYPTION_FAILED,
 			             _("Failed to decrypt the private key."));
@@ -215,7 +236,6 @@ crypto_decrypt (const char *cipher,
 		}
 	}
 
-	*out_len = decrypted_len;
 	success = TRUE;
 
 out:
@@ -228,27 +248,25 @@ out:
 	if (slot)
 		PK11_FreeSlot (slot);
 
-	if (!success) {
-		if (output) {
-			/* Don't expose key material */
-			memset (output, 0, data_len);
-			g_free (output);
-			output = NULL;
-		}
-	}
-	return output;
+	if (!success)
+		return NULL;
+
+	if (decrypted_len < output.len)
+		nm_explicit_bzero (&output.bin[decrypted_len], output.len - decrypted_len);
+	*out_len = decrypted_len;
+	return g_steal_pointer (&output.bin);
 }
 
-char *
-crypto_encrypt (const char *cipher,
-                const guint8 *data,
-                gsize data_len,
-                const char *iv,
-                gsize iv_len,
-                const char *key,
-                gsize key_len,
-                gsize *out_len,
-                GError **error)
+guint8 *
+_nmtst_crypto_encrypt (NMCryptoCipherType cipher,
+                       const guint8 *data,
+                       gsize data_len,
+                       const guint8 *iv,
+                       gsize iv_len,
+                       const guint8 *key,
+                       gsize key_len,
+                       gsize *out_len,
+                       GError **error)
 {
 	SECStatus ret;
 	CK_MECHANISM_TYPE cipher_mech = CKM_DES3_CBC_PAD;
@@ -258,46 +276,29 @@ crypto_encrypt (const char *cipher,
 	PK11SymKey *sym_key = NULL;
 	SECItem *sec_param = NULL;
 	PK11Context *ctx = NULL;
-	unsigned char *output, *padded_buf;
-	gsize output_len;
+	nm_auto_clear_secret_ptr NMSecretPtr padded_buf = { 0 };
+	nm_auto_clear_secret_ptr NMSecretPtr output = { 0 };
 	int encrypted_len = 0, i;
 	gboolean success = FALSE;
-	gsize padded_buf_len, pad_len;
-
-	if (!crypto_init (error))
-		return NULL;
+	gsize pad_len;
 
-	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
-		cipher_mech = CKM_DES3_CBC_PAD;
-	else if (!strcmp (cipher, CIPHER_AES_CBC))
-		cipher_mech = CKM_AES_CBC_PAD;
-	else {
+	if (   cipher == NM_CRYPTO_CIPHER_DES_CBC
+	    || !_get_cipher_info (cipher, &cipher_mech, NULL)) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_UNKNOWN_CIPHER,
-		             _("Private key cipher '%s' was unknown."),
-		             cipher);
+		             _("Unsupported key cipher for encryption"));
 		return NULL;
 	}
 
-	/* If data->len % ivlen == 0, then we add another complete block
-	 * onto the end so that the decrypter knows there's padding.
-	 */
-	pad_len = iv_len - (data_len % iv_len);
-	output_len = padded_buf_len = data_len + pad_len;
-	padded_buf = g_malloc0 (padded_buf_len);
-
-	memcpy (padded_buf, data, data_len);
-	for (i = 0; i < pad_len; i++)
-		padded_buf[data_len + i] = (guint8) (pad_len & 0xFF);
-
-	output = g_malloc0 (output_len);
+	if (!_nm_crypto_init (error))
+		return NULL;
 
 	slot = PK11_GetBestSlot (cipher_mech, NULL);
 	if (!slot) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_FAILED,
 		             _("Failed to initialize the encryption cipher slot."));
-		goto out;
+		return NULL;
 	}
 
 	sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL);
@@ -324,7 +325,22 @@ crypto_encrypt (const char *cipher,
 		goto out;
 	}
 
-	ret = PK11_CipherOp (ctx, output, &encrypted_len, output_len, padded_buf, padded_buf_len);
+	/* If data->len % ivlen == 0, then we add another complete block
+	 * onto the end so that the decrypter knows there's padding.
+	 */
+	pad_len = iv_len - (data_len % iv_len);
+
+	padded_buf.len = data_len + pad_len;
+	padded_buf.bin = g_malloc (padded_buf.len);
+
+	memcpy (padded_buf.bin, data, data_len);
+	for (i = 0; i < pad_len; i++)
+		padded_buf.bin[data_len + i] = (guint8) (pad_len & 0xFF);
+
+	output.len = padded_buf.len;
+	output.bin = g_malloc (output.len);
+
+	ret = PK11_CipherOp (ctx, output.bin, &encrypted_len, output.len, padded_buf.bin, padded_buf.len);
 	if (ret != SECSuccess) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_ENCRYPTION_FAILED,
@@ -333,46 +349,41 @@ crypto_encrypt (const char *cipher,
 		goto out;
 	}
 
-	if (encrypted_len != output_len) {
+	if (encrypted_len != output.len) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_ENCRYPTION_FAILED,
 		             _("Unexpected amount of data after encrypting."));
 		goto out;
 	}
 
-	*out_len = encrypted_len;
 	success = TRUE;
 
 out:
 	if (ctx)
 		PK11_DestroyContext (ctx, PR_TRUE);
-	if (sym_key)
-		PK11_FreeSymKey (sym_key);
 	if (sec_param)
 		SECITEM_FreeItem (sec_param, PR_TRUE);
+	if (sym_key)
+		PK11_FreeSymKey (sym_key);
 	if (slot)
 		PK11_FreeSlot (slot);
 
-	memset (padded_buf, 0, padded_buf_len);
-	g_free (padded_buf);
+	if (!success)
+		return NULL;
 
-	if (!success) {
-		memset (output, 0, output_len);
-		g_free (output);
-		output = NULL;
-	}
-	return (char *) output;
+	*out_len = output.len;
+	return g_steal_pointer (&output.bin);
 }
 
-NMCryptoFileFormat
-crypto_verify_cert (const unsigned char *data,
-                    gsize len,
-                    GError **error)
+gboolean
+_nm_crypto_verify_x509 (const guint8 *data,
+                        gsize len,
+                        GError **error)
 {
 	CERTCertificate *cert;
 
-	if (!crypto_init (error))
-		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	if (!_nm_crypto_init (error))
+		return FALSE;
 
 	/* Try DER/PEM first */
 	cert = CERT_DecodeCertFromPackage ((char *) data, len);
@@ -380,76 +391,85 @@ crypto_verify_cert (const unsigned char *data,
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_INVALID_DATA,
 		             _("Couldn't decode certificate: %d"),
-		             PORT_GetError());
-		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+		             PORT_GetError ());
+		return FALSE;
 	}
 
 	CERT_DestroyCertificate (cert);
-	return NM_CRYPTO_FILE_FORMAT_X509;
+	return TRUE;
 }
 
 gboolean
-crypto_verify_pkcs12 (const guint8 *data,
-                      gsize data_len,
-                      const char *password,
-                      GError **error)
+_nm_crypto_verify_pkcs12 (const guint8 *data,
+                          gsize data_len,
+                          const char *password,
+                          GError **error)
 {
 	SEC_PKCS12DecoderContext *p12ctx = NULL;
 	SECItem pw = { 0 };
 	PK11SlotInfo *slot = NULL;
 	SECStatus s;
-	gunichar2 *ucs2_password;
-	glong ucs2_chars = 0;
-#ifndef WORDS_BIGENDIAN
-	guint16 *p;
-#endif /* WORDS_BIGENDIAN */
+	gboolean success = FALSE;
 
-	if (error)
-		g_return_val_if_fail (*error == NULL, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
 
-	if (!crypto_init (error))
+	if (!_nm_crypto_init (error))
 		return FALSE;
 
 	/* PKCS#12 passwords are apparently UCS2 BIG ENDIAN, and NSS doesn't do
 	 * any conversions for us.
 	 */
 	if (password && *password) {
-		if (!g_utf8_validate (password, -1, NULL)) {
+		nm_auto_clear_secret_ptr NMSecretPtr ucs2_password = { 0 };
+
+		if (g_utf8_validate (password, -1, NULL)) {
+			long ucs2_chars;
+
+			ucs2_password.bin = (guint8 *) g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL);
+
+			/* cannot fail, because password is valid UTF-8*/
+			nm_assert (ucs2_password.bin && ucs2_chars > 0);
+
+			ucs2_password.len = ucs2_chars * 2;
+		}
+
+		if (!ucs2_password.bin || ucs2_password.len == 0) {
 			g_set_error (error, NM_CRYPTO_ERROR,
 			             NM_CRYPTO_ERROR_INVALID_PASSWORD,
 			             _("Password must be UTF-8"));
 			return FALSE;
 		}
-		ucs2_password = g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL);
-		/* Can't fail if g_utf8_validate() succeeded */
-		g_return_val_if_fail (ucs2_password != NULL && ucs2_chars != 0, FALSE);
-
-		ucs2_chars *= 2;  /* convert # UCS2 characters -> bytes */
-		pw.data = PORT_ZAlloc(ucs2_chars + 2);
-		memcpy (pw.data, ucs2_password, ucs2_chars);
-		pw.len = ucs2_chars + 2;  /* include terminating NULL */
-
-		memset (ucs2_password, 0, ucs2_chars);
-		g_free (ucs2_password);
-
-#ifndef WORDS_BIGENDIAN
-		for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++)
-			*p = GUINT16_SWAP_LE_BE (*p);
-#endif /* WORDS_BIGENDIAN */
-	} else {
-		/* NULL password */
-		pw.data = NULL;
-		pw.len = 0;
+
+		pw.data = PORT_ZAlloc (ucs2_password.len + 2);
+		memcpy (pw.data, ucs2_password.bin, ucs2_password.len);
+		pw.len = ucs2_password.len + 2;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		{
+			guint16 *p, *p_end;
+
+			p_end = (guint16 *) &(((guint8 *) pw.data)[ucs2_password.len]);
+			for (p = (guint16 *) pw.data; p < p_end; p++)
+				*p = GUINT16_SWAP_LE_BE (*p);
+		}
+#endif
+	}
+
+	slot = PK11_GetInternalKeySlot ();
+	if (!slot) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_FAILED,
+		             _("Couldn't initialize slot"));
+		goto out;
 	}
 
-	slot = PK11_GetInternalKeySlot();
 	p12ctx = SEC_PKCS12DecoderStart (&pw, slot, NULL, NULL, NULL, NULL, NULL, NULL);
 	if (!p12ctx) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_FAILED,
 		             _("Couldn't initialize PKCS#12 decoder: %d"),
-		             PORT_GetError());
-		goto error;
+		             PORT_GetError ());
+		goto out;
 	}
 
 	s = SEC_PKCS12DecoderUpdate (p12ctx, (guint8 *)data, data_len);
@@ -457,8 +477,8 @@ crypto_verify_pkcs12 (const guint8 *data,
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_INVALID_DATA,
 		             _("Couldn't decode PKCS#12 file: %d"),
-		             PORT_GetError());
-		goto error;
+		             PORT_GetError ());
+		goto out;
 	}
 
 	s = SEC_PKCS12DecoderVerify (p12ctx);
@@ -466,35 +486,34 @@ crypto_verify_pkcs12 (const guint8 *data,
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERROR_DECRYPTION_FAILED,
 		             _("Couldn't verify PKCS#12 file: %d"),
-		             PORT_GetError());
-		goto error;
+		             PORT_GetError ());
+		goto out;
 	}
 
-	SEC_PKCS12DecoderFinish (p12ctx);
-	SECITEM_ZfreeItem (&pw, PR_FALSE);
-	return TRUE;
+	success = TRUE;
 
-error:
+out:
 	if (p12ctx)
 		SEC_PKCS12DecoderFinish (p12ctx);
-
 	if (slot)
-		PK11_FreeSlot(slot);
+		PK11_FreeSlot (slot);
+
+	if (pw.data)
+		SECITEM_ZfreeItem (&pw, PR_FALSE);
 
-	SECITEM_ZfreeItem (&pw, PR_FALSE);
-	return FALSE;
+	return success;
 }
 
 gboolean
-crypto_verify_pkcs8 (const guint8 *data,
-                     gsize data_len,
-                     gboolean is_encrypted,
-                     const char *password,
-                     GError **error)
+_nm_crypto_verify_pkcs8 (const guint8 *data,
+                         gsize data_len,
+                         gboolean is_encrypted,
+                         const char *password,
+                         GError **error)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
 
-	if (!crypto_init (error))
+	if (!_nm_crypto_init (error))
 		return FALSE;
 
 	/* NSS apparently doesn't do PKCS#8 natively, but you have to put the
@@ -505,11 +524,11 @@ crypto_verify_pkcs8 (const guint8 *data,
 }
 
 gboolean
-crypto_randomize (void *buffer, gsize buffer_len, GError **error)
+_nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error)
 {
 	SECStatus s;
 
-	if (!crypto_init (error))
+	if (!_nm_crypto_init (error))
 		return FALSE;
 
 	s = PK11_GenerateRandom (buffer, buffer_len);
diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c
new file mode 100644
index 00000000..7a6e8d18
--- /dev/null
+++ b/libnm-core/nm-crypto.c
@@ -0,0 +1,1035 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * 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 2007 - 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-crypto.h"
+
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "nm-utils/nm-secret-utils.h"
+#include "nm-utils/nm-io-utils.h"
+
+#include "nm-crypto-impl.h"
+#include "nm-utils.h"
+#include "nm-errors.h"
+
+#define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
+#define PEM_RSA_KEY_END   "-----END RSA PRIVATE KEY-----"
+
+#define PEM_DSA_KEY_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
+#define PEM_DSA_KEY_END   "-----END DSA PRIVATE KEY-----"
+
+#define PEM_CERT_BEGIN    "-----BEGIN CERTIFICATE-----"
+#define PEM_CERT_END      "-----END CERTIFICATE-----"
+
+#define PEM_PKCS8_ENC_KEY_BEGIN "-----BEGIN ENCRYPTED PRIVATE KEY-----"
+#define PEM_PKCS8_ENC_KEY_END   "-----END ENCRYPTED PRIVATE KEY-----"
+
+#define PEM_PKCS8_DEC_KEY_BEGIN "-----BEGIN PRIVATE KEY-----"
+#define PEM_PKCS8_DEC_KEY_END   "-----END PRIVATE KEY-----"
+
+/*****************************************************************************/
+
+static const NMCryptoCipherInfo cipher_infos[] = {
+#define _CI(_cipher, _name, _digest_len, _real_iv_len) \
+	[(_cipher) - 1] = { .cipher = _cipher, .name = ""_name"", .digest_len = _digest_len, .real_iv_len = _real_iv_len }
+	_CI (NM_CRYPTO_CIPHER_DES_EDE3_CBC, "DES-EDE3-CBC", 24,  8),
+	_CI (NM_CRYPTO_CIPHER_DES_CBC,      "DES-CBC",       8,  8),
+	_CI (NM_CRYPTO_CIPHER_AES_128_CBC,  "AES-128-CBC",  16, 16),
+	_CI (NM_CRYPTO_CIPHER_AES_192_CBC,  "AES-192-CBC",  24, 16),
+	_CI (NM_CRYPTO_CIPHER_AES_256_CBC,  "AES-256-CBC",  32, 16),
+};
+
+const NMCryptoCipherInfo *
+nm_crypto_cipher_get_info (NMCryptoCipherType cipher)
+{
+	g_return_val_if_fail (cipher > NM_CRYPTO_CIPHER_UNKNOWN && (gsize) cipher < G_N_ELEMENTS (cipher_infos) + 1, NULL);
+
+#if NM_MORE_ASSERTS > 10
+	{
+		int i, j;
+
+		for (i = 0; i < (int) G_N_ELEMENTS (cipher_infos); i++) {
+			const NMCryptoCipherInfo *info = &cipher_infos[i];
+
+			nm_assert (info->cipher == (NMCryptoCipherType) (i + 1));
+			nm_assert (info->name && info->name[0]);
+			for (j = 0; j < i; j++)
+				nm_assert (g_ascii_strcasecmp (info->name, cipher_infos[j].name) != 0);
+		}
+	}
+#endif
+
+	return &cipher_infos[cipher - 1];
+}
+
+const NMCryptoCipherInfo *
+nm_crypto_cipher_get_info_by_name (const char *cipher_name, gssize p_len)
+{
+	int i;
+
+	nm_assert (nm_crypto_cipher_get_info (NM_CRYPTO_CIPHER_DES_CBC)->cipher == NM_CRYPTO_CIPHER_DES_CBC);
+
+	if (p_len < 0) {
+		if (!cipher_name)
+			return FALSE;
+		p_len = strlen (cipher_name);
+	}
+
+	for (i = 0; i < (int) G_N_ELEMENTS (cipher_infos); i++) {
+		const NMCryptoCipherInfo *info = &cipher_infos[i];
+
+		if (   (gsize) p_len == strlen (info->name)
+		    && g_ascii_strncasecmp (info->name, cipher_name, p_len) == 0)
+			return info;
+	}
+	return NULL;
+}
+
+/*****************************************************************************/
+
+static gboolean
+find_tag (const char *tag,
+          const guint8 *data,
+          gsize data_len,
+          gsize start_at,
+          gsize *out_pos)
+{
+	gsize i, taglen;
+	gsize len = data_len - start_at;
+
+	g_return_val_if_fail (out_pos != NULL, FALSE);
+
+	taglen = strlen (tag);
+	if (len >= taglen) {
+		for (i = 0; i < len - taglen + 1; i++) {
+			if (memcmp (data + start_at + i, tag, taglen) == 0) {
+				*out_pos = start_at + i;
+				return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
+
+#define DEK_INFO_TAG "DEK-Info: "
+#define PROC_TYPE_TAG "Proc-Type: "
+
+static char *
+_extract_line (const guint8 **p, const guint8 *p_end)
+{
+	const guint8 *x, *x0;
+
+	nm_assert (p);
+	nm_assert (p_end);
+	nm_assert (*p);
+	nm_assert (*p < p_end);
+
+	x = x0 = *p;
+	while (TRUE) {
+		if (x == p_end) {
+			*p = p_end;
+			break;
+		}
+		if (*x == '\0') {
+			/* the data contains embedded NUL. This is the end. */
+			*p = p_end;
+			break;
+		}
+		if (*x == '\n') {
+			*p = x + 1;
+			break;
+		}
+		x++;
+	}
+
+	if (x == x0)
+		return NULL;
+	return g_strndup ((char *) x0, x - x0);
+}
+
+static gboolean
+parse_old_openssl_key_file (const guint8 *data,
+                            gsize data_len,
+                            NMSecretPtr *out_parsed,
+                            NMCryptoKeyType *out_key_type,
+                            NMCryptoCipherType *out_cipher,
+                            char **out_iv,
+                            GError **error)
+{
+	gsize start = 0, end = 0;
+	nm_auto_free_secret char *str = NULL;
+	char *str_p;
+	gsize str_len;
+	int enc_tags = 0;
+	NMCryptoKeyType key_type;
+	nm_auto_clear_secret_ptr NMSecretPtr parsed = { 0 };
+	nm_auto_clear_secret_ptr NMSecretPtr data_content = { 0 };
+	nm_auto_free_secret char *iv = NULL;
+	NMCryptoCipherType cipher = NM_CRYPTO_CIPHER_UNKNOWN;
+	const char *start_tag;
+	const char *end_tag;
+	const guint8 *data_start, *data_end;
+
+	nm_assert (!out_parsed || (out_parsed->len == 0 && !out_parsed->bin));
+	nm_assert (!out_iv || !*out_iv);
+
+	NM_SET_OUT (out_key_type, NM_CRYPTO_KEY_TYPE_UNKNOWN);
+	NM_SET_OUT (out_cipher, NM_CRYPTO_CIPHER_UNKNOWN);
+
+	if (find_tag (PEM_RSA_KEY_BEGIN, data, data_len, 0, &start)) {
+		key_type = NM_CRYPTO_KEY_TYPE_RSA;
+		start_tag = PEM_RSA_KEY_BEGIN;
+		end_tag = PEM_RSA_KEY_END;
+	} else if (find_tag (PEM_DSA_KEY_BEGIN, data, data_len, 0, &start)) {
+		key_type = NM_CRYPTO_KEY_TYPE_DSA;
+		start_tag = PEM_DSA_KEY_BEGIN;
+		end_tag = PEM_DSA_KEY_END;
+	} else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("PEM key file had no start tag"));
+		return FALSE;
+	}
+
+	start += strlen (start_tag);
+	if (!find_tag (end_tag, data, data_len, start, &end)) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("PEM key file had no end tag '%s'."),
+		             end_tag);
+		return FALSE;
+	}
+
+	str_len = end - start + 1;
+	str = g_new (char, str_len);
+	str[0] = '\0';
+	str_p = str;
+
+	data_start = &data[start];
+	data_end = &data[end];
+
+	while (data_start < data_end) {
+		nm_auto_free_secret char *line = NULL;
+		char *p;
+
+		line = _extract_line (&data_start, data_end);
+		if (!line)
+			continue;
+
+		p = nm_secret_strchomp (nm_str_skip_leading_spaces (line));
+
+		if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) {
+			if (enc_tags++ != 0 || str_p != str) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERROR_INVALID_DATA,
+				             _("Malformed PEM file: Proc-Type was not first tag."));
+				return FALSE;
+			}
+
+			p += strlen (PROC_TYPE_TAG);
+			if (strcmp (p, "4,ENCRYPTED")) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERROR_INVALID_DATA,
+				             _("Malformed PEM file: unknown Proc-Type tag '%s'."),
+				             p);
+				return FALSE;
+			}
+		} else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) {
+			const NMCryptoCipherInfo *cipher_info;
+			char *comma;
+			gsize p_len;
+
+			if (enc_tags++ != 1 || str_p != str) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERROR_INVALID_DATA,
+				             _("Malformed PEM file: DEK-Info was not the second tag."));
+				return FALSE;
+			}
+
+			p += strlen (DEK_INFO_TAG);
+
+			/* Grab the IV first */
+			comma = strchr (p, ',');
+			if (!comma || (*(comma + 1) == '\0')) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERROR_INVALID_DATA,
+				             _("Malformed PEM file: no IV found in DEK-Info tag."));
+				return FALSE;
+			}
+			p_len = comma - p;
+			comma++;
+			if (!g_ascii_isxdigit (*comma)) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERROR_INVALID_DATA,
+				             _("Malformed PEM file: invalid format of IV in DEK-Info tag."));
+				return FALSE;
+			}
+			nm_free_secret (iv);
+			iv = g_strdup (comma);
+
+			/* Get the private key cipher */
+			cipher_info = nm_crypto_cipher_get_info_by_name (p, p_len);
+			if (!cipher_info) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERROR_INVALID_DATA,
+				             _("Malformed PEM file: unknown private key cipher '%s'."),
+				             p);
+				return FALSE;
+			}
+			cipher = cipher_info->cipher;
+		} else {
+			if (enc_tags == 1) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERROR_INVALID_DATA,
+				             "Malformed PEM file: both Proc-Type and DEK-Info tags are required.");
+				return FALSE;
+			}
+			nm_utils_strbuf_append_str (&str_p, &str_len, p);
+			nm_assert (str_len > 0);
+		}
+	}
+
+	parsed.bin = (guint8 *) g_base64_decode (str, &parsed.len);
+	if (!parsed.bin || parsed.len == 0) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("Could not decode private key."));
+		nm_secret_ptr_clear (&parsed);
+		return FALSE;
+	}
+
+	NM_SET_OUT (out_key_type, key_type);
+	NM_SET_OUT (out_iv, g_steal_pointer (&iv));
+	NM_SET_OUT (out_cipher, cipher);
+	nm_secret_ptr_move (out_parsed, &parsed);
+	return TRUE;
+}
+
+static gboolean
+parse_pkcs8_key_file (const guint8 *data,
+                      gsize data_len,
+                      NMSecretPtr *parsed,
+                      gboolean *out_encrypted,
+                      GError **error)
+{
+	gsize start = 0, end = 0;
+	gs_free guchar *der = NULL;
+	const char *start_tag = NULL, *end_tag = NULL;
+	gboolean encrypted = FALSE;
+	nm_auto_free_secret char *der_base64 = NULL;
+
+	nm_assert (parsed);
+	nm_assert (!parsed->bin);
+	nm_assert (parsed->len == 0);
+	nm_assert (out_encrypted);
+
+	/* Try encrypted first, decrypted next */
+	if (find_tag (PEM_PKCS8_ENC_KEY_BEGIN, data, data_len, 0, &start)) {
+		start_tag = PEM_PKCS8_ENC_KEY_BEGIN;
+		end_tag = PEM_PKCS8_ENC_KEY_END;
+		encrypted = TRUE;
+	} else if (find_tag (PEM_PKCS8_DEC_KEY_BEGIN, data, data_len, 0, &start)) {
+		start_tag = PEM_PKCS8_DEC_KEY_BEGIN;
+		end_tag = PEM_PKCS8_DEC_KEY_END;
+		encrypted = FALSE;
+	} else {
+		g_set_error_literal (error, NM_CRYPTO_ERROR,
+		                     NM_CRYPTO_ERROR_INVALID_DATA,
+		                     _("Failed to find expected PKCS#8 start tag."));
+		return FALSE;
+	}
+
+	start += strlen (start_tag);
+	if (!find_tag (end_tag, data, data_len, start, &end)) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("Failed to find expected PKCS#8 end tag '%s'."),
+		             end_tag);
+		return FALSE;
+	}
+
+	/* g_base64_decode() wants a NULL-terminated string */
+	der_base64 = g_strndup ((char *) &data[start], end - start);
+
+	parsed->bin = (guint8 *) g_base64_decode (der_base64, &parsed->len);
+	if (!parsed->bin || parsed->len == 0) {
+		g_set_error_literal (error, NM_CRYPTO_ERROR,
+		                     NM_CRYPTO_ERROR_INVALID_DATA,
+		                     _("Failed to decode PKCS#8 private key."));
+		nm_secret_ptr_clear (parsed);
+		return FALSE;
+	}
+
+	*out_encrypted = encrypted;
+	return TRUE;
+}
+
+static gboolean
+file_read_contents (const char *filename,
+                    NMSecretPtr *out_contents,
+                    GError **error)
+{
+	nm_assert (out_contents);
+	nm_assert (out_contents->len == 0);
+	nm_assert (!out_contents->str);
+
+	return nm_utils_file_get_contents (-1,
+	                                   filename,
+	                                   100*1024*1024,
+	                                   NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET,
+	                                   &out_contents->str,
+	                                   &out_contents->len,
+	                                   error) >= 0;
+}
+
+GBytes *
+nm_crypto_read_file (const char *filename,
+                     GError **error)
+{
+	nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 };
+
+	g_return_val_if_fail (filename, NULL);
+
+	if (!file_read_contents (filename, &contents, error))
+		return NULL;
+	return nm_secret_copy_to_gbytes (contents.bin, contents.len);
+}
+
+/*
+ * Convert a hex string into bytes.
+ */
+static guint8 *
+_nmtst_convert_iv (const char *src,
+                   gsize *out_len,
+                   GError **error)
+{
+	gsize i, num;
+	gs_free guint8 *c = NULL;
+	int c0, c1;
+
+	nm_assert (src);
+
+	num = strlen (src);
+	if (   num == 0
+	    || (num % 2) != 0) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("IV must be an even number of bytes in length."));
+		return NULL;
+	}
+
+	num /= 2;
+	c = g_malloc (num + 1);
+
+	/* defensively add trailing NUL. This function returns binary data,
+	 * do not assume it's NUL terminated. */
+	c[num] = '\0';
+
+	for (i = 0; i < num; i++) {
+		if (   ((c0 = nm_utils_hexchar_to_int (*(src++))) < 0)
+		    || ((c1 = nm_utils_hexchar_to_int (*(src++))) < 0)) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERROR_INVALID_DATA,
+			             _("IV contains non-hexadecimal digits."));
+			nm_explicit_bzero (c, i);
+			return FALSE;
+		}
+
+		c[i] = (c0 << 4) + c1;
+	}
+	*out_len = num;
+	return g_steal_pointer (&c);
+}
+
+guint8 *
+nmtst_crypto_make_des_aes_key (NMCryptoCipherType cipher,
+                               const guint8 *salt,
+                               gsize salt_len,
+                               const char *password,
+                               gsize *out_len,
+                               GError **error)
+{
+	guint8 *key;
+	const NMCryptoCipherInfo *cipher_info;
+
+	g_return_val_if_fail (salt != NULL, NULL);
+	g_return_val_if_fail (salt_len >= 8, NULL);
+	g_return_val_if_fail (password != NULL, NULL);
+	g_return_val_if_fail (out_len != NULL, NULL);
+
+	*out_len = 0;
+
+	cipher_info = nm_crypto_cipher_get_info (cipher);
+
+	g_return_val_if_fail (cipher_info, NULL);
+
+	if (password[0] == '\0')
+		return NULL;
+
+	key = g_malloc (cipher_info->digest_len);
+
+	nm_crypto_md5_hash (salt,
+	                    8,
+	                    (guint8 *) password,
+	                    strlen (password),
+	                    key,
+	                    cipher_info->digest_len);
+
+	*out_len = cipher_info->digest_len;
+	return key;
+}
+
+static gboolean
+_nmtst_decrypt_key (NMCryptoCipherType cipher,
+                    const guint8 *data,
+                    gsize data_len,
+                    const char *iv,
+                    const char *password,
+                    NMSecretPtr *parsed,
+                    GError **error)
+{
+	nm_auto_clear_secret_ptr NMSecretPtr bin_iv = { 0 };
+	nm_auto_clear_secret_ptr NMSecretPtr key = { 0 };
+	gs_free char *output = NULL;
+
+	nm_assert (password);
+	nm_assert (cipher != NM_CRYPTO_CIPHER_UNKNOWN);
+	nm_assert (iv);
+	nm_assert (parsed);
+	nm_assert (!parsed->bin);
+	nm_assert (parsed->len == 0);
+
+	bin_iv.bin = _nmtst_convert_iv (iv, &bin_iv.len, error);
+	if (!bin_iv.bin)
+		return FALSE;
+
+	if (bin_iv.len < 8) {
+		g_set_error (error,
+		             NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("IV must contain at least 8 characters"));
+		return FALSE;
+	}
+
+	/* Convert the password and IV into a DES or AES key */
+	key.bin = nmtst_crypto_make_des_aes_key (cipher, bin_iv.bin, bin_iv.len, password, &key.len, error);
+	if (!key.bin || !key.len)
+		return FALSE;
+
+	parsed->bin = _nmtst_crypto_decrypt (cipher,
+	                                     data,
+	                                     data_len,
+	                                     bin_iv.bin,
+	                                     bin_iv.len,
+	                                     key.bin,
+	                                     key.len,
+	                                     &parsed->len,
+	                                     error);
+	if (!parsed->bin || parsed->len == 0) {
+		nm_secret_ptr_clear (parsed);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+GBytes *
+nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data,
+                                               gsize data_len,
+                                               const char *password,
+                                               NMCryptoKeyType *out_key_type,
+                                               GError **error)
+{
+	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+	nm_auto_clear_secret_ptr NMSecretPtr parsed = { 0 };
+	nm_auto_free_secret char *iv = NULL;
+	NMCryptoCipherType cipher = NM_CRYPTO_CIPHER_UNKNOWN;
+
+	g_return_val_if_fail (data != NULL, NULL);
+
+	NM_SET_OUT (out_key_type, NM_CRYPTO_KEY_TYPE_UNKNOWN);
+
+	if (!_nm_crypto_init (error))
+		return NULL;
+
+	if (!parse_old_openssl_key_file (data, data_len, &parsed, &key_type, &cipher, &iv, NULL)) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("Unable to determine private key type."));
+		return NULL;
+	}
+
+	NM_SET_OUT (out_key_type, key_type);
+
+	if (password) {
+		nm_auto_clear_secret_ptr NMSecretPtr parsed2 = { 0 };
+
+		if (cipher == NM_CRYPTO_CIPHER_UNKNOWN || !iv) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERROR_INVALID_PASSWORD,
+			             _("Password provided, but key was not encrypted."));
+			return NULL;
+		}
+
+		if (!_nmtst_decrypt_key (cipher,
+		                         parsed.bin,
+		                         parsed.len,
+		                         iv,
+		                         password,
+		                         &parsed2,
+		                         error))
+			return NULL;
+
+		return nm_secret_copy_to_gbytes (parsed2.bin, parsed2.len);
+	}
+
+	if (cipher != NM_CRYPTO_CIPHER_UNKNOWN || iv)
+		return NULL;
+
+	return nm_secret_copy_to_gbytes (parsed.bin, parsed.len);
+}
+
+GBytes *
+nmtst_crypto_decrypt_openssl_private_key (const char *file,
+                                          const char *password,
+                                          NMCryptoKeyType *out_key_type,
+                                          GError **error)
+{
+	nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 };
+
+	if (!_nm_crypto_init (error))
+		return NULL;
+
+	if (!file_read_contents (file, &contents, error))
+		return NULL;
+
+	return nmtst_crypto_decrypt_openssl_private_key_data (contents.bin,
+	                                                      contents.len,
+	                                                      password,
+	                                                      out_key_type,
+	                                                      error);
+}
+
+static gboolean
+extract_pem_cert_data (const guint8 *contents,
+                       gsize contents_len,
+                       NMSecretPtr *out_cert,
+                       GError **error)
+{
+	gsize start = 0;
+	gsize end = 0;
+	nm_auto_free_secret char *der_base64 = NULL;
+
+	nm_assert (contents);
+	nm_assert (out_cert);
+	nm_assert (out_cert->len == 0);
+	nm_assert (!out_cert->ptr);
+
+	if (!find_tag (PEM_CERT_BEGIN, contents, contents_len, 0, &start)) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("PEM certificate had no start tag '%s'."),
+		             PEM_CERT_BEGIN);
+		return FALSE;
+	}
+
+	start += strlen (PEM_CERT_BEGIN);
+	if (!find_tag (PEM_CERT_END, contents, contents_len, start, &end)) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("PEM certificate had no end tag '%s'."),
+		             PEM_CERT_END);
+		return FALSE;
+	}
+
+	/* g_base64_decode() wants a NULL-terminated string */
+	der_base64 = g_strndup ((const char *) &contents[start], end - start);
+
+	out_cert->bin = (guint8 *) g_base64_decode (der_base64, &out_cert->len);
+	if (!out_cert->bin || !out_cert->len) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("Failed to decode certificate."));
+		nm_secret_ptr_clear (out_cert);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_crypto_load_and_verify_certificate (const char *file,
+                                       NMCryptoFileFormat *out_file_format,
+                                       GBytes **out_certificate,
+                                       GError **error)
+{
+	nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 };
+
+	g_return_val_if_fail (file, FALSE);
+	nm_assert (!error || !*error);
+
+	if (!_nm_crypto_init (error))
+		goto out;
+
+	if (!file_read_contents (file, &contents, error))
+		goto out;
+
+	if (contents.len == 0) {
+		g_set_error (error,
+		             NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("Certificate file is empty"));
+		goto out;
+	}
+
+	/* Check for PKCS#12 */
+	if (nm_crypto_is_pkcs12_data (contents.bin, contents.len, NULL)) {
+		NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_PKCS12);
+		NM_SET_OUT (out_certificate, nm_secret_copy_to_gbytes (contents.bin, contents.len));
+		return TRUE;
+	}
+
+	/* Check for plain DER format */
+	if (contents.len > 2 && contents.bin[0] == 0x30 && contents.bin[1] == 0x82) {
+		if (_nm_crypto_verify_x509 (contents.bin, contents.len, NULL)) {
+			NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_X509);
+			NM_SET_OUT (out_certificate, nm_secret_copy_to_gbytes (contents.bin, contents.len));
+			return TRUE;
+		}
+	} else {
+		nm_auto_clear_secret_ptr NMSecretPtr pem_cert = { 0 };
+
+		if (extract_pem_cert_data (contents.bin, contents.len, &pem_cert, NULL)) {
+			if (_nm_crypto_verify_x509 (pem_cert.bin, pem_cert.len, NULL)) {
+				NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_X509);
+				NM_SET_OUT (out_certificate, nm_secret_copy_to_gbytes (contents.bin, contents.len));
+				return TRUE;
+			}
+		}
+	}
+
+	g_set_error (error,
+	             NM_CRYPTO_ERROR,
+	             NM_CRYPTO_ERROR_INVALID_DATA,
+	             _("Failed to recognize certificate"));
+
+out:
+	NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
+	NM_SET_OUT (out_certificate, NULL);
+	return FALSE;
+}
+
+gboolean
+nm_crypto_is_pkcs12_data (const guint8 *data,
+                          gsize data_len,
+                          GError **error)
+{
+	GError *local = NULL;
+	gboolean success;
+
+	if (!data_len) {
+		g_set_error (error,
+		             NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("Certificate file is empty"));
+		return FALSE;
+	}
+
+	g_return_val_if_fail (data != NULL, FALSE);
+
+	if (!_nm_crypto_init (error))
+		return FALSE;
+
+	success = _nm_crypto_verify_pkcs12 (data, data_len, NULL, &local);
+	if (success == FALSE) {
+		/* If the error was just a decryption error, then it's pkcs#12 */
+		if (local) {
+			if (g_error_matches (local, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED)) {
+				success = TRUE;
+				g_error_free (local);
+			} else
+				g_propagate_error (error, local);
+		}
+	}
+	return success;
+}
+
+gboolean
+nm_crypto_is_pkcs12_file (const char *file, GError **error)
+{
+	nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 };
+
+	g_return_val_if_fail (file != NULL, FALSE);
+
+	if (!_nm_crypto_init (error))
+		return FALSE;
+
+	if (!file_read_contents (file, &contents, error))
+		return FALSE;
+
+	return nm_crypto_is_pkcs12_data (contents.bin, contents.len, error);
+}
+
+/* Verifies that a private key can be read, and if a password is given, that
+ * the private key can be decrypted with that password.
+ */
+NMCryptoFileFormat
+nm_crypto_verify_private_key_data (const guint8 *data,
+                                   gsize data_len,
+                                   const char *password,
+                                   gboolean *out_is_encrypted,
+                                   GError **error)
+{
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	gboolean is_encrypted = FALSE;
+
+	g_return_val_if_fail (data != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
+	g_return_val_if_fail (out_is_encrypted == NULL || *out_is_encrypted == FALSE, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
+
+	if (!_nm_crypto_init (error))
+		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+
+	/* Check for PKCS#12 first */
+	if (nm_crypto_is_pkcs12_data (data, data_len, NULL)) {
+		is_encrypted = TRUE;
+		if (   !password
+		    || _nm_crypto_verify_pkcs12 (data, data_len, password, error))
+			format = NM_CRYPTO_FILE_FORMAT_PKCS12;
+	} else {
+		nm_auto_clear_secret_ptr NMSecretPtr parsed = { 0 };
+
+		/* Maybe it's PKCS#8 */
+		if (parse_pkcs8_key_file (data, data_len, &parsed, &is_encrypted, NULL)) {
+			if (   !password
+			    || _nm_crypto_verify_pkcs8 (parsed.bin, parsed.len, is_encrypted, password, error))
+				format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
+		} else {
+			NMCryptoCipherType cipher;
+			nm_auto_free_secret char *iv = NULL;
+
+			/* Or it's old-style OpenSSL */
+			if (parse_old_openssl_key_file (data, data_len, NULL, NULL, &cipher, &iv, NULL)) {
+				format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
+				is_encrypted = (cipher != NM_CRYPTO_CIPHER_UNKNOWN && iv);
+			}
+		}
+	}
+
+	if (   format == NM_CRYPTO_FILE_FORMAT_UNKNOWN
+	    && error
+	    && !*error) {
+		g_set_error (error,
+		             NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERROR_INVALID_DATA,
+		             _("not a valid private key"));
+	}
+
+	if (out_is_encrypted)
+		*out_is_encrypted = is_encrypted;
+	return format;
+}
+
+NMCryptoFileFormat
+nm_crypto_verify_private_key (const char *filename,
+                              const char *password,
+                              gboolean *out_is_encrypted,
+                              GError **error)
+{
+	nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 };
+
+	g_return_val_if_fail (filename != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
+
+	if (!_nm_crypto_init (error))
+		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+
+	if (!file_read_contents (filename, &contents, error))
+		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+
+	return nm_crypto_verify_private_key_data (contents.bin, contents.len, password, out_is_encrypted, error);
+}
+
+void
+nm_crypto_md5_hash (const guint8 *salt,
+                    gsize salt_len,
+                    const guint8 *password,
+                    gsize password_len,
+                    guint8 *buffer,
+                    gsize buflen)
+{
+	nm_auto_free_checksum GChecksum *ctx = NULL;
+#define MD5_DIGEST_LEN 16
+	nm_auto_clear_static_secret_ptr const NMSecretPtr digest = NM_SECRET_PTR_STATIC (MD5_DIGEST_LEN);
+	gsize bufidx = 0;
+	int i;
+
+	nm_assert (g_checksum_type_get_length (G_CHECKSUM_MD5) == MD5_DIGEST_LEN);
+
+	g_return_if_fail (password_len == 0 || password);
+	g_return_if_fail (buffer);
+	g_return_if_fail (buflen > 0);
+	g_return_if_fail (salt_len == 0 || salt);
+
+	ctx = g_checksum_new (G_CHECKSUM_MD5);
+
+	for (;;) {
+		gsize digest_len;
+
+		if (password_len > 0)
+			g_checksum_update (ctx, (const guchar *) password, password_len);
+		if (salt_len > 0)
+			g_checksum_update (ctx, (const guchar *) salt, salt_len);
+
+		digest_len = MD5_DIGEST_LEN;
+		g_checksum_get_digest (ctx, digest.bin, &digest_len);
+		nm_assert (digest_len == MD5_DIGEST_LEN);
+
+		for (i = 0; i < MD5_DIGEST_LEN; i++) {
+			if (bufidx >= buflen)
+				return;
+			buffer[bufidx++] = digest.bin[i];
+		}
+
+		g_checksum_reset (ctx);
+		g_checksum_update (ctx, digest.ptr, MD5_DIGEST_LEN);
+	}
+}
+
+gboolean
+nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error)
+{
+	return _nm_crypto_randomize (buffer, buffer_len, error);
+}
+
+
+/**
+ * nmtst_crypto_rsa_key_encrypt:
+ * @data: (array length=len): RSA private key data to be encrypted
+ * @len: length of @data
+ * @in_password: (allow-none): existing password to use, if any
+ * @out_password: (out) (allow-none): if @in_password was %NULL, a random
+ *  password will be generated and returned in this argument
+ * @error: detailed error information on return, if an error occurred
+ *
+ * Encrypts the given RSA private key data with the given password (or generates
+ * a password if no password was given) and converts the data to PEM format
+ * suitable for writing to a file. It uses Triple DES cipher for the encryption.
+ *
+ * Returns: (transfer full): on success, PEM-formatted data suitable for writing
+ * to a PEM-formatted certificate/private key file.
+ **/
+GBytes *
+nmtst_crypto_rsa_key_encrypt (const guint8 *data,
+                              gsize len,
+                              const char *in_password,
+                              char **out_password,
+                              GError **error)
+{
+	guint8 salt[8];
+	nm_auto_clear_secret_ptr NMSecretPtr key = { 0 };
+	nm_auto_clear_secret_ptr NMSecretPtr enc = { 0 };
+	gs_unref_ptrarray GPtrArray *pem = NULL;
+	nm_auto_free_secret char *tmp_password = NULL;
+	nm_auto_free_secret char *enc_base64 = NULL;
+	gsize enc_base64_len;
+	const char *p;
+	gsize ret_len, ret_idx;
+	guint i;
+	NMSecretBuf *ret;
+
+	g_return_val_if_fail (data, NULL);
+	g_return_val_if_fail (len > 0, NULL);
+	g_return_val_if_fail (!out_password || !*out_password, NULL);
+
+	/* Make the password if needed */
+	if (!in_password) {
+		nm_auto_clear_static_secret_ptr NMSecretPtr pw_buf = NM_SECRET_PTR_STATIC (32);
+
+		if (!nm_crypto_randomize (pw_buf.bin, pw_buf.len, error))
+			return NULL;
+		tmp_password = nm_utils_bin2hexstr (pw_buf.bin, pw_buf.len, -1);
+		in_password = tmp_password;
+	}
+
+	if (!nm_crypto_randomize (salt, sizeof (salt), error))
+		return NULL;
+
+	key.bin = nmtst_crypto_make_des_aes_key (NM_CRYPTO_CIPHER_DES_EDE3_CBC, salt, sizeof (salt), in_password, &key.len, NULL);
+	if (!key.bin)
+		g_return_val_if_reached (NULL);
+
+	enc.bin = _nmtst_crypto_encrypt (NM_CRYPTO_CIPHER_DES_EDE3_CBC, data, len, salt, sizeof (salt), key.bin, key.len, &enc.len, error);
+	if (!enc.bin)
+		return NULL;
+
+	/* What follows is not the most efficient way to construct the pem
+	 * file line-by-line. At least, it makes sure, that the data will be cleared
+	 * again and not left around in memory.
+	 *
+	 * If this would not be test code, we should improve the implementation
+	 * to avoid some of the copying. */
+	pem = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_free_secret);
+
+	g_ptr_array_add (pem, g_strdup ("-----BEGIN RSA PRIVATE KEY-----\n"));
+	g_ptr_array_add (pem, g_strdup ("Proc-Type: 4,ENCRYPTED\n"));
+
+	/* Convert the salt to a hex string */
+	g_ptr_array_add (pem, g_strdup_printf ("DEK-Info: %s,",
+	                                       nm_crypto_cipher_get_info (NM_CRYPTO_CIPHER_DES_EDE3_CBC)->name));
+	g_ptr_array_add (pem, nm_utils_bin2hexstr (salt, sizeof (salt), sizeof (salt) * 2));
+	g_ptr_array_add (pem, g_strdup ("\n\n"));
+
+	/* Convert the encrypted key to a base64 string */
+	enc_base64 = g_base64_encode ((const guchar *) enc.bin, enc.len);
+	enc_base64_len = strlen (enc_base64);
+	for (p = enc_base64; (p - enc_base64) < (ptrdiff_t) enc_base64_len; p += 64) {
+		g_ptr_array_add (pem, g_strndup (p, 64));
+		g_ptr_array_add (pem, g_strdup ("\n"));
+	}
+
+	g_ptr_array_add (pem, g_strdup ("-----END RSA PRIVATE KEY-----\n"));
+
+	ret_len = 0;
+	for (i = 0; i < pem->len; i++)
+		ret_len += strlen (pem->pdata[i]);
+
+	ret = nm_secret_buf_new (ret_len + 1);
+	ret_idx = 0;
+	for (i = 0; i < pem->len; i++) {
+		const char *line = pem->pdata[i];
+		gsize line_l = strlen (line);
+
+		memcpy (&ret->bin[ret_idx], line, line_l);
+		ret_idx += line_l;
+		nm_assert (ret_idx <= ret_len);
+	}
+	nm_assert (ret_idx == ret_len);
+	ret->bin[ret_len] = '\0';
+
+	NM_SET_OUT (out_password, g_strdup (tmp_password));
+	return nm_secret_buf_to_gbytes_take (ret, ret_len);
+}
diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h
new file mode 100644
index 00000000..54fbbc5f
--- /dev/null
+++ b/libnm-core/nm-crypto.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * 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 2007 - 2014 Red Hat, Inc.
+ */
+
+#ifndef __NM_CRYPTO_H__
+#define __NM_CRYPTO_H__
+
+#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
+#error Cannot use this header.
+#endif
+
+typedef enum {
+	NM_CRYPTO_CIPHER_UNKNOWN,
+	NM_CRYPTO_CIPHER_DES_EDE3_CBC,
+	NM_CRYPTO_CIPHER_DES_CBC,
+	NM_CRYPTO_CIPHER_AES_128_CBC,
+	NM_CRYPTO_CIPHER_AES_192_CBC,
+	NM_CRYPTO_CIPHER_AES_256_CBC,
+} NMCryptoCipherType;
+
+typedef struct {
+	const char *name;
+	NMCryptoCipherType cipher;
+	guint8 digest_len;
+	guint8 real_iv_len;
+} NMCryptoCipherInfo;
+
+const NMCryptoCipherInfo *nm_crypto_cipher_get_info (NMCryptoCipherType cipher);
+const NMCryptoCipherInfo *nm_crypto_cipher_get_info_by_name (const char *cipher_name, gssize p_len);
+
+typedef enum {
+	NM_CRYPTO_KEY_TYPE_UNKNOWN = 0,
+	NM_CRYPTO_KEY_TYPE_RSA,
+	NM_CRYPTO_KEY_TYPE_DSA
+} NMCryptoKeyType;
+
+typedef enum {
+	NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0,
+	NM_CRYPTO_FILE_FORMAT_X509,
+	NM_CRYPTO_FILE_FORMAT_RAW_KEY,
+	NM_CRYPTO_FILE_FORMAT_PKCS12
+} NMCryptoFileFormat;
+
+/*****************************************************************************/
+
+GBytes *nm_crypto_read_file (const char *filename,
+                             GError **error);
+
+gboolean nm_crypto_load_and_verify_certificate (const char *file,
+                                                NMCryptoFileFormat *out_file_format,
+                                                GBytes **out_certificat,
+                                                GError **error);
+
+gboolean nm_crypto_is_pkcs12_file (const char *file, GError **error);
+
+gboolean nm_crypto_is_pkcs12_data (const guint8 *data, gsize len, GError **error);
+
+NMCryptoFileFormat nm_crypto_verify_private_key_data (const guint8 *data,
+                                                      gsize data_len,
+                                                      const char *password,
+                                                      gboolean *out_is_encrypted,
+                                                      GError **error);
+
+NMCryptoFileFormat nm_crypto_verify_private_key (const char *file,
+                                                 const char *password,
+                                                 gboolean *out_is_encrypted,
+                                                 GError **error);
+
+void nm_crypto_md5_hash (const guint8 *salt,
+                         gsize salt_len,
+                         const guint8 *password,
+                         gsize password_len,
+                         guint8 *buffer,
+                         gsize buflen);
+
+gboolean nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error);
+
+/*****************************************************************************/
+
+GBytes *nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data,
+                                                       gsize data_len,
+                                                       const char *password,
+                                                       NMCryptoKeyType *out_key_type,
+                                                       GError **error);
+
+GBytes *nmtst_crypto_decrypt_openssl_private_key (const char *file,
+                                                  const char *password,
+                                                  NMCryptoKeyType *out_key_type,
+                                                  GError **error);
+
+GBytes *nmtst_crypto_rsa_key_encrypt (const guint8 *data,
+                                      gsize len,
+                                      const char *in_password,
+                                      char **out_password,
+                                      GError **error);
+
+guint8 *nmtst_crypto_make_des_aes_key (NMCryptoCipherType cipher,
+                                       const guint8 *salt,
+                                       gsize salt_len,
+                                       const char *password,
+                                       gsize *out_len,
+                                       GError **error);
+
+/*****************************************************************************/
+
+#endif  /* __NM_CRYPTO_H__ */
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index e7c0bc2c..4e12dc63 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 - 2017 Red Hat, Inc.
+ * Copyright 2004 - 2018 Red Hat, Inc.
  */
 
 /* Definitions related to NetworkManager's D-Bus interfaces.
@@ -73,8 +73,11 @@
 #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_WIREGUARD     NM_DBUS_INTERFACE_DEVICE ".WireGuard"
 #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_WPAN          NM_DBUS_INTERFACE_DEVICE ".Wpan"
+#define NM_DBUS_INTERFACE_DEVICE_6LOWPAN       NM_DBUS_INTERFACE_DEVICE ".Lowpan"
 #define NM_DBUS_INTERFACE_DEVICE_STATISTICS    NM_DBUS_INTERFACE_DEVICE ".Statistics"
 #define NM_DBUS_INTERFACE_CHECKPOINT           NM_DBUS_INTERFACE ".Checkpoint"
 
@@ -212,6 +215,9 @@ typedef enum {
  * @NM_DEVICE_TYPE_OVS_INTERFACE: a Open vSwitch interface
  * @NM_DEVICE_TYPE_OVS_PORT: a Open vSwitch port
  * @NM_DEVICE_TYPE_OVS_BRIDGE: a Open vSwitch bridge
+ * @NM_DEVICE_TYPE_WPAN: a IEEE 802.15.4 (WPAN) MAC Layer Device
+ * @NM_DEVICE_TYPE_6LOWPAN: 6LoWPAN interface
+ * @NM_DEVICE_TYPE_WIREGUARD: a WireGuard interface
  *
  * #NMDeviceType values indicate the type of hardware represented by a
  * device object.
@@ -244,6 +250,9 @@ typedef enum {
 	NM_DEVICE_TYPE_OVS_INTERFACE = 24,
 	NM_DEVICE_TYPE_OVS_PORT      = 25,
 	NM_DEVICE_TYPE_OVS_BRIDGE    = 26,
+	NM_DEVICE_TYPE_WPAN          = 27,
+	NM_DEVICE_TYPE_6LOWPAN       = 28,
+	NM_DEVICE_TYPE_WIREGUARD     = 29,
 } NMDeviceType;
 
 /**
@@ -553,6 +562,7 @@ typedef enum {
  * @NM_DEVICE_STATE_REASON_OVSDB_FAILED: problem communicating with Open vSwitch database
  * @NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE: a duplicate IP address was detected
  * @NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED: The selected IP method is not supported
+ * @NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED: configuration of SR-IOV parameters failed
  *
  * Device state change reason codes
  */
@@ -623,6 +633,7 @@ typedef enum {
 	NM_DEVICE_STATE_REASON_OVSDB_FAILED                   = 63,
 	NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE           = 64,
 	NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED          = 65,
+	NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED     = 66,
 } NMDeviceStateReason;
 
 /**
@@ -645,6 +656,30 @@ typedef enum {
 } NMMetered;
 
 /**
+ * NMConnectionMultiConnect:
+ * @NM_CONNECTION_MULTI_CONNECT_DEFAULT: indicates that the per-connection
+ *   setting is unspecified. In this case, it will fallback to the default
+ *   value, which is @NM_CONNECTION_MULTI_CONNECT_SINGLE.
+ * @NM_CONNECTION_MULTI_CONNECT_SINGLE: the connection profile can only
+ *   be active once at each moment. Activating a profile that is already active,
+ *   will first deactivate it.
+ * @NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE: the profile can
+ *   be manually activated multiple times on different devices. However,
+ *   regarding autoconnect, the profile will autoconnect only if it is
+ *   currently not connected otherwise.
+ * @NM_CONNECTION_MULTI_CONNECT_MULTIPLE: the profile can autoactivate
+ *   and be manually activated multiple times together.
+ *
+ * Since: 1.14
+ */
+typedef enum {
+	NM_CONNECTION_MULTI_CONNECT_DEFAULT           = 0,
+	NM_CONNECTION_MULTI_CONNECT_SINGLE            = 1,
+	NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE   = 2,
+	NM_CONNECTION_MULTI_CONNECT_MULTIPLE          = 3,
+} NMConnectionMultiConnect;
+
+/**
  * NMActiveConnectionState:
  * @NM_ACTIVE_CONNECTION_STATE_UNKNOWN: the state of the connection is unknown
  * @NM_ACTIVE_CONNECTION_STATE_ACTIVATING: a network connection is being prepared
@@ -803,16 +838,18 @@ typedef enum /*< flags >*/ {
 
 /**
  * NMIPTunnelMode:
- * @NM_IP_TUNNEL_MODE_UNKNOWN: Unknown/unset tunnel mode
- * @NM_IP_TUNNEL_MODE_IPIP:    IP in IP tunnel
- * @NM_IP_TUNNEL_MODE_GRE:     GRE tunnel
- * @NM_IP_TUNNEL_MODE_SIT:     SIT tunnel
- * @NM_IP_TUNNEL_MODE_ISATAP:  ISATAP tunnel
- * @NM_IP_TUNNEL_MODE_VTI:     VTI tunnel
- * @NM_IP_TUNNEL_MODE_IP6IP6:  IPv6 in IPv6 tunnel
- * @NM_IP_TUNNEL_MODE_IPIP6:   IPv4 in IPv6 tunnel
- * @NM_IP_TUNNEL_MODE_IP6GRE:  IPv6 GRE tunnel
- * @NM_IP_TUNNEL_MODE_VTI6:    IPv6 VTI tunnel
+ * @NM_IP_TUNNEL_MODE_UNKNOWN:   Unknown/unset tunnel mode
+ * @NM_IP_TUNNEL_MODE_IPIP:      IP in IP tunnel
+ * @NM_IP_TUNNEL_MODE_GRE:       GRE tunnel
+ * @NM_IP_TUNNEL_MODE_SIT:       SIT tunnel
+ * @NM_IP_TUNNEL_MODE_ISATAP:    ISATAP tunnel
+ * @NM_IP_TUNNEL_MODE_VTI:       VTI tunnel
+ * @NM_IP_TUNNEL_MODE_IP6IP6:    IPv6 in IPv6 tunnel
+ * @NM_IP_TUNNEL_MODE_IPIP6:     IPv4 in IPv6 tunnel
+ * @NM_IP_TUNNEL_MODE_IP6GRE:    IPv6 GRE tunnel
+ * @NM_IP_TUNNEL_MODE_VTI6:      IPv6 VTI tunnel
+ * @NM_IP_TUNNEL_MODE_GRETAP:    GRETAP tunnel
+ * @NM_IP_TUNNEL_MODE_IP6GRETAP: IPv6 GRETAP tunnel
  *
  * The tunneling mode.
  *
@@ -829,6 +866,8 @@ typedef enum {
 	NM_IP_TUNNEL_MODE_IPIP6       = 7,
 	NM_IP_TUNNEL_MODE_IP6GRE      = 8,
 	NM_IP_TUNNEL_MODE_VTI6        = 9,
+	NM_IP_TUNNEL_MODE_GRETAP      = 10,
+	NM_IP_TUNNEL_MODE_IP6GRETAP   = 11,
 } NMIPTunnelMode;
 
 /**
@@ -981,4 +1020,20 @@ typedef enum { /*< flags >*/
 	NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT          = (1LL <<  5),
 } NMSettingsUpdate2Flags;
 
+/**
+ * NMTernary:
+ * @NM_TERNARY_DEFAULT: use the globally-configured default value.
+ * @NM_TERNARY_FALSE: the option is disabled.
+ * @NM_TERNARY_TRUE: the option is enabled.
+ *
+ * An boolean value that can be overridden by a default.
+ *
+ * Since: 1.14
+ **/
+typedef enum {
+	NM_TERNARY_DEFAULT = -1,
+	NM_TERNARY_FALSE = 0,
+	NM_TERNARY_TRUE = 1,
+} NMTernary;
+
 #endif /* __NM_DBUS_INTERFACE_H__ */
diff --git a/libnm-core/nm-dbus-types.xml b/libnm-core/nm-dbus-types.xml
index 117e4243..e3e9fca9 100644
--- a/libnm-core/nm-dbus-types.xml
+++ b/libnm-core/nm-dbus-types.xml
@@ -292,6 +292,21 @@
               <entry role="enum_member_value"><para>= <literal>26</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>a Open vSwitch bridge</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_WPAN</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>27</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>a IEEE 802.15.4 (WPAN) MAC Layer Device</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_6LOWPAN</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>28</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>6LoWPAN interface</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_WIREGUARD</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>29</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>a WireGuard interface</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -1132,6 +1147,11 @@
               <entry role="enum_member_value"><para>= <literal>65</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>The selected IP method is not supported</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>66</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>configuration of SR-IOV parameters failed</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -1183,6 +1203,46 @@
     </refsect3>
   </refsect2>
 
+  <refsect2 id="NMConnectionMultiConnect" role="enum">
+    <title>enum NMConnectionMultiConnect</title>
+    <indexterm zone="NMConnectionMultiConnect">
+      <primary>NMConnectionMultiConnect</primary>
+    </indexterm>
+    <para><para>Since: 1.14</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_CONNECTION_MULTI_CONNECT_DEFAULT</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>indicates that the per-connection setting is unspecified. In this case, it will fallback to the default value, which is @NM_CONNECTION_MULTI_CONNECT_SINGLE.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_CONNECTION_MULTI_CONNECT_SINGLE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>the connection profile can only be active once at each moment. Activating a profile that is already active, will first deactivate it.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>the profile can be manually activated multiple times on different devices. However, regarding autoconnect, the profile will autoconnect only if it is currently not connected otherwise.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_CONNECTION_MULTI_CONNECT_MULTIPLE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>the profile can autoactivate and be manually activated multiple times together.</para><para></para></entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </informaltable>
+    </refsect3>
+  </refsect2>
+
   <refsect2 id="NMActiveConnectionState" role="enum">
     <title>enum NMActiveConnectionState</title>
     <indexterm zone="NMActiveConnectionState">
@@ -1472,6 +1532,16 @@
               <entry role="enum_member_value"><para>= <literal>9</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>IPv6 VTI tunnel</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_GRETAP</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>10</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>GRETAP tunnel</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_IP6GRETAP</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>11</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>IPv6 GRETAP tunnel</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -1713,4 +1783,39 @@
     </refsect3>
   </refsect2>
 
+  <refsect2 id="NMTernary" role="enum">
+    <title>enum NMTernary</title>
+    <indexterm zone="NMTernary">
+      <primary>NMTernary</primary>
+    </indexterm>
+    <para><para>An boolean value that can be overridden by a default.</para><para>Since: 1.14</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_TERNARY_DEFAULT</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>-1</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>use the globally-configured default value.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_TERNARY_FALSE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>the option is disabled.</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_TERNARY_TRUE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>the option is enabled.</para><para></para></entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </informaltable>
+    </refsect3>
+  </refsect2>
+
 </refentry>
diff --git a/libnm-core/nm-dbus-utils.c b/libnm-core/nm-dbus-utils.c
index 8389bf36..25af7c99 100644
--- a/libnm-core/nm-dbus-utils.c
+++ b/libnm-core/nm-dbus-utils.c
@@ -117,7 +117,7 @@ dbus_signal_meta_marshal (GClosure     *closure,
  *
  * The argument types in @c_handler correspond to the types output by
  * g_dbus_gvariant_to_gvalue(), except for 'ay' and 'aay'. In particular:
- * - both 16-bit and 32-bit integers are passed as #gint/#guint
+ * - both 16-bit and 32-bit integers are passed as #int/#guint
  * - 'as' values are passed as #GStrv (char **)
  * - all other array, tuple, and dict types are passed as #GVariant
  *
@@ -245,11 +245,11 @@ _nm_dbus_proxy_call_finish (GDBusProxy          *proxy,
  */
 GVariant *
 _nm_dbus_proxy_call_sync (GDBusProxy          *proxy,
-                          const gchar         *method_name,
+                          const char          *method_name,
                           GVariant            *parameters,
                           const GVariantType  *reply_type,
                           GDBusCallFlags       flags,
-                          gint                 timeout_msec,
+                          int                  timeout_msec,
                           GCancellable        *cancellable,
                           GError             **error)
 {
diff --git a/libnm-core/nm-keyfile-utils.c b/libnm-core/nm-keyfile-utils.c
index 5000b583..21f8b07d 100644
--- a/libnm-core/nm-keyfile-utils.c
+++ b/libnm-core/nm-keyfile-utils.c
@@ -109,8 +109,8 @@ nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \
 	g_key_file_set_##stype##_list (kf, alias ?: group, key, list, length); \
 }
 
-DEFINE_KF_LIST_WRAPPER(integer, gint*, gint);
-DEFINE_KF_LIST_WRAPPER(string, gchar **, const gchar* const);
+DEFINE_KF_LIST_WRAPPER(integer, int*, int);
+DEFINE_KF_LIST_WRAPPER(string, char **, const char* const);
 
 void
 nm_keyfile_plugin_kf_set_integer_list_uint8 (GKeyFile *kf,
@@ -173,19 +173,19 @@ nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
 	g_key_file_set_##stype (kf, alias ?: group, key, value); \
 }
 
-DEFINE_KF_WRAPPER(string, gchar*, const gchar*);
-DEFINE_KF_WRAPPER(integer, gint, gint);
+DEFINE_KF_WRAPPER(string, char*, const char*);
+DEFINE_KF_WRAPPER(integer, int, int);
 DEFINE_KF_WRAPPER(uint64, guint64, guint64);
 DEFINE_KF_WRAPPER(boolean, gboolean, gboolean);
-DEFINE_KF_WRAPPER(value, gchar*, const gchar*);
+DEFINE_KF_WRAPPER(value, char*, const char*);
 
-gchar **
+char **
 nm_keyfile_plugin_kf_get_keys (GKeyFile *kf,
                                const char *group,
                                gsize *out_length,
                                GError **error)
 {
-	gchar **keys;
+	char **keys;
 	const char *alias;
 	GError *local = NULL;
 
diff --git a/libnm-core/nm-keyfile-utils.h b/libnm-core/nm-keyfile-utils.h
index 832ad0b4..46a6d564 100644
--- a/libnm-core/nm-keyfile-utils.h
+++ b/libnm-core/nm-keyfile-utils.h
@@ -46,8 +46,8 @@ void nm_keyfile_plugin_kf_set_##stype##_list  (GKeyFile *kf, \
                                                const char *key, \
                                                set_ctype list[], \
                                                gsize length);
-DEFINE_KF_LIST_WRAPPER_PROTO(integer, gint*, gint)
-DEFINE_KF_LIST_WRAPPER_PROTO(string, gchar**, const gchar* const)
+DEFINE_KF_LIST_WRAPPER_PROTO(integer, int*, int)
+DEFINE_KF_LIST_WRAPPER_PROTO(string, char**, const char* const)
 
 void nm_keyfile_plugin_kf_set_integer_list_uint8 (GKeyFile *kf,
                                                   const char *group,
@@ -66,14 +66,14 @@ void nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
                                        const char *group, \
                                        const char *key, \
                                        set_ctype value);
-DEFINE_KF_WRAPPER_PROTO(string, gchar*, const gchar*)
-DEFINE_KF_WRAPPER_PROTO(integer, gint, gint)
+DEFINE_KF_WRAPPER_PROTO(string, char*, const char*)
+DEFINE_KF_WRAPPER_PROTO(integer, int, int)
 DEFINE_KF_WRAPPER_PROTO(uint64, guint64, guint64)
 DEFINE_KF_WRAPPER_PROTO(boolean, gboolean, gboolean)
-DEFINE_KF_WRAPPER_PROTO(value, gchar*, const gchar*)
+DEFINE_KF_WRAPPER_PROTO(value, char*, const char*)
 
 /* Misc */
-gchar ** nm_keyfile_plugin_kf_get_keys    (GKeyFile *kf,
+char ** nm_keyfile_plugin_kf_get_keys    (GKeyFile *kf,
                                            const char *group,
                                            gsize *out_length,
                                            GError **error);
diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c
index 5c94c35c..c7d87869 100644
--- a/libnm-core/nm-keyfile.c
+++ b/libnm-core/nm-keyfile.c
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <linux/pkt_sched.h>
 
+#include "nm-utils/nm-secret-utils.h"
 #include "nm-common-macros.h"
 #include "nm-core-internal.h"
 #include "nm-keyfile-utils.h"
@@ -119,6 +120,49 @@ setting_alias_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *k
 }
 
 static void
+sriov_vfs_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+	const char *setting_name = nm_setting_get_name (setting);
+	gs_unref_ptrarray GPtrArray *vfs = NULL;
+	gs_strfreev char **keys = NULL;
+	gsize n_keys = 0;
+	int i;
+
+	keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
+	if (!keys || n_keys == 0)
+		return;
+
+	vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+
+	for (i = 0; i < n_keys; i++) {
+		gs_free char *value = NULL;
+		NMSriovVF *vf;
+		const char *rest;
+
+		if (!g_str_has_prefix (keys[i], "vf."))
+			continue;
+
+		rest = &keys[i][3];
+
+		if (!NM_STRCHAR_ALL (rest, ch, g_ascii_isdigit (ch)))
+			continue;
+
+		value = nm_keyfile_plugin_kf_get_string (info->keyfile,
+		                                         setting_name,
+		                                         keys[i],
+		                                         NULL);
+
+		vf = _nm_utils_sriov_vf_from_strparts (rest, value, NULL);
+		if (vf)
+			g_ptr_array_add (vfs, vf);
+	}
+
+	g_object_set (G_OBJECT (setting),
+	              key, vfs,
+	              NULL);
+}
+
+static void
 read_array_of_uint (GKeyFile *file,
                     NMSetting *setting,
                     const char *key)
@@ -731,7 +775,7 @@ ip6_addr_gen_mode_parser (KeyfileReaderInfo *info, NMSetting *setting, const cha
 	} else
 		addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64;
 
-	g_object_set (G_OBJECT (setting), key, (gint) addr_gen_mode, NULL);
+	g_object_set (G_OBJECT (setting), key, (int) addr_gen_mode, NULL);
 }
 
 static void
@@ -896,7 +940,7 @@ unescape_semicolons (char *str)
 			i++;
 		str[j++] = str[i++];;
 	}
-	str[j] = '\0';
+	nm_explicit_bzero (&str[j], i - j);
 	return j;
 }
 
@@ -907,9 +951,10 @@ get_bytes (KeyfileReaderInfo *info,
            gboolean zero_terminate,
            gboolean unescape_semicolon)
 {
-	gs_free char *tmp_string = NULL;
+	nm_auto_free_secret char *tmp_string = NULL;
 	gboolean may_be_int_list = TRUE;
 	gsize length;
+	GBytes *result;
 
 	/* New format: just a string
 	 * Old format: integer list; e.g. 11;25;38;
@@ -926,7 +971,7 @@ get_bytes (KeyfileReaderInfo *info,
 		 * byte-array. The reason is that zero_terminate is there to terminate
 		 * *valid* strings. It's not there to terminated invalid (empty) strings.
 		 */
-		return g_bytes_new_take (tmp_string, 0);
+		return g_bytes_new_static ("", 0);
 	}
 
 	for (length = 0; tmp_string[length]; length++) {
@@ -943,12 +988,11 @@ get_bytes (KeyfileReaderInfo *info,
 
 	/* Try to parse the string as a integer list. */
 	if (may_be_int_list && length > 0) {
-		gs_free guint8 *bin_data = NULL;
+		nm_auto_free_secret_buf NMSecretBuf *bin = NULL;
 		const char *const s = tmp_string;
 		gsize i, d;
-		const gsize BIN_DATA_LEN = (length / 2 + 3);
 
-		bin_data = g_malloc (BIN_DATA_LEN);
+		bin = nm_secret_buf_new (length / 2 + 3);
 
 #define DIGIT(c) ((c) - '0')
 		i = 0;
@@ -981,8 +1025,8 @@ get_bytes (KeyfileReaderInfo *info,
 				break;
 			}
 
-			bin_data[d++] = n;
-			nm_assert (d < BIN_DATA_LEN);
+			nm_assert (d < bin->len);
+			bin->bin[d++] = n;
 
 			/* allow whitespace after the digit. */
 			while (g_ascii_isspace (s[i]))
@@ -1000,16 +1044,23 @@ get_bytes (KeyfileReaderInfo *info,
 		 * string format before. We expect that this conversion cannot fail. */
 		if (d > 0) {
 			/* note that @zero_terminate does not add a terminating '\0' to
-			 * binary data as an integer list.
-			 *
-			 * But we add a '\0' to the bin_data pointer, just to avoid somebody
-			 * (erronously!) reading the binary data as C-string.
+			 * binary data as an integer list. If the bytes are expressed as
+			 * an integer list, all potential NUL characters are supposed to
+			 * be included there explicitly.
 			 *
-			 * @d itself does not entail the '\0'. */
-			nm_assert (d + 1 <= BIN_DATA_LEN);
-			bin_data = g_realloc (bin_data, d + 1);
-			bin_data[d] = '\0';
-			return g_bytes_new_take (g_steal_pointer (&bin_data), d);
+			 * However, in the spirit of defensive programming, we do append a
+			 * NUL character to the buffer, although this character is hidden
+			 * and only a mitigation for bugs. */
+
+			if (d + 10 < bin->len) {
+				/* hm, too much unused memory. Copy the memory to a suitable
+				 * sized buffer. */
+				return nm_secret_copy_to_gbytes (bin->bin, d);
+			}
+
+			nm_assert (d < bin->len);
+			bin->bin[d] = '\0';
+			return nm_secret_buf_to_gbytes_take (g_steal_pointer (&bin), d);
 		}
 	}
 
@@ -1020,8 +1071,13 @@ get_bytes (KeyfileReaderInfo *info,
 		length++;
 	if (length == 0)
 		return NULL;
-	tmp_string = g_realloc (tmp_string, length + (zero_terminate ? 0 : 1));
-	return g_bytes_new_take (g_steal_pointer (&tmp_string), length);
+
+	result = g_bytes_new_with_free_func (tmp_string,
+	                                     length,
+	                                     (GDestroyNotify) nm_free_secret,
+	                                     tmp_string);
+	tmp_string = NULL;
+	return result;
 }
 
 static void
@@ -1065,12 +1121,12 @@ get_cert_path (const char *base_dir, const guint8 *cert_path, gsize cert_path_le
 	g_return_val_if_fail (base_dir != NULL, NULL);
 	g_return_val_if_fail (cert_path != NULL, NULL);
 
-	base = path = g_malloc0 (cert_path_len + 1);
-	memcpy (path, cert_path, cert_path_len);
+	path = g_strndup ((char *) cert_path, cert_path_len);
 
 	if (path[0] == '/')
 		return path;
 
+	base = path;
 	p = strrchr (path, '/');
 	if (p)
 		base = p + 1;
@@ -1104,8 +1160,9 @@ nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
 	const char *data = pdata;
 	gboolean exists = FALSE;
 	gsize validate_len;
+	gsize path_len, pathuri_len;
 	gs_free char *path = NULL;
-	GByteArray *tmp;
+	gs_free char *pathuri = NULL;
 
 	g_return_val_if_fail (base_dir && base_dir[0] == '/', NULL);
 
@@ -1148,18 +1205,16 @@ nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
 	 * When returning TRUE, we must also be sure that @data_len does not look like
 	 * the deprecated format of list of integers. With this implementation that is the
 	 * case, as long as @consider_exists is FALSE. */
-	tmp = g_byte_array_sized_new (strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) + strlen (path) + 1);
-	g_byte_array_append (tmp, (const guint8 *) NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH));
-	g_byte_array_append (tmp, (const guint8 *) path, strlen (path) + 1);
-	if (nm_setting_802_1x_check_cert_scheme (tmp->data, tmp->len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH) {
-		g_byte_array_unref (tmp);
+	path_len = strlen (path);
+	pathuri_len = (NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) + 1) + path_len;
+	pathuri = g_new (char, pathuri_len);
+	memcpy (pathuri, NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH));
+	memcpy (&pathuri[NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)], path, path_len + 1);
+	if (nm_setting_802_1x_check_cert_scheme (pathuri, pathuri_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH)
 		return NULL;
-	}
-	g_free (path);
-	path = (char *) g_byte_array_free (tmp, FALSE);
 
 	NM_SET_OUT (out_exists, exists);
-	return g_steal_pointer (&path);
+	return g_steal_pointer (&pathuri);
 }
 
 #define HAS_SCHEME_PREFIX(bin, bin_len, scheme) \
@@ -1393,7 +1448,7 @@ qdisc_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
 	const char *setting_name = nm_setting_get_name (setting);
 	GPtrArray *qdiscs;
-	gs_strfreev gchar **keys = NULL;
+	gs_strfreev char **keys = NULL;
 	gsize n_keys = 0;
 	int i;
 
@@ -1441,7 +1496,7 @@ tfilter_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
 	const char *setting_name = nm_setting_get_name (setting);
 	GPtrArray *tfilters;
-	gs_strfreev gchar **keys = NULL;
+	gs_strfreev char **keys = NULL;
 	gsize n_keys = 0;
 	int i;
 
@@ -1510,6 +1565,37 @@ setting_alias_writer (KeyfileWriterInfo *info,
 }
 
 static void
+sriov_vfs_writer (KeyfileWriterInfo *info,
+                  NMSetting *setting,
+                  const char *key,
+                  const GValue *value)
+{
+	GPtrArray *vfs;
+	guint i;
+
+	vfs = g_value_get_boxed (value);
+	if (!vfs)
+		return;
+
+	for (i = 0; i < vfs->len; i++) {
+		const NMSriovVF *vf = vfs->pdata[i];
+		gs_free char *kf_value = NULL;
+		char kf_key[32];
+
+		kf_value = nm_utils_sriov_vf_to_str (vf, TRUE, NULL);
+		if (!kf_value)
+			continue;
+
+		nm_sprintf_buf (kf_key, "vf.%u", nm_sriov_vf_get_index (vf));
+
+		nm_keyfile_plugin_kf_set_string (info->keyfile,
+		                                 nm_setting_get_name (setting),
+		                                 kf_key,
+		                                 kf_value);
+	}
+}
+
+static void
 write_array_of_uint (GKeyFile *file,
                      NMSetting *setting,
                      const char *key,
@@ -1525,7 +1611,7 @@ write_array_of_uint (GKeyFile *file,
 
 	g_return_if_fail (g_array_get_element_size (array) == sizeof (guint));
 
-	tmp_array = g_new (gint, array->len);
+	tmp_array = g_new (int, array->len);
 	for (i = 0; i < array->len; i++) {
 		guint v = g_array_index (array, guint, i);
 
@@ -2022,18 +2108,16 @@ typedef struct {
 	})
 
 typedef struct {
-	const char *setting_name;
 	const ParseInfoProperty*const*properties;
 } ParseInfoSetting;
 
-#define PARSE_INFO_SETTING(_setting_name, ...) \
-	{ \
-		.setting_name = _setting_name, \
+#define PARSE_INFO_SETTING(setting_type, ...) \
+	[setting_type] = (&((const ParseInfoSetting) { \
 		__VA_ARGS__ \
-	}
+	}))
 
-static const ParseInfoSetting parse_infos[] = {
-	PARSE_INFO_SETTING (NM_SETTING_WIRELESS_SETTING_NAME,
+static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = {
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_WIRELESS,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_WIRELESS_BSSID,
 				.parser        = mac_address_parser_ETHER,
@@ -2050,7 +2134,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_802_1X_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_802_1X,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_802_1X_CA_CERT,
 				.parser        = cert_parser,
@@ -2082,7 +2166,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_WIRED_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_WIRED,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
 				.parser        = mac_address_parser_ETHER_cloned,
@@ -2092,28 +2176,28 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_BLUETOOTH_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BLUETOOTH,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_BLUETOOTH_BDADDR,
 				.parser        = mac_address_parser_ETHER,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_BOND_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BOND,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_BOND_OPTIONS,
 				.parser_no_check_key = TRUE,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_BRIDGE_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_BRIDGE,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_MAC_ADDRESS,
 				.parser        = mac_address_parser_ETHER,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_CONNECTION_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_CONNECTION,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_CONNECTION_READ_ONLY,
 				.parser_skip   = TRUE,
@@ -2125,14 +2209,14 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_INFINIBAND_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_INFINIBAND,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_INFINIBAND_MAC_ADDRESS,
 				.parser        = mac_address_parser_INFINIBAND,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_IP4_CONFIG,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ADDRESSES,
 				.parser_no_check_key = TRUE,
@@ -2154,7 +2238,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_IP6_CONFIG,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
 				.parser_no_check_key = TRUE,
@@ -2182,14 +2266,23 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_SERIAL_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_SERIAL,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_SERIAL_PARITY,
 				.parser        = parity_parser,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_TC_CONFIG_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_SRIOV,
+		PARSE_INFO_PROPERTIES (
+			PARSE_INFO_PROPERTY (NM_SETTING_SRIOV_VFS,
+				.parser_no_check_key = TRUE,
+				.parser        = sriov_vfs_parser,
+				.writer        = sriov_vfs_writer,
+			),
+		),
+	),
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_TC_CONFIG,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_TC_CONFIG_QDISCS,
 				.parser_no_check_key = TRUE,
@@ -2203,7 +2296,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_TEAM_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_TEAM,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_CONFIG,
 				.parser        = team_config_parser,
@@ -2270,7 +2363,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_TEAM_PORT_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_TEAM_PORT,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_CONFIG,
 				.parser        = team_config_parser,
@@ -2301,21 +2394,21 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_USER_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_USER,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_USER_DATA,
 				.parser_no_check_key = TRUE,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_VLAN_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_VLAN,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_VLAN_FLAGS,
 				.writer_persist_default = TRUE,
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_VPN_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_VPN,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_VPN_DATA,
 				.parser_no_check_key = TRUE,
@@ -2337,7 +2430,7 @@ static const ParseInfoSetting parse_infos[] = {
 			),
 		),
 	),
-	PARSE_INFO_SETTING (NM_SETTING_WIMAX_SETTING_NAME,
+	PARSE_INFO_SETTING (NM_META_SETTING_TYPE_WIMAX,
 		PARSE_INFO_PROPERTIES (
 			PARSE_INFO_PROPERTY (NM_SETTING_WIMAX_MAC_ADDRESS,
 				.parser        = mac_address_parser_ETHER,
@@ -2347,21 +2440,24 @@ static const ParseInfoSetting parse_infos[] = {
 };
 
 static const ParseInfoProperty *
-_parse_info_find (const char *setting_name, const char *property_name)
+_parse_info_find (NMSetting *setting,
+                  const char *property_name,
+                  const char **out_setting_name)
 {
+	const NMMetaSettingInfo *setting_info;
+	const ParseInfoSetting *pis;
 	gssize idx;
 
-#if NM_MORE_ASSERTS > 5
+#if NM_MORE_ASSERTS > 10
 	{
 		guint i, j;
 
 		for (i = 0; i < G_N_ELEMENTS (parse_infos); i++) {
-			const ParseInfoSetting *pis = &parse_infos[i];
+			pis = parse_infos[i];
+
+			if (!pis)
+				continue;
 
-			g_assert (pis->setting_name);
-			if (   i > 0
-				&& strcmp (pis[-1].setting_name, pis->setting_name) >= 0)
-				g_error ("Wrong order at index #%d: \"%s\" before \"%s\"", i - 1, pis[-1].setting_name, pis->setting_name);
 			g_assert (pis->properties);
 			g_assert (pis->properties[0]);
 			for (j = 0; pis->properties[j]; j++) {
@@ -2371,31 +2467,35 @@ _parse_info_find (const char *setting_name, const char *property_name)
 				g_assert (pip->property_name);
 				if (   j > 0
 				    && (pip0 = pis->properties[j - 1])
-				    && strcmp (pip0->property_name, pip->property_name) >= 0)
-					g_error ("Wrong order at index #%d.%d: \"%s.%s\" before \"%s.%s\"", i, j - 1, pis->setting_name, pip0->property_name, pis->setting_name, pip->property_name);
+				    && strcmp (pip0->property_name, pip->property_name) >= 0) {
+					g_error ("Wrong order at index #%d.%d: \"%s.%s\" before \"%s.%s\"",
+					         i, j - 1,
+					         nm_meta_setting_infos[i].setting_name, pip0->property_name,
+					         nm_meta_setting_infos[i].setting_name, pip->property_name);
+				}
 			}
 		}
 	}
 #endif
 
-	G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (ParseInfoSetting, setting_name) == 0);
-	idx = _nm_utils_array_find_binary_search (parse_infos,
-	                                          sizeof (ParseInfoSetting),
-	                                          G_N_ELEMENTS (parse_infos),
-	                                          &setting_name,
-	                                          nm_strcmp_p_with_data,
-	                                          NULL);
-	if (idx >= 0) {
-		const ParseInfoSetting *pis = &parse_infos[idx];
-
-		nm_assert (nm_streq (pis->setting_name, setting_name));
-		idx = _nm_utils_ptrarray_find_binary_search ((gconstpointer *) pis->properties,
-		                                             NM_PTRARRAY_LEN (pis->properties),
-		                                             &property_name,
-		                                             nm_strcmp_p_with_data,
-		                                             NULL,
-		                                             NULL,
-		                                             NULL);
+	if (   !NM_IS_SETTING (setting)
+	    || !(setting_info = NM_SETTING_GET_CLASS (setting)->setting_info)) {
+		/* handle invalid setting objects gracefully. */
+		*out_setting_name = NULL;
+		return NULL;
+	}
+
+	*out_setting_name = setting_info->setting_name;
+
+	if ((pis = parse_infos[setting_info->meta_type])) {
+		G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (ParseInfoProperty, property_name) == 0);
+		idx = nm_utils_ptrarray_find_binary_search ((gconstpointer *) pis->properties,
+		                                            NM_PTRARRAY_LEN (pis->properties),
+		                                            &property_name,
+		                                            nm_strcmp_p_with_data,
+		                                            NULL,
+		                                            NULL,
+		                                            NULL);
 		if (idx >= 0)
 			return pis->properties[idx];
 	}
@@ -2426,9 +2526,9 @@ read_one_setting_value (NMSetting *setting,
 	if (!(flags & G_PARAM_WRITABLE))
 		return;
 
-	setting_name = nm_setting_get_name (setting);
+	pip = _parse_info_find (setting, key, &setting_name);
 
-	pip = _parse_info_find (setting_name, key);
+	nm_assert (setting_name);
 
 	if (   !pip
 	    && nm_streq (key, NM_SETTING_NAME))
@@ -2521,7 +2621,7 @@ read_one_setting_value (NMSetting *setting,
 		} else
 			g_object_set (setting, key, int_val, NULL);
 	} else if (type == G_TYPE_BYTES) {
-		gint *tmp;
+		int *tmp;
 		GByteArray *array;
 		GBytes *bytes;
 		gsize length;
@@ -2554,7 +2654,7 @@ read_one_setting_value (NMSetting *setting,
 		g_bytes_unref (bytes);
 		g_free (tmp);
 	} else if (type == G_TYPE_STRV) {
-		gchar **sa;
+		char **sa;
 		gsize length;
 
 		sa = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
@@ -2580,11 +2680,11 @@ read_one_setting_value (NMSetting *setting,
 			}
 		}
 	} else if (G_VALUE_HOLDS_ENUM (value)) {
-		gint int_val;
+		int int_val;
 
 		int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, &err);
 		if (!err)
-			g_object_set (setting, key, (gint) int_val, NULL);
+			g_object_set (setting, key, (int) int_val, NULL);
 	} else {
 		if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
 		                 _("unhandled setting property type '%s'"),
@@ -2596,6 +2696,8 @@ read_one_setting_value (NMSetting *setting,
 static NMSetting *
 read_setting (KeyfileReaderInfo *info)
 {
+	const NMSettInfoSetting *sett_info;
+	gs_unref_object NMSetting *setting = NULL;
 	const char *alias;
 	GType type;
 
@@ -2604,22 +2706,92 @@ read_setting (KeyfileReaderInfo *info)
 		alias = info->group;
 
 	type = nm_setting_lookup_type (alias);
-	if (type) {
-		NMSetting *setting = g_object_new (type, NULL);
-
-		info->setting = setting;
-		nm_setting_enumerate_values (setting, read_one_setting_value, info);
-		info->setting = NULL;
-		if (!info->error)
-			return setting;
-
-		g_object_unref (setting);
-	} else {
+	if (!type) {
 		handle_warn (info, NULL, NM_KEYFILE_WARN_SEVERITY_WARN,
 		             _("invalid setting name '%s'"), info->group);
+		return NULL;
 	}
 
-	return NULL;
+	setting = g_object_new (type, NULL);
+
+	info->setting = setting;
+
+	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+
+	if (sett_info->detail.gendata_info) {
+		gs_free char **keys = NULL;
+		gsize i, n_keys;
+
+		keys = g_key_file_get_keys (info->keyfile, info->group, &n_keys, NULL);
+		if (n_keys > 0) {
+			GHashTable *h = _nm_setting_gendata_hash (setting, TRUE);
+
+			nm_utils_strv_sort (keys, n_keys);
+			for (i = 0; i < n_keys; i++) {
+				gs_free char *key = keys[i];
+				gs_free_error GError *local = NULL;
+				const GVariantType *variant_type;
+				GVariant *variant;
+
+				/* a GKeyfile can return duplicate keys, there is just no API to make sense
+				 * of them. Skip them. */
+				if (   i + 1 < n_keys
+				    && nm_streq (key, keys[i + 1]))
+					continue;
+
+				/* currently, the API is very simple. The setting class just returns
+				 * the desired variant type, and keyfile reader will try to parse
+				 * it accordingly. Note, that this does currently not allow, that
+				 * a particular key can contain different variant types, nor is it
+				 * very flexible in general.
+				 *
+				 * We add flexibility when we need it. Keep it simple for now. */
+				variant_type = sett_info->detail.gendata_info->get_variant_type (sett_info,
+				                                                                 key,
+				                                                                 &local);
+				if (!variant_type) {
+					if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+					                  _("invalid key '%s.%s'"),
+					                  info->group, key))
+						break;
+					continue;
+				}
+
+				if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_BOOLEAN)) {
+					gboolean v;
+
+					v = g_key_file_get_boolean (info->keyfile,
+					                            info->group,
+					                            key,
+					                            &local);
+					if (local) {
+						if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+						                  _("key '%s.%s' is not boolean"),
+						                  info->group, key))
+							break;
+						continue;
+					}
+					variant = g_variant_new_boolean (v);
+				} else {
+					nm_assert_not_reached ();
+					continue;
+				}
+
+				g_hash_table_insert (h,
+				                     g_steal_pointer (&key),
+				                     g_variant_take_ref (variant));
+			}
+			for (; i < n_keys; i++)
+				g_free (keys[i]);
+		}
+	} else
+		nm_setting_enumerate_values (setting, read_one_setting_value, info);
+
+	info->setting = NULL;
+
+	if (info->error)
+		return NULL;
+	return g_steal_pointer (&setting);
 }
 
 static void
@@ -2674,7 +2846,7 @@ nm_keyfile_read (GKeyFile *keyfile,
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
 	NMSetting *setting;
-	gchar **groups;
+	char **groups;
 	gsize length;
 	int i;
 	gboolean vpn_secrets = FALSE;
@@ -2694,7 +2866,7 @@ nm_keyfile_read (GKeyFile *keyfile,
 			base_dir = base_dir_free = g_get_current_dir ();
 		}
 	} else
-		g_return_val_if_fail ("/", NULL);
+		g_return_val_if_fail (base_dir[0] == '/', NULL);
 
 	connection = nm_simple_connection_new ();
 
@@ -2800,12 +2972,22 @@ write_setting_value (NMSetting *setting,
 	if (info->error)
 		return;
 
-	setting_name = nm_setting_get_name (setting);
-
 	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
 	nm_assert (pspec);
 
-	pip = _parse_info_find (setting_name, key);
+	pip = _parse_info_find (setting, key, &setting_name);
+
+	if (!setting_name) {
+		/* the setting type is unknown. That is highly unexpected
+		 * (and as this is currently only called from NetworkManager
+		 * daemon, not possible).
+		 *
+		 * Still, handle it gracefully, because later keyfile writer will become
+		 * public API of libnm, where @setting is (untrusted) user input.
+		 *
+		 * Gracefully here just means: ignore the setting. */
+		return;
+	}
 
 	if (   !pip
 	    && nm_streq (key, NM_SETTING_NAME))
@@ -2879,7 +3061,7 @@ write_setting_value (NMSetting *setting,
 		char **array;
 
 		array = (char **) g_value_get_boxed (value);
-		nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, g_strv_length (array));
+		nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const char **const) array, g_strv_length (array));
 	} else if (type == G_TYPE_HASH_TABLE) {
 		write_hash_of_string (info->keyfile, setting, key, value);
 	} else if (type == G_TYPE_ARRAY) {
@@ -2888,7 +3070,7 @@ write_setting_value (NMSetting *setting,
 		/* Flags are guint but GKeyFile has no uint reader, just uint64 */
 		nm_keyfile_plugin_kf_set_uint64 (info->keyfile, setting_name, key, (guint64) g_value_get_flags (value));
 	} else if (G_VALUE_HOLDS_ENUM (value))
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (gint) g_value_get_enum (value));
+		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_enum (value));
 	else
 		g_warn_if_reached ();
 }
@@ -2900,6 +3082,8 @@ nm_keyfile_write (NMConnection *connection,
                   GError **error)
 {
 	KeyfileWriterInfo info = { 0 };
+	gs_free NMSetting **settings = NULL;
+	guint i, length = 0;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
@@ -2912,12 +3096,59 @@ nm_keyfile_write (NMConnection *connection,
 	info.error = NULL;
 	info.handler = handler;
 	info.user_data = user_data;
-	nm_connection_for_each_setting_value (connection, write_setting_value, &info);
+
+	settings = nm_connection_get_settings (connection, &length);
+	for (i = 0; i < length; i++) {
+		const NMSettInfoSetting *sett_info;
+		NMSetting *setting = settings[i];
+
+		sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+
+		if (sett_info->detail.gendata_info) {
+			guint k, n_keys;
+			const char *const*keys;
+
+			nm_assert (!nm_keyfile_plugin_get_alias_for_setting_name (sett_info->setting_class->setting_info->setting_name));
+
+			n_keys = _nm_setting_gendata_get_all (setting, &keys, NULL);
+
+			if (n_keys > 0) {
+				const char *setting_name = sett_info->setting_class->setting_info->setting_name;
+				GHashTable *h = _nm_setting_gendata_hash (setting, FALSE);
+
+				for (k = 0; k < n_keys; k++) {
+					const char *key = keys[k];
+					GVariant *v;
+
+					v = g_hash_table_lookup (h, key);
+
+					if (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN)) {
+						g_key_file_set_boolean (info.keyfile,
+						                        setting_name,
+						                        key,
+						                        g_variant_get_boolean (v));
+					} else {
+						/* BUG: The variant type is not implemented. Since the connection
+						 * verifies, this can only mean we either wrongly didn't reject
+						 * the connection as invalid, or we didn't properly implement the
+						 * variant type. */
+						nm_assert_not_reached ();
+						continue;
+					}
+				}
+			}
+		} else
+			nm_setting_enumerate_values (setting, write_setting_value, &info);
+
+		if (info.error)
+			break;
+	}
 
 	if (info.error) {
 		g_propagate_error (error, info.error);
 		g_key_file_unref (info.keyfile);
 		return NULL;
 	}
+
 	return info.keyfile;
 }
diff --git a/libnm-core/nm-property-compare.c b/libnm-core/nm-property-compare.c
index 64ed663c..2a3ee397 100644
--- a/libnm-core/nm-property-compare.c
+++ b/libnm-core/nm-property-compare.c
@@ -27,7 +27,7 @@
 
 #include "nm-property-compare.h"
 
-static gint
+static int
 _nm_property_compare_collection (GVariant *value1, GVariant *value2)
 {
 	GVariant *child1, *child2;
@@ -55,7 +55,7 @@ _nm_property_compare_collection (GVariant *value1, GVariant *value2)
 	return 0;
 }
 
-static gint
+static int
 _nm_property_compare_vardict (GVariant *value1, GVariant *value2)
 {
 	GVariantIter iter;
@@ -87,7 +87,7 @@ _nm_property_compare_vardict (GVariant *value1, GVariant *value2)
 	return 0;
 }
 
-static gint
+static int
 _nm_property_compare_strdict (GVariant *value1, GVariant *value2)
 {
 	GVariantIter iter;
@@ -119,7 +119,7 @@ nm_property_compare (GVariant *value1, GVariant *value2)
 {
 	const GVariantType *type1;
 	const GVariantType *type2;
-	gint ret;
+	int ret;
 
 	if (value1 == value2)
 		return 0;
diff --git a/libnm-core/nm-setting-6lowpan.c b/libnm-core/nm-setting-6lowpan.c
new file mode 100644
index 00000000..c0ce232d
--- /dev/null
+++ b/libnm-core/nm-setting-6lowpan.c
@@ -0,0 +1,230 @@
+/*
+ * 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 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-private.h"
+#include "nm-setting-6lowpan.h"
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PARENT,
+);
+
+typedef struct {
+	char *parent;
+} NMSetting6LowpanPrivate;
+
+/**
+ * NMSetting6Lowpan:
+ *
+ * 6LoWPAN Settings
+ */
+struct _NMSetting6Lowpan {
+	NMSetting parent;
+};
+
+struct _NMSetting6LowpanClass {
+	NMSettingClass parent;
+};
+
+/**
+ * SECTION:nm-setting-6lowpan
+ * @short_description: Describes connection properties for 6LoWPAN interfaces
+ *
+ * The #NMSetting6Lowpan object is a #NMSetting subclass that describes properties
+ * necessary for connection to 6LoWPAN interfaces.
+ **/
+
+G_DEFINE_TYPE (NMSetting6Lowpan, nm_setting_6lowpan, NM_TYPE_SETTING)
+
+#define NM_SETTING_6LOWPAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_6LOWPAN, NMSetting6LowpanPrivate))
+
+
+/**
+ * nm_setting_6lowpan_new:
+ *
+ * Creates a new #NMSetting6Lowpan object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSetting6Lowpan object
+ *
+ * Since: 1.14
+ **/
+NMSetting *
+nm_setting_6lowpan_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_6LOWPAN, NULL);
+}
+
+/**
+ * nm_setting_6lowpan_get_parent:
+ * @setting: the #NMSetting6Lowpan
+ *
+ * Returns: the #NMSetting6Lowpan:parent property of the setting
+ *
+ * Since: 1.14
+ **/
+const char *
+nm_setting_6lowpan_get_parent (NMSetting6Lowpan *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_6LOWPAN (setting), NULL);
+	return NM_SETTING_6LOWPAN_GET_PRIVATE (setting)->parent;
+}
+
+/*********************************************************************/
+
+static gboolean
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	NMSetting6LowpanPrivate *priv = NM_SETTING_6LOWPAN_GET_PRIVATE (setting);
+	NMSettingConnection *s_con = NULL;
+
+	if (connection)
+		s_con = nm_connection_get_setting_connection (connection);
+
+	if (!priv->parent) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_MISSING_PROPERTY,
+		             _("property is not specified"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_6LOWPAN_SETTING_NAME, NM_SETTING_6LOWPAN_PARENT);
+		return FALSE;
+	}
+
+
+
+	if (nm_utils_is_uuid (priv->parent)) {
+		/* If we have an NMSettingConnection:master with slave-type="6lowpan",
+		 * then it must be the same UUID.
+		 */
+		if (s_con) {
+			const char *master = NULL, *slave_type = NULL;
+
+			slave_type = nm_setting_connection_get_slave_type (s_con);
+			if (!g_strcmp0 (slave_type, NM_SETTING_6LOWPAN_SETTING_NAME))
+				master = nm_setting_connection_get_master (s_con);
+
+			if (master && g_strcmp0 (priv->parent, master) != 0) {
+				g_set_error (error,
+				             NM_CONNECTION_ERROR,
+				             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				             _("'%s' value doesn't match '%s=%s'"),
+				             priv->parent, NM_SETTING_CONNECTION_MASTER, master);
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_6LOWPAN_SETTING_NAME, NM_SETTING_6LOWPAN_PARENT);
+				return FALSE;
+			}
+		}
+	} else if (!nm_utils_iface_valid_name (priv->parent)) {
+		/* parent must be either a UUID or an interface name */
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' is neither an UUID nor an interface name"),
+		             priv->parent);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_6LOWPAN_SETTING_NAME, NM_SETTING_6LOWPAN_PARENT);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+nm_setting_6lowpan_init (NMSetting6Lowpan *setting)
+{
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSetting6Lowpan *setting = NM_SETTING_6LOWPAN (object);
+	NMSetting6LowpanPrivate *priv = NM_SETTING_6LOWPAN_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_PARENT:
+		g_free (priv->parent);
+		priv->parent = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSetting6Lowpan *setting = NM_SETTING_6LOWPAN (object);
+	NMSetting6LowpanPrivate *priv = NM_SETTING_6LOWPAN_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_PARENT:
+		g_value_set_string (value, priv->parent);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSetting6Lowpan *setting = NM_SETTING_6LOWPAN (object);
+	NMSetting6LowpanPrivate *priv = NM_SETTING_6LOWPAN_GET_PRIVATE (setting);
+
+	g_free (priv->parent);
+
+	G_OBJECT_CLASS (nm_setting_6lowpan_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_6lowpan_class_init (NMSetting6LowpanClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMSetting6LowpanPrivate));
+
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+
+	setting_class->verify = verify;
+
+	/**
+	 * NMSetting6Lowpan:parent:
+	 *
+	 * If given, specifies the parent interface name or parent connection UUID
+	 * from which this 6LowPAN interface should be created.
+	 *
+	 * Since: 1.14
+	 **/
+	obj_properties[PROP_PARENT] =
+	    g_param_spec_string (NM_SETTING_6LOWPAN_PARENT, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_6LOWPAN);
+}
diff --git a/libnm-core/nm-setting-6lowpan.h b/libnm-core/nm-setting-6lowpan.h
new file mode 100644
index 00000000..73f57b7c
--- /dev/null
+++ b/libnm-core/nm-setting-6lowpan.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 2018 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_6LOWPAN_H__
+#define __NM_SETTING_6LOWPAN_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_6LOWPAN            (nm_setting_6lowpan_get_type ())
+#define NM_SETTING_6LOWPAN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_6LOWPAN, NMSetting6Lowpan))
+#define NM_SETTING_6LOWPAN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_6LOWPANCONFIG, NMSetting6LowpanClass))
+#define NM_IS_SETTING_6LOWPAN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_6LOWPAN))
+#define NM_IS_SETTING_6LOWPAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_6LOWPAN))
+#define NM_SETTING_6LOWPAN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_6LOWPAN, NMSetting6LowpanClass))
+
+#define NM_SETTING_6LOWPAN_SETTING_NAME         "6lowpan"
+
+#define NM_SETTING_6LOWPAN_PARENT               "parent"
+
+typedef struct _NMSetting6LowpanClass NMSetting6LowpanClass;
+
+NM_AVAILABLE_IN_1_14
+GType nm_setting_6lowpan_get_type (void);
+NM_AVAILABLE_IN_1_14
+NMSetting *nm_setting_6lowpan_new (void);
+
+NM_AVAILABLE_IN_1_14
+const char *nm_setting_6lowpan_get_parent (NMSetting6Lowpan *setting);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_6LOWPAN_H__ */
diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c
index 3b4240e6..7e004c90 100644
--- a/libnm-core/nm-setting-8021x.c
+++ b/libnm-core/nm-setting-8021x.c
@@ -26,8 +26,9 @@
 
 #include <string.h>
 
+#include "nm-utils/nm-secret-utils.h"
 #include "nm-utils.h"
-#include "crypto.h"
+#include "nm-crypto.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
 #include "nm-core-enum-types.h"
@@ -60,66 +61,44 @@
  *       ISBN: 978-1587051548
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING,
-                         _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))
+static NMSetting8021xCKFormat
+_crypto_format_to_ck (NMCryptoFileFormat format)
+{
+	G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_UNKNOWN == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_UNKNOWN) );
+	G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_X509    == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_X509) );
+	G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_RAW_KEY == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_RAW_KEY) );
+	G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_PKCS12  == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_PKCS12) );
+
+	nm_assert (NM_IN_SET (format, NM_CRYPTO_FILE_FORMAT_UNKNOWN,
+	                              NM_CRYPTO_FILE_FORMAT_X509,
+	                              NM_CRYPTO_FILE_FORMAT_RAW_KEY,
+	                              NM_CRYPTO_FILE_FORMAT_PKCS12));
+	return (NMSetting8021xCKFormat) format;
+}
 
-G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_UNKNOWN == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_UNKNOWN) );
-G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_X509    == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_X509) );
-G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_RAW_KEY == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_RAW_KEY) );
-G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_PKCS12  == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_PKCS12) );
+/*****************************************************************************/
+
+typedef void (*EAPMethodNeedSecretsFunc) (NMSetting8021x *self,
+                                          GPtrArray *secrets,
+                                          gboolean phase2);
+
+typedef gboolean (*EAPMethodValidateFunc)(NMSetting8021x *self,
+                                          gboolean phase2,
+                                          GError **error);
 
 typedef struct {
-	GSList *eap; /* GSList of strings */
-	char *identity;
-	char *anonymous_identity;
-	char *pac_file;
-	GBytes *ca_cert;
-	char *ca_cert_password;
-	NMSettingSecretFlags ca_cert_password_flags;
-	char *ca_path;
-	char *subject_match;
-	GSList *altsubject_matches;
-	char *domain_suffix_match;
-	GBytes *client_cert;
-	char *client_cert_password;
-	NMSettingSecretFlags client_cert_password_flags;
-	char *phase1_peapver;
-	char *phase1_peaplabel;
-	char *phase1_fast_provisioning;
-	NMSetting8021xAuthFlags phase1_auth_flags;
-	char *phase2_auth;
-	char *phase2_autheap;
-	GBytes *phase2_ca_cert;
-	char *phase2_ca_cert_password;
-	NMSettingSecretFlags phase2_ca_cert_password_flags;
-	char *phase2_ca_path;
-	char *phase2_subject_match;
-	GSList *phase2_altsubject_matches;
-	char *phase2_domain_suffix_match;
-	GBytes *phase2_client_cert;
-	char *phase2_client_cert_password;
-	NMSettingSecretFlags phase2_client_cert_password_flags;
-	char *password;
-	NMSettingSecretFlags password_flags;
-	GBytes *password_raw;
-	NMSettingSecretFlags password_raw_flags;
-	char *pin;
-	NMSettingSecretFlags pin_flags;
-	GBytes *private_key;
-	char *private_key_password;
-	NMSettingSecretFlags private_key_password_flags;
-	GBytes *phase2_private_key;
-	char *phase2_private_key_password;
-	NMSettingSecretFlags phase2_private_key_password_flags;
-	gboolean system_ca_certs;
-	gint auth_timeout;
-} NMSetting8021xPrivate;
+	const char *method;
+	EAPMethodNeedSecretsFunc ns_func;
+	EAPMethodValidateFunc v_func;
+} EAPMethodsTable;
 
-enum {
-	PROP_0,
+static EAPMethodsTable eap_methods_table[];
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMSetting8021x,
 	PROP_EAP,
 	PROP_IDENTITY,
 	PROP_ANONYMOUS_IDENTITY,
@@ -164,21 +143,558 @@ enum {
 	PROP_PIN_FLAGS,
 	PROP_SYSTEM_CA_CERTS,
 	PROP_AUTH_TIMEOUT,
+);
 
-	LAST_PROP
-};
+typedef struct {
+	GSList *eap; /* GSList of strings */
+	char *identity;
+	char *anonymous_identity;
+	char *pac_file;
+	GBytes *ca_cert;
+	char *ca_cert_password;
+	NMSettingSecretFlags ca_cert_password_flags;
+	char *ca_path;
+	char *subject_match;
+	GSList *altsubject_matches;
+	char *domain_suffix_match;
+	GBytes *client_cert;
+	char *client_cert_password;
+	NMSettingSecretFlags client_cert_password_flags;
+	char *phase1_peapver;
+	char *phase1_peaplabel;
+	char *phase1_fast_provisioning;
+	NMSetting8021xAuthFlags phase1_auth_flags;
+	char *phase2_auth;
+	char *phase2_autheap;
+	GBytes *phase2_ca_cert;
+	char *phase2_ca_cert_password;
+	NMSettingSecretFlags phase2_ca_cert_password_flags;
+	char *phase2_ca_path;
+	char *phase2_subject_match;
+	GSList *phase2_altsubject_matches;
+	char *phase2_domain_suffix_match;
+	GBytes *phase2_client_cert;
+	char *phase2_client_cert_password;
+	NMSettingSecretFlags phase2_client_cert_password_flags;
+	char *password;
+	NMSettingSecretFlags password_flags;
+	GBytes *password_raw;
+	NMSettingSecretFlags password_raw_flags;
+	char *pin;
+	NMSettingSecretFlags pin_flags;
+	GBytes *private_key;
+	char *private_key_password;
+	NMSettingSecretFlags private_key_password_flags;
+	GBytes *phase2_private_key;
+	char *phase2_private_key_password;
+	NMSettingSecretFlags phase2_private_key_password_flags;
+	gboolean system_ca_certs;
+	int auth_timeout;
+} NMSetting8021xPrivate;
+
+G_DEFINE_TYPE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING)
+
+#define NM_SETTING_802_1X_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_802_1X, NMSetting8021xPrivate))
+
+/*****************************************************************************/
 
 /**
- * nm_setting_802_1x_new:
+ * nm_setting_802_1x_check_cert_scheme:
+ * @pdata: (allow-none): the data pointer
+ * @length: the length of the data
+ * @error: (allow-none): (out): validation reason
  *
- * Creates a new #NMSetting8021x object with default values.
+ * Determines and verifies the blob type.
+ * When setting certificate properties of NMSetting8021x
+ * the blob must be not UNKNOWN (or NULL).
  *
- * Returns: the new empty #NMSetting8021x object
+ * Returns: the scheme of the blob or %NM_SETTING_802_1X_CK_SCHEME_UNKNOWN.
+ * For NULL it also returns NM_SETTING_802_1X_CK_SCHEME_UNKNOWN.
+ *
+ * Since: 1.2
  **/
-NMSetting *
-nm_setting_802_1x_new (void)
+NMSetting8021xCKScheme
+nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError **error)
 {
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_802_1X, NULL);
+	const char *data = pdata;
+	NMSetting8021xCKScheme scheme;
+	gsize prefix_length;
+
+	g_return_val_if_fail (!length || data, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+	if (!length || !data) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("binary data missing"));
+		return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+	}
+
+	if (   length >= NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)
+	    && !memcmp (data, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH))) {
+		scheme = NM_SETTING_802_1X_CK_SCHEME_PATH;
+		prefix_length = NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
+	} else if (   length >= NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11)
+	           && !memcmp (data, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11))) {
+		scheme = NM_SETTING_802_1X_CK_SCHEME_PKCS11;
+		prefix_length = NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11);
+	} else {
+		scheme = NM_SETTING_802_1X_CK_SCHEME_BLOB;
+		prefix_length = 0;
+	}
+
+	if (scheme != NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+		/* An actual URI must be NUL terminated, contain at least
+		 * one non-NUL character, and contain only one trailing NUL
+		 * chracter.
+		 * And ensure it's UTF-8 valid too so we can pass it through
+		 * D-Bus and stuff like that. */
+
+		if (data[length - 1] != '\0') {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("URI not NUL terminated"));
+			return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+		}
+		length--;
+
+		if (length <= prefix_length) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("URI is empty"));
+			return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+		}
+
+		if (!g_utf8_validate (data + prefix_length, length - prefix_length, NULL)) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("URI is not valid UTF-8"));
+			return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+		}
+	}
+
+	return scheme;
+}
+
+NMSetting8021xCKScheme
+_nm_setting_802_1x_cert_get_scheme (GBytes *bytes, GError **error)
+{
+	const char *data;
+	gsize length;
+
+	if (!bytes) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("data missing"));
+		return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+	}
+
+	data = g_bytes_get_data (bytes, &length);
+	return nm_setting_802_1x_check_cert_scheme (data, length, error);
+}
+
+static gboolean
+_cert_verify_scheme (NMSetting8021xCKScheme scheme,
+                     GBytes *bytes,
+                     GError **error)
+{
+	GError *local = NULL;
+	NMSetting8021xCKScheme scheme_detected;
+
+	nm_assert (bytes);
+
+	scheme_detected = _nm_setting_802_1x_cert_get_scheme (bytes, &local);
+	if (scheme_detected == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("certificate is invalid: %s"), local->message);
+		return FALSE;
+	}
+
+	if (scheme_detected != scheme) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("certificate detected as invalid scheme"));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+GBytes *
+_nm_setting_802_1x_cert_value_to_bytes (NMSetting8021xCKScheme scheme,
+                                        const guint8 *val_bin,
+                                        gssize val_len,
+                                        GError **error)
+{
+	gs_unref_bytes GBytes *bytes = NULL;
+	guint8 *mem;
+	gsize total_len;
+
+	nm_assert (val_bin);
+
+	switch (scheme) {
+	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
+		if (val_len < 0)
+			val_len = strlen ((char *) val_bin) + 1;
+
+		bytes = g_bytes_new (val_bin, val_len);
+		break;
+	case NM_SETTING_802_1X_CK_SCHEME_PATH:
+		if (val_len < 0)
+			val_len = strlen ((char *) val_bin) + 1;
+
+		total_len = NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + ((gsize) val_len);
+
+		mem = g_new (guint8, total_len);
+		memcpy (mem, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH));
+		memcpy (&mem[NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)], val_bin, val_len);
+		bytes = g_bytes_new_take (mem, total_len);
+		break;
+	default:
+		g_return_val_if_reached (NULL);
+	}
+
+	if (!_cert_verify_scheme (scheme, bytes, error))
+		return NULL;
+
+	return g_steal_pointer (&bytes);
+}
+
+static const char *
+_cert_get_path (GBytes *bytes)
+{
+	const guint8 *bin;
+
+	nm_assert (bytes);
+	nm_assert (g_bytes_get_size (bytes) >= NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH));
+
+	bin = g_bytes_get_data (bytes, NULL);
+
+	nm_assert (bin);
+	nm_assert (bin[g_bytes_get_size (bytes) - 1] == '\0');
+	nm_assert (g_str_has_prefix ((const char *) bin, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH));
+
+	return (const char *) &bin[NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)];
+}
+
+#define _cert_assert_scheme(cert, check_scheme, ret_val) \
+	G_STMT_START { \
+		NMSetting8021xCKScheme scheme; \
+		\
+		scheme = _nm_setting_802_1x_cert_get_scheme ((cert), NULL); \
+		if (scheme != check_scheme) { \
+			g_return_val_if_fail (scheme == check_scheme, ret_val); \
+			return ret_val; \
+		} \
+	} G_STMT_END
+
+#define _cert_impl_get_scheme(setting, cert_field) \
+	G_STMT_START { \
+		NMSetting8021x *const _setting = (setting); \
+		GBytes *_cert; \
+		\
+		g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); \
+		\
+		_cert = NM_SETTING_802_1X_GET_PRIVATE (_setting)->cert_field; \
+		\
+		return _nm_setting_802_1x_cert_get_scheme (_cert, NULL); \
+	} G_STMT_END
+
+#define _cert_impl_get_blob(setting, cert_field) \
+	G_STMT_START { \
+		NMSetting8021x *const _setting = (setting); \
+		GBytes *_cert; \
+		\
+		g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NULL); \
+		\
+		_cert = NM_SETTING_802_1X_GET_PRIVATE (_setting)->cert_field; \
+		\
+		_cert_assert_scheme (_cert, NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); \
+		\
+		return _cert; \
+	} G_STMT_END
+
+#define _cert_impl_get_path(setting, cert_field) \
+	G_STMT_START { \
+		NMSetting8021x *const _setting = (setting); \
+		GBytes *_cert; \
+		\
+		g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NULL); \
+		\
+		_cert = NM_SETTING_802_1X_GET_PRIVATE (_setting)->cert_field; \
+		\
+		_cert_assert_scheme (_cert, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); \
+		\
+		return _cert_get_path (_cert); \
+	} G_STMT_END
+
+#define _cert_impl_get_uri(setting, cert_field) \
+	G_STMT_START { \
+		NMSetting8021x *const _setting = (setting); \
+		GBytes *_cert; \
+		\
+		g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NULL); \
+		\
+		_cert = NM_SETTING_802_1X_GET_PRIVATE (_setting)->cert_field; \
+		\
+		_cert_assert_scheme (_cert, NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL); \
+		\
+		return g_bytes_get_data (_cert, NULL); \
+	} G_STMT_END
+
+static gboolean
+_cert_impl_set (NMSetting8021x *setting,
+                _PropertyEnums property,
+                const char *value,
+                const char *password,
+                NMSetting8021xCKScheme scheme,
+                NMSetting8021xCKFormat *out_format,
+                GError **error)
+{
+	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	gs_unref_bytes GBytes *cert = NULL;
+	GBytes **p_cert = NULL;
+	GBytes **p_client_cert = NULL;
+	char **p_password = NULL;
+	_PropertyEnums notify_cert = property;
+	_PropertyEnums notify_password = PROP_0;
+	_PropertyEnums notify_client_cert = PROP_0;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+	if (value) {
+		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
+		g_return_val_if_fail (NM_IN_SET (scheme, NM_SETTING_802_1X_CK_SCHEME_BLOB,
+		                                         NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                                         NM_SETTING_802_1X_CK_SCHEME_PKCS11), FALSE);
+	}
+
+	if (!value) {
+		/* coerce password to %NULL. It should be already. */
+		password = NULL;
+	}
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+	if (!value) {
+		/* pass. */
+	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
+		cert = _nm_setting_802_1x_cert_value_to_bytes (scheme, (guint8 *) value, -1, error);
+		if (!cert)
+			goto err;
+	} else {
+		gs_unref_bytes GBytes *file = NULL;
+
+		if (NM_IN_SET (property, PROP_PRIVATE_KEY,
+		                         PROP_PHASE2_PRIVATE_KEY)) {
+			file = nm_crypto_read_file (value, error);
+			if (!file)
+				goto err;
+			format = nm_crypto_verify_private_key_data (g_bytes_get_data (file, NULL),
+			                                            g_bytes_get_size (file),
+			                                            password,
+			                                            NULL,
+			                                            error);
+			if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN)
+				goto err;
+		} else {
+			if (!nm_crypto_load_and_verify_certificate (value, &format, &file, error))
+				goto err;
+		}
+
+		nm_assert (format != NM_CRYPTO_FILE_FORMAT_UNKNOWN);
+		nm_assert (file);
+
+		if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+			cert = g_steal_pointer (&file);
+			if (!_cert_verify_scheme (scheme, cert, error))
+				goto err;
+		} else {
+			cert = _nm_setting_802_1x_cert_value_to_bytes (scheme, (guint8 *) value, -1, error);
+			if (!cert)
+				goto err;
+		}
+	}
+
+	switch (property) {
+	case PROP_CA_CERT:
+	case PROP_PHASE2_CA_CERT:
+		if (   value
+		    && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11
+		    && format != NM_CRYPTO_FILE_FORMAT_X509) {
+			/* wpa_supplicant can only use raw x509 CA certs */
+			g_set_error_literal (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("CA certificate must be in X.509 format"));
+			goto err;
+		}
+		p_cert = (property == PROP_CA_CERT)
+		         ? &priv->ca_cert
+		         : &priv->phase2_ca_cert;
+		break;
+	case PROP_CLIENT_CERT:
+	case PROP_PHASE2_CLIENT_CERT:
+		if (   value
+		    && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11
+		    && !NM_IN_SET (format, NM_CRYPTO_FILE_FORMAT_X509,
+		                           NM_CRYPTO_FILE_FORMAT_PKCS12)) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("invalid certificate format"));
+			goto err;
+		}
+		p_cert = (property == PROP_CLIENT_CERT)
+		         ? &priv->client_cert
+		         : &priv->phase2_client_cert;
+		break;
+	case PROP_PRIVATE_KEY:
+		p_cert = &priv->private_key;
+		p_password = &priv->private_key_password;
+		p_client_cert = &priv->client_cert;
+		notify_password = PROP_PRIVATE_KEY_PASSWORD;
+		notify_client_cert = PROP_CLIENT_CERT;
+		break;
+	case PROP_PHASE2_PRIVATE_KEY:
+		p_cert = &priv->phase2_private_key;
+		p_password = &priv->phase2_private_key_password;
+		p_client_cert = &priv->phase2_client_cert;
+		notify_password = PROP_PHASE2_PRIVATE_KEY_PASSWORD;
+		notify_client_cert = PROP_PHASE2_CLIENT_CERT;
+		break;
+	default:
+		nm_assert_not_reached ();
+		break;
+	}
+
+	/* As required by NM and wpa_supplicant, set the client-cert
+	 * property to the same PKCS#12 data.
+	 */
+	if (   cert
+	    && p_client_cert
+	    && format == NM_CRYPTO_FILE_FORMAT_PKCS12
+	    && !nm_gbytes_equal0 (cert, *p_client_cert)) {
+		g_bytes_unref (*p_client_cert);
+		*p_client_cert = g_bytes_ref (cert);
+	} else
+		notify_client_cert = PROP_0;
+
+	if (   p_cert
+	    && !nm_gbytes_equal0 (cert, *p_cert)) {
+		g_bytes_unref (*p_cert);
+		*p_cert = g_steal_pointer (&cert);
+	} else
+		notify_cert = PROP_0;
+
+	if (   p_password
+	    && !nm_streq0 (password, *p_password)) {
+		nm_free_secret (*p_password);
+		*p_password = g_strdup (password);
+	} else
+		notify_password = PROP_0;
+
+	nm_gobject_notify_together (setting, notify_cert,
+	                                     notify_password,
+	                                     notify_client_cert);
+
+	NM_SET_OUT (out_format, _crypto_format_to_ck (format));
+	return TRUE;
+
+err:
+	g_prefix_error (error,
+	                "%s.%s: ",
+	                NM_SETTING_802_1X_SETTING_NAME,
+	                obj_properties[property]->name);
+	NM_SET_OUT (out_format, NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
+	return FALSE;
+}
+
+static NMSetting8021xCKFormat
+_cert_impl_get_key_format_from_bytes (GBytes *private_key)
+{
+	const char *path;
+	GError *error = NULL;
+
+	if (!private_key)
+		return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	switch (_nm_setting_802_1x_cert_get_scheme (private_key, NULL)) {
+	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+		if (nm_crypto_is_pkcs12_data (g_bytes_get_data (private_key, NULL),
+		                              g_bytes_get_size (private_key),
+		                              NULL))
+			return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+		return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
+	case NM_SETTING_802_1X_CK_SCHEME_PATH:
+		path = _cert_get_path (private_key);
+		if (nm_crypto_is_pkcs12_file (path, &error))
+			return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+		if (error && error->domain == G_FILE_ERROR) {
+			g_error_free (error);
+			return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+		}
+		g_error_free (error);
+		return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
+	default:
+		break;
+	}
+
+	return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+}
+#define _cert_impl_get_key_format(setting, private_key_field) \
+	({ \
+		NMSetting8021x *_setting = (setting); \
+		NMSetting8021xPrivate *_priv; \
+		\
+		g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); \
+		\
+		_priv = NM_SETTING_802_1X_GET_PRIVATE (_setting); \
+		_cert_impl_get_key_format_from_bytes (_priv->private_key_field); \
+	})
+
+static gboolean
+_cert_verify_property (GBytes *bytes,
+                       const char *prop_name,
+                       const char *password,
+                       const char *password_prop_name,
+                       GError **error)
+{
+	GError *local = NULL;
+	NMSetting8021xCKScheme scheme;
+
+	if (!bytes)
+		return TRUE;
+
+	scheme = _nm_setting_802_1x_cert_get_scheme (bytes, &local);
+	if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("certificate is invalid: %s"), local->message);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_name);
+		g_error_free (local);
+		return FALSE;
+	}
+
+	if (password && (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("password is not supported when certificate is not on a PKCS#11 token"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, password_prop_name);
+		return FALSE;
+	}
+
+	return TRUE;
 }
 
 /*****************************************************************************/
@@ -253,7 +769,7 @@ nm_setting_802_1x_add_eap_method (NMSetting8021x *setting, const char *eap)
 	}
 
 	priv->eap = g_slist_append (priv->eap, g_ascii_strdown (eap, -1));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP);
+	_notify (setting, PROP_EAP);
 	return TRUE;
 }
 
@@ -278,7 +794,7 @@ nm_setting_802_1x_remove_eap_method (NMSetting8021x *setting, guint32 i)
 
 	g_free (elt->data);
 	priv->eap = g_slist_delete_link (priv->eap, elt);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP);
+	_notify (setting, PROP_EAP);
 }
 
 /**
@@ -304,7 +820,7 @@ nm_setting_802_1x_remove_eap_method_by_value (NMSetting8021x *setting,
 	for (iter = priv->eap; iter; iter = g_slist_next (iter)) {
 		if (!strcmp (eap, (char *) iter->data)) {
 			priv->eap = g_slist_delete_link (priv->eap, iter);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP);
+			_notify (setting, PROP_EAP);
 			return TRUE;
 		}
 	}
@@ -327,7 +843,7 @@ nm_setting_802_1x_clear_eap_methods (NMSetting8021x *setting)
 	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
 	g_slist_free_full (priv->eap, g_free);
 	priv->eap = NULL;
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP);
+	_notify (setting, PROP_EAP);
 }
 
 /**
@@ -423,134 +939,6 @@ nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting)
 	return NM_SETTING_802_1X_GET_PRIVATE (setting)->system_ca_certs;
 }
 
-static NMSetting8021xCKScheme
-get_cert_scheme (GBytes *bytes, GError **error)
-{
-	const char *data;
-	gsize length;
-
-	if (!bytes) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		                     _("data missing"));
-		return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
-	}
-
-	data = g_bytes_get_data (bytes, &length);
-	return nm_setting_802_1x_check_cert_scheme (data, length, error);
-}
-
-/**
- * nm_setting_802_1x_check_cert_scheme:
- * @pdata: (allow-none): the data pointer
- * @length: the length of the data
- * @error: (allow-none): (out): validation reason
- *
- * Determines and verifies the blob type.
- * When setting certificate properties of NMSetting8021x
- * the blob must be not UNKNOWN (or NULL).
- *
- * Returns: the scheme of the blob or %NM_SETTING_802_1X_CK_SCHEME_UNKNOWN.
- * For NULL it also returns NM_SETTING_802_1X_CK_SCHEME_UNKNOWN.
- *
- * Since: 1.2
- **/
-NMSetting8021xCKScheme
-nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError **error)
-{
-	const char *data = pdata;
-	NMSetting8021xCKScheme scheme;
-	gsize prefix_length;
-
-	g_return_val_if_fail (!length || data, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
-
-	if (!length || !data) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		                     _("binary data missing"));
-		return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
-	}
-
-	if (   length >= NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)
-	    && !memcmp (data, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH))) {
-		scheme = NM_SETTING_802_1X_CK_SCHEME_PATH;
-		prefix_length = NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
-	} else if (   length >= NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11)
-	           && !memcmp (data, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11))) {
-		scheme = NM_SETTING_802_1X_CK_SCHEME_PKCS11;
-		prefix_length = NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11);
-	} else {
-		scheme = NM_SETTING_802_1X_CK_SCHEME_BLOB;
-		prefix_length = 0;
-	}
-
-	if (scheme != NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-		/* An actual URI must be NUL terminated, contain at least
-		 * one non-NUL character, and contain only one trailing NUL
-		 * chracter.
-		 * And ensure it's UTF-8 valid too so we can pass it through
-		 * D-Bus and stuff like that. */
-
-		if (data[length - 1] != '\0') {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("URI not NUL terminated"));
-			return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
-		}
-		length--;
-
-		if (length <= prefix_length) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("URI is empty"));
-			return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
-		}
-
-		if (!g_utf8_validate (data + prefix_length, length - prefix_length, NULL)) {
-			g_set_error_literal (error,
-					     NM_CONNECTION_ERROR,
-					     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-					     _("URI is not valid UTF-8"));
-			return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
-		}
-	}
-
-	return scheme;
-}
-
-static GByteArray *
-load_and_verify_certificate (const char *cert_path,
-                             NMSetting8021xCKScheme scheme,
-                             NMCryptoFileFormat *out_file_format,
-                             GError **error)
-{
-	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	GByteArray *array;
-
-	array = crypto_load_and_verify_certificate (cert_path, &format, error);
-
-	if (!array || !array->len || format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) {
-		/* the array is empty or the format is already unknown. */
-		format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-		/* If we load the file as blob, we must ensure that the binary data does not
-		 * start with file://. NMSetting8021x cannot represent blobs that start with
-		 * file://.
-		 * If that's the case, coerce the format to UNKNOWN. The callers will take care
-		 * of that and not set the blob. */
-		if (nm_setting_802_1x_check_cert_scheme (array->data, array->len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB)
-			format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	}
-
-	if (out_file_format)
-		*out_file_format = format;
-	return array;
-}
-
 /**
  * nm_setting_802_1x_get_ca_cert_scheme:
  * @setting: the #NMSetting8021x
@@ -565,9 +953,7 @@ load_and_verify_certificate (const char *cert_path,
 NMSetting8021xCKScheme
 nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting)
 {
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
-
-	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert, NULL);
+	_cert_impl_get_scheme (setting, ca_cert);
 }
 
 /**
@@ -586,14 +972,7 @@ nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting)
 GBytes *
 nm_setting_802_1x_get_ca_cert_blob (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_ca_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
-
-	return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert;
+	_cert_impl_get_blob (setting, ca_cert);
 }
 
 /**
@@ -612,16 +991,7 @@ nm_setting_802_1x_get_ca_cert_blob (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_ca_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert, NULL);
-	return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
+	_cert_impl_get_path (setting, ca_cert);
 }
 
 /**
@@ -643,35 +1013,7 @@ nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_ca_cert_uri (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_ca_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert, NULL);
-	return (const char *)data;
-}
-
-static GBytes *
-path_to_scheme_value (const char *path)
-{
-	GByteArray *array;
-	gsize len;
-
-	g_return_val_if_fail (path != NULL && path[0], NULL);
-
-	len = strlen (path);
-
-	/* Add the path scheme tag to the front, then the filename */
-	array = g_byte_array_sized_new (len + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1);
-	g_byte_array_append (array, (const guint8 *) NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH));
-	g_byte_array_append (array, (const guint8 *) path, len);
-	g_byte_array_append (array, (const guint8 *) "\0", 1);
-
-	return g_byte_array_free_to_bytes (array);
+	_cert_impl_get_uri (setting, ca_cert);
 }
 
 /**
@@ -700,65 +1042,7 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting,
                                NMSetting8021xCKFormat *out_format,
                                GError **error)
 {
-	NMSetting8021xPrivate *priv;
-	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	GByteArray *data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
-
-	if (value) {
-		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
-		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11,
-		                      FALSE);
-	}
-
-	if (out_format)
-		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
-
-	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
-
-	g_clear_pointer (&priv->ca_cert, g_bytes_unref);
-
-	if (!value) {
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT);
-		return TRUE;
-	}
-
-	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
-		priv->ca_cert = g_bytes_new (value, strlen (value) + 1);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT);
-		return TRUE;
-	}
-
-	data = load_and_verify_certificate (value, scheme, &format, error);
-	if (data) {
-		/* wpa_supplicant can only use raw x509 CA certs */
-		if (format == NM_CRYPTO_FILE_FORMAT_X509) {
-			if (out_format)
-				*out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
-
-			if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-				priv->ca_cert = g_byte_array_free_to_bytes (data);
-				data = NULL;
-			} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
-				priv->ca_cert = path_to_scheme_value (value);
-			else
-				g_assert_not_reached ();
-		} else {
-			g_set_error_literal (error,
-			             NM_CONNECTION_ERROR,
-			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			             _("CA certificate must be in X.509 format"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT);
-		}
-		if (data)
-			g_byte_array_unref (data);
-	}
-
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT);
-	return priv->ca_cert != NULL;
+	return _cert_impl_set (setting, PROP_CA_CERT, value, NULL, scheme, out_format, error);
 }
 
 /**
@@ -882,7 +1166,7 @@ nm_setting_802_1x_add_altsubject_match (NMSetting8021x *setting,
 
 	priv->altsubject_matches = g_slist_append (priv->altsubject_matches,
 	                                           g_strdup (altsubject_match));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
+	_notify (setting, PROP_ALTSUBJECT_MATCHES);
 	return TRUE;
 }
 
@@ -907,7 +1191,7 @@ nm_setting_802_1x_remove_altsubject_match (NMSetting8021x *setting, guint32 i)
 
 	g_free (elt->data);
 	priv->altsubject_matches = g_slist_delete_link (priv->altsubject_matches, elt);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
+	_notify (setting, PROP_ALTSUBJECT_MATCHES);
 }
 
 /**
@@ -934,7 +1218,7 @@ nm_setting_802_1x_remove_altsubject_match_by_value (NMSetting8021x *setting,
 	for (iter = priv->altsubject_matches; iter; iter = g_slist_next (iter)) {
 		if (!strcmp (altsubject_match, (char *) iter->data)) {
 			priv->altsubject_matches = g_slist_delete_link (priv->altsubject_matches, iter);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
+			_notify (setting, PROP_ALTSUBJECT_MATCHES);
 			return TRUE;
 		}
 	}
@@ -957,7 +1241,7 @@ nm_setting_802_1x_clear_altsubject_matches (NMSetting8021x *setting)
 	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
 	g_slist_free_full (priv->altsubject_matches, g_free);
 	priv->altsubject_matches = NULL;
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
+	_notify (setting, PROP_ALTSUBJECT_MATCHES);
 }
 
 /**
@@ -990,9 +1274,7 @@ nm_setting_802_1x_get_domain_suffix_match (NMSetting8021x *setting)
 NMSetting8021xCKScheme
 nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting)
 {
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
-
-	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert, NULL);
+	_cert_impl_get_scheme (setting, client_cert);
 }
 
 /**
@@ -1008,14 +1290,7 @@ nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting)
 GBytes *
 nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_client_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
-
-	return NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert;
+	_cert_impl_get_blob (setting, client_cert);
 }
 
 /**
@@ -1031,16 +1306,7 @@ nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_client_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert, NULL);
-	return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
+	_cert_impl_get_path (setting, client_cert);
 }
 
 /**
@@ -1062,16 +1328,7 @@ nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_client_cert_uri (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_client_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert, NULL);
-	return (const char *)data;
+	_cert_impl_get_uri (setting, client_cert);
 }
 
 /**
@@ -1104,77 +1361,7 @@ nm_setting_802_1x_set_client_cert (NMSetting8021x *setting,
                                    NMSetting8021xCKFormat *out_format,
                                    GError **error)
 {
-	NMSetting8021xPrivate *priv;
-	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	GByteArray *data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
-
-	if (value) {
-		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
-		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11,
-		                      FALSE);
-	}
-
-	if (out_format)
-		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
-
-	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
-
-	g_clear_pointer (&priv->client_cert, g_bytes_unref);
-
-	if (!value) {
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT);
-		return TRUE;
-	}
-
-	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
-		priv->client_cert = g_bytes_new (value, strlen (value) + 1);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT);
-		return TRUE;
-	}
-
-	data = load_and_verify_certificate (value, scheme, &format, error);
-	if (data) {
-		gboolean valid = FALSE;
-
-		switch (format) {
-		case NM_CRYPTO_FILE_FORMAT_X509:
-			if (out_format)
-				*out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
-			valid = TRUE;
-			break;
-		case NM_CRYPTO_FILE_FORMAT_PKCS12:
-			if (out_format)
-				*out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12;
-			valid = TRUE;
-			break;
-		default:
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("invalid certificate format"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT);
-			break;
-		}
-
-		if (valid) {
-			if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-				priv->client_cert = g_byte_array_free_to_bytes (data);
-				data = NULL;
-			} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
-				priv->client_cert = path_to_scheme_value (value);
-			else
-				g_assert_not_reached ();
-		}
-		if (data)
-			g_byte_array_unref (data);
-	}
-
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT);
-	return priv->client_cert != NULL;
+	return _cert_impl_set (setting, PROP_CLIENT_CERT, value, NULL, scheme, out_format, error);
 }
 
 /**
@@ -1343,9 +1530,7 @@ nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting)
 NMSetting8021xCKScheme
 nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting)
 {
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
-
-	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert, NULL);
+	_cert_impl_get_scheme (setting, phase2_ca_cert);
 }
 
 /**
@@ -1364,14 +1549,7 @@ nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting)
 GBytes *
 nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
-
-	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert;
+	_cert_impl_get_blob (setting, phase2_ca_cert);
 }
 
 /**
@@ -1390,16 +1568,7 @@ nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert, NULL);
-	return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
+	_cert_impl_get_path (setting, phase2_ca_cert);
 }
 
 /**
@@ -1421,16 +1590,7 @@ nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_phase2_ca_cert_uri (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert, NULL);
-	return (const char *)data;
+	_cert_impl_get_uri (setting, phase2_ca_cert);
 }
 
 /**
@@ -1459,65 +1619,7 @@ nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting,
                                       NMSetting8021xCKFormat *out_format,
                                       GError **error)
 {
-	NMSetting8021xPrivate *priv;
-	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	GByteArray *data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
-
-	if (value) {
-		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
-		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11,
-		                      FALSE);
-	}
-
-	if (out_format)
-		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
-
-	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
-
-	g_clear_pointer (&priv->phase2_ca_cert, g_bytes_unref);
-
-	if (!value) {
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT);
-		return TRUE;
-	}
-
-	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
-		priv->phase2_ca_cert = g_bytes_new (value, strlen (value) + 1);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT);
-		return TRUE;
-	}
-
-	data = load_and_verify_certificate (value, scheme, &format, error);
-	if (data) {
-		/* wpa_supplicant can only use raw x509 CA certs */
-		if (format == NM_CRYPTO_FILE_FORMAT_X509) {
-			if (out_format)
-				*out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
-
-			if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-				priv->phase2_ca_cert = g_byte_array_free_to_bytes (data);
-				data = NULL;
-			} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
-				priv->phase2_ca_cert = path_to_scheme_value (value);
-			else
-				g_assert_not_reached ();
-		} else {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("invalid certificate format"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CA_CERT);
-		}
-		if (data)
-			g_byte_array_unref (data);
-	}
-
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT);
-	return priv->phase2_ca_cert != NULL;
+	return _cert_impl_set (setting, PROP_PHASE2_CA_CERT, value, NULL, scheme, out_format, error);
 }
 
 /**
@@ -1658,7 +1760,7 @@ nm_setting_802_1x_add_phase2_altsubject_match (NMSetting8021x *setting,
 
 	priv->phase2_altsubject_matches = g_slist_append (priv->phase2_altsubject_matches,
 	                                                  g_strdup (phase2_altsubject_match));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
+	_notify (setting, PROP_PHASE2_ALTSUBJECT_MATCHES);
 	return TRUE;
 }
 
@@ -1683,7 +1785,7 @@ nm_setting_802_1x_remove_phase2_altsubject_match (NMSetting8021x *setting, guint
 
 	g_free (elt->data);
 	priv->phase2_altsubject_matches = g_slist_delete_link (priv->phase2_altsubject_matches, elt);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
+	_notify (setting, PROP_PHASE2_ALTSUBJECT_MATCHES);
 }
 
 /**
@@ -1710,7 +1812,7 @@ nm_setting_802_1x_remove_phase2_altsubject_match_by_value (NMSetting8021x *setti
 	for (iter = priv->phase2_altsubject_matches; iter; iter = g_slist_next (iter)) {
 		if (!strcmp (phase2_altsubject_match, (char *) iter->data)) {
 			priv->phase2_altsubject_matches = g_slist_delete_link (priv->phase2_altsubject_matches, iter);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
+			_notify (setting, PROP_PHASE2_ALTSUBJECT_MATCHES);
 			return TRUE;
 		}
 	}
@@ -1733,7 +1835,7 @@ nm_setting_802_1x_clear_phase2_altsubject_matches (NMSetting8021x *setting)
 	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
 	g_slist_free_full (priv->phase2_altsubject_matches, g_free);
 	priv->phase2_altsubject_matches = NULL;
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
+	_notify (setting, PROP_PHASE2_ALTSUBJECT_MATCHES);
 }
 
 /**
@@ -1753,9 +1855,7 @@ nm_setting_802_1x_clear_phase2_altsubject_matches (NMSetting8021x *setting)
 NMSetting8021xCKScheme
 nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting)
 {
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
-
-	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert, NULL);
+	_cert_impl_get_scheme (setting, phase2_client_cert);
 }
 
 /**
@@ -1771,14 +1871,7 @@ nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting)
 GBytes *
 nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
-
-	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert;
+	_cert_impl_get_blob (setting, phase2_client_cert);
 }
 
 /**
@@ -1794,16 +1887,7 @@ nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert, NULL);
-	return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
+	_cert_impl_get_path (setting, phase2_client_cert);
 }
 
 /**
@@ -1825,16 +1909,7 @@ nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_phase2_client_cert_uri (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert, NULL);
-	return (const char *)data;
+	_cert_impl_get_uri (setting, phase2_client_cert);
 }
 
 /**
@@ -1867,78 +1942,7 @@ nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting,
                                           NMSetting8021xCKFormat *out_format,
                                           GError **error)
 {
-	NMSetting8021xPrivate *priv;
-	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	GByteArray *data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
-
-	if (value) {
-		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
-		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11,
-		                      FALSE);
-	}
-
-	if (out_format)
-		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
-
-	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
-
-	g_clear_pointer (&priv->phase2_client_cert, g_bytes_unref);
-
-	if (!value) {
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
-		return TRUE;
-	}
-
-	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
-		priv->phase2_client_cert = g_bytes_new (value, strlen (value) + 1);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
-		return TRUE;
-	}
-
-	data = load_and_verify_certificate (value, scheme, &format, error);
-	if (data) {
-		gboolean valid = FALSE;
-
-		/* wpa_supplicant can only use raw x509 CA certs */
-		switch (format) {
-		case NM_CRYPTO_FILE_FORMAT_X509:
-			if (out_format)
-				*out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
-			valid = TRUE;
-			break;
-		case NM_CRYPTO_FILE_FORMAT_PKCS12:
-			if (out_format)
-				*out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12;
-			valid = TRUE;
-			break;
-		default:
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("invalid certificate format"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
-			break;
-		}
-
-		if (valid) {
-			if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-				priv->phase2_client_cert = g_byte_array_free_to_bytes (data);
-				data = NULL;
-			} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
-				priv->phase2_client_cert = path_to_scheme_value (value);
-			else
-				g_assert_not_reached ();
-		}
-		if (data)
-			g_byte_array_unref (data);
-	}
-
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
-	return priv->phase2_client_cert != NULL;
+	return _cert_impl_set (setting, PROP_PHASE2_CLIENT_CERT, value, NULL, scheme, out_format, error);
 }
 
 /**
@@ -2083,9 +2087,7 @@ nm_setting_802_1x_get_pin_flags (NMSetting8021x *setting)
 NMSetting8021xCKScheme
 nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting)
 {
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
-
-	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key, NULL);
+	_cert_impl_get_scheme (setting, private_key);
 }
 
 /**
@@ -2105,14 +2107,7 @@ nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting)
 GBytes *
 nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_private_key_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
-
-	return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key;
+	_cert_impl_get_blob (setting, private_key);
 }
 
 /**
@@ -2128,16 +2123,7 @@ nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_private_key_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key, NULL);
-	return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
+	_cert_impl_get_path (setting, private_key);
 }
 
 /**
@@ -2159,42 +2145,7 @@ nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_private_key_uri (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_private_key_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key, NULL);
-	return (const char *)data;
-}
-
-static void
-free_secure_bytes (gpointer data)
-{
-	GByteArray *array = data;
-
-	memset (array->data, 0, array->len);
-	g_byte_array_unref (array);
-}
-
-static GBytes *
-file_to_secure_bytes (const char *filename)
-{
-	char *contents;
-	GByteArray *array = NULL;
-	gsize length = 0;
-
-	if (g_file_get_contents (filename, &contents, &length, NULL)) {
-		array = g_byte_array_sized_new (length);
-		g_byte_array_append (array, (guint8 *) contents, length);
-		memset (contents, 0, length);
-		g_free (contents);
-		return g_bytes_new_with_free_func (array->data, array->len, free_secure_bytes, array);
-	}
-	return NULL;
+	_cert_impl_get_uri (setting, private_key);
 }
 
 /**
@@ -2244,88 +2195,7 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting,
                                    NMSetting8021xCKFormat *out_format,
                                    GError **error)
 {
-	NMSetting8021xPrivate *priv;
-	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	gboolean password_changed = FALSE;
-	GError *local_err = NULL;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
-
-	if (value) {
-		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
-		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11,
-		                      FALSE);
-	}
-
-	if (out_format)
-		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
-
-	/* Ensure the private key is a recognized format and if the password was
-	 * given, that it decrypts the private key.
-	 */
-	if (value && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
-		format = crypto_verify_private_key (value, password, NULL, &local_err);
-		if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     local_err ? local_err->message : _("invalid private key"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY);
-			g_clear_error (&local_err);
-			return FALSE;
-		}
-	}
-
-	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
-
-	if (value == NULL) {
-		if (priv->private_key) {
-			g_clear_pointer (&priv->private_key, g_bytes_unref);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY);
-		}
-		if (nm_clear_g_free (&priv->private_key_password))
-			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
-		return TRUE;
-	}
-
-	/* this makes password self-assignment safe. */
-	if (!nm_streq0 (priv->private_key_password, password)) {
-		g_free (priv->private_key_password);
-		priv->private_key_password = g_strdup (password);
-		password_changed = TRUE;
-	}
-
-	g_bytes_unref (priv->private_key);
-	if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-		/* FIXME: potential race after verifying the private key above */
-		/* FIXME: ensure blob doesn't start with file:// */
-		priv->private_key = file_to_secure_bytes (value);
-		nm_assert (priv->private_key);
-	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
-		priv->private_key = path_to_scheme_value (value);
-	else {
-		nm_assert (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11);
-		priv->private_key = g_bytes_new (value, strlen (value) + 1);
-	}
-
-	/* As required by NM and wpa_supplicant, set the client-cert
-	 * property to the same PKCS#12 data.
-	 */
-	if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) {
-		if (priv->client_cert)
-			g_bytes_unref (priv->client_cert);
-		priv->client_cert = g_bytes_ref (priv->private_key);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT);
-	}
-
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY);
-	if (password_changed)
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
-
-	NM_SET_OUT (out_format, (NMSetting8021xCKFormat) format);
-	return priv->private_key != NULL;
+	return _cert_impl_set (setting, PROP_PRIVATE_KEY, value, password, scheme, out_format, error);
 }
 
 /**
@@ -2369,38 +2239,7 @@ nm_setting_802_1x_get_private_key_password_flags (NMSetting8021x *setting)
 NMSetting8021xCKFormat
 nm_setting_802_1x_get_private_key_format (NMSetting8021x *setting)
 {
-	NMSetting8021xPrivate *priv;
-	const char *path;
-	GError *error = NULL;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
-	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
-
-	if (!priv->private_key)
-		return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
-
-	switch (nm_setting_802_1x_get_private_key_scheme (setting)) {
-	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
-		if (crypto_is_pkcs12_data (g_bytes_get_data (priv->private_key, NULL),
-		                           g_bytes_get_size (priv->private_key),
-		                           NULL))
-			return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
-		return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
-	case NM_SETTING_802_1X_CK_SCHEME_PATH:
-		path = nm_setting_802_1x_get_private_key_path (setting);
-		if (crypto_is_pkcs12_file (path, &error))
-			return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
-		if (error && error->domain == G_FILE_ERROR) {
-			g_error_free (error);
-			return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
-		}
-		g_error_free (error);
-		return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
-	default:
-		break;
-	}
-
-	return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+	return _cert_impl_get_key_format (setting, private_key);
 }
 
 /**
@@ -2451,9 +2290,7 @@ nm_setting_802_1x_get_phase2_private_key_password_flags (NMSetting8021x *setting
 NMSetting8021xCKScheme
 nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting)
 {
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
-
-	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key, NULL);
+	_cert_impl_get_scheme (setting, phase2_private_key);
 }
 
 /**
@@ -2473,14 +2310,7 @@ nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting)
 GBytes *
 nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
-
-	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key;
+	_cert_impl_get_blob (setting, phase2_private_key);
 }
 
 /**
@@ -2496,16 +2326,7 @@ nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key, NULL);
-	return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
+	_cert_impl_get_path (setting, phase2_private_key);
 }
 
 /**
@@ -2527,16 +2348,7 @@ nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting)
 const char *
 nm_setting_802_1x_get_phase2_private_key_uri (NMSetting8021x *setting)
 {
-	NMSetting8021xCKScheme scheme;
-	gconstpointer data;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
-
-	scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting);
-	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL);
-
-	data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key, NULL);
-	return (const char *)data;
+	_cert_impl_get_uri (setting, phase2_private_key);
 }
 
 /**
@@ -2586,88 +2398,7 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting,
                                           NMSetting8021xCKFormat *out_format,
                                           GError **error)
 {
-	NMSetting8021xPrivate *priv;
-	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	gboolean password_changed = FALSE;
-	GError *local_err = NULL;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
-
-	if (value) {
-		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
-		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11,
-		                      FALSE);
-	}
-
-	if (out_format)
-		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
-
-	/* Ensure the private key is a recognized format and if the password was
-	 * given, that it decrypts the private key.
-	 */
-	if (value && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
-		format = crypto_verify_private_key (value, password, NULL, &local_err);
-		if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     local_err ? local_err->message : _("invalid phase2 private key"));
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
-			g_clear_error (&local_err);
-			return FALSE;
-		}
-	}
-
-	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
-
-	if (value == NULL) {
-		if (priv->phase2_private_key) {
-			g_clear_pointer (&priv->phase2_private_key, g_bytes_unref);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
-		}
-		if (nm_clear_g_free (&priv->phase2_private_key_password))
-			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
-		return TRUE;
-	}
-
-	/* this makes password self-assignment safe. */
-	if (!nm_streq0 (priv->phase2_private_key_password, password)) {
-		g_free (priv->phase2_private_key_password);
-		priv->phase2_private_key_password = g_strdup (password);
-		password_changed = TRUE;
-	}
-
-	if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-		/* FIXME: potential race after verifying the private key above */
-		/* FIXME: ensure blob doesn't start with file:// */
-		priv->phase2_private_key = file_to_secure_bytes (value);
-		nm_assert (priv->phase2_private_key);
-	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
-		priv->phase2_private_key = path_to_scheme_value (value);
-	else {
-		nm_assert (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11);
-		priv->phase2_private_key = g_bytes_new (value, strlen (value) + 1);
-	}
-
-	/* As required by NM and wpa_supplicant, set the client-cert
-	 * property to the same PKCS#12 data.
-	 */
-	if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) {
-		if (priv->phase2_client_cert)
-			g_bytes_unref (priv->phase2_client_cert);
-
-		priv->phase2_client_cert = g_bytes_ref (priv->phase2_private_key);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
-	}
-
-	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
-	if (password_changed)
-		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
-
-	NM_SET_OUT (out_format, (NMSetting8021xCKFormat) format);
-	return priv->phase2_private_key != NULL;
+	return _cert_impl_set (setting, PROP_PHASE2_PRIVATE_KEY, value, password, scheme, out_format, error);
 }
 
 /**
@@ -2680,38 +2411,7 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting,
 NMSetting8021xCKFormat
 nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting)
 {
-	NMSetting8021xPrivate *priv;
-	const char *path;
-	GError *error = NULL;
-
-	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
-	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
-
-	if (!priv->phase2_private_key)
-		return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
-
-	switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) {
-	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
-		if (crypto_is_pkcs12_data (g_bytes_get_data (priv->phase2_private_key, NULL),
-		                           g_bytes_get_size (priv->phase2_private_key),
-		                           NULL))
-			return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
-		return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
-	case NM_SETTING_802_1X_CK_SCHEME_PATH:
-		path = nm_setting_802_1x_get_phase2_private_key_path (setting);
-		if (crypto_is_pkcs12_file (path, &error))
-			return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
-		if (error && error->domain == G_FILE_ERROR) {
-			g_error_free (error);
-			return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
-		}
-		g_error_free (error);
-		return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
-	default:
-		break;
-	}
-
-	return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+	return _cert_impl_get_key_format (setting, phase2_private_key);
 }
 
 /**
@@ -2725,7 +2425,7 @@ nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting)
  *
  * Since: 1.8
  **/
-gint
+int
 nm_setting_802_1x_get_auth_timeout (NMSetting8021x *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), 0);
@@ -2733,6 +2433,8 @@ nm_setting_802_1x_get_auth_timeout (NMSetting8021x *setting)
 	return NM_SETTING_802_1X_GET_PRIVATE (setting)->auth_timeout;
 }
 
+/*****************************************************************************/
+
 static void
 need_secrets_password (NMSetting8021x *self,
                        GPtrArray *secrets,
@@ -2777,11 +2479,11 @@ need_private_key_password (GBytes *blob,
 	/* Private key password is required */
 	if (password) {
 		if (path)
-			format = crypto_verify_private_key (path, password, NULL, NULL);
+			format = nm_crypto_verify_private_key (path, password, NULL, NULL);
 		else if (blob)
-			format = crypto_verify_private_key_data (g_bytes_get_data (blob, NULL),
-			                                         g_bytes_get_size (blob),
-			                                         password, NULL, NULL);
+			format = nm_crypto_verify_private_key_data (g_bytes_get_data (blob, NULL),
+			                                            g_bytes_get_size (blob),
+			                                            password, NULL, NULL);
 		else
 			return FALSE;
 	}
@@ -2896,9 +2598,9 @@ verify_tls (NMSetting8021x *self, gboolean phase2, GError **error)
 		}
 
 		/* If the private key is PKCS#12, check that it matches the client cert */
-		if (crypto_is_pkcs12_data (g_bytes_get_data (priv->phase2_private_key, NULL),
-		                           g_bytes_get_size (priv->phase2_private_key),
-		                           NULL)) {
+		if (nm_crypto_is_pkcs12_data (g_bytes_get_data (priv->phase2_private_key, NULL),
+		                              g_bytes_get_size (priv->phase2_private_key),
+		                              NULL)) {
 			if (!g_bytes_equal (priv->phase2_private_key, priv->phase2_client_cert)) {
 				g_set_error (error,
 				             NM_CONNECTION_ERROR,
@@ -2944,9 +2646,9 @@ verify_tls (NMSetting8021x *self, gboolean phase2, GError **error)
 		}
 
 		/* If the private key is PKCS#12, check that it matches the client cert */
-		if (crypto_is_pkcs12_data (g_bytes_get_data (priv->private_key, NULL),
-		                           g_bytes_get_size (priv->private_key),
-		                           NULL)) {
+		if (nm_crypto_is_pkcs12_data (g_bytes_get_data (priv->private_key, NULL),
+		                              g_bytes_get_size (priv->private_key),
+		                              NULL)) {
 			if (!g_bytes_equal (priv->private_key, priv->client_cert)) {
 				g_set_error (error,
 				             NM_CONNECTION_ERROR,
@@ -3054,43 +2756,6 @@ verify_identity (NMSetting8021x *self, gboolean phase2, GError **error)
 	return TRUE;
 }
 
-/* Implemented below... */
-static void need_secrets_phase2 (NMSetting8021x *self,
-                                 GPtrArray *secrets,
-                                 gboolean phase2);
-
-typedef void (*EAPMethodNeedSecretsFunc) (NMSetting8021x *self,
-                                          GPtrArray *secrets,
-                                          gboolean phase2);
-
-typedef gboolean (*EAPMethodValidateFunc)(NMSetting8021x *self,
-                                          gboolean phase2,
-                                          GError **error);
-
-typedef struct {
-	const char *method;
-	EAPMethodNeedSecretsFunc ns_func;
-	EAPMethodValidateFunc v_func;
-} EAPMethodsTable;
-
-static EAPMethodsTable eap_methods_table[] = {
-	{ "leap", need_secrets_password, verify_identity },
-	{ "pwd", need_secrets_password, verify_identity },
-	{ "md5", need_secrets_password, verify_identity },
-	{ "pap", need_secrets_password, verify_identity },
-	{ "chap", need_secrets_password, verify_identity },
-	{ "mschap", need_secrets_password, verify_identity },
-	{ "mschapv2", need_secrets_password, verify_identity },
-	{ "fast", need_secrets_password, verify_identity },
-	{ "tls", need_secrets_tls, verify_tls },
-	{ "peap", need_secrets_phase2, verify_ttls },
-	{ "ttls", need_secrets_phase2, verify_ttls },
-	{ "sim", need_secrets_sim, NULL },
-	{ "gtc", need_secrets_password, verify_identity },
-	{ "otp", NULL, NULL },  // FIXME: implement
-	{ NULL, NULL, NULL }
-};
-
 static void
 need_secrets_phase2 (NMSetting8021x *self,
                      GPtrArray *secrets,
@@ -3124,91 +2789,31 @@ need_secrets_phase2 (NMSetting8021x *self,
 	}
 }
 
-static GPtrArray *
-need_secrets (NMSetting *setting)
-{
-	NMSetting8021x *self = NM_SETTING_802_1X (setting);
-	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
-	GSList *iter;
-	GPtrArray *secrets;
-	gboolean eap_method_found = FALSE;
-
-	secrets = g_ptr_array_sized_new (4);
-
-	/* Ask each configured EAP method if it needs secrets */
-	for (iter = priv->eap; iter && !eap_method_found; iter = g_slist_next (iter)) {
-		const char *method = (const char *) iter->data;
-		int i;
-
-		for (i = 0; eap_methods_table[i].method; i++) {
-			if (eap_methods_table[i].ns_func == NULL)
-				continue;
-			if (!strcmp (eap_methods_table[i].method, method)) {
-				(*eap_methods_table[i].ns_func) (self, secrets, FALSE);
-
-				/* Only break out of the outer loop if this EAP method
-				 * needed secrets.
-				 */
-				if (secrets->len > 0)
-					eap_method_found = TRUE;
-				break;
-			}
-		}
-	}
-
-	if (secrets->len == 0) {
-		g_ptr_array_free (secrets, TRUE);
-		secrets = NULL;
-	}
-
-	return secrets;
-}
-
-static gboolean
-verify_cert (GBytes *bytes, const char *prop_name,
-             const char *password, const char *password_prop_name, GError **error)
-{
-	GError *local = NULL;
-	NMSetting8021xCKScheme scheme;
-
-	if (bytes)
-		scheme = get_cert_scheme (bytes, &local);
-	else
-		return TRUE;
-
-	if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
-		g_set_error (error,
-		             NM_CONNECTION_ERROR,
-		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		             _("certificate is invalid: %s"), local->message);
-		g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_name);
-		g_error_free (local);
-		return FALSE;
-	}
-
-	if (password && (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11)) {
-		g_set_error (error,
-		             NM_CONNECTION_ERROR,
-		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		             _("password is not supported when certificate is not on a PKCS#11 token"));
-		g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, password_prop_name);
-		return FALSE;
-	}
-
-	return TRUE;
-}
+static EAPMethodsTable eap_methods_table[] = {
+	{ "leap", need_secrets_password, verify_identity },
+	{ "pwd", need_secrets_password, verify_identity },
+	{ "md5", need_secrets_password, verify_identity },
+	{ "pap", need_secrets_password, verify_identity },
+	{ "chap", need_secrets_password, verify_identity },
+	{ "mschap", need_secrets_password, verify_identity },
+	{ "mschapv2", need_secrets_password, verify_identity },
+	{ "fast", need_secrets_password, verify_identity },
+	{ "tls", need_secrets_tls, verify_tls },
+	{ "peap", need_secrets_phase2, verify_ttls },
+	{ "ttls", need_secrets_phase2, verify_ttls },
+	{ "sim", need_secrets_sim, NULL },
+	{ "gtc", need_secrets_password, verify_identity },
+	{ "otp", NULL, NULL },  // FIXME: implement
+	{ "external", NULL, NULL },
+	{ NULL, NULL, NULL }
+};
 
 static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSetting8021x *self = NM_SETTING_802_1X (setting);
 	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
-	const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd", NULL };
-	const char *valid_phase1_peapver[] = { "0", "1", NULL };
-	const char *valid_phase1_peaplabel[] = { "0", "1", NULL };
-	const char *valid_phase1_fast_pac[] = { "0", "1", "2", "3", NULL };
-	const char *valid_phase2_auth[] = { "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls", NULL };
-	const char *valid_phase2_autheap[] = { "md5", "mschapv2", "otp", "gtc", "tls", NULL };
+	const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd", "external", NULL };
 	GSList *iter;
 
 	if (error)
@@ -3248,7 +2853,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		}
 	}
 
-	if (priv->phase1_peapver && !g_strv_contains (valid_phase1_peapver, priv->phase1_peapver)) {
+	if (!NM_IN_STRSET (priv->phase1_peapver, NULL,
+	                                         "0",
+	                                         "1")) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -3258,7 +2865,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (priv->phase1_peaplabel && !g_strv_contains (valid_phase1_peaplabel, priv->phase1_peaplabel)) {
+	if (!NM_IN_STRSET (priv->phase1_peaplabel, NULL,
+	                                           "0",
+	                                           "1")) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -3268,7 +2877,11 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (priv->phase1_fast_provisioning && !g_strv_contains (valid_phase1_fast_pac, priv->phase1_fast_provisioning)) {
+	if (!NM_IN_STRSET (priv->phase1_fast_provisioning, NULL,
+	                                                   "0",
+	                                                   "1",
+	                                                   "2",
+	                                                   "3")) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -3287,7 +2900,15 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (priv->phase2_auth && !g_strv_contains (valid_phase2_auth, priv->phase2_auth)) {
+	if (!NM_IN_STRSET (priv->phase2_auth, NULL,
+	                                      "pap",
+	                                      "chap",
+	                                      "mschap",
+	                                      "mschapv2",
+	                                      "gtc",
+	                                      "otp",
+	                                      "md5",
+	                                      "tls")) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -3297,7 +2918,12 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (priv->phase2_autheap && !g_strv_contains (valid_phase2_autheap, priv->phase2_autheap)) {
+	if (!NM_IN_STRSET (priv->phase2_autheap, NULL,
+	                                         "md5",
+	                                         "mschapv2",
+	                                         "otp",
+	                                         "gtc",
+	                                         "tls")) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -3307,307 +2933,231 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (!verify_cert (priv->ca_cert, NM_SETTING_802_1X_CA_CERT,
-	                  priv->ca_cert_password, NM_SETTING_802_1X_CA_CERT_PASSWORD, error))
+	if (!_cert_verify_property (priv->ca_cert,
+	                            NM_SETTING_802_1X_CA_CERT,
+	                            priv->ca_cert_password,
+	                            NM_SETTING_802_1X_CA_CERT_PASSWORD,
+	                            error))
 		return FALSE;
-	if (!verify_cert (priv->phase2_ca_cert, NM_SETTING_802_1X_PHASE2_CA_CERT,
-	                  priv->phase2_ca_cert_password, NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD, error))
+	if (!_cert_verify_property (priv->phase2_ca_cert,
+	                            NM_SETTING_802_1X_PHASE2_CA_CERT,
+	                            priv->phase2_ca_cert_password,
+	                            NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD,
+	                            error))
 		return FALSE;
 
-	if (!verify_cert (priv->client_cert, NM_SETTING_802_1X_CLIENT_CERT,
-	                  priv->client_cert_password, NM_SETTING_802_1X_CLIENT_CERT_PASSWORD, error))
+	if (!_cert_verify_property (priv->client_cert,
+	                            NM_SETTING_802_1X_CLIENT_CERT,
+	                            priv->client_cert_password,
+	                            NM_SETTING_802_1X_CLIENT_CERT_PASSWORD,
+	                            error))
 		return FALSE;
-	if (!verify_cert (priv->phase2_client_cert, NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-	                  priv->phase2_client_cert_password, NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD, error))
+	if (!_cert_verify_property (priv->phase2_client_cert,
+	                            NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
+	                            priv->phase2_client_cert_password,
+	                            NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD,
+	                            error))
 		return FALSE;
 
-	if (!verify_cert (priv->private_key, NM_SETTING_802_1X_PRIVATE_KEY, NULL, NULL, error))
+	if (!_cert_verify_property (priv->private_key,
+	                            NM_SETTING_802_1X_PRIVATE_KEY,
+	                            NULL,
+	                            NULL,
+	                            error))
 		return FALSE;
-	if (!verify_cert (priv->phase2_private_key, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, NULL, NULL, error))
+	if (!_cert_verify_property (priv->phase2_private_key,
+	                            NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
+	                            NULL,
+	                            NULL,
+	                            error))
 		return FALSE;
 
-	/* FIXME: finish */
-
 	return TRUE;
 }
 
-static void
-nm_setting_802_1x_init (NMSetting8021x *setting)
-{
-}
+/*****************************************************************************/
 
-static void
-finalize (GObject *object)
+static GPtrArray *
+need_secrets (NMSetting *setting)
 {
-	NMSetting8021x *self = NM_SETTING_802_1X (object);
+	NMSetting8021x *self = NM_SETTING_802_1X (setting);
 	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+	GSList *iter;
+	GPtrArray *secrets;
+	gboolean eap_method_found = FALSE;
 
-	/* Strings first. g_free() already checks for NULLs so we don't have to */
-
-	g_free (priv->identity);
-	g_free (priv->anonymous_identity);
-	g_free (priv->ca_path);
-	g_free (priv->subject_match);
-	g_free (priv->domain_suffix_match);
-	g_free (priv->phase1_peapver);
-	g_free (priv->phase1_peaplabel);
-	g_free (priv->phase1_fast_provisioning);
-	g_free (priv->phase2_auth);
-	g_free (priv->phase2_autheap);
-	g_free (priv->phase2_ca_path);
-	g_free (priv->phase2_subject_match);
-	g_free (priv->phase2_domain_suffix_match);
-	g_free (priv->password);
-	if (priv->password_raw)
-		g_bytes_unref (priv->password_raw);
-	g_free (priv->pin);
+	secrets = g_ptr_array_sized_new (4);
 
-	g_slist_free_full (priv->eap, g_free);
-	g_slist_free_full (priv->altsubject_matches, g_free);
-	g_slist_free_full (priv->phase2_altsubject_matches, g_free);
+	/* Ask each configured EAP method if it needs secrets */
+	for (iter = priv->eap; iter && !eap_method_found; iter = g_slist_next (iter)) {
+		const char *method = (const char *) iter->data;
+		int i;
 
-	if (priv->ca_cert)
-		g_bytes_unref (priv->ca_cert);
-	g_free (priv->ca_cert_password);
-	if (priv->client_cert)
-		g_bytes_unref (priv->client_cert);
-	g_free (priv->client_cert_password);
-	if (priv->private_key)
-		g_bytes_unref (priv->private_key);
-	g_free (priv->private_key_password);
-	if (priv->phase2_ca_cert)
-		g_bytes_unref (priv->phase2_ca_cert);
-	g_free (priv->phase2_ca_cert_password);
-	if (priv->phase2_client_cert)
-		g_bytes_unref (priv->phase2_client_cert);
-	g_free (priv->phase2_client_cert_password);
-	if (priv->phase2_private_key)
-		g_bytes_unref (priv->phase2_private_key);
-	g_free (priv->phase2_private_key_password);
+		for (i = 0; eap_methods_table[i].method; i++) {
+			if (eap_methods_table[i].ns_func == NULL)
+				continue;
+			if (!strcmp (eap_methods_table[i].method, method)) {
+				(*eap_methods_table[i].ns_func) (self, secrets, FALSE);
 
-	G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object);
-}
+				/* Only break out of the outer loop if this EAP method
+				 * needed secrets.
+				 */
+				if (secrets->len > 0)
+					eap_method_found = TRUE;
+				break;
+			}
+		}
+	}
 
-static GBytes *
-set_cert_prop_helper (const GValue *value, const char *prop_name, GError **error)
-{
-	gboolean valid;
-	GBytes *bytes = NULL;
-
-	bytes = g_value_dup_boxed (value);
-	/* Verify the new data */
-	if (bytes) {
-		valid = verify_cert (bytes, prop_name, NULL, NULL, error);
-		if (!valid)
-			g_clear_pointer (&bytes, g_bytes_unref);
+	if (secrets->len == 0) {
+		g_ptr_array_free (secrets, TRUE);
+		return NULL;
 	}
-	return bytes;
+
+	return secrets;
 }
 
+/*****************************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSetting8021x *setting = NM_SETTING_802_1X (object);
 	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
-	GError *error = NULL;
 
 	switch (prop_id) {
 	case PROP_EAP:
-		g_slist_free_full (priv->eap, g_free);
-		priv->eap = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
+		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->eap, TRUE));
 		break;
 	case PROP_IDENTITY:
-		g_free (priv->identity);
-		priv->identity = g_value_dup_string (value);
+		g_value_set_string (value, priv->identity);
 		break;
 	case PROP_ANONYMOUS_IDENTITY:
-		g_free (priv->anonymous_identity);
-		priv->anonymous_identity = g_value_dup_string (value);
+		g_value_set_string (value, priv->anonymous_identity);
 		break;
 	case PROP_PAC_FILE:
-		g_free (priv->pac_file);
-		priv->pac_file = g_value_dup_string (value);
+		g_value_set_string (value, priv->pac_file);
 		break;
 	case PROP_CA_CERT:
-		if (priv->ca_cert)
-			g_bytes_unref (priv->ca_cert);
-		priv->ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CA_CERT, &error);
-		if (error) {
-			g_warning ("Error setting certificate (invalid data): %s", error->message);
-			g_error_free (error);
-		}
+		g_value_set_boxed (value, priv->ca_cert);
 		break;
 	case PROP_CA_CERT_PASSWORD:
-		g_free (priv->ca_cert_password);
-		priv->ca_cert_password = g_value_dup_string (value);
+		g_value_set_string (value, priv->ca_cert_password);
 		break;
 	case PROP_CA_CERT_PASSWORD_FLAGS:
-		priv->ca_cert_password_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->ca_cert_password_flags);
 		break;
 	case PROP_CA_PATH:
-		g_free (priv->ca_path);
-		priv->ca_path = g_value_dup_string (value);
+		g_value_set_string (value, priv->ca_path);
 		break;
 	case PROP_SUBJECT_MATCH:
-		g_free (priv->subject_match);
-		priv->subject_match = nm_strdup_not_empty (g_value_get_string (value));
+		g_value_set_string (value, priv->subject_match);
 		break;
 	case PROP_ALTSUBJECT_MATCHES:
-		g_slist_free_full (priv->altsubject_matches, g_free);
-		priv->altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
+		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->altsubject_matches, TRUE));
 		break;
 	case PROP_DOMAIN_SUFFIX_MATCH:
-		g_free (priv->domain_suffix_match);
-		priv->domain_suffix_match = nm_strdup_not_empty (g_value_get_string (value));
+		g_value_set_string (value, priv->domain_suffix_match);
 		break;
 	case PROP_CLIENT_CERT:
-		if (priv->client_cert)
-			g_bytes_unref (priv->client_cert);
-		priv->client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CLIENT_CERT, &error);
-		if (error) {
-			g_warning ("Error setting certificate (invalid data): %s", error->message);
-			g_error_free (error);
-		}
+		g_value_set_boxed (value, priv->client_cert);
 		break;
 	case PROP_CLIENT_CERT_PASSWORD:
-		g_free (priv->client_cert_password);
-		priv->client_cert_password = g_value_dup_string (value);
+		g_value_set_string (value, priv->client_cert_password);
 		break;
 	case PROP_CLIENT_CERT_PASSWORD_FLAGS:
-		priv->client_cert_password_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->client_cert_password_flags);
 		break;
 	case PROP_PHASE1_PEAPVER:
-		g_free (priv->phase1_peapver);
-		priv->phase1_peapver = g_value_dup_string (value);
+		g_value_set_string (value, priv->phase1_peapver);
 		break;
 	case PROP_PHASE1_PEAPLABEL:
-		g_free (priv->phase1_peaplabel);
-		priv->phase1_peaplabel = g_value_dup_string (value);
+		g_value_set_string (value, priv->phase1_peaplabel);
 		break;
 	case PROP_PHASE1_FAST_PROVISIONING:
-		g_free (priv->phase1_fast_provisioning);
-		priv->phase1_fast_provisioning = g_value_dup_string (value);
+		g_value_set_string (value, priv->phase1_fast_provisioning);
 		break;
 	case PROP_PHASE1_AUTH_FLAGS:
-		priv->phase1_auth_flags = g_value_get_uint (value);
+		g_value_set_uint (value, priv->phase1_auth_flags);
 		break;
 	case PROP_PHASE2_AUTH:
-		g_free (priv->phase2_auth);
-		priv->phase2_auth = g_value_dup_string (value);
+		g_value_set_string (value, priv->phase2_auth);
 		break;
 	case PROP_PHASE2_AUTHEAP:
-		g_free (priv->phase2_autheap);
-		priv->phase2_autheap = g_value_dup_string (value);
+		g_value_set_string (value, priv->phase2_autheap);
 		break;
 	case PROP_PHASE2_CA_CERT:
-		if (priv->phase2_ca_cert)
-			g_bytes_unref (priv->phase2_ca_cert);
-		priv->phase2_ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CA_CERT, &error);
-		if (error) {
-			g_warning ("Error setting certificate (invalid data): %s", error->message);
-			g_error_free (error);
-		}
+		g_value_set_boxed (value, priv->phase2_ca_cert);
 		break;
 	case PROP_PHASE2_CA_CERT_PASSWORD:
-		g_free (priv->phase2_ca_cert_password);
-		priv->phase2_ca_cert_password = g_value_dup_string (value);
+		g_value_set_string (value, priv->phase2_ca_cert_password);
 		break;
 	case PROP_PHASE2_CA_CERT_PASSWORD_FLAGS:
-		priv->phase2_ca_cert_password_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->phase2_ca_cert_password_flags);
 		break;
 	case PROP_PHASE2_CA_PATH:
-		g_free (priv->phase2_ca_path);
-		priv->phase2_ca_path = g_value_dup_string (value);
+		g_value_set_string (value, priv->phase2_ca_path);
 		break;
 	case PROP_PHASE2_SUBJECT_MATCH:
-		g_free (priv->phase2_subject_match);
-		priv->phase2_subject_match = nm_strdup_not_empty (g_value_get_string (value));
+		g_value_set_string (value, priv->phase2_subject_match);
 		break;
 	case PROP_PHASE2_ALTSUBJECT_MATCHES:
-		g_slist_free_full (priv->phase2_altsubject_matches, g_free);
-		priv->phase2_altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
+		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->phase2_altsubject_matches, TRUE));
 		break;
 	case PROP_PHASE2_DOMAIN_SUFFIX_MATCH:
-		g_free (priv->phase2_domain_suffix_match);
-		priv->phase2_domain_suffix_match = nm_strdup_not_empty (g_value_get_string (value));
+		g_value_set_string (value, priv->phase2_domain_suffix_match);
 		break;
 	case PROP_PHASE2_CLIENT_CERT:
-
-		if (priv->phase2_client_cert)
-			g_bytes_unref (priv->phase2_client_cert);
-		priv->phase2_client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &error);
-		if (error) {
-			g_warning ("Error setting certificate (invalid data): %s", error->message);
-			g_error_free (error);
-		}
+		g_value_set_boxed (value, priv->phase2_client_cert);
 		break;
 	case PROP_PHASE2_CLIENT_CERT_PASSWORD:
-		g_free (priv->phase2_client_cert_password);
-		priv->phase2_client_cert_password = g_value_dup_string (value);
+		g_value_set_string (value, priv->phase2_client_cert_password);
 		break;
 	case PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS:
-		priv->phase2_client_cert_password_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->phase2_client_cert_password_flags);
 		break;
 	case PROP_PASSWORD:
-		g_free (priv->password);
-		priv->password = g_value_dup_string (value);
+		g_value_set_string (value, priv->password);
 		break;
 	case PROP_PASSWORD_FLAGS:
-		priv->password_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->password_flags);
 		break;
 	case PROP_PASSWORD_RAW:
-		if (priv->password_raw)
-			g_bytes_unref (priv->password_raw);
-		priv->password_raw = g_value_dup_boxed (value);
+		g_value_set_boxed (value, priv->password_raw);
 		break;
 	case PROP_PASSWORD_RAW_FLAGS:
-		priv->password_raw_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->password_raw_flags);
 		break;
 	case PROP_PRIVATE_KEY:
-		if (priv->private_key)
-			g_bytes_unref (priv->private_key);
-		priv->private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PRIVATE_KEY, &error);
-		if (error) {
-			g_warning ("Error setting private key (invalid data): %s", error->message);
-			g_error_free (error);
-		}
+		g_value_set_boxed (value, priv->private_key);
 		break;
 	case PROP_PRIVATE_KEY_PASSWORD:
-		g_free (priv->private_key_password);
-		priv->private_key_password = g_value_dup_string (value);
+		g_value_set_string (value, priv->private_key_password);
 		break;
 	case PROP_PRIVATE_KEY_PASSWORD_FLAGS:
-		priv->private_key_password_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->private_key_password_flags);
 		break;
 	case PROP_PHASE2_PRIVATE_KEY:
-		if (priv->phase2_private_key)
-			g_bytes_unref (priv->phase2_private_key);
-		priv->phase2_private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &error);
-		if (error) {
-			g_warning ("Error setting private key (invalid data): %s", error->message);
-			g_error_free (error);
-		}
+		g_value_set_boxed (value, priv->phase2_private_key);
 		break;
 	case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
-		g_free (priv->phase2_private_key_password);
-		priv->phase2_private_key_password = g_value_dup_string (value);
+		g_value_set_string (value, priv->phase2_private_key_password);
 		break;
 	case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS:
-		priv->phase2_private_key_password_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->phase2_private_key_password_flags);
 		break;
 	case PROP_PIN:
-		g_free (priv->pin);
-		priv->pin = g_value_dup_string (value);
+		g_value_set_string (value, priv->pin);
 		break;
 	case PROP_PIN_FLAGS:
-		priv->pin_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->pin_flags);
 		break;
 	case PROP_SYSTEM_CA_CERTS:
-		priv->system_ca_certs = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->system_ca_certs);
 		break;
 	case PROP_AUTH_TIMEOUT:
-		priv->auth_timeout = g_value_get_int (value);
+		g_value_set_int (value, priv->auth_timeout);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -3616,144 +3166,176 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSetting8021x *setting = NM_SETTING_802_1X (object);
 	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_EAP:
-		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->eap, TRUE));
+		g_slist_free_full (priv->eap, g_free);
+		priv->eap = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
 		break;
 	case PROP_IDENTITY:
-		g_value_set_string (value, priv->identity);
+		g_free (priv->identity);
+		priv->identity = g_value_dup_string (value);
 		break;
 	case PROP_ANONYMOUS_IDENTITY:
-		g_value_set_string (value, priv->anonymous_identity);
+		g_free (priv->anonymous_identity);
+		priv->anonymous_identity = g_value_dup_string (value);
 		break;
 	case PROP_PAC_FILE:
-		g_value_set_string (value, priv->pac_file);
+		g_free (priv->pac_file);
+		priv->pac_file = g_value_dup_string (value);
 		break;
 	case PROP_CA_CERT:
-		g_value_set_boxed (value, priv->ca_cert);
+		g_bytes_unref (priv->ca_cert);
+		priv->ca_cert = g_value_dup_boxed (value);
 		break;
 	case PROP_CA_CERT_PASSWORD:
-		g_value_set_string (value, priv->ca_cert_password);
+		g_free (priv->ca_cert_password);
+		priv->ca_cert_password = g_value_dup_string (value);
 		break;
 	case PROP_CA_CERT_PASSWORD_FLAGS:
-		g_value_set_flags (value, priv->ca_cert_password_flags);
+		priv->ca_cert_password_flags = g_value_get_flags (value);
 		break;
 	case PROP_CA_PATH:
-		g_value_set_string (value, priv->ca_path);
+		g_free (priv->ca_path);
+		priv->ca_path = g_value_dup_string (value);
 		break;
 	case PROP_SUBJECT_MATCH:
-		g_value_set_string (value, priv->subject_match);
+		g_free (priv->subject_match);
+		priv->subject_match = nm_strdup_not_empty (g_value_get_string (value));
 		break;
 	case PROP_ALTSUBJECT_MATCHES:
-		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->altsubject_matches, TRUE));
+		g_slist_free_full (priv->altsubject_matches, g_free);
+		priv->altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
 		break;
 	case PROP_DOMAIN_SUFFIX_MATCH:
-		g_value_set_string (value, priv->domain_suffix_match);
+		g_free (priv->domain_suffix_match);
+		priv->domain_suffix_match = nm_strdup_not_empty (g_value_get_string (value));
 		break;
 	case PROP_CLIENT_CERT:
-		g_value_set_boxed (value, priv->client_cert);
+		g_bytes_unref (priv->client_cert);
+		priv->client_cert = g_value_dup_boxed (value);
 		break;
 	case PROP_CLIENT_CERT_PASSWORD:
-		g_value_set_string (value, priv->client_cert_password);
+		g_free (priv->client_cert_password);
+		priv->client_cert_password = g_value_dup_string (value);
 		break;
 	case PROP_CLIENT_CERT_PASSWORD_FLAGS:
-		g_value_set_flags (value, priv->client_cert_password_flags);
+		priv->client_cert_password_flags = g_value_get_flags (value);
 		break;
 	case PROP_PHASE1_PEAPVER:
-		g_value_set_string (value, priv->phase1_peapver);
+		g_free (priv->phase1_peapver);
+		priv->phase1_peapver = g_value_dup_string (value);
 		break;
 	case PROP_PHASE1_PEAPLABEL:
-		g_value_set_string (value, priv->phase1_peaplabel);
+		g_free (priv->phase1_peaplabel);
+		priv->phase1_peaplabel = g_value_dup_string (value);
 		break;
 	case PROP_PHASE1_FAST_PROVISIONING:
-		g_value_set_string (value, priv->phase1_fast_provisioning);
+		g_free (priv->phase1_fast_provisioning);
+		priv->phase1_fast_provisioning = g_value_dup_string (value);
 		break;
 	case PROP_PHASE1_AUTH_FLAGS:
-		g_value_set_uint (value, priv->phase1_auth_flags);
+		priv->phase1_auth_flags = g_value_get_uint (value);
 		break;
 	case PROP_PHASE2_AUTH:
-		g_value_set_string (value, priv->phase2_auth);
+		g_free (priv->phase2_auth);
+		priv->phase2_auth = g_value_dup_string (value);
 		break;
 	case PROP_PHASE2_AUTHEAP:
-		g_value_set_string (value, priv->phase2_autheap);
+		g_free (priv->phase2_autheap);
+		priv->phase2_autheap = g_value_dup_string (value);
 		break;
 	case PROP_PHASE2_CA_CERT:
-		g_value_set_boxed (value, priv->phase2_ca_cert);
+		g_bytes_unref (priv->phase2_ca_cert);
+		priv->phase2_ca_cert = g_value_dup_boxed (value);
 		break;
 	case PROP_PHASE2_CA_CERT_PASSWORD:
-		g_value_set_string (value, priv->phase2_ca_cert_password);
+		g_free (priv->phase2_ca_cert_password);
+		priv->phase2_ca_cert_password = g_value_dup_string (value);
 		break;
 	case PROP_PHASE2_CA_CERT_PASSWORD_FLAGS:
-		g_value_set_flags (value, priv->phase2_ca_cert_password_flags);
+		priv->phase2_ca_cert_password_flags = g_value_get_flags (value);
 		break;
 	case PROP_PHASE2_CA_PATH:
-		g_value_set_string (value, priv->phase2_ca_path);
+		g_free (priv->phase2_ca_path);
+		priv->phase2_ca_path = g_value_dup_string (value);
 		break;
 	case PROP_PHASE2_SUBJECT_MATCH:
-		g_value_set_string (value, priv->phase2_subject_match);
+		g_free (priv->phase2_subject_match);
+		priv->phase2_subject_match = nm_strdup_not_empty (g_value_get_string (value));
 		break;
 	case PROP_PHASE2_ALTSUBJECT_MATCHES:
-		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->phase2_altsubject_matches, TRUE));
+		g_slist_free_full (priv->phase2_altsubject_matches, g_free);
+		priv->phase2_altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
 		break;
 	case PROP_PHASE2_DOMAIN_SUFFIX_MATCH:
-		g_value_set_string (value, priv->phase2_domain_suffix_match);
+		g_free (priv->phase2_domain_suffix_match);
+		priv->phase2_domain_suffix_match = nm_strdup_not_empty (g_value_get_string (value));
 		break;
 	case PROP_PHASE2_CLIENT_CERT:
-		g_value_set_boxed (value, priv->phase2_client_cert);
+		g_bytes_unref (priv->phase2_client_cert);
+		priv->phase2_client_cert = g_value_dup_boxed (value);
 		break;
 	case PROP_PHASE2_CLIENT_CERT_PASSWORD:
-		g_value_set_string (value, priv->phase2_client_cert_password);
+		g_free (priv->phase2_client_cert_password);
+		priv->phase2_client_cert_password = g_value_dup_string (value);
 		break;
 	case PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS:
-		g_value_set_flags (value, priv->phase2_client_cert_password_flags);
+		priv->phase2_client_cert_password_flags = g_value_get_flags (value);
 		break;
 	case PROP_PASSWORD:
-		g_value_set_string (value, priv->password);
+		g_free (priv->password);
+		priv->password = g_value_dup_string (value);
 		break;
 	case PROP_PASSWORD_FLAGS:
-		g_value_set_flags (value, priv->password_flags);
+		priv->password_flags = g_value_get_flags (value);
 		break;
 	case PROP_PASSWORD_RAW:
-		g_value_set_boxed (value, priv->password_raw);
+		g_bytes_unref (priv->password_raw);
+		priv->password_raw = g_value_dup_boxed (value);
 		break;
 	case PROP_PASSWORD_RAW_FLAGS:
-		g_value_set_flags (value, priv->password_raw_flags);
+		priv->password_raw_flags = g_value_get_flags (value);
 		break;
 	case PROP_PRIVATE_KEY:
-		g_value_set_boxed (value, priv->private_key);
+		g_bytes_unref (priv->private_key);
+		priv->private_key = g_value_dup_boxed (value);
 		break;
 	case PROP_PRIVATE_KEY_PASSWORD:
-		g_value_set_string (value, priv->private_key_password);
+		nm_free_secret (priv->private_key_password);
+		priv->private_key_password = g_value_dup_string (value);
 		break;
 	case PROP_PRIVATE_KEY_PASSWORD_FLAGS:
-		g_value_set_flags (value, priv->private_key_password_flags);
+		priv->private_key_password_flags = g_value_get_flags (value);
 		break;
 	case PROP_PHASE2_PRIVATE_KEY:
-		g_value_set_boxed (value, priv->phase2_private_key);
+		g_bytes_unref (priv->phase2_private_key);
+		priv->phase2_private_key = g_value_dup_boxed (value);
 		break;
 	case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
-		g_value_set_string (value, priv->phase2_private_key_password);
+		nm_free_secret (priv->phase2_private_key_password);
+		priv->phase2_private_key_password = g_value_dup_string (value);
 		break;
 	case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS:
-		g_value_set_flags (value, priv->phase2_private_key_password_flags);
+		priv->phase2_private_key_password_flags = g_value_get_flags (value);
 		break;
 	case PROP_PIN:
-		g_value_set_string (value, priv->pin);
+		g_free (priv->pin);
+		priv->pin = g_value_dup_string (value);
 		break;
 	case PROP_PIN_FLAGS:
-		g_value_set_flags (value, priv->pin_flags);
+		priv->pin_flags = g_value_get_flags (value);
 		break;
 	case PROP_SYSTEM_CA_CERTS:
-		g_value_set_boolean (value, priv->system_ca_certs);
+		priv->system_ca_certs = g_value_get_boolean (value);
 		break;
 	case PROP_AUTH_TIMEOUT:
-		g_value_set_int (value, priv->auth_timeout);
+		priv->auth_timeout = g_value_get_int (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -3761,23 +3343,85 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_802_1x_init (NMSetting8021x *setting)
+{
+}
+
+/**
+ * nm_setting_802_1x_new:
+ *
+ * Creates a new #NMSetting8021x object with default values.
+ *
+ * Returns: the new empty #NMSetting8021x object
+ **/
+NMSetting *
+nm_setting_802_1x_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_802_1X, NULL);
+}
+
 static void
-nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
+finalize (GObject *object)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	NMSetting8021x *self = NM_SETTING_802_1X (object);
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	/* Strings first. g_free() already checks for NULLs so we don't have to */
 
-	g_type_class_add_private (setting_class, sizeof (NMSetting8021xPrivate));
+	g_free (priv->identity);
+	g_free (priv->anonymous_identity);
+	g_free (priv->ca_path);
+	g_free (priv->subject_match);
+	g_free (priv->domain_suffix_match);
+	g_free (priv->phase1_peapver);
+	g_free (priv->phase1_peaplabel);
+	g_free (priv->phase1_fast_provisioning);
+	g_free (priv->phase2_auth);
+	g_free (priv->phase2_autheap);
+	g_free (priv->phase2_ca_path);
+	g_free (priv->phase2_subject_match);
+	g_free (priv->phase2_domain_suffix_match);
+	g_free (priv->password);
+	g_bytes_unref (priv->password_raw);
+	g_free (priv->pin);
+
+	g_slist_free_full (priv->eap, g_free);
+	g_slist_free_full (priv->altsubject_matches, g_free);
+	g_slist_free_full (priv->phase2_altsubject_matches, g_free);
+
+	g_bytes_unref (priv->ca_cert);
+	g_free (priv->ca_cert_password);
+	g_bytes_unref (priv->client_cert);
+	g_free (priv->client_cert_password);
+	g_bytes_unref (priv->private_key);
+	nm_free_secret (priv->private_key_password);
+	g_bytes_unref (priv->phase2_ca_cert);
+	g_free (priv->phase2_ca_cert_password);
+	g_bytes_unref (priv->phase2_client_cert);
+	g_free (priv->phase2_client_cert_password);
+	g_bytes_unref (priv->phase2_private_key);
+	nm_free_secret (priv->phase2_private_key_password);
+
+	G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_802_1x_class_init (NMSetting8021xClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMSetting8021xPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
 
-	parent_class->verify         = verify;
-	parent_class->need_secrets   = need_secrets;
-
-	/* Properties */
+	setting_class->verify       = verify;
+	setting_class->need_secrets = need_secrets;
 
 	/**
 	 * NMSetting8021x:eap:
@@ -3796,12 +3440,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_EAP_METHODS=PEAP
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_EAP,
-		 g_param_spec_boxed (NM_SETTING_802_1X_EAP, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_EAP] =
+	    g_param_spec_boxed (NM_SETTING_802_1X_EAP, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:identity:
@@ -3816,12 +3459,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_IDENTITY=itsme
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_IDENTITY,
-		 g_param_spec_string (NM_SETTING_802_1X_IDENTITY, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_IDENTITY] =
+	    g_param_spec_string (NM_SETTING_802_1X_IDENTITY, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:anonymous-identity:
@@ -3836,12 +3478,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: Anonymous identity for EAP authentication methods.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_ANONYMOUS_IDENTITY,
-		 g_param_spec_string (NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ANONYMOUS_IDENTITY] =
+	    g_param_spec_string (NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:pac-file:
@@ -3855,12 +3496,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_PAC_FILE=/home/joe/my-fast.pac
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PAC_FILE,
-		 g_param_spec_string (NM_SETTING_802_1X_PAC_FILE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PAC_FILE] =
+	    g_param_spec_string (NM_SETTING_802_1X_PAC_FILE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:ca-cert:
@@ -3887,12 +3527,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_CA_CERT=/home/joe/cacert.crt
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CA_CERT,
-		 g_param_spec_boxed (NM_SETTING_802_1X_CA_CERT, "", "",
-		                     G_TYPE_BYTES,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CA_CERT] =
+	    g_param_spec_boxed (NM_SETTING_802_1X_CA_CERT, "", "",
+	                        G_TYPE_BYTES,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:ca-cert-password:
@@ -3906,13 +3545,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	/* ---ifcfg-rh---
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CA_CERT_PASSWORD,
-		 g_param_spec_string (NM_SETTING_802_1X_CA_CERT_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CA_CERT_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_802_1X_CA_CERT_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:ca-cert-password-flags:
@@ -3924,13 +3562,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	/* ---ifcfg-rh---
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CA_CERT_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CA_CERT_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:ca-path:
@@ -3945,12 +3582,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: The property is not handled by ifcfg-rh plugin.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CA_PATH,
-		 g_param_spec_string (NM_SETTING_802_1X_CA_PATH, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CA_PATH] =
+	    g_param_spec_string (NM_SETTING_802_1X_CA_PATH, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:subject-match:
@@ -3968,12 +3604,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_SUBJECT_MATCH="Red Hat"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_SUBJECT_MATCH,
-		 g_param_spec_string (NM_SETTING_802_1X_SUBJECT_MATCH, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SUBJECT_MATCH] =
+	    g_param_spec_string (NM_SETTING_802_1X_SUBJECT_MATCH, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:altsubject-matches:
@@ -3989,12 +3624,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_ALTSUBJECT_MATCHES="s1.domain.cc"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_ALTSUBJECT_MATCHES,
-		 g_param_spec_boxed (NM_SETTING_802_1X_ALTSUBJECT_MATCHES, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ALTSUBJECT_MATCHES] =
+	    g_param_spec_boxed (NM_SETTING_802_1X_ALTSUBJECT_MATCHES, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:domain-suffix-match:
@@ -4013,12 +3647,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * variable: IEEE_8021X_DOMAIN_SUFFIX_MATCH(+)
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_DOMAIN_SUFFIX_MATCH,
-		 g_param_spec_string (NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DOMAIN_SUFFIX_MATCH] =
+	    g_param_spec_string (NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:client-cert:
@@ -4043,12 +3676,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_CLIENT_CERT=/home/joe/mycert.crt
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CLIENT_CERT,
-		 g_param_spec_boxed (NM_SETTING_802_1X_CLIENT_CERT, "", "",
-		                     G_TYPE_BYTES,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CLIENT_CERT] =
+	    g_param_spec_boxed (NM_SETTING_802_1X_CLIENT_CERT, "", "",
+	                        G_TYPE_BYTES,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:client-cert-password:
@@ -4062,13 +3694,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	/* ---ifcfg-rh---
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CLIENT_CERT_PASSWORD,
-		 g_param_spec_string (NM_SETTING_802_1X_CLIENT_CERT_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CLIENT_CERT_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_802_1X_CLIENT_CERT_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:client-cert-password-flags:
@@ -4080,13 +3711,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	/* ---ifcfg-rh---
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CLIENT_CERT_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CLIENT_CERT_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase1-peapver:
@@ -4105,12 +3735,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: Use to force a specific PEAP version.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE1_PEAPVER,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPVER, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE1_PEAPVER] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPVER, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase1-peaplabel:
@@ -4128,12 +3757,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: Use to force the new PEAP label during key derivation.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE1_PEAPLABEL,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPLABEL, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE1_PEAPLABEL] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPLABEL, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase1-fast-provisioning:
@@ -4153,12 +3781,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_FAST_PROVISIONING="allow-auth allow-unauth"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE1_FAST_PROVISIONING,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE1_FAST_PROVISIONING] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase1-auth-flags:
@@ -4180,13 +3807,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_PHASE1_AUTH_FLAGS="tls-1-0-disable tls-1-1-disable"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE1_AUTH_FLAGS,
-		 g_param_spec_uint (NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, "", "",
-		                    0, G_MAXUINT32, NM_SETTING_802_1X_AUTH_FLAGS_NONE,
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE1_AUTH_FLAGS] =
+	    g_param_spec_uint (NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, "", "",
+	                       0, G_MAXUINT32, NM_SETTING_802_1X_AUTH_FLAGS_NONE,
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-auth:
@@ -4207,12 +3833,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_INNER_AUTH_METHODS=PAP
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_AUTH,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTH, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_AUTH] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTH, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-autheap:
@@ -4233,12 +3858,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_INNER_AUTH_METHODS="MSCHAPV2 EAP-TLS"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_AUTHEAP,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTHEAP, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_AUTHEAP] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTHEAP, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-ca-cert:
@@ -4259,12 +3883,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * Setting this property directly is discouraged; use the
 	 * nm_setting_802_1x_set_phase2_ca_cert() function instead.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_CA_CERT,
-		 g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_CA_CERT, "", "",
-		                     G_TYPE_BYTES,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_CA_CERT] =
+	    g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_CA_CERT, "", "",
+	                        G_TYPE_BYTES,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-ca-cert-password:
@@ -4278,13 +3901,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	/* ---ifcfg-rh---
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_CA_CERT_PASSWORD,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_CA_CERT_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-ca-cert-password-flags:
@@ -4296,13 +3918,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	/* ---ifcfg-rh---
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_CA_CERT_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_CA_CERT_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-ca-path:
@@ -4311,12 +3932,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * certificates to be added to the verification chain in addition to the
 	 * certificate specified in the #NMSetting8021x:phase2-ca-cert property.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_CA_PATH,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_PATH, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_CA_PATH] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_PATH, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-subject-match:
@@ -4335,12 +3955,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_PHASE2_SUBJECT_MATCH="Red Hat"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_SUBJECT_MATCH,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_SUBJECT_MATCH] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-altsubject-matches:
@@ -4355,12 +3974,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * variable: IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES(+)
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_ALTSUBJECT_MATCHES,
-		 g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_ALTSUBJECT_MATCHES] =
+	    g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-domain-suffix-match:
@@ -4380,12 +3998,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * variable: IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH(+)
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_DOMAIN_SUFFIX_MATCH,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_DOMAIN_SUFFIX_MATCH] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-client-cert:
@@ -4413,12 +4030,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_INNER_CLIENT_CERT=/home/joe/mycert.crt
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_CLIENT_CERT,
-		 g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_CLIENT_CERT, "", "",
-		                     G_TYPE_BYTES,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_CLIENT_CERT] =
+	    g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_CLIENT_CERT, "", "",
+	                        G_TYPE_BYTES,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-client-cert-password:
@@ -4432,13 +4048,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	/* ---ifcfg-rh---
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_CLIENT_CERT_PASSWORD,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_CLIENT_CERT_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-client-cert-password-flags:
@@ -4450,13 +4065,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	/* ---ifcfg-rh---
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:password:
@@ -4472,13 +4086,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 *   lookaside file, or it can be owned by a secret agent.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD,
-		 g_param_spec_string (NM_SETTING_802_1X_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_802_1X_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:password-flags:
@@ -4492,13 +4105,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: Password flags for IEEE_8021X_PASSWORD password.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_802_1X_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_802_1X_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:password-raw:
@@ -4516,13 +4128,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_PASSWORD_RAW=041c8320083aa4bf
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD_RAW,
-		 g_param_spec_boxed (NM_SETTING_802_1X_PASSWORD_RAW, "", "",
-		                     G_TYPE_BYTES,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_SECRET |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD_RAW] =
+	    g_param_spec_boxed (NM_SETTING_802_1X_PASSWORD_RAW, "", "",
+	                        G_TYPE_BYTES,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_SECRET |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:password-raw-flags:
@@ -4535,13 +4146,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: The property is not handled by ifcfg-rh plugin.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD_RAW_FLAGS,
-		 g_param_spec_flags (NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD_RAW_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:private-key:
@@ -4581,12 +4191,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * example: IEEE_8021X_PRIVATE_KEY=/home/joe/mykey.p12
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIVATE_KEY,
-		 g_param_spec_boxed (NM_SETTING_802_1X_PRIVATE_KEY, "", "",
-		                     G_TYPE_BYTES,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIVATE_KEY] =
+	    g_param_spec_boxed (NM_SETTING_802_1X_PRIVATE_KEY, "", "",
+	                        G_TYPE_BYTES,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:private-key-password:
@@ -4605,13 +4214,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 *   lookaside file, or it can be owned by a secret agent.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIVATE_KEY_PASSWORD,
-		 g_param_spec_string (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIVATE_KEY_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:private-key-password-flags:
@@ -4626,13 +4234,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: Password flags for IEEE_8021X_PRIVATE_KEY_PASSWORD password.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIVATE_KEY_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIVATE_KEY_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-private-key:
@@ -4666,12 +4273,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: Private key for inner authentication method for EAP-TLS.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_PRIVATE_KEY,
-		 g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, "", "",
-		                     G_TYPE_BYTES,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_PRIVATE_KEY] =
+	    g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, "", "",
+	                        G_TYPE_BYTES,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-private-key-password:
@@ -4691,13 +4297,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 *   lookaside file, or it can be owned by a secret agent.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD,
-		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_PRIVATE_KEY_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:phase2-private-key-password-flags:
@@ -4712,13 +4317,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: Password flags for IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD password.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:pin:
@@ -4731,13 +4335,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: The property is not handled by ifcfg-rh plugin.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PIN,
-		 g_param_spec_string (NM_SETTING_802_1X_PIN, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PIN] =
+	    g_param_spec_string (NM_SETTING_802_1X_PIN, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:pin-flags:
@@ -4750,13 +4353,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: The property is not handled by ifcfg-rh plugin.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PIN_FLAGS,
-		 g_param_spec_flags (NM_SETTING_802_1X_PIN_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PIN_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_802_1X_PIN_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:system-ca-certs:
@@ -4777,13 +4379,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: The property is not handled by ifcfg-rh plugin.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_SYSTEM_CA_CERTS,
-		 g_param_spec_boolean (NM_SETTING_802_1X_SYSTEM_CA_CERTS, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SYSTEM_CA_CERTS] =
+	    g_param_spec_boolean (NM_SETTING_802_1X_SYSTEM_CA_CERTS, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSetting8021x:auth-timeout:
@@ -4800,11 +4401,14 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
 	 * description: Timeout in seconds for the 802.1X authentication. Zero means the global default or 25.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_AUTH_TIMEOUT,
-		 g_param_spec_int (NM_SETTING_802_1X_AUTH_TIMEOUT, "", "",
-		                   0, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   NM_SETTING_PARAM_FUZZY_IGNORE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_AUTH_TIMEOUT] =
+	    g_param_spec_int (NM_SETTING_802_1X_AUTH_TIMEOUT, "", "",
+	                      0, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      NM_SETTING_PARAM_FUZZY_IGNORE |
+	                      G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_802_1X);
 }
diff --git a/libnm-core/nm-setting-8021x.h b/libnm-core/nm-setting-8021x.h
index 986d0488..82ef5eba 100644
--- a/libnm-core/nm-setting-8021x.h
+++ b/libnm-core/nm-setting-8021x.h
@@ -359,7 +359,7 @@ NMSetting8021xCKFormat nm_setting_802_1x_get_phase2_private_key_format   (NMSett
 NM_AVAILABLE_IN_1_8
 NMSetting8021xAuthFlags nm_setting_802_1x_get_phase1_auth_flags          (NMSetting8021x *setting);
 NM_AVAILABLE_IN_1_8
-gint                   nm_setting_802_1x_get_auth_timeout                (NMSetting8021x *setting);
+int                    nm_setting_802_1x_get_auth_timeout                (NMSetting8021x *setting);
 
 G_END_DECLS
 
diff --git a/libnm-core/nm-setting-adsl.c b/libnm-core/nm-setting-adsl.c
index c2eb5db4..9ffff927 100644
--- a/libnm-core/nm-setting-adsl.c
+++ b/libnm-core/nm-setting-adsl.c
@@ -37,9 +37,7 @@
  * properties of ADSL connections.
  */
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingAdsl, nm_setting_adsl, NM_TYPE_SETTING,
-                         _nm_register_setting (ADSL, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_ADSL)
+G_DEFINE_TYPE (NMSettingAdsl, nm_setting_adsl, NM_TYPE_SETTING)
 
 #define NM_SETTING_ADSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_ADSL, NMSettingAdslPrivate))
 
@@ -346,22 +344,20 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_adsl_class_init (NMSettingAdslClass *setting_class)
+nm_setting_adsl_class_init (NMSettingAdslClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingAdslPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingAdslPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
-	parent_class->verify_secrets = verify_secrets;
-	parent_class->need_secrets = need_secrets;
 
-	/* Properties */
+	setting_class->verify         = verify;
+	setting_class->verify_secrets = verify_secrets;
+	setting_class->need_secrets   = need_secrets;
 
 	/**
 	 * NMSettingAdsl:username:
@@ -448,4 +444,6 @@ nm_setting_adsl_class_init (NMSettingAdslClass *setting_class)
 		                    0, 65536, 0,
 		                    G_PARAM_READWRITE |
 		                    G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_ADSL);
 }
diff --git a/libnm-core/nm-setting-bluetooth.c b/libnm-core/nm-setting-bluetooth.c
index 5b6ef3eb..bbc6a83d 100644
--- a/libnm-core/nm-setting-bluetooth.c
+++ b/libnm-core/nm-setting-bluetooth.c
@@ -43,9 +43,7 @@
  * Point (NAP) profiles.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingBluetooth, nm_setting_bluetooth, NM_TYPE_SETTING,
-                         _nm_register_setting (BLUETOOTH, NM_SETTING_PRIORITY_HW_NON_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BLUETOOTH)
+G_DEFINE_TYPE (NMSettingBluetooth, nm_setting_bluetooth, NM_TYPE_SETTING)
 
 #define NM_SETTING_BLUETOOTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothPrivate))
 
@@ -283,20 +281,19 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_bluetooth_class_init (NMSettingBluetoothClass *setting_class)
+nm_setting_bluetooth_class_init (NMSettingBluetoothClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingBluetoothPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingBluetoothPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify       = verify;
 
 	/**
 	 * NMSettingBluetooth:bdaddr:
@@ -310,10 +307,13 @@ nm_setting_bluetooth_class_init (NMSettingBluetoothClass *setting_class)
 		                      G_PARAM_READWRITE |
 		                      NM_SETTING_PARAM_INFERRABLE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_BLUETOOTH_BDADDR,
-	                                      G_VARIANT_TYPE_BYTESTRING,
-	                                      _nm_utils_hwaddr_to_dbus,
-	                                      _nm_utils_hwaddr_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_BLUETOOTH_BDADDR),
+	                                    G_VARIANT_TYPE_BYTESTRING,
+	                                    _nm_utils_hwaddr_to_dbus,
+	                                    _nm_utils_hwaddr_from_dbus);
 
 	/**
 	 * NMSettingBluetooth:type:
@@ -328,4 +328,7 @@ nm_setting_bluetooth_class_init (NMSettingBluetoothClass *setting_class)
 		                      G_PARAM_READWRITE |
 		                      NM_SETTING_PARAM_INFERRABLE |
 		                      G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_BLUETOOTH,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-bond.c b/libnm-core/nm-setting-bond.c
index 9b4b3641..0c84b3a0 100644
--- a/libnm-core/nm-setting-bond.c
+++ b/libnm-core/nm-setting-bond.c
@@ -57,9 +57,7 @@ typedef struct {
 	NMUtilsNamedValue *options_idx_cache;
 } NMSettingBondPrivate;
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingBond, nm_setting_bond, NM_TYPE_SETTING,
-                         _nm_register_setting (BOND, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BOND)
+G_DEFINE_TYPE (NMSettingBond, nm_setting_bond, NM_TYPE_SETTING)
 
 #define NM_SETTING_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BOND, NMSettingBondPrivate))
 
@@ -180,7 +178,7 @@ nm_setting_bond_get_option (NMSettingBond *setting,
 static gboolean
 validate_int (const char *name, const char *value, const BondDefault *def)
 {
-	glong num;
+	long num;
 	guint i;
 
 	for (i = 0; i < strlen (value); i++) {
@@ -873,7 +871,7 @@ compare_property (NMSetting *setting,
                   const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	NMSettingClass *parent_class;
+	NMSettingClass *setting_class;
 
 	if (nm_streq0 (prop_spec->name, NM_SETTING_BOND_OPTIONS)) {
 		return options_equal (NM_SETTING_BOND (setting),
@@ -882,9 +880,8 @@ compare_property (NMSetting *setting,
 		                      flags);
 	}
 
-	/* Otherwise chain up to parent to handle generic compare */
-	parent_class = NM_SETTING_CLASS (nm_setting_bond_parent_class);
-	return parent_class->compare_property (setting, other, prop_spec, flags);
+	setting_class = NM_SETTING_CLASS (nm_setting_bond_parent_class);
+	return setting_class->compare_property (setting, other, prop_spec, flags);
 }
 
 /*****************************************************************************/
@@ -961,21 +958,21 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_bond_class_init (NMSettingBondClass *setting_class)
+nm_setting_bond_class_init (NMSettingBondClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingBondPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingBondPrivate));
 
-	/* virtual methods */
 	object_class->set_property     = set_property;
 	object_class->get_property     = get_property;
 	object_class->finalize         = finalize;
-	parent_class->verify           = verify;
-	parent_class->compare_property = compare_property;
 
-	/* Properties */
+	setting_class->verify           = verify;
+	setting_class->compare_property = compare_property;
+
 	/**
 	 * NMSettingBond:options: (type GHashTable(utf8,utf8)):
 	 *
@@ -997,10 +994,13 @@ nm_setting_bond_class_init (NMSettingBondClass *setting_class)
 		                     G_PARAM_READWRITE |
 		                     NM_SETTING_PARAM_INFERRABLE |
 		                     G_PARAM_STATIC_STRINGS));
-	 _nm_setting_class_transform_property (parent_class, NM_SETTING_BOND_OPTIONS,
-	                                       G_VARIANT_TYPE ("a{ss}"),
-	                                       _nm_utils_strdict_to_dbus,
-	                                       _nm_utils_strdict_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_BOND_OPTIONS),
+	                                    G_VARIANT_TYPE ("a{ss}"),
+	                                    _nm_utils_strdict_to_dbus,
+	                                    _nm_utils_strdict_from_dbus);
 
 	 /* ---dbus---
 	  * property: interface-name
@@ -1010,8 +1010,12 @@ nm_setting_bond_class_init (NMSettingBondClass *setting_class)
 	  *   bond's interface name.
 	  * ---end---
 	  */
-	 _nm_setting_class_add_dbus_only_property (parent_class, "interface-name",
-	                                           G_VARIANT_TYPE_STRING,
-	                                           _nm_setting_get_deprecated_virtual_interface_name,
-	                                           NULL);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "interface-name",
+	                                    G_VARIANT_TYPE_STRING,
+	                                    _nm_setting_get_deprecated_virtual_interface_name,
+	                                    NULL);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_BOND,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-bridge-port.c b/libnm-core/nm-setting-bridge-port.c
index 3fc0e873..4104f8c5 100644
--- a/libnm-core/nm-setting-bridge-port.c
+++ b/libnm-core/nm-setting-bridge-port.c
@@ -40,9 +40,7 @@
  * optional properties that apply to bridge ports.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingBridgePort, nm_setting_bridge_port, NM_TYPE_SETTING,
-                         _nm_register_setting (BRIDGE_PORT, NM_SETTING_PRIORITY_AUX))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BRIDGE_PORT)
+G_DEFINE_TYPE (NMSettingBridgePort, nm_setting_bridge_port, NM_TYPE_SETTING)
 
 #define NM_SETTING_BRIDGE_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortPrivate))
 
@@ -212,19 +210,18 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_bridge_port_class_init (NMSettingBridgePortClass *setting_class)
+nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingBridgePortPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingBridgePortPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify = verify;
+
 	/**
 	 * NMSettingBridgePort:priority:
 	 *
@@ -290,4 +287,6 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *setting_class)
 		                       G_PARAM_READWRITE |
 		                       NM_SETTING_PARAM_INFERRABLE |
 		                       G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_BRIDGE_PORT);
 }
diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c
index 112b499f..71fe2ed2 100644
--- a/libnm-core/nm-setting-bridge.c
+++ b/libnm-core/nm-setting-bridge.c
@@ -38,9 +38,7 @@
  * necessary for bridging connections.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING,
-                         _nm_register_setting (BRIDGE, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BRIDGE)
+G_DEFINE_TYPE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING)
 
 #define NM_SETTING_BRIDGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE, NMSettingBridgePrivate))
 
@@ -393,20 +391,20 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
+nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingBridgePrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingBridgePrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify = verify;
+
 	/**
 	 * NMSettingBridge:mac-address:
 	 *
@@ -444,10 +442,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
 		                      G_PARAM_READWRITE |
 		                      NM_SETTING_PARAM_INFERRABLE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_BRIDGE_MAC_ADDRESS,
-	                                      G_VARIANT_TYPE_BYTESTRING,
-	                                      _nm_utils_hwaddr_to_dbus,
-	                                      _nm_utils_hwaddr_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_BRIDGE_MAC_ADDRESS),
+	                                    G_VARIANT_TYPE_BYTESTRING,
+	                                    _nm_utils_hwaddr_to_dbus,
+	                                    _nm_utils_hwaddr_from_dbus);
 
 	/**
 	 * NMSettingBridge:stp:
@@ -638,8 +639,12 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
 	 *   bridge's interface name.
 	 * ---end---
 	 */
-	_nm_setting_class_add_dbus_only_property (parent_class, "interface-name",
-	                                          G_VARIANT_TYPE_STRING,
-	                                          _nm_setting_get_deprecated_virtual_interface_name,
-	                                          NULL);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "interface-name",
+	                                    G_VARIANT_TYPE_STRING,
+	                                    _nm_setting_get_deprecated_virtual_interface_name,
+	                                    NULL);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_BRIDGE,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-cdma.c b/libnm-core/nm-setting-cdma.c
index 63178951..a5d5428d 100644
--- a/libnm-core/nm-setting-cdma.c
+++ b/libnm-core/nm-setting-cdma.c
@@ -37,9 +37,7 @@
  * networks, including those using CDMA2000/EVDO technology.
  */
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingCdma, nm_setting_cdma, NM_TYPE_SETTING,
-                         _nm_register_setting (CDMA, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_CDMA)
+G_DEFINE_TYPE (NMSettingCdma, nm_setting_cdma, NM_TYPE_SETTING)
 
 #define NM_SETTING_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CDMA, NMSettingCdmaPrivate))
 
@@ -285,22 +283,20 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_cdma_class_init (NMSettingCdmaClass *setting_class)
+nm_setting_cdma_class_init (NMSettingCdmaClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingCdmaPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingCdmaPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
-	parent_class->verify_secrets = verify_secrets;
-	parent_class->need_secrets = need_secrets;
 
-	/* Properties */
+	setting_class->verify         = verify;
+	setting_class->verify_secrets = verify_secrets;
+	setting_class->need_secrets   = need_secrets;
 
 	/**
 	 * NMSettingCdma:number:
@@ -375,4 +371,5 @@ nm_setting_cdma_class_init (NMSettingCdmaClass *setting_class)
 		                    NM_SETTING_PARAM_FUZZY_IGNORE |
 		                    G_PARAM_STATIC_STRINGS));
 
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_CDMA);
 }
diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c
index 689ba794..50240bc9 100644
--- a/libnm-core/nm-setting-connection.c
+++ b/libnm-core/nm-setting-connection.c
@@ -45,9 +45,7 @@
  * a #NMSettingConnection setting.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingConnection, nm_setting_connection, NM_TYPE_SETTING,
-                         _nm_register_setting (CONNECTION, NM_SETTING_PRIORITY_CONNECTION))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_CONNECTION)
+G_DEFINE_TYPE (NMSettingConnection, nm_setting_connection, NM_TYPE_SETTING)
 
 #define NM_SETTING_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionPrivate))
 
@@ -71,8 +69,9 @@ typedef struct {
 	NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
 	GSList *permissions; /* list of Permission structs */
 	gboolean autoconnect;
-	gint autoconnect_priority;
-	gint autoconnect_retries;
+	int autoconnect_priority;
+	int autoconnect_retries;
+	int multi_connect;
 	guint64 timestamp;
 	gboolean read_only;
 	char *zone;
@@ -80,8 +79,9 @@ typedef struct {
 	guint gateway_ping_timeout;
 	NMMetered metered;
 	NMSettingConnectionLldp lldp;
-	gint auth_retries;
+	int auth_retries;
 	int mdns;
+	int llmnr;
 } NMSettingConnectionPrivate;
 
 enum {
@@ -94,6 +94,7 @@ enum {
 	PROP_AUTOCONNECT,
 	PROP_AUTOCONNECT_PRIORITY,
 	PROP_AUTOCONNECT_RETRIES,
+	PROP_MULTI_CONNECT,
 	PROP_TIMESTAMP,
 	PROP_READ_ONLY,
 	PROP_ZONE,
@@ -105,6 +106,7 @@ enum {
 	PROP_METERED,
 	PROP_LLDP,
 	PROP_MDNS,
+	PROP_LLMNR,
 	PROP_STABLE_ID,
 	PROP_AUTH_RETRIES,
 
@@ -528,7 +530,7 @@ nm_setting_connection_get_autoconnect (NMSettingConnection *setting)
  *
  * Returns: the connection's autoconnect priority
  **/
-gint
+int
 nm_setting_connection_get_autoconnect_priority (NMSettingConnection *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0);
@@ -547,7 +549,7 @@ nm_setting_connection_get_autoconnect_priority (NMSettingConnection *setting)
  *
  * Since: 1.6
  **/
-gint
+int
 nm_setting_connection_get_autoconnect_retries (NMSettingConnection *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), -1);
@@ -556,6 +558,22 @@ nm_setting_connection_get_autoconnect_retries (NMSettingConnection *setting)
 }
 
 /**
+ * nm_setting_connection_get_multi_connect:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns: the #NMSettingConnection:multi-connect property of the connection.
+ *
+ * Since: 1.14
+ **/
+NMConnectionMultiConnect
+nm_setting_connection_get_multi_connect (NMSettingConnection *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), -1);
+
+	return (NMConnectionMultiConnect) NM_SETTING_CONNECTION_GET_PRIVATE (setting)->multi_connect;
+}
+
+/**
  * nm_setting_connection_get_auth_retries:
  * @setting: the #NMSettingConnection
  *
@@ -566,7 +584,7 @@ nm_setting_connection_get_autoconnect_retries (NMSettingConnection *setting)
  *
  * Since: 1.10
  **/
-gint
+int
 nm_setting_connection_get_auth_retries (NMSettingConnection *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), -1);
@@ -880,6 +898,23 @@ nm_setting_connection_get_mdns (NMSettingConnection *setting)
 	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->mdns;
 }
 
+/**
+ * nm_setting_connection_get_llmnr:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns: the #NMSettingConnection:llmnr property of the setting.
+ *
+ * Since: 1.14
+ **/
+NMSettingConnectionLlmnr
+nm_setting_connection_get_llmnr (NMSettingConnection *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting),
+	                      NM_SETTING_CONNECTION_LLMNR_DEFAULT);
+
+	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->llmnr;
+}
+
 static void
 _set_error_missing_base_setting (GError **error, const char *type)
 {
@@ -1077,17 +1112,41 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (   priv->mdns < NM_SETTING_CONNECTION_MDNS_DEFAULT
-	    || priv->mdns > NM_SETTING_CONNECTION_MDNS_YES) {
+	if (   priv->mdns < (int) NM_SETTING_CONNECTION_MDNS_DEFAULT
+	    || priv->mdns > (int) NM_SETTING_CONNECTION_MDNS_YES) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		             _("mdns value %d is not valid"), priv->mdns);
+		             _("value %d is not valid"), priv->mdns);
 		g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME,
 		                NM_SETTING_CONNECTION_MDNS);
 		return FALSE;
 	}
 
+	if (   priv->llmnr < (int) NM_SETTING_CONNECTION_LLMNR_DEFAULT
+	    || priv->llmnr > (int) NM_SETTING_CONNECTION_LLMNR_YES) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("value %d is not valid"), priv->llmnr);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME,
+		                NM_SETTING_CONNECTION_LLMNR);
+		return FALSE;
+	}
+
+	if (!NM_IN_SET (priv->multi_connect, (int) NM_CONNECTION_MULTI_CONNECT_DEFAULT,
+	                                     (int) NM_CONNECTION_MULTI_CONNECT_SINGLE,
+	                                     (int) NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE,
+	                                     (int) NM_CONNECTION_MULTI_CONNECT_MULTIPLE)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("value %d is not valid"), priv->multi_connect);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME,
+		                NM_SETTING_CONNECTION_MULTI_CONNECT);
+		return FALSE;
+	}
+
 	/* *** errors above here should be always fatal, below NORMALIZABLE_ERROR *** */
 
 	if (!priv->uuid) {
@@ -1238,7 +1297,6 @@ compare_property (NMSetting *setting,
 	    && g_strcmp0 (prop_spec->name, NM_SETTING_CONNECTION_TIMESTAMP) == 0)
 		return TRUE;
 
-	/* Otherwise chain up to parent to handle generic compare */
 	return NM_SETTING_CLASS (nm_setting_connection_parent_class)->compare_property (setting, other, prop_spec, flags);
 }
 
@@ -1248,6 +1306,7 @@ nm_setting_connection_init (NMSettingConnection *setting)
 	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
 
 	priv->mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
+	priv->llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
 }
 
 static void
@@ -1329,6 +1388,9 @@ set_property (GObject *object, guint prop_id,
 	case PROP_AUTOCONNECT_RETRIES:
 		priv->autoconnect_retries = g_value_get_int (value);
 		break;
+	case PROP_MULTI_CONNECT:
+		priv->multi_connect = g_value_get_int (value);
+		break;
 	case PROP_TIMESTAMP:
 		priv->timestamp = g_value_get_uint64 (value);
 		break;
@@ -1369,6 +1431,9 @@ set_property (GObject *object, guint prop_id,
 	case PROP_MDNS:
 		priv->mdns = g_value_get_int (value);
 		break;
+	case PROP_LLMNR:
+		priv->llmnr = g_value_get_int (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1424,6 +1489,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_AUTOCONNECT_RETRIES:
 		g_value_set_int (value, nm_setting_connection_get_autoconnect_retries (setting));
 		break;
+	case PROP_MULTI_CONNECT:
+		g_value_set_int (value, priv->multi_connect);
+		break;
 	case PROP_TIMESTAMP:
 		g_value_set_uint64 (value, nm_setting_connection_get_timestamp (setting));
 		break;
@@ -1460,6 +1528,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_MDNS:
 		g_value_set_int (value, priv->mdns);
 		break;
+	case PROP_LLMNR:
+		g_value_set_int (value, priv->llmnr);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1467,21 +1538,20 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
+nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingConnectionPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingConnectionPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
-	parent_class->compare_property = compare_property;
 
-	/* Properties */
+	setting_class->verify           = verify;
+	setting_class->compare_property = compare_property;
 
 	/**
 	 * NMSettingConnection:id:
@@ -1618,11 +1688,14 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 		                      G_PARAM_READWRITE |
 		                      NM_SETTING_PARAM_INFERRABLE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_override_property (parent_class, NM_SETTING_CONNECTION_INTERFACE_NAME,
-	                                     G_VARIANT_TYPE_STRING,
-	                                     NULL,
-	                                     nm_setting_connection_set_interface_name,
-	                                     nm_setting_connection_no_interface_name);
+
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_CONNECTION_INTERFACE_NAME),
+	                                   G_VARIANT_TYPE_STRING,
+	                                   NULL,
+	                                   nm_setting_connection_set_interface_name,
+	                                   nm_setting_connection_no_interface_name);
 
 	/**
 	 * NMSettingConnection:type:
@@ -1766,6 +1839,30 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 	                       G_PARAM_STATIC_STRINGS));
 
 	/**
+	 * NMSettingConnection:multi-connect:
+	 *
+	 * Specifies whether the profile can be active multiple times at a particular
+	 * moment. The value is of type #NMConnectionMultiConnect.
+	 *
+	 * Since: 1.14
+	 */
+	/* ---ifcfg-rh---
+	 * property: multi-connect
+	 * variable: MULTI_CONNECT(+)
+	 * description: whether the profile can be active on multiple devices at a given
+	 *   moment. The values are numbers corresponding to #NMConnectionMultiConnect enum.
+	 * example: ZONE=3
+	 * ---end---
+	 */
+	g_object_class_install_property
+	    (object_class, PROP_MULTI_CONNECT,
+	     g_param_spec_int (NM_SETTING_CONNECTION_MULTI_CONNECT, "", "",
+	                       G_MININT32, G_MAXINT32, NM_CONNECTION_MULTI_CONNECT_DEFAULT,
+	                       G_PARAM_READWRITE |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS));
+
+	/**
 	 * NMSettingConnection:timestamp:
 	 *
 	 * The time, in seconds since the Unix Epoch, that the connection was last
@@ -2044,8 +2141,6 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 	 * The permitted values are: yes: register hostname and resolving
 	 * for the connection, no: disable mDNS for the interface, resolve:
 	 * do not register hostname but allow resolving of mDNS host names.
-	 * When updating this property on a currently activated connection,
-	 * the change takes effect immediately.
 	 *
 	 * This feature requires a plugin which supports mDNS. One such
 	 * plugin is dns-systemd-resolved.
@@ -2054,11 +2149,11 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 	 **/
 	/* ---ifcfg-rh---
 	 * property: mdns
-	 * variable: CONNECTION_MDNS(+)
+	 * variable: MDNS(+)
 	 * values: yes,no,resolve
 	 * default: missing variable means global default
 	 * description: Whether or not mDNS is enabled for the connection
-	 * example: CONNECTION_MDNS=yes
+	 * example: MDNS=yes
 	 * ---end---
 	 */
 	g_object_class_install_property
@@ -2068,4 +2163,41 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 		                   NM_SETTING_CONNECTION_MDNS_DEFAULT,
 		                   G_PARAM_READWRITE |
 		                   G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingConnection:llmnr:
+	 *
+	 * Whether Link-Local Multicast Name Resolution (LLMNR) is enabled
+	 * for the connection. LLMNR is a protocol based on the Domain Name
+	 * System (DNS) packet format that allows both IPv4 and IPv6 hosts
+	 * to perform name resolution for hosts on the same local link.
+	 *
+	 * The permitted values are: yes: register hostname and resolving
+	 * for the connection, no: disable LLMNR for the interface, resolve:
+	 * do not register hostname but allow resolving of LLMNR host names.
+	 *
+	 * This feature requires a plugin which supports LLMNR. One such
+	 * plugin is dns-systemd-resolved.
+	 *
+	 * Since: 1.14
+	 **/
+	/* ---ifcfg-rh---
+	 * property: llmnr
+	 * variable: LLMNR(+)
+	 * values: yes,no,resolve
+	 * default: missing variable means global default
+	 * description: Whether or not LLMNR is enabled for the connection
+	 * example: LLMNR=yes
+	 * ---end---
+	 */
+	g_object_class_install_property
+		(object_class, PROP_LLMNR,
+		 g_param_spec_int (NM_SETTING_CONNECTION_LLMNR, "", "",
+		                   G_MININT32, G_MAXINT32,
+		                   NM_SETTING_CONNECTION_LLMNR_DEFAULT,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_CONNECTION,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h
index cf083a48..b65fb67b 100644
--- a/libnm-core/nm-setting-connection.h
+++ b/libnm-core/nm-setting-connection.h
@@ -52,6 +52,7 @@ G_BEGIN_DECLS
 #define NM_SETTING_CONNECTION_AUTOCONNECT    "autoconnect"
 #define NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY "autoconnect-priority"
 #define NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES "autoconnect-retries"
+#define NM_SETTING_CONNECTION_MULTI_CONNECT  "multi-connect"
 #define NM_SETTING_CONNECTION_TIMESTAMP      "timestamp"
 #define NM_SETTING_CONNECTION_READ_ONLY      "read-only"
 #define NM_SETTING_CONNECTION_PERMISSIONS    "permissions"
@@ -65,6 +66,7 @@ G_BEGIN_DECLS
 #define NM_SETTING_CONNECTION_LLDP           "lldp"
 #define NM_SETTING_CONNECTION_AUTH_RETRIES   "auth-retries"
 #define NM_SETTING_CONNECTION_MDNS           "mdns"
+#define NM_SETTING_CONNECTION_LLMNR          "llmnr"
 
 /* Types for property values */
 /**
@@ -117,6 +119,24 @@ typedef enum {
 } NMSettingConnectionMdns;
 
 /**
+ * NMSettingConnectionLlmnr:
+ * @NM_SETTING_CONNECTION_LLMNR_DEFAULT: default value
+ * @NM_SETTING_CONNECTION_LLMNR_NO: disable LLMNR
+ * @NM_SETTING_CONNECTION_LLMNR_RESOLVE: support only resolving, do not register hostname
+ * @NM_SETTING_CONNECTION_LLMNR_YES: enable LLMNR
+ *
+ * #NMSettingConnectionLlmnr values indicate whether LLMNR should be enabled.
+ *
+ * Since: 1.14
+ */
+typedef enum {
+	NM_SETTING_CONNECTION_LLMNR_DEFAULT      = -1,
+	NM_SETTING_CONNECTION_LLMNR_NO           = 0,
+	NM_SETTING_CONNECTION_LLMNR_RESOLVE      = 1,
+	NM_SETTING_CONNECTION_LLMNR_YES          = 2,
+} NMSettingConnectionLlmnr;
+
+/**
  * NMSettingConnection:
  *
  * General Connection Profile Settings
@@ -142,9 +162,11 @@ const char *nm_setting_connection_get_stable_id        (NMSettingConnection *set
 const char *nm_setting_connection_get_interface_name   (NMSettingConnection *setting);
 const char *nm_setting_connection_get_connection_type  (NMSettingConnection *setting);
 gboolean    nm_setting_connection_get_autoconnect      (NMSettingConnection *setting);
-gint        nm_setting_connection_get_autoconnect_priority (NMSettingConnection *setting);
+int         nm_setting_connection_get_autoconnect_priority (NMSettingConnection *setting);
 NM_AVAILABLE_IN_1_6
-gint        nm_setting_connection_get_autoconnect_retries (NMSettingConnection *setting);
+int         nm_setting_connection_get_autoconnect_retries (NMSettingConnection *setting);
+NM_AVAILABLE_IN_1_14
+NMConnectionMultiConnect nm_setting_connection_get_multi_connect (NMSettingConnection *setting);
 guint64     nm_setting_connection_get_timestamp        (NMSettingConnection *setting);
 gboolean    nm_setting_connection_get_read_only        (NMSettingConnection *setting);
 
@@ -187,10 +209,13 @@ 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);
+int         nm_setting_connection_get_auth_retries     (NMSettingConnection *setting);
 
 NM_AVAILABLE_IN_1_12
 NMSettingConnectionMdns   nm_setting_connection_get_mdns (NMSettingConnection *setting);
+NM_AVAILABLE_IN_1_14
+NMSettingConnectionLlmnr  nm_setting_connection_get_llmnr (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 491d9151..dabcbbaf 100644
--- a/libnm-core/nm-setting-dcb.c
+++ b/libnm-core/nm-setting-dcb.c
@@ -40,22 +40,20 @@
  * of storage technologies like Fibre Channel over Ethernet (FCoE) and iSCSI.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingDcb, nm_setting_dcb, NM_TYPE_SETTING,
-                         _nm_register_setting (DCB, NM_SETTING_PRIORITY_HW_AUX))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_DCB)
+G_DEFINE_TYPE (NMSettingDcb, nm_setting_dcb, NM_TYPE_SETTING)
 
 #define NM_SETTING_DCB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_DCB, NMSettingDcbPrivate))
 
 typedef struct {
 	NMSettingDcbFlags app_fcoe_flags;
-	gint              app_fcoe_priority;
+	int               app_fcoe_priority;
 	char *            app_fcoe_mode;
 
 	NMSettingDcbFlags app_iscsi_flags;
-	gint              app_iscsi_priority;
+	int               app_iscsi_priority;
 
 	NMSettingDcbFlags app_fip_flags;
-	gint              app_fip_priority;
+	int               app_fip_priority;
 
 	/* Priority Flow Control */
 	NMSettingDcbFlags pfc_flags;
@@ -128,7 +126,7 @@ nm_setting_dcb_get_app_fcoe_flags (NMSettingDcb *setting)
  *
  * Returns: the #NMSettingDcb:app-fcoe-priority property of the setting
  **/
-gint
+int
 nm_setting_dcb_get_app_fcoe_priority (NMSettingDcb *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
@@ -170,7 +168,7 @@ nm_setting_dcb_get_app_iscsi_flags (NMSettingDcb *setting)
  *
  * Returns: the #NMSettingDcb:app-iscsi-priority property of the setting
  **/
-gint
+int
 nm_setting_dcb_get_app_iscsi_priority (NMSettingDcb *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
@@ -198,7 +196,7 @@ nm_setting_dcb_get_app_fip_flags (NMSettingDcb *setting)
  *
  * Returns: the #NMSettingDcb:app-fip-priority property of the setting
  **/
-gint
+int
 nm_setting_dcb_get_app_fip_priority (NMSettingDcb *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
@@ -594,7 +592,7 @@ check_uint_array (const guint *array,
 }
 
 static gboolean
-check_priority (gint val,
+check_priority (int val,
                 NMSettingDcbFlags flags,
                 const char *prop_name,
                 GError **error)
@@ -907,20 +905,20 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
+nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingDcbPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingDcbPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize = finalize;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify = verify;
+
 	/**
 	 * NMSettingDcb:app-fcoe-flags:
 	 *
@@ -1119,10 +1117,13 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 		                     G_TYPE_ARRAY,
 		                     G_PARAM_READWRITE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_DCB_PRIORITY_FLOW_CONTROL,
-	                                      G_VARIANT_TYPE ("au"),
-	                                      _nm_setting_dcb_uint_array_to_dbus,
-	                                      _nm_setting_dcb_uint_array_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_DCB_PRIORITY_FLOW_CONTROL),
+	                                    G_VARIANT_TYPE ("au"),
+	                                    _nm_setting_dcb_uint_array_to_dbus,
+	                                    _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
 	 * NMSettingDcb:priority-group-flags:
@@ -1167,10 +1168,13 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 		                     G_TYPE_ARRAY,
 		                     G_PARAM_READWRITE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_DCB_PRIORITY_GROUP_ID,
-	                                      G_VARIANT_TYPE ("au"),
-	                                      _nm_setting_dcb_uint_array_to_dbus,
-	                                      _nm_setting_dcb_uint_array_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_DCB_PRIORITY_GROUP_ID),
+	                                    G_VARIANT_TYPE ("au"),
+	                                    _nm_setting_dcb_uint_array_to_dbus,
+	                                    _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
 	 * NMSettingDcb:priority-group-bandwidth: (type GArray(guint))
@@ -1193,10 +1197,13 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 		                     G_TYPE_ARRAY,
 		                     G_PARAM_READWRITE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH,
-	                                      G_VARIANT_TYPE ("au"),
-	                                      _nm_setting_dcb_uint_array_to_dbus,
-	                                      _nm_setting_dcb_uint_array_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH),
+	                                    G_VARIANT_TYPE ("au"),
+	                                    _nm_setting_dcb_uint_array_to_dbus,
+	                                    _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
 	 * NMSettingDcb:priority-bandwidth: (type GArray(guint))
@@ -1221,10 +1228,13 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 		                     G_TYPE_ARRAY,
 		                     G_PARAM_READWRITE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_DCB_PRIORITY_BANDWIDTH,
-	                                      G_VARIANT_TYPE ("au"),
-	                                      _nm_setting_dcb_uint_array_to_dbus,
-	                                      _nm_setting_dcb_uint_array_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_DCB_PRIORITY_BANDWIDTH),
+	                                    G_VARIANT_TYPE ("au"),
+	                                    _nm_setting_dcb_uint_array_to_dbus,
+	                                    _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
 	 * NMSettingDcb:priority-strict-bandwidth: (type GArray(gboolean))
@@ -1247,10 +1257,13 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 		                     G_TYPE_ARRAY,
 		                     G_PARAM_READWRITE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH,
-	                                      G_VARIANT_TYPE ("au"),
-	                                      _nm_setting_dcb_uint_array_to_dbus,
-	                                      _nm_setting_dcb_uint_array_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH),
+	                                    G_VARIANT_TYPE ("au"),
+	                                    _nm_setting_dcb_uint_array_to_dbus,
+	                                    _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
 	 * NMSettingDcb:priority-traffic-class: (type GArray(guint))
@@ -1272,8 +1285,14 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 		                     G_TYPE_ARRAY,
 		                     G_PARAM_READWRITE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS,
-	                                      G_VARIANT_TYPE ("au"),
-	                                      _nm_setting_dcb_uint_array_to_dbus,
-	                                      _nm_setting_dcb_uint_array_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS),
+	                                    G_VARIANT_TYPE ("au"),
+	                                    _nm_setting_dcb_uint_array_to_dbus,
+	                                    _nm_setting_dcb_uint_array_from_dbus);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_DCB,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-dcb.h b/libnm-core/nm-setting-dcb.h
index 51a883fb..92ca2682 100644
--- a/libnm-core/nm-setting-dcb.h
+++ b/libnm-core/nm-setting-dcb.h
@@ -112,14 +112,14 @@ GType nm_setting_dcb_get_type (void);
 NMSetting *       nm_setting_dcb_new                      (void);
 
 NMSettingDcbFlags nm_setting_dcb_get_app_fcoe_flags     (NMSettingDcb *setting);
-gint              nm_setting_dcb_get_app_fcoe_priority  (NMSettingDcb *setting);
+int               nm_setting_dcb_get_app_fcoe_priority  (NMSettingDcb *setting);
 const char *      nm_setting_dcb_get_app_fcoe_mode      (NMSettingDcb *setting);
 
 NMSettingDcbFlags nm_setting_dcb_get_app_iscsi_flags    (NMSettingDcb *setting);
-gint              nm_setting_dcb_get_app_iscsi_priority (NMSettingDcb *setting);
+int               nm_setting_dcb_get_app_iscsi_priority (NMSettingDcb *setting);
 
 NMSettingDcbFlags nm_setting_dcb_get_app_fip_flags      (NMSettingDcb *setting);
-gint              nm_setting_dcb_get_app_fip_priority   (NMSettingDcb *setting);
+int               nm_setting_dcb_get_app_fip_priority   (NMSettingDcb *setting);
 
 /* Priority Flow Control */
 NMSettingDcbFlags nm_setting_dcb_get_priority_flow_control_flags    (NMSettingDcb *setting);
diff --git a/libnm-core/nm-setting-dummy.c b/libnm-core/nm-setting-dummy.c
index 4b3c41f0..9ae05ec4 100644
--- a/libnm-core/nm-setting-dummy.c
+++ b/libnm-core/nm-setting-dummy.c
@@ -34,9 +34,7 @@
  * necessary for connection to dummy devices
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingDummy, nm_setting_dummy, NM_TYPE_SETTING,
-                         _nm_register_setting (DUMMY, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_DUMMY)
+G_DEFINE_TYPE (NMSettingDummy, nm_setting_dummy, NM_TYPE_SETTING)
 
 /**
  * nm_setting_dummy_new:
@@ -68,9 +66,11 @@ nm_setting_dummy_init (NMSettingDummy *setting)
 }
 
 static void
-nm_setting_dummy_class_init (NMSettingDummyClass *setting_class)
+nm_setting_dummy_class_init (NMSettingDummyClass *klass)
 {
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	parent_class->verify           = verify;
+	setting_class->verify = verify;
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_DUMMY);
 }
diff --git a/libnm-core/nm-setting-ethtool.c b/libnm-core/nm-setting-ethtool.c
new file mode 100644
index 00000000..7bdbcb1a
--- /dev/null
+++ b/libnm-core/nm-setting-ethtool.c
@@ -0,0 +1,342 @@
+/* -*- 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 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-ethtool.h"
+
+#include "nm-setting-private.h"
+#include "nm-ethtool-utils.h"
+
+/*****************************************************************************/
+
+/**
+ * SECTION:nm-setting-ethtool
+ * @short_description: Describes connection properties for ethtool related options
+ *
+ * The #NMSettingEthtool object is a #NMSetting subclass that describes properties
+ * to control network driver and hardware settings.
+ **/
+
+/*****************************************************************************/
+
+/**
+ * nm_ethtool_optname_is_feature:
+ * @optname: the option name to check
+ *
+ * Checks whether @optname is a valid option name for an offload feature.
+ *
+ * %Returns: %TRUE, if @optname is valid
+ *
+ * Since: 1.14
+ */
+gboolean
+nm_ethtool_optname_is_feature (const char *optname)
+{
+	return optname && nm_ethtool_id_is_feature (nm_ethtool_id_get_by_name (optname));
+}
+
+/*****************************************************************************/
+
+/**
+ * NMSettingEthtool:
+ *
+ * Ethtool Ethernet Settings
+ *
+ * Since: 1.14
+ */
+struct _NMSettingEthtool {
+	NMSetting parent;
+};
+
+struct _NMSettingEthtoolClass {
+	NMSettingClass parent;
+};
+
+G_DEFINE_TYPE (NMSettingEthtool, nm_setting_ethtool, NM_TYPE_SETTING)
+
+#define NM_SETTING_ETHTOOL_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSettingEthtool, NM_IS_SETTING_ETHTOOL, NMSetting)
+
+/*****************************************************************************/
+
+static void
+_notify_attributes (NMSettingEthtool *self)
+{
+	_nm_setting_gendata_notify (NM_SETTING (self), TRUE);
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_ethtool_get_feature:
+ * @setting: the #NMSettingEthtool
+ * @optname: option name of the offload feature to get
+ *
+ * Gets and offload feature setting. Returns %NM_TERNARY_DEFAULT if the
+ * feature is not set.
+ *
+ * Returns: a #NMTernary value indicating whether the offload feature
+ *   is enabled, disabled, or left untouched.
+ *
+ * Since: 1.14
+ */
+NMTernary
+nm_setting_ethtool_get_feature (NMSettingEthtool *setting,
+                                const char *optname)
+{
+	GVariant *v;
+
+	g_return_val_if_fail (NM_IS_SETTING_ETHTOOL (setting), NM_TERNARY_DEFAULT);
+	g_return_val_if_fail (optname && nm_ethtool_optname_is_feature (optname), NM_TERNARY_DEFAULT);
+
+	v = nm_setting_gendata_get (NM_SETTING (setting), optname);
+	if (   v
+	    && g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN)) {
+		return g_variant_get_boolean (v)
+		       ? NM_TERNARY_TRUE
+		       : NM_TERNARY_FALSE;
+	}
+	return NM_TERNARY_DEFAULT;
+}
+
+/**
+ * nm_setting_ethtool_set_feature:
+ * @setting: the #NMSettingEthtool
+ * @optname: option name of the offload feature to get
+ * @value: the new value to set. The special value %NM_TERNARY_DEFAULT
+ *   means to clear the offload feature setting.
+ *
+ * Sets and offload feature setting.
+ *
+ * Since: 1.14
+ */
+void
+nm_setting_ethtool_set_feature (NMSettingEthtool *setting,
+                                const char *optname,
+                                NMTernary value)
+{
+	GHashTable *hash;
+	GVariant *v;
+
+	g_return_if_fail (NM_IS_SETTING_ETHTOOL (setting));
+	g_return_if_fail (optname && nm_ethtool_optname_is_feature (optname));
+	g_return_if_fail (NM_IN_SET (value, NM_TERNARY_DEFAULT,
+	                                    NM_TERNARY_FALSE,
+	                                    NM_TERNARY_TRUE));
+
+	hash = _nm_setting_gendata_hash (NM_SETTING (setting),
+	                                 value != NM_TERNARY_DEFAULT);
+
+	if (value == NM_TERNARY_DEFAULT) {
+		if (hash) {
+			if (g_hash_table_remove (hash, optname))
+				_notify_attributes (setting);
+		}
+		return;
+	}
+
+	v = g_hash_table_lookup (hash, optname);
+	if (   v
+	    && g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN)) {
+		if (g_variant_get_boolean (v)) {
+			if (value == NM_TERNARY_TRUE)
+				return;
+		} else {
+			if (value == NM_TERNARY_FALSE)
+				return;
+		}
+	}
+
+	v = g_variant_ref_sink (g_variant_new_boolean (value != NM_TERNARY_FALSE));
+	g_hash_table_insert (hash,
+	                     g_strdup (optname),
+	                     v);
+	_notify_attributes (setting);
+}
+
+/**
+ * nm_setting_ethtool_clear_features:
+ * @setting: the #NMSettingEthtool
+ *
+ * Clears all offload features settings
+ *
+ * Since: 1.14
+ */
+void
+nm_setting_ethtool_clear_features (NMSettingEthtool *setting)
+{
+	GHashTable *hash;
+	GHashTableIter iter;
+	const char *name;
+	gboolean changed = FALSE;
+
+	g_return_if_fail (NM_IS_SETTING_ETHTOOL (setting));
+
+	hash = _nm_setting_gendata_hash (NM_SETTING (setting), FALSE);
+	if (!hash)
+		return;
+
+	g_hash_table_iter_init (&iter, hash);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &name, NULL)) {
+		if (nm_ethtool_optname_is_feature (name)) {
+			g_hash_table_iter_remove (&iter);
+			changed = TRUE;
+		}
+	}
+
+	if (changed)
+		_notify_attributes (setting);
+}
+
+guint
+nm_setting_ethtool_init_features (NMSettingEthtool *setting,
+                                  NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */)
+{
+	GHashTable *hash;
+	GHashTableIter iter;
+	guint i;
+	guint n_req = 0;
+	const char *name;
+	GVariant *variant;
+
+	nm_assert (NM_IS_SETTING_ETHTOOL (setting));
+	nm_assert (requested);
+
+	for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++)
+		requested[i] = NM_TERNARY_DEFAULT;
+
+	hash = _nm_setting_gendata_hash (NM_SETTING (setting), FALSE);
+	if (!hash)
+		return 0;
+
+	g_hash_table_iter_init (&iter, hash);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) {
+		NMEthtoolID ethtool_id = nm_ethtool_id_get_by_name (name);
+
+		if (!nm_ethtool_id_is_feature (ethtool_id))
+			continue;
+		if (!g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN))
+			continue;
+
+		requested[ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST] = g_variant_get_boolean (variant)
+		                                                       ? NM_TERNARY_TRUE
+		                                                       : NM_TERNARY_FALSE;
+		n_req++;
+	}
+
+	return n_req;
+}
+
+/*****************************************************************************/
+
+static gboolean
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	GHashTable *hash;
+	GHashTableIter iter;
+	const char *optname;
+	GVariant *variant;
+
+	hash = _nm_setting_gendata_hash (setting, FALSE);
+
+	if (!hash)
+		goto out;
+
+	g_hash_table_iter_init (&iter, hash);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &optname, (gpointer *) &variant)) {
+		if (!nm_ethtool_optname_is_feature (optname)) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("unsupported offload feature"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_ETHTOOL_SETTING_NAME, optname);
+			return FALSE;
+		}
+		if (!g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("offload feature has invalid variant type"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_ETHTOOL_SETTING_NAME, optname);
+			return FALSE;
+		}
+	}
+
+out:
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+static const GVariantType *
+get_variant_type (const NMSettInfoSetting *sett_info,
+                  const char *name,
+                  GError **error)
+{
+	if (nm_ethtool_optname_is_feature (name))
+		return G_VARIANT_TYPE_BOOLEAN;
+
+	g_set_error (error,
+	             NM_CONNECTION_ERROR,
+	             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	             _("unknown ethtool option '%s'"),
+	             name);
+	return NULL;
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ethtool_init (NMSettingEthtool *setting)
+{
+}
+
+/**
+ * nm_setting_ethtool_new:
+ *
+ * Creates a new #NMSettingEthtool object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingEthtool object
+ *
+ * Since: 1.14
+ **/
+NMSetting *
+nm_setting_ethtool_new (void)
+{
+	return g_object_new (NM_TYPE_SETTING_ETHTOOL, NULL);
+}
+
+static void
+nm_setting_ethtool_class_init (NMSettingEthtoolClass *klass)
+{
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+
+	setting_class->verify = verify;
+
+	_nm_setting_class_commit_full (setting_class,
+	                               NM_META_SETTING_TYPE_ETHTOOL,
+	                               NM_SETT_INFO_SETT_DETAIL (
+	                                 .gendata_info = NM_SETT_INFO_SETT_GENDATA (
+	                                     .get_variant_type = get_variant_type,
+	                                 ),
+	                               ),
+	                               NULL);
+}
diff --git a/libnm-core/nm-setting-ethtool.h b/libnm-core/nm-setting-ethtool.h
new file mode 100644
index 00000000..6a0458a5
--- /dev/null
+++ b/libnm-core/nm-setting-ethtool.h
@@ -0,0 +1,124 @@
+/* -*- 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 2018 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_ETHTOOL_H__
+#define __NM_SETTING_ETHTOOL_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_ETHTOOL_OPTNAME_FEATURE_ESP_HW_OFFLOAD               "feature-esp-hw-offload"
+#define NM_ETHTOOL_OPTNAME_FEATURE_ESP_TX_CSUM_HW_OFFLOAD       "feature-esp-tx-csum-hw-offload"
+#define NM_ETHTOOL_OPTNAME_FEATURE_FCOE_MTU                     "feature-fcoe-mtu"
+#define NM_ETHTOOL_OPTNAME_FEATURE_GRO                          "feature-gro"
+#define NM_ETHTOOL_OPTNAME_FEATURE_GSO                          "feature-gso"
+#define NM_ETHTOOL_OPTNAME_FEATURE_HIGHDMA                      "feature-highdma"
+#define NM_ETHTOOL_OPTNAME_FEATURE_HW_TC_OFFLOAD                "feature-hw-tc-offload"
+#define NM_ETHTOOL_OPTNAME_FEATURE_L2_FWD_OFFLOAD               "feature-l2-fwd-offload"
+#define NM_ETHTOOL_OPTNAME_FEATURE_LOOPBACK                     "feature-loopback"
+#define NM_ETHTOOL_OPTNAME_FEATURE_LRO                          "feature-lro"
+#define NM_ETHTOOL_OPTNAME_FEATURE_NTUPLE                       "feature-ntuple"
+#define NM_ETHTOOL_OPTNAME_FEATURE_RX                           "feature-rx"
+#define NM_ETHTOOL_OPTNAME_FEATURE_RXHASH                       "feature-rxhash"
+#define NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN                       "feature-rxvlan"
+#define NM_ETHTOOL_OPTNAME_FEATURE_RX_ALL                       "feature-rx-all"
+#define NM_ETHTOOL_OPTNAME_FEATURE_RX_FCS                       "feature-rx-fcs"
+#define NM_ETHTOOL_OPTNAME_FEATURE_RX_GRO_HW                    "feature-rx-gro-hw"
+#define NM_ETHTOOL_OPTNAME_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD   "feature-rx-udp_tunnel-port-offload"
+#define NM_ETHTOOL_OPTNAME_FEATURE_RX_VLAN_FILTER               "feature-rx-vlan-filter"
+#define NM_ETHTOOL_OPTNAME_FEATURE_RX_VLAN_STAG_FILTER          "feature-rx-vlan-stag-filter"
+#define NM_ETHTOOL_OPTNAME_FEATURE_RX_VLAN_STAG_HW_PARSE        "feature-rx-vlan-stag-hw-parse"
+#define NM_ETHTOOL_OPTNAME_FEATURE_SG                           "feature-sg"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TLS_HW_RECORD                "feature-tls-hw-record"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TLS_HW_TX_OFFLOAD            "feature-tls-hw-tx-offload"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TSO                          "feature-tso"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX                           "feature-tx"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TXVLAN                       "feature-txvlan"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_FCOE_CRC         "feature-tx-checksum-fcoe-crc"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_IPV4             "feature-tx-checksum-ipv4"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_IPV6             "feature-tx-checksum-ipv6"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_IP_GENERIC       "feature-tx-checksum-ip-generic"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_SCTP             "feature-tx-checksum-sctp"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_ESP_SEGMENTATION          "feature-tx-esp-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_FCOE_SEGMENTATION         "feature-tx-fcoe-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GRE_CSUM_SEGMENTATION     "feature-tx-gre-csum-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GRE_SEGMENTATION          "feature-tx-gre-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GSO_PARTIAL               "feature-tx-gso-partial"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GSO_ROBUST                "feature-tx-gso-robust"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_IPXIP4_SEGMENTATION       "feature-tx-ipxip4-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_IPXIP6_SEGMENTATION       "feature-tx-ipxip6-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_NOCACHE_COPY              "feature-tx-nocache-copy"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_SCATTER_GATHER            "feature-tx-scatter-gather"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_SCATTER_GATHER_FRAGLIST   "feature-tx-scatter-gather-fraglist"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_SCTP_SEGMENTATION         "feature-tx-sctp-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP6_SEGMENTATION         "feature-tx-tcp6-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP_ECN_SEGMENTATION      "feature-tx-tcp-ecn-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP_MANGLEID_SEGMENTATION "feature-tx-tcp-mangleid-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP_SEGMENTATION          "feature-tx-tcp-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_UDP_SEGMENTATION          "feature-tx-udp-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION "feature-tx-udp_tnl-csum-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_UDP_TNL_SEGMENTATION      "feature-tx-udp_tnl-segmentation"
+#define NM_ETHTOOL_OPTNAME_FEATURE_TX_VLAN_STAG_HW_INSERT       "feature-tx-vlan-stag-hw-insert"
+
+gboolean nm_ethtool_optname_is_feature (const char *optname);
+
+/*****************************************************************************/
+
+#define NM_TYPE_SETTING_ETHTOOL            (nm_setting_ethtool_get_type ())
+#define NM_SETTING_ETHTOOL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_ETHTOOL, NMSettingEthtool))
+#define NM_SETTING_ETHTOOL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_ETHTOOL, NMSettingEthtoolClass))
+#define NM_IS_SETTING_ETHTOOL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_ETHTOOL))
+#define NM_IS_SETTING_ETHTOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_ETHTOOL))
+#define NM_SETTING_ETHTOOL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_ETHTOOL, NMSettingEthtoolClass))
+
+#define NM_SETTING_ETHTOOL_SETTING_NAME "ethtool"
+
+/*****************************************************************************/
+
+typedef struct _NMSettingEthtoolClass NMSettingEthtoolClass;
+
+NM_AVAILABLE_IN_1_14
+GType nm_setting_ethtool_get_type (void);
+
+NM_AVAILABLE_IN_1_14
+NMSetting        *nm_setting_ethtool_new (void);
+
+/*****************************************************************************/
+
+NM_AVAILABLE_IN_1_14
+NMTernary         nm_setting_ethtool_get_feature (NMSettingEthtool *setting,
+                                                  const char *optname);
+NM_AVAILABLE_IN_1_14
+void              nm_setting_ethtool_set_feature (NMSettingEthtool *setting,
+                                                  const char *optname,
+                                                  NMTernary value);
+NM_AVAILABLE_IN_1_14
+void              nm_setting_ethtool_clear_features (NMSettingEthtool *setting);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_ETHTOOL_H__ */
diff --git a/libnm-core/nm-setting-generic.c b/libnm-core/nm-setting-generic.c
index 96f26ac2..44402e39 100644
--- a/libnm-core/nm-setting-generic.c
+++ b/libnm-core/nm-setting-generic.c
@@ -36,9 +36,7 @@
  * the "connection type" setting on #NMConnections for generic devices.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingGeneric, nm_setting_generic, NM_TYPE_SETTING,
-                         _nm_register_setting (GENERIC, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_GENERIC)
+G_DEFINE_TYPE (NMSettingGeneric, nm_setting_generic, NM_TYPE_SETTING)
 
 #define NM_SETTING_GENERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GENERIC, NMSettingGenericPrivate))
 
@@ -67,7 +65,11 @@ nm_setting_generic_init (NMSettingGeneric *setting)
 }
 
 static void
-nm_setting_generic_class_init (NMSettingGenericClass *setting_class)
+nm_setting_generic_class_init (NMSettingGenericClass *klass)
 {
-	g_type_class_add_private (setting_class, sizeof (NMSettingGenericPrivate));
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMSettingGenericPrivate));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_GENERIC);
 }
diff --git a/libnm-core/nm-setting-gsm.c b/libnm-core/nm-setting-gsm.c
index 3011d917..b31da689 100644
--- a/libnm-core/nm-setting-gsm.c
+++ b/libnm-core/nm-setting-gsm.c
@@ -38,9 +38,7 @@
  * networks, including those using GPRS/EDGE and UMTS/HSPA technology.
  */
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingGsm, nm_setting_gsm, NM_TYPE_SETTING,
-                         _nm_register_setting (GSM, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_GSM)
+G_DEFINE_TYPE (NMSettingGsm, nm_setting_gsm, NM_TYPE_SETTING)
 
 #define NM_SETTING_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GSM, NMSettingGsmPrivate))
 
@@ -604,22 +602,21 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_gsm_class_init (NMSettingGsmClass *setting_class)
+nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingGsmPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingGsmPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
-	parent_class->verify_secrets = verify_secrets;
-	parent_class->need_secrets = need_secrets;
 
-	/* Properties */
+	setting_class->verify         = verify;
+	setting_class->verify_secrets = verify_secrets;
+	setting_class->need_secrets   = need_secrets;
 
 	/**
 	 * NMSettingGsm:number:
@@ -822,10 +819,18 @@ nm_setting_gsm_class_init (NMSettingGsmClass *setting_class)
 		                    G_PARAM_STATIC_STRINGS));
 
 	/* Ignore incoming deprecated properties */
-	_nm_setting_class_add_dbus_only_property (parent_class, "allowed-bands",
-	                                          G_VARIANT_TYPE_UINT32,
-	                                          NULL, NULL);
-	_nm_setting_class_add_dbus_only_property (parent_class, "network-type",
-	                                          G_VARIANT_TYPE_INT32,
-	                                          NULL, NULL);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "allowed-bands",
+	                                    G_VARIANT_TYPE_UINT32,
+	                                    NULL,
+	                                    NULL);
+
+	_properties_override_add_dbus_only (properties_override,
+	                                    "network-type",
+	                                    G_VARIANT_TYPE_INT32,
+	                                    NULL,
+	                                    NULL);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_GSM,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-infiniband.c b/libnm-core/nm-setting-infiniband.c
index 8c49849a..d5ae7085 100644
--- a/libnm-core/nm-setting-infiniband.c
+++ b/libnm-core/nm-setting-infiniband.c
@@ -37,9 +37,7 @@
  * necessary for connection to IP-over-InfiniBand networks.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingInfiniband, nm_setting_infiniband, NM_TYPE_SETTING,
-                         _nm_register_setting (INFINIBAND, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_INFINIBAND)
+G_DEFINE_TYPE (NMSettingInfiniband, nm_setting_infiniband, NM_TYPE_SETTING)
 
 #define NM_SETTING_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_INFINIBAND, NMSettingInfinibandPrivate))
 
@@ -378,21 +376,20 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_infiniband_class_init (NMSettingInfinibandClass *setting_class)
+nm_setting_infiniband_class_init (NMSettingInfinibandClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingInfinibandPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingInfinibandPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
 
-	parent_class->verify       = verify;
+	setting_class->verify = verify;
 
-	/* Properties */
 	/**
 	 * NMSettingInfiniband:mac-address:
 	 *
@@ -425,10 +422,13 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *setting_class)
 		                      G_PARAM_READWRITE |
 		                      NM_SETTING_PARAM_INFERRABLE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_INFINIBAND_MAC_ADDRESS,
-	                                      G_VARIANT_TYPE_BYTESTRING,
-	                                      _nm_utils_hwaddr_to_dbus,
-	                                      _nm_utils_hwaddr_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_INFINIBAND_MAC_ADDRESS),
+	                                    G_VARIANT_TYPE_BYTESTRING,
+	                                    _nm_utils_hwaddr_to_dbus,
+	                                    _nm_utils_hwaddr_from_dbus);
 
 	/**
 	 * NMSettingInfiniband:mtu:
@@ -526,4 +526,6 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *setting_class)
 		                      NM_SETTING_PARAM_INFERRABLE |
 		                      G_PARAM_STATIC_STRINGS));
 
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_INFINIBAND,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c
index c7f8c555..fa2535b8 100644
--- a/libnm-core/nm-setting-ip-config.c
+++ b/libnm-core/nm-setting-ip-config.c
@@ -1391,7 +1391,7 @@ typedef struct {
 	GPtrArray *dns;        /* array of IP address strings */
 	GPtrArray *dns_search; /* array of domain name strings */
 	GPtrArray *dns_options;/* array of DNS options */
-	gint dns_priority;
+	int dns_priority;
 	GPtrArray *addresses;  /* array of NMIPAddress */
 	GPtrArray *routes;     /* array of NMIPRoute */
 	gint64 route_metric;
@@ -1403,8 +1403,8 @@ typedef struct {
 	gboolean dhcp_send_hostname;
 	gboolean never_default;
 	gboolean may_fail;
-	gint dad_timeout;
-	gint dhcp_timeout;
+	int dad_timeout;
+	int dhcp_timeout;
 } NMSettingIPConfigPrivate;
 
 enum {
@@ -1589,8 +1589,11 @@ nm_setting_ip_config_clear_dns (NMSettingIPConfig *setting)
 	g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	g_ptr_array_set_size (priv->dns, 0);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS);
+
+	if (priv->dns->len != 0) {
+		g_ptr_array_set_size (priv->dns, 0);
+		g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS);
+	}
 }
 
 /**
@@ -1688,8 +1691,6 @@ nm_setting_ip_config_remove_dns_search (NMSettingIPConfig *setting, int idx)
  * Removes the DNS search domain @dns_search.
  *
  * Returns: %TRUE if the DNS search domain was found and removed; %FALSE if it was not.
- *
- * Since 0.9.10
  **/
 gboolean
 nm_setting_ip_config_remove_dns_search_by_value (NMSettingIPConfig *setting,
@@ -1727,8 +1728,11 @@ nm_setting_ip_config_clear_dns_searches (NMSettingIPConfig *setting)
 	g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
 
 	priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-	g_ptr_array_set_size (priv->dns_search, 0);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH);
+
+	if (priv->dns_search->len != 0) {
+		g_ptr_array_set_size (priv->dns_search, 0);
+		g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH);
+	}
 }
 
 /**
@@ -1804,7 +1808,7 @@ nm_setting_ip_config_get_dns_option (NMSettingIPConfig *setting, guint idx)
  *
  * Since: 1.2
  **/
-gint
+int
 nm_setting_ip_config_next_valid_dns_option (NMSettingIPConfig *setting, guint idx)
 {
 	NMSettingIPConfigPrivate *priv;
@@ -1966,7 +1970,7 @@ nm_setting_ip_config_clear_dns_options (NMSettingIPConfig *setting, gboolean is_
  *
  * Since: 1.4
  **/
-gint
+int
 nm_setting_ip_config_get_dns_priority (NMSettingIPConfig *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), 0);
@@ -2107,8 +2111,10 @@ nm_setting_ip_config_clear_addresses (NMSettingIPConfig *setting)
 
 	g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
 
-	g_ptr_array_set_size (priv->addresses, 0);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
+	if (priv->addresses->len != 0) {
+		g_ptr_array_set_size (priv->addresses, 0);
+		g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
+	}
 }
 
 /**
@@ -2264,8 +2270,10 @@ nm_setting_ip_config_clear_routes (NMSettingIPConfig *setting)
 
 	g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting));
 
-	g_ptr_array_set_size (priv->routes, 0);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES);
+	if (priv->routes->len != 0) {
+		g_ptr_array_set_size (priv->routes, 0);
+		g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES);
+	}
 }
 
 /**
@@ -2421,7 +2429,7 @@ nm_setting_ip_config_get_may_fail (NMSettingIPConfig *setting)
  *
  * Since: 1.2
  **/
-gint
+int
 nm_setting_ip_config_get_dad_timeout (NMSettingIPConfig *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), 0);
@@ -2441,7 +2449,7 @@ nm_setting_ip_config_get_dad_timeout (NMSettingIPConfig *setting)
  *
  * Since: 1.2
  **/
-gint
+int
 nm_setting_ip_config_get_dhcp_timeout (NMSettingIPConfig *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), 0);
@@ -2611,7 +2619,7 @@ compare_property (NMSetting *setting,
                   NMSettingCompareFlags flags)
 {
 	NMSettingIPConfigPrivate *a_priv, *b_priv;
-	NMSettingClass *parent_class;
+	NMSettingClass *setting_class;
 	guint i;
 
 	if (nm_streq (prop_spec->name, NM_SETTING_IP_CONFIG_ADDRESSES)) {
@@ -2640,9 +2648,8 @@ compare_property (NMSetting *setting,
 		return TRUE;
 	}
 
-	/* Otherwise chain up to parent to handle generic compare */
-	parent_class = NM_SETTING_CLASS (nm_setting_ip_config_parent_class);
-	return parent_class->compare_property (setting, other, prop_spec, flags);
+	setting_class = NM_SETTING_CLASS (nm_setting_ip_config_parent_class);
+	return setting_class->compare_property (setting, other, prop_spec, flags);
 }
 
 /*****************************************************************************/
@@ -2869,22 +2876,37 @@ ip_gateway_set (NMSetting  *setting,
 	return TRUE;
 }
 
+GArray *
+_nm_sett_info_property_override_create_array_ip_config (void)
+{
+	nm_auto_unref_gtypeclass NMSettingClass *setting_class = g_type_class_ref (NM_TYPE_SETTING_IP_CONFIG);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
+
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_IP_CONFIG_GATEWAY),
+	                                   G_VARIANT_TYPE_STRING,
+	                                   NULL,
+	                                   ip_gateway_set,
+	                                   NULL);
+
+	return properties_override;
+}
+
 static void
-nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
+nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingIPConfigPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingIPConfigPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
-	parent_class->compare_property = compare_property;
 
-	/* Properties */
+	setting_class->verify           = verify;
+	setting_class->compare_property = compare_property;
 
 	/**
 	 * NMSettingIPConfig:method:
@@ -2905,7 +2927,9 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
 	 * "link-local", these properties must be empty.
 	 *
 	 * For IPv4 method "shared", the IP subnet can be configured by adding one
-	 * manual IPv4 address or otherwise 10.42.x.0/24 is chosen.
+	 * manual IPv4 address or otherwise 10.42.x.0/24 is chosen. Note that the
+	 * shared method must be configured on the interface which shares the internet
+	 * to a subnet, not on the uplink which is shared.
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_METHOD,
@@ -3034,13 +3058,6 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
 		                      NM_SETTING_PARAM_INFERRABLE |
 		                      G_PARAM_STATIC_STRINGS));
 
-	_nm_setting_class_override_property (parent_class,
-	                                     NM_SETTING_IP_CONFIG_GATEWAY,
-	                                     G_VARIANT_TYPE_STRING,
-	                                     NULL,
-	                                     ip_gateway_set,
-	                                     NULL);
-
 	/**
 	 * NMSettingIPConfig:routes: (type GPtrArray(NMIPRoute))
 	 *
diff --git a/libnm-core/nm-setting-ip-config.h b/libnm-core/nm-setting-ip-config.h
index 8b3c85de..882a88b6 100644
--- a/libnm-core/nm-setting-ip-config.h
+++ b/libnm-core/nm-setting-ip-config.h
@@ -247,7 +247,7 @@ guint         nm_setting_ip_config_get_num_dns_options        (NMSettingIPConfig
 gboolean      nm_setting_ip_config_has_dns_options            (NMSettingIPConfig *setting);
 const char   *nm_setting_ip_config_get_dns_option             (NMSettingIPConfig *setting,
                                                                guint              idx);
-gint          nm_setting_ip_config_next_valid_dns_option      (NMSettingIPConfig *setting,
+int           nm_setting_ip_config_next_valid_dns_option      (NMSettingIPConfig *setting,
                                                                guint              idx);
 gboolean      nm_setting_ip_config_add_dns_option             (NMSettingIPConfig *setting,
                                                                const char        *dns_option);
@@ -258,7 +258,7 @@ gboolean      nm_setting_ip_config_remove_dns_option_by_value (NMSettingIPConfig
 void          nm_setting_ip_config_clear_dns_options          (NMSettingIPConfig *setting, gboolean is_set);
 
 NM_AVAILABLE_IN_1_4
-gint          nm_setting_ip_config_get_dns_priority (NMSettingIPConfig *setting);
+int           nm_setting_ip_config_get_dns_priority (NMSettingIPConfig *setting);
 
 guint         nm_setting_ip_config_get_num_addresses          (NMSettingIPConfig *setting);
 NMIPAddress  *nm_setting_ip_config_get_address                (NMSettingIPConfig *setting,
@@ -298,9 +298,9 @@ gboolean      nm_setting_ip_config_get_dhcp_send_hostname     (NMSettingIPConfig
 gboolean      nm_setting_ip_config_get_never_default          (NMSettingIPConfig *setting);
 gboolean      nm_setting_ip_config_get_may_fail               (NMSettingIPConfig *setting);
 NM_AVAILABLE_IN_1_2
-gint          nm_setting_ip_config_get_dad_timeout            (NMSettingIPConfig *setting);
+int           nm_setting_ip_config_get_dad_timeout            (NMSettingIPConfig *setting);
 NM_AVAILABLE_IN_1_2
-gint          nm_setting_ip_config_get_dhcp_timeout           (NMSettingIPConfig *setting);
+int           nm_setting_ip_config_get_dhcp_timeout           (NMSettingIPConfig *setting);
 
 G_END_DECLS
 
diff --git a/libnm-core/nm-setting-ip-tunnel.c b/libnm-core/nm-setting-ip-tunnel.c
index c9cb97df..41314f58 100644
--- a/libnm-core/nm-setting-ip-tunnel.c
+++ b/libnm-core/nm-setting-ip-tunnel.c
@@ -30,9 +30,7 @@
  * @short_description: Describes connection properties for IP tunnel devices
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingIPTunnel, nm_setting_ip_tunnel, NM_TYPE_SETTING,
-                         _nm_register_setting (IP_TUNNEL, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP_TUNNEL)
+G_DEFINE_TYPE (NMSettingIPTunnel, nm_setting_ip_tunnel, NM_TYPE_SETTING)
 
 #define NM_SETTING_IP_TUNNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP_TUNNEL, NMSettingIPTunnelPrivate))
 
@@ -320,12 +318,14 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	case NM_IP_TUNNEL_MODE_ISATAP:
 	case NM_IP_TUNNEL_MODE_GRE:
 	case NM_IP_TUNNEL_MODE_VTI:
+	case NM_IP_TUNNEL_MODE_GRETAP:
 		family = AF_INET;
 		break;
 	case NM_IP_TUNNEL_MODE_IP6IP6:
 	case NM_IP_TUNNEL_MODE_IPIP6:
 	case NM_IP_TUNNEL_MODE_IP6GRE:
 	case NM_IP_TUNNEL_MODE_VTI6:
+	case NM_IP_TUNNEL_MODE_IP6GRETAP:
 		family = AF_INET6;
 		break;
 	case NM_IP_TUNNEL_MODE_UNKNOWN:
@@ -388,8 +388,11 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 
 	if (   (priv->input_key && priv->input_key[0])
 	    || (priv->output_key && priv->output_key[0])) {
-		if (   priv->mode != NM_IP_TUNNEL_MODE_GRE
-		    && priv->mode != NM_IP_TUNNEL_MODE_IP6GRE) {
+		if (!NM_IN_SET (priv->mode,
+		                NM_IP_TUNNEL_MODE_GRE,
+		                NM_IP_TUNNEL_MODE_GRETAP,
+		                NM_IP_TUNNEL_MODE_IP6GRE,
+		                NM_IP_TUNNEL_MODE_IP6GRETAP)) {
 			g_set_error_literal (error,
 			                     NM_CONNECTION_ERROR,
 			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -410,7 +413,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			             priv->input_key);
 			g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME,
 			                NM_SETTING_IP_TUNNEL_INPUT_KEY);
-		return FALSE;
+			return FALSE;
 		}
 	}
 
@@ -426,7 +429,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			             priv->output_key);
 			g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME,
 			                NM_SETTING_IP_TUNNEL_OUTPUT_KEY);
-		return FALSE;
+			return FALSE;
 		}
 	}
 
@@ -454,6 +457,20 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
+	if (   nm_connection_get_setting_wired (connection)
+	    && !NM_IN_SET (priv->mode,
+	                   NM_IP_TUNNEL_MODE_GRETAP,
+	                   NM_IP_TUNNEL_MODE_IP6GRETAP)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("wired setting not allowed for mode %s"),
+		             nm_utils_enum_to_str (nm_ip_tunnel_mode_get_type (), priv->mode));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME,
+		                NM_SETTING_IP_TUNNEL_MODE);
+		return NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+	}
+
 	return TRUE;
 }
 
@@ -604,18 +621,18 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *setting_class)
+nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingIPTunnelPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingIPTunnelPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
+
+	setting_class->verify = verify;
 
 	/**
 	 * NMSettingIPTunnel:parent:
@@ -833,4 +850,6 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *setting_class)
 		                    G_PARAM_READWRITE |
 		                    NM_SETTING_PARAM_FUZZY_IGNORE |
 		                    G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_IP_TUNNEL);
 }
diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c
index 0c668d8c..19f1cc8d 100644
--- a/libnm-core/nm-setting-ip4-config.c
+++ b/libnm-core/nm-setting-ip4-config.c
@@ -50,9 +50,7 @@
  * connection.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingIP4Config, nm_setting_ip4_config, NM_TYPE_SETTING_IP_CONFIG,
-                         _nm_register_setting (IP4_CONFIG, NM_SETTING_PRIORITY_IP))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP4_CONFIG)
+G_DEFINE_TYPE (NMSettingIP4Config, nm_setting_ip4_config, NM_TYPE_SETTING_IP_CONFIG)
 
 #define NM_SETTING_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4ConfigPrivate))
 
@@ -525,20 +523,19 @@ ip4_route_data_set (NMSetting  *setting,
 }
 
 static void
-nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
+nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *klass)
 {
-	NMSettingClass *setting_class = NM_SETTING_CLASS (ip4_class);
-	GObjectClass *object_class = G_OBJECT_CLASS (ip4_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array_ip_config ();
 
 	g_type_class_add_private (setting_class, sizeof (NMSettingIP4ConfigPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	setting_class->verify = verify;
 
-	/* properties */
+	setting_class->verify = verify;
 
 	/* ---ifcfg-rh---
 	 * property: method
@@ -721,7 +718,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
 	 *
 	 * The special values "mac" and "perm-mac" are supported, which use the
 	 * current or permanent MAC address of the device to generate a client identifier
-	 * with type ethernet type (01). Currently, these options only work for ethernet
+	 * with type ethernet (01). Currently, these options only work for ethernet
 	 * type of links.
 	 *
 	 * The special value "stable" is supported to generate a type 0 client identifier based
@@ -747,7 +744,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
 
 	/* ---ifcfg-rh---
 	 * property: dad-timeout
-	 * variable: ACD_TIMEOUT, ARPING_WAIT
+	 * variable: ACD_TIMEOUT(+), ARPING_WAIT
 	 * default: missing variable means global default (config override or zero)
 	 * description: Timeout (in milliseconds for ACD_TIMEOUT or in seconds
 	 *   for ARPING_WAIT) for address conflict detection before configuring
@@ -798,11 +795,12 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
 	 *   integers)
 	 * ---end---
 	 */
-	_nm_setting_class_transform_property (setting_class,
-	                                      NM_SETTING_IP_CONFIG_DNS,
-	                                      G_VARIANT_TYPE ("au"),
-	                                      ip4_dns_to_dbus,
-	                                      ip4_dns_from_dbus);
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_IP_CONFIG_DNS),
+	                                    G_VARIANT_TYPE ("au"),
+	                                    ip4_dns_to_dbus,
+	                                    ip4_dns_from_dbus);
 
 	/* ---dbus---
 	 * property: addresses
@@ -819,18 +817,19 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
 	 *   for that subnet.
 	 * ---end---
 	 */
-	_nm_setting_class_override_property (setting_class,
-	                                     NM_SETTING_IP_CONFIG_ADDRESSES,
-	                                     G_VARIANT_TYPE ("aau"),
-	                                     ip4_addresses_get,
-	                                     ip4_addresses_set,
-	                                     NULL);
-
-	_nm_setting_class_add_dbus_only_property (setting_class,
-	                                          "address-labels",
-	                                          G_VARIANT_TYPE_STRING_ARRAY,
-	                                          ip4_address_labels_get,
-	                                          NULL);
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_IP_CONFIG_ADDRESSES),
+	                                   G_VARIANT_TYPE ("aau"),
+	                                   ip4_addresses_get,
+	                                   ip4_addresses_set,
+	                                   NULL);
+
+	_properties_override_add_dbus_only (properties_override,
+	                                    "address-labels",
+	                                    G_VARIANT_TYPE_STRING_ARRAY,
+	                                    ip4_address_labels_get,
+	                                    NULL);
 
 	/* ---dbus---
 	 * property: address-data
@@ -841,11 +840,11 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
 	 *   also exist on some addresses.
 	 * ---end---
 	 */
-	_nm_setting_class_add_dbus_only_property (setting_class,
-	                                          "address-data",
-	                                          G_VARIANT_TYPE ("aa{sv}"),
-	                                          ip4_address_data_get,
-	                                          ip4_address_data_set);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "address-data",
+	                                    G_VARIANT_TYPE ("aa{sv}"),
+	                                    ip4_address_data_get,
+	                                    ip4_address_data_set);
 
 	/* ---dbus---
 	 * property: routes
@@ -864,12 +863,13 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
 	 *   property.)
 	 * ---end---
 	 */
-	_nm_setting_class_override_property (setting_class,
-	                                     NM_SETTING_IP_CONFIG_ROUTES,
-	                                     G_VARIANT_TYPE ("aau"),
-	                                     ip4_routes_get,
-	                                     ip4_routes_set,
-	                                     NULL);
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_IP_CONFIG_ROUTES),
+	                                   G_VARIANT_TYPE ("aau"),
+	                                   ip4_routes_get,
+	                                   ip4_routes_set,
+	                                   NULL);
 
 	/* ---dbus---
 	 * property: route-data
@@ -884,10 +884,12 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
 	 *   also exist on some routes.
 	 * ---end---
 	 */
-	_nm_setting_class_add_dbus_only_property (setting_class,
-	                                          "route-data",
-	                                          G_VARIANT_TYPE ("aa{sv}"),
-	                                          ip4_route_data_get,
-	                                          ip4_route_data_set);
-
+	_properties_override_add_dbus_only (properties_override,
+	                                    "route-data",
+	                                    G_VARIANT_TYPE ("aa{sv}"),
+	                                    ip4_route_data_get,
+	                                    ip4_route_data_set);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_IP4_CONFIG,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c
index 808c88a7..a55fd801 100644
--- a/libnm-core/nm-setting-ip6-config.c
+++ b/libnm-core/nm-setting-ip6-config.c
@@ -52,9 +52,7 @@
  * supported.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingIP6Config, nm_setting_ip6_config, NM_TYPE_SETTING_IP_CONFIG,
-                         _nm_register_setting (IP6_CONFIG, NM_SETTING_PRIORITY_IP))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP6_CONFIG)
+G_DEFINE_TYPE (NMSettingIP6Config, nm_setting_ip6_config, NM_TYPE_SETTING_IP_CONFIG)
 
 #define NM_SETTING_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6ConfigPrivate))
 
@@ -549,20 +547,19 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
+nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (ip6_class);
-	NMSettingClass *setting_class = NM_SETTING_CLASS (ip6_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array_ip_config ();
 
-	g_type_class_add_private (ip6_class, sizeof (NMSettingIP6ConfigPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingIP6ConfigPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	object_class->finalize = finalize;
-	setting_class->verify = verify;
+	object_class->finalize     = finalize;
 
-	/* Properties */
+	setting_class->verify = verify;
 
 	/* ---ifcfg-rh---
 	 * property: method
@@ -590,7 +587,7 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
 
 	/* ---ifcfg-rh---
 	 * property: dns-search
-	 * variable: IPV6_DOMAIN
+	 * variable: IPV6_DOMAIN(+)
 	 * format:   string (space-separated domains)
 	 * description: List of DNS search domains.
 	 * ---end---
@@ -891,11 +888,12 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
 	 * description: Array of IP addresses of DNS servers (in network byte order)
 	 * ---end---
 	 */
-	_nm_setting_class_transform_property (setting_class,
-	                                      NM_SETTING_IP_CONFIG_DNS,
-	                                      G_VARIANT_TYPE ("aay"),
-	                                      ip6_dns_to_dbus,
-	                                      ip6_dns_from_dbus);
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_IP_CONFIG_DNS),
+	                                    G_VARIANT_TYPE ("aay"),
+	                                    ip6_dns_to_dbus,
+	                                    ip6_dns_from_dbus);
 
 	/* ---dbus---
 	 * property: addresses
@@ -911,12 +909,13 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
 	 *   that subnet.
 	 * ---end---
 	 */
-	_nm_setting_class_override_property (setting_class,
-	                                     NM_SETTING_IP_CONFIG_ADDRESSES,
-	                                     G_VARIANT_TYPE ("a(ayuay)"),
-	                                     ip6_addresses_get,
-	                                     ip6_addresses_set,
-	                                     NULL);
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_IP_CONFIG_ADDRESSES),
+	                                   G_VARIANT_TYPE ("a(ayuay)"),
+	                                   ip6_addresses_get,
+	                                   ip6_addresses_set,
+	                                   NULL);
 
 	/* ---dbus---
 	 * property: address-data
@@ -927,11 +926,11 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
 	 *   also exist on some addresses.
 	 * ---end---
 	 */
-	_nm_setting_class_add_dbus_only_property (setting_class,
-	                                          "address-data",
-	                                          G_VARIANT_TYPE ("aa{sv}"),
-	                                          ip6_address_data_get,
-	                                          ip6_address_data_set);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "address-data",
+	                                    G_VARIANT_TYPE ("aa{sv}"),
+	                                    ip6_address_data_get,
+	                                    ip6_address_data_set);
 
 	/* ---dbus---
 	 * property: routes
@@ -947,12 +946,13 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
 	 *   default metric for the device.
 	 * ---end---
 	 */
-	_nm_setting_class_override_property (setting_class,
-	                                     NM_SETTING_IP_CONFIG_ROUTES,
-	                                     G_VARIANT_TYPE ("a(ayuayu)"),
-	                                     ip6_routes_get,
-	                                     ip6_routes_set,
-	                                     NULL);
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_IP_CONFIG_ROUTES),
+	                                   G_VARIANT_TYPE ("a(ayuayu)"),
+	                                   ip6_routes_get,
+	                                   ip6_routes_set,
+	                                   NULL);
 
 	/* ---dbus---
 	 * property: route-data
@@ -967,9 +967,12 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
 	 *   also exist on some routes.
 	 * ---end---
 	 */
-	_nm_setting_class_add_dbus_only_property (setting_class,
-	                                          "route-data",
-	                                          G_VARIANT_TYPE ("aa{sv}"),
-	                                          ip6_route_data_get,
-	                                          ip6_route_data_set);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "route-data",
+	                                    G_VARIANT_TYPE ("aa{sv}"),
+	                                    ip6_route_data_get,
+	                                    ip6_route_data_set);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_IP6_CONFIG,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-macsec.c b/libnm-core/nm-setting-macsec.c
index 92ebafa6..2c7cff23 100644
--- a/libnm-core/nm-setting-macsec.c
+++ b/libnm-core/nm-setting-macsec.c
@@ -40,9 +40,7 @@
  * necessary for connection to MACsec (IEEE 802.1AE) interfaces.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingMacsec, nm_setting_macsec, NM_TYPE_SETTING,
-                         _nm_register_setting (MACSEC, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_MACSEC)
+G_DEFINE_TYPE (NMSettingMacsec, nm_setting_macsec, NM_TYPE_SETTING)
 
 #define NM_SETTING_MACSEC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_MACSEC, NMSettingMacsecPrivate))
 
@@ -474,18 +472,19 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_macsec_class_init (NMSettingMacsecClass *setting_class)
+nm_setting_macsec_class_init (NMSettingMacsecClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingMacsecPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingMacsecPrivate));
 
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
-	parent_class->need_secrets = need_secrets;
+
+	setting_class->verify       = verify;
+	setting_class->need_secrets = need_secrets;
 
 	/**
 	 * NMSettingMacsec:parent:
@@ -627,4 +626,6 @@ nm_setting_macsec_class_init (NMSettingMacsecClass *setting_class)
 	                          G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_MACSEC);
 }
diff --git a/libnm-core/nm-setting-macvlan.c b/libnm-core/nm-setting-macvlan.c
index 6fea8b0d..1fc16116 100644
--- a/libnm-core/nm-setting-macvlan.c
+++ b/libnm-core/nm-setting-macvlan.c
@@ -39,9 +39,7 @@
  * necessary for connection to macvlan interfaces.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingMacvlan, nm_setting_macvlan, NM_TYPE_SETTING,
-                         _nm_register_setting (MACVLAN, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_MACVLAN)
+G_DEFINE_TYPE (NMSettingMacvlan, nm_setting_macvlan, NM_TYPE_SETTING)
 
 #define NM_SETTING_MACVLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_MACVLAN, NMSettingMacvlanPrivate))
 
@@ -260,20 +258,18 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_macvlan_class_init (NMSettingMacvlanClass *setting_class)
+nm_setting_macvlan_class_init (NMSettingMacvlanClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingMacvlanPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingMacvlanPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify = verify;
 
 	/**
 	 * NMSettingMacvlan:parent:
@@ -342,4 +338,6 @@ nm_setting_macvlan_class_init (NMSettingMacvlanClass *setting_class)
 		                       G_PARAM_CONSTRUCT |
 		                       NM_SETTING_PARAM_INFERRABLE |
 		                       G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_MACVLAN);
 }
diff --git a/libnm-core/nm-setting-match.c b/libnm-core/nm-setting-match.c
new file mode 100644
index 00000000..0964c644
--- /dev/null
+++ b/libnm-core/nm-setting-match.c
@@ -0,0 +1,295 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-match.h"
+#include "nm-setting-private.h"
+#include "nm-utils-private.h"
+
+/**
+ * SECTION:nm-setting-match
+ * @short_description: Properties to match a connection with a device.
+ * @include: nm-setting-match.h
+ **/
+
+/**
+ * NMSettingMatch:
+ *
+ * Match settings.
+ *
+ * Since: 1.14
+ */
+struct _NMSettingMatch {
+	NMSetting parent;
+	GPtrArray *interface_name;
+};
+
+struct _NMSettingMatchClass {
+	NMSettingClass parent;
+};
+
+G_DEFINE_TYPE (NMSettingMatch, nm_setting_match, NM_TYPE_SETTING)
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingMatch,
+	PROP_INTERFACE_NAME,
+);
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_match_get_num_interface_names:
+ * @setting: the #NMSettingMatch
+ *
+ * Returns: the number of configured interface names
+ *
+ * Since: 1.14
+ **/
+guint
+nm_setting_match_get_num_interface_names (NMSettingMatch *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_MATCH (setting), 0);
+
+	return setting->interface_name->len;
+}
+
+/**
+ * nm_setting_match_get_interface_name:
+ * @setting: the #NMSettingMatch
+ * @idx: index number of the DNS search domain to return
+ *
+ * Returns: the interface name at index @idx
+ *
+ * Since: 1.14
+ **/
+const char *
+nm_setting_match_get_interface_name (NMSettingMatch *setting, int idx)
+{
+	g_return_val_if_fail (NM_IS_SETTING_MATCH (setting), NULL);
+
+	g_return_val_if_fail (idx >= 0 && idx < setting->interface_name->len, NULL);
+
+	return setting->interface_name->pdata[idx];
+}
+
+/**
+ * nm_setting_match_add_interface_name:
+ * @setting: the #NMSettingMatch
+ * @interface_name: the interface name to add
+ *
+ * Adds a new interface name to the setting.
+ *
+ * Since: 1.14
+ **/
+void
+nm_setting_match_add_interface_name (NMSettingMatch *setting,
+                                     const char *interface_name)
+{
+	g_return_if_fail (NM_IS_SETTING_MATCH (setting));
+	g_return_if_fail (interface_name != NULL);
+	g_return_if_fail (interface_name[0] != '\0');
+
+	g_ptr_array_add (setting->interface_name, g_strdup (interface_name));
+	_notify (setting, PROP_INTERFACE_NAME);
+}
+
+/**
+ * nm_setting_match_remove_interface_name:
+ * @setting: the #NMSettingMatch
+ * @idx: index number of the interface name
+ *
+ * Removes the interface name at index @idx.
+ *
+ * Since: 1.14
+ **/
+void
+nm_setting_match_remove_interface_name (NMSettingMatch *setting, int idx)
+{
+	g_return_if_fail (NM_IS_SETTING_MATCH (setting));
+
+	g_return_if_fail (idx >= 0 && idx < setting->interface_name->len);
+
+	g_ptr_array_remove_index (setting->interface_name, idx);
+	_notify (setting, PROP_INTERFACE_NAME);
+}
+
+/**
+ * nm_setting_match_remove_interface_name_by_value:
+ * @setting: the #NMSettingMatch
+ * @interface_name: the interface name to remove
+ *
+ * Removes @interface_name.
+ *
+ * Returns: %TRUE if the interface name was found and removed; %FALSE if it was not.
+ *
+ * Since: 1.14
+ **/
+gboolean
+nm_setting_match_remove_interface_name_by_value (NMSettingMatch *setting,
+                                                 const char *interface_name)
+{
+	guint i;
+
+	g_return_val_if_fail (NM_IS_SETTING_MATCH (setting), FALSE);
+	g_return_val_if_fail (interface_name != NULL, FALSE);
+	g_return_val_if_fail (interface_name[0] != '\0', FALSE);
+
+	for (i = 0; i < setting->interface_name->len; i++) {
+		if (nm_streq (interface_name, setting->interface_name->pdata[i])) {
+			g_ptr_array_remove_index (setting->interface_name, i);
+			_notify (setting, PROP_INTERFACE_NAME);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+/**
+ * nm_setting_match_clear_interface_names:
+ * @setting: the #NMSettingMatch
+ *
+ * Removes all configured interface names.
+ *
+ * Since: 1.14
+ **/
+void
+nm_setting_match_clear_interface_names (NMSettingMatch *setting)
+{
+	g_return_if_fail (NM_IS_SETTING_MATCH (setting));
+
+	if (setting->interface_name->len != 0) {
+		g_ptr_array_set_size (setting->interface_name, 0);
+		_notify (setting, PROP_INTERFACE_NAME);
+	}
+}
+
+/**
+ * nm_setting_match_get_interface_names:
+ * @setting: the #NMSettingMatch
+ *
+ * Returns all the interface names.
+ *
+ * Returns: (transfer none): the configured interface names.
+ *
+ * Since: 1.14
+ **/
+const char *const *
+nm_setting_match_get_interface_names (NMSettingMatch *setting, guint *length)
+{
+	g_return_val_if_fail (NM_IS_SETTING_MATCH (setting), NULL);
+	g_return_val_if_fail (length, NULL);
+
+	NM_SET_OUT (length, setting->interface_name->len);
+	return (const char *const *) setting->interface_name->pdata;
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingMatch *self = NM_SETTING_MATCH (object);
+
+	switch (prop_id) {
+	case PROP_INTERFACE_NAME:
+		g_value_take_boxed (value, _nm_utils_ptrarray_to_strv (self->interface_name));
+		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)
+{
+	NMSettingMatch *self = NM_SETTING_MATCH (object);
+
+	switch (prop_id) {
+	case PROP_INTERFACE_NAME:
+		g_ptr_array_unref (self->interface_name);
+		self->interface_name = _nm_utils_strv_to_ptrarray (g_value_get_boxed (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_match_init (NMSettingMatch *setting)
+{
+	setting->interface_name = g_ptr_array_new_with_free_func (g_free);
+}
+
+/**
+ * nm_setting_match_new:
+ *
+ * Creates a new #NMSettingMatch object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingMatch object
+ *
+ * Since: 1.14
+ **/
+NMSetting *
+nm_setting_match_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_MATCH, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingMatch *self = NM_SETTING_MATCH (object);
+
+	g_ptr_array_unref (self->interface_name);
+
+	G_OBJECT_CLASS (nm_setting_match_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_match_class_init (NMSettingMatchClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+
+	object_class->finalize     = finalize;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+
+	/**
+	 * NMSettingMatch:interface-name
+	 *
+	 * A list of interface names to match. Each element is a shell wildcard
+	 * pattern.  When an element is prefixed with exclamation mark (!) the
+	 * condition is inverted.
+	 *
+	 * A candidate interface name is considered matching when both these
+	 * conditions are satisfied: (a) any of the elements not prefixed with '!'
+	 * matches or there aren't such elements; (b) none of the elements
+	 * prefixed with '!' match.
+	 *
+	 * Since: 1.14
+	 **/
+	obj_properties[PROP_INTERFACE_NAME] =
+	    g_param_spec_boxed (NM_SETTING_MATCH_INTERFACE_NAME, "", "",
+	                        G_TYPE_STRV,
+	                        NM_SETTING_PARAM_FUZZY_IGNORE |
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_MATCH);
+}
diff --git a/libnm-core/nm-setting-match.h b/libnm-core/nm-setting-match.h
new file mode 100644
index 00000000..a39feca2
--- /dev/null
+++ b/libnm-core/nm-setting-match.h
@@ -0,0 +1,63 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_MATCH_H
+#define NM_SETTING_MATCH_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_MATCH            (nm_setting_match_get_type ())
+#define NM_SETTING_MATCH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_MATCH, NMSettingMatch))
+#define NM_SETTING_MATCH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_MATCH, NMSettingMatchClass))
+#define NM_IS_SETTING_MATCH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_MATCH))
+#define NM_IS_SETTING_MATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_MATCH))
+#define NM_SETTING_MATCH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_MATCH, NMSettingMatchClass))
+
+#define NM_SETTING_MATCH_SETTING_NAME      "match"
+
+#define NM_SETTING_MATCH_INTERFACE_NAME    "interface-name"
+
+typedef struct _NMSettingMatchClass NMSettingMatchClass;
+
+
+NM_AVAILABLE_IN_1_14
+GType nm_setting_match_get_type (void);
+NM_AVAILABLE_IN_1_14
+NMSetting *nm_setting_match_new (void);
+
+NM_AVAILABLE_IN_1_14
+guint nm_setting_match_get_num_interface_names (NMSettingMatch *setting);
+NM_AVAILABLE_IN_1_14
+const char *nm_setting_match_get_interface_name (NMSettingMatch *setting, int idx);
+NM_AVAILABLE_IN_1_14
+void nm_setting_match_remove_interface_name (NMSettingMatch *setting, int idx);
+NM_AVAILABLE_IN_1_14
+gboolean nm_setting_match_remove_interface_name_by_value (NMSettingMatch *setting,
+                                                          const char *interface_name);
+NM_AVAILABLE_IN_1_14
+void nm_setting_match_add_interface_name (NMSettingMatch *setting,
+                                          const char *interface_name);
+NM_AVAILABLE_IN_1_14
+void nm_setting_match_clear_interface_names (NMSettingMatch *setting);
+NM_AVAILABLE_IN_1_14
+const char *const *nm_setting_match_get_interface_names (NMSettingMatch *setting, guint *length);
+G_END_DECLS
+
+#endif /* NM_SETTING_MATCH_H */
diff --git a/libnm-core/nm-setting-olpc-mesh.c b/libnm-core/nm-setting-olpc-mesh.c
index 783d143f..7f4a1bbf 100644
--- a/libnm-core/nm-setting-olpc-mesh.c
+++ b/libnm-core/nm-setting-olpc-mesh.c
@@ -39,9 +39,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, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OLPC_MESH)
+G_DEFINE_TYPE (NMSettingOlpcMesh, nm_setting_olpc_mesh, NM_TYPE_SETTING)
 
 #define NM_SETTING_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshPrivate))
 
@@ -214,20 +212,20 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *setting_class)
+nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingOlpcMeshPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingOlpcMeshPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify = verify;
+
 	/**
 	 * NMSettingOlpcMesh:ssid:
 	 *
@@ -268,8 +266,14 @@ nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *setting_class)
 		                      NULL,
 		                      G_PARAM_READWRITE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS,
-	                                      G_VARIANT_TYPE_BYTESTRING,
-	                                      _nm_utils_hwaddr_to_dbus,
-	                                      _nm_utils_hwaddr_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS),
+	                                    G_VARIANT_TYPE_BYTESTRING,
+	                                    _nm_utils_hwaddr_to_dbus,
+	                                    _nm_utils_hwaddr_from_dbus);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_OLPC_MESH,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-ovs-bridge.c b/libnm-core/nm-setting-ovs-bridge.c
index 4993d13f..9ca5566f 100644
--- a/libnm-core/nm-setting-ovs-bridge.c
+++ b/libnm-core/nm-setting-ovs-bridge.c
@@ -61,9 +61,7 @@ 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)
+G_DEFINE_TYPE (NMSettingOvsBridge, nm_setting_ovs_bridge, NM_TYPE_SETTING)
 
 /*****************************************************************************/
 
@@ -264,15 +262,16 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *setting_class)
+nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	object_class->finalize = finalize;
-	parent_class->verify = verify;
+	object_class->finalize     = finalize;
+
+	setting_class->verify = verify;
 
 	/**
 	 * NMSettingOvsBridge:fail-mode:
@@ -334,4 +333,6 @@ nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *setting_class)
 	                               G_PARAM_READWRITE |
 	                               G_PARAM_CONSTRUCT |
 	                               G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_OVS_BRIDGE);
 }
diff --git a/libnm-core/nm-setting-ovs-interface.c b/libnm-core/nm-setting-ovs-interface.c
index ff9e4c1d..a177098d 100644
--- a/libnm-core/nm-setting-ovs-interface.c
+++ b/libnm-core/nm-setting-ovs-interface.c
@@ -54,9 +54,7 @@ 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)
+G_DEFINE_TYPE (NMSettingOvsInterface, nm_setting_ovs_interface, NM_TYPE_SETTING)
 
 /*****************************************************************************/
 
@@ -363,15 +361,16 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_ovs_interface_class_init (NMSettingOvsInterfaceClass *setting_class)
+nm_setting_ovs_interface_class_init (NMSettingOvsInterfaceClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	object_class->finalize = finalize;
-	parent_class->verify = verify;
+	object_class->finalize     = finalize;
+
+	setting_class->verify = verify;
 
 	/**
 	 * NMSettingOvsInterface:type:
@@ -388,4 +387,6 @@ nm_setting_ovs_interface_class_init (NMSettingOvsInterfaceClass *setting_class)
 	                              G_PARAM_CONSTRUCT |
 	                              NM_SETTING_PARAM_INFERRABLE |
 	                              G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_OVS_INTERFACE);
 }
diff --git a/libnm-core/nm-setting-ovs-patch.c b/libnm-core/nm-setting-ovs-patch.c
index 980015f2..15005eea 100644
--- a/libnm-core/nm-setting-ovs-patch.c
+++ b/libnm-core/nm-setting-ovs-patch.c
@@ -55,9 +55,7 @@ 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)
+G_DEFINE_TYPE (NMSettingOvsPatch, nm_setting_ovs_patch, NM_TYPE_SETTING)
 
 /*****************************************************************************/
 
@@ -182,15 +180,16 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_ovs_patch_class_init (NMSettingOvsPatchClass *setting_class)
+nm_setting_ovs_patch_class_init (NMSettingOvsPatchClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	object_class->finalize = finalize;
-	parent_class->verify = verify;
+	object_class->finalize     = finalize;
+
+	setting_class->verify = verify;
 
 	/**
 	 * NMSettingOvsPatch:peer:
@@ -208,4 +207,6 @@ nm_setting_ovs_patch_class_init (NMSettingOvsPatchClass *setting_class)
 	                              G_PARAM_CONSTRUCT |
 	                              NM_SETTING_PARAM_INFERRABLE |
 	                              G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_OVS_PATCH);
 }
diff --git a/libnm-core/nm-setting-ovs-port.c b/libnm-core/nm-setting-ovs-port.c
index b61df0ce..10e542e7 100644
--- a/libnm-core/nm-setting-ovs-port.c
+++ b/libnm-core/nm-setting-ovs-port.c
@@ -65,9 +65,7 @@ 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)
+G_DEFINE_TYPE (NMSettingOvsPort, nm_setting_ovs_port, NM_TYPE_SETTING)
 
 /*****************************************************************************/
 
@@ -361,15 +359,16 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_ovs_port_class_init (NMSettingOvsPortClass *setting_class)
+nm_setting_ovs_port_class_init (NMSettingOvsPortClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	object_class->finalize = finalize;
-	parent_class->verify = verify;
+	object_class->finalize     = finalize;
+
+	setting_class->verify = verify;
 
 	/**
 	 * NMSettingOvsPort:vlan-mode:
@@ -467,4 +466,6 @@ nm_setting_ovs_port_class_init (NMSettingOvsPortClass *setting_class)
 	                            G_PARAM_CONSTRUCT |
 	                            NM_SETTING_PARAM_INFERRABLE |
 	                            G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_OVS_PORT);
 }
diff --git a/libnm-core/nm-setting-ppp.c b/libnm-core/nm-setting-ppp.c
index 32900c84..7425da19 100644
--- a/libnm-core/nm-setting-ppp.c
+++ b/libnm-core/nm-setting-ppp.c
@@ -35,9 +35,7 @@
  * cable and DSL modems and some mobile broadband devices.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingPpp, nm_setting_ppp, NM_TYPE_SETTING,
-                         _nm_register_setting (PPP, NM_SETTING_PRIORITY_AUX))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_PPP)
+G_DEFINE_TYPE (NMSettingPpp, nm_setting_ppp, NM_TYPE_SETTING)
 
 #define NM_SETTING_PPP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPP, NMSettingPppPrivate))
 
@@ -525,19 +523,18 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_ppp_class_init (NMSettingPppClass *setting_class)
+nm_setting_ppp_class_init (NMSettingPppClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingPppPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingPppPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify = verify;
+
 	/**
 	 * NMSettingPpp:noauth:
 	 *
@@ -798,4 +795,6 @@ nm_setting_ppp_class_init (NMSettingPppClass *setting_class)
 		                    G_PARAM_CONSTRUCT |
 		                    NM_SETTING_PARAM_FUZZY_IGNORE |
 		                    G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_PPP);
 }
diff --git a/libnm-core/nm-setting-pppoe.c b/libnm-core/nm-setting-pppoe.c
index d7a1a0db..793d93ce 100644
--- a/libnm-core/nm-setting-pppoe.c
+++ b/libnm-core/nm-setting-pppoe.c
@@ -38,9 +38,7 @@
  * to provide IP transport, for example cable or DSL modems.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingPppoe, nm_setting_pppoe, NM_TYPE_SETTING,
-                         _nm_register_setting (PPPOE, NM_SETTING_PRIORITY_AUX))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_PPPOE)
+G_DEFINE_TYPE (NMSettingPppoe, nm_setting_pppoe, NM_TYPE_SETTING)
 
 #define NM_SETTING_PPPOE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPPOE, NMSettingPppoePrivate))
 
@@ -288,21 +286,20 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_pppoe_class_init (NMSettingPppoeClass *setting_class)
+nm_setting_pppoe_class_init (NMSettingPppoeClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingPppoePrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingPppoePrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
-	parent_class->need_secrets = need_secrets;
 
-	/* Properties */
+	setting_class->verify       = verify;
+	setting_class->need_secrets = need_secrets;
+
 	/**
 	 * NMSettingPppoe:parent:
 	 *
@@ -374,4 +371,6 @@ nm_setting_pppoe_class_init (NMSettingPppoeClass *setting_class)
 		                     NM_SETTING_SECRET_FLAG_NONE,
 		                     G_PARAM_READWRITE |
 		                     G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_PPPOE);
 }
diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h
index 863ea9f3..1e25226e 100644
--- a/libnm-core/nm-setting-private.h
+++ b/libnm-core/nm-setting-private.h
@@ -31,18 +31,13 @@
 
 #include "nm-core-internal.h"
 
-void _nm_register_setting_impl (const char *name,
-                                GType type,
-                                NMSettingPriority priority);
-
-#define _nm_register_setting(name, priority) \
-	G_STMT_START { \
-		_nm_register_setting_impl ("" NM_SETTING_ ## name ## _SETTING_NAME "", g_define_type_id, priority); \
-	} G_STMT_END
+/*****************************************************************************/
 
 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);
+int _nm_setting_compare_priority (gconstpointer a, gconstpointer b);
+
+/*****************************************************************************/
 
 typedef enum NMSettingUpdateSecretResult {
 	NM_SETTING_UPDATE_SECRET_ERROR              = FALSE,
@@ -85,10 +80,7 @@ gboolean _nm_setting_clear_secrets_with_flags (NMSetting *setting,
  */
 #define NM_SETTING_PARAM_REAPPLY_IMMEDIATELY (1 << (6 + G_PARAM_USER_SHIFT))
 
-/* Ensure the setting's GType is registered at library load time */
-#define NM_SETTING_REGISTER_TYPE(x) \
-static void __attribute__((constructor)) register_setting (void) \
-{ g_type_ensure (x); }
+#define NM_SETTING_PARAM_GENDATA_BACKED (1 << (7 + G_PARAM_USER_SHIFT))
 
 GVariant *_nm_setting_get_deprecated_virtual_interface_name (NMSetting *setting,
                                                              NMConnection *connection,
@@ -115,44 +107,78 @@ NMSetting  *_nm_setting_new_from_dbus (GType setting_type,
                                        NMSettingParseFlags parse_flags,
                                        GError **error);
 
-typedef GVariant * (*NMSettingPropertyGetFunc)    (NMSetting     *setting,
-                                                   const char    *property);
-typedef GVariant * (*NMSettingPropertySynthFunc)  (NMSetting     *setting,
-                                                   NMConnection  *connection,
-                                                   const char    *property);
-typedef gboolean   (*NMSettingPropertySetFunc)    (NMSetting     *setting,
-                                                   GVariant      *connection_dict,
-                                                   const char    *property,
-                                                   GVariant      *value,
-                                                   NMSettingParseFlags parse_flags,
-                                                   GError       **error);
-typedef gboolean   (*NMSettingPropertyNotSetFunc) (NMSetting     *setting,
-                                                   GVariant      *connection_dict,
-                                                   const char    *property,
-                                                   NMSettingParseFlags parse_flags,
-                                                   GError       **error);
-
-void _nm_setting_class_add_dbus_only_property (NMSettingClass *setting_class,
-                                               const char *property_name,
-                                               const GVariantType *dbus_type,
-                                               NMSettingPropertySynthFunc synth_func,
-                                               NMSettingPropertySetFunc set_func);
-
-void _nm_setting_class_override_property (NMSettingClass *setting_class,
-                                          const char *property_name,
-                                          const GVariantType *dbus_type,
-                                          NMSettingPropertyGetFunc get_func,
-                                          NMSettingPropertySetFunc set_func,
-                                          NMSettingPropertyNotSetFunc not_set_func);
-
-typedef GVariant * (*NMSettingPropertyTransformToFunc) (const GValue *from);
-typedef void (*NMSettingPropertyTransformFromFunc) (GVariant *from, GValue *to);
-
-void _nm_setting_class_transform_property (NMSettingClass *setting_class,
-                                           const char *property_name,
-                                           const GVariantType *dbus_type,
-                                           NMSettingPropertyTransformToFunc to_dbus,
-                                           NMSettingPropertyTransformFromFunc from_dbus);
+/*****************************************************************************/
+
+static inline GArray *
+_nm_sett_info_property_override_create_array (void)
+{
+	return g_array_new (FALSE, FALSE, sizeof (NMSettInfoProperty));
+}
+
+GArray *_nm_sett_info_property_override_create_array_ip_config (void);
+
+void _nm_setting_class_commit_full (NMSettingClass *setting_class,
+                                    NMMetaSettingType meta_type,
+                                    const NMSettInfoSettDetail *detail,
+                                    GArray *properties_override);
+
+static inline void
+_nm_setting_class_commit (NMSettingClass *setting_class,
+                          NMMetaSettingType meta_type)
+{
+	_nm_setting_class_commit_full (setting_class, meta_type, NULL, NULL);
+}
+
+#define NM_SETT_INFO_SETT_GENDATA(...) \
+	({ \
+		static const NMSettInfoSettGendata _g = { \
+			__VA_ARGS__ \
+		}; \
+		\
+		&_g; \
+	})
+
+#define NM_SETT_INFO_SETT_DETAIL(...) \
+	(&((const NMSettInfoSettDetail) { \
+		__VA_ARGS__ \
+	}))
+
+#define NM_SETT_INFO_PROPERTY(...) \
+	(&((const NMSettInfoProperty) { \
+		__VA_ARGS__ \
+	}))
+
+void _properties_override_add_struct (GArray *properties_override,
+                                      const NMSettInfoProperty *prop_info);
+
+void _properties_override_add__helper (GArray *properties_override,
+                                       NMSettInfoProperty *prop_info);
+
+#define _properties_override_add(properties_override, \
+                                 ...) \
+	(_properties_override_add_struct (properties_override, \
+	                                  NM_SETT_INFO_PROPERTY (__VA_ARGS__)))
+
+void _properties_override_add_dbus_only (GArray *properties_override,
+                                         const char *property_name,
+                                         const GVariantType *dbus_type,
+                                         NMSettingPropertySynthFunc synth_func,
+                                         NMSettingPropertySetFunc set_func);
+
+void _properties_override_add_override (GArray *properties_override,
+                                        GParamSpec *param_spec,
+                                        const GVariantType *dbus_type,
+                                        NMSettingPropertyGetFunc get_func,
+                                        NMSettingPropertySetFunc set_func,
+                                        NMSettingPropertyNotSetFunc not_set_func);
+
+void _properties_override_add_transform (GArray *properties_override,
+                                         GParamSpec *param_spec,
+                                         const GVariantType *dbus_type,
+                                         NMSettingPropertyTransformToFunc to_dbus,
+                                         NMSettingPropertyTransformFromFunc from_dbus);
+
+/*****************************************************************************/
 
 gboolean _nm_setting_use_legacy_property (NMSetting *setting,
                                           GVariant *connection_dict,
@@ -161,4 +187,6 @@ gboolean _nm_setting_use_legacy_property (NMSetting *setting,
 
 GPtrArray  *_nm_setting_need_secrets (NMSetting *setting);
 
+/*****************************************************************************/
+
 #endif  /* NM_SETTING_PRIVATE_H */
diff --git a/libnm-core/nm-setting-proxy.c b/libnm-core/nm-setting-proxy.c
index 58a22080..d544c7fa 100644
--- a/libnm-core/nm-setting-proxy.c
+++ b/libnm-core/nm-setting-proxy.c
@@ -39,9 +39,7 @@
  * to fulfill client queries.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingProxy, nm_setting_proxy, NM_TYPE_SETTING,
-                         _nm_register_setting (PROXY, NM_SETTING_PRIORITY_IP))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_PROXY)
+G_DEFINE_TYPE (NMSettingProxy, nm_setting_proxy, NM_TYPE_SETTING)
 
 #define NM_SETTING_PROXY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PROXY, NMSettingProxyPrivate))
 
@@ -285,18 +283,18 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_proxy_class_init (NMSettingProxyClass *setting_class)
+nm_setting_proxy_class_init (NMSettingProxyClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingProxyPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingProxyPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	object_class->finalize = finalize;
-	parent_class->verify = verify;
+	object_class->finalize     = finalize;
+
+	setting_class->verify = verify;
 
 	/**
 	 * NMSettingProxy:method:
@@ -384,4 +382,6 @@ nm_setting_proxy_class_init (NMSettingProxyClass *setting_class)
 	                          NULL,
 	                          G_PARAM_READWRITE |
 	                          G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_PROXY);
 }
diff --git a/libnm-core/nm-setting-serial.c b/libnm-core/nm-setting-serial.c
index e86988b5..b173197d 100644
--- a/libnm-core/nm-setting-serial.c
+++ b/libnm-core/nm-setting-serial.c
@@ -16,7 +16,7 @@
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301 USA.
  *
- * Copyright 2007 - 2011 Red Hat, Inc.
+ * Copyright 2007 - 2018 Red Hat, Inc.
  * Copyright 2007 - 2008 Novell, Inc.
  */
 
@@ -37,9 +37,7 @@
  * such as mobile broadband or analog telephone connections.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingSerial, nm_setting_serial, NM_TYPE_SETTING,
-                         _nm_register_setting (SERIAL, NM_SETTING_PRIORITY_HW_AUX))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_SERIAL)
+G_DEFINE_TYPE (NMSettingSerial, nm_setting_serial, NM_TYPE_SETTING)
 
 #define NM_SETTING_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_SERIAL, NMSettingSerialPrivate))
 
@@ -145,12 +143,6 @@ nm_setting_serial_get_send_delay (NMSettingSerial *setting)
 	return NM_SETTING_SERIAL_GET_PRIVATE (setting)->send_delay;
 }
 
-static gboolean
-verify (NMSetting *setting, NMConnection *connection, GError **error)
-{
-	return TRUE;
-}
-
 static void
 nm_setting_serial_init (NMSettingSerial *setting)
 {
@@ -244,19 +236,16 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_serial_class_init (NMSettingSerialClass *setting_class)
+nm_setting_serial_class_init (NMSettingSerialClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingSerialPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingSerialPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	parent_class->verify       = verify;
-
-	/* Properties */
 
 	/**
 	 * NMSettingSerial:baud:
@@ -314,11 +303,13 @@ nm_setting_serial_class_init (NMSettingSerialClass *setting_class)
 		                    G_PARAM_READWRITE |
 		                    G_PARAM_CONSTRUCT |
 		                    G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class,
-	                                      NM_SETTING_SERIAL_PARITY,
-	                                      G_VARIANT_TYPE_BYTE,
-	                                      parity_to_dbus,
-	                                      parity_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_SERIAL_PARITY),
+	                                    G_VARIANT_TYPE_BYTE,
+	                                    parity_to_dbus,
+	                                    parity_from_dbus);
 
 	/**
 	 * NMSettingSerial:stopbits:
@@ -346,4 +337,7 @@ nm_setting_serial_class_init (NMSettingSerialClass *setting_class)
 		                      G_PARAM_READWRITE |
 		                      G_PARAM_CONSTRUCT |
 		                      G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_SERIAL,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-sriov.c b/libnm-core/nm-setting-sriov.c
new file mode 100644
index 00000000..45b3a1d2
--- /dev/null
+++ b/libnm-core/nm-setting-sriov.c
@@ -0,0 +1,1357 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-sriov.h"
+#include "nm-setting-private.h"
+#include "nm-utils-private.h"
+
+/**
+ * SECTION:nm-setting-sriov
+ * @short_description: Describes SR-IOV connection properties
+ * @include: nm-setting-sriov.h
+ **/
+
+/**
+ * NMSettingSriov:
+ *
+ * SR-IOV settings.
+ *
+ * Since: 1.14
+ */
+struct _NMSettingSriov {
+	NMSetting parent;
+	GPtrArray *vfs;
+	guint total_vfs;
+	NMTernary autoprobe_drivers;
+};
+
+struct _NMSettingSriovClass {
+	NMSettingClass parent;
+};
+
+G_DEFINE_TYPE (NMSettingSriov, nm_setting_sriov, NM_TYPE_SETTING)
+
+enum {
+	PROP_0,
+	PROP_TOTAL_VFS,
+	PROP_VFS,
+	PROP_AUTOPROBE_DRIVERS,
+
+	LAST_PROP
+};
+
+/*****************************************************************************/
+
+G_DEFINE_BOXED_TYPE (NMSriovVF, nm_sriov_vf, nm_sriov_vf_dup, nm_sriov_vf_unref)
+
+struct _NMSriovVF {
+	guint refcount;
+	guint index;
+	GHashTable *attributes;
+	GHashTable *vlans;
+	guint *vlan_ids;
+};
+
+typedef struct {
+	guint id;
+	guint qos;
+	NMSriovVFVlanProtocol protocol;
+} VFVlan;
+
+static guint
+_vf_vlan_hash (gconstpointer ptr)
+{
+	return nm_hash_val (1348254767u, *((guint *) ptr));
+}
+
+static gboolean
+_vf_vlan_equal (gconstpointer a, gconstpointer b)
+{
+	return *((guint *) a) == *((guint *) b);
+}
+
+static GHashTable *
+_vf_vlan_create_hash (void)
+{
+	G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (VFVlan, id) == 0);
+	return g_hash_table_new_full (_vf_vlan_hash,
+	                              _vf_vlan_equal,
+	                              NULL,
+	                              nm_g_slice_free_fcn (VFVlan));
+}
+
+/**
+ * nm_srio_vf_new:
+ * @index: the VF index
+ *
+ * Creates a new #NMSriovVF object.
+ *
+ * Returns: (transfer full): the new #NMSriovVF object.
+ *
+ * Since: 1.14
+ **/
+NMSriovVF *
+nm_sriov_vf_new (guint index)
+{
+	NMSriovVF *vf;
+
+	vf = g_slice_new0 (NMSriovVF);
+	vf->refcount = 1;
+	vf->index = index;
+	vf->attributes = g_hash_table_new_full (nm_str_hash,
+	                                        g_str_equal,
+	                                        g_free,
+	                                        (GDestroyNotify) g_variant_unref);
+	return vf;
+}
+
+/**
+ * nm_sriov_vf_ref:
+ * @vf: the #NMSriovVF
+ *
+ * Increases the reference count of the object.
+ *
+ * Since: 1.14
+ **/
+void
+nm_sriov_vf_ref (NMSriovVF *vf)
+{
+	g_return_if_fail (vf);
+	g_return_if_fail (vf->refcount > 0);
+
+	vf->refcount++;
+}
+
+/**
+ * nm_sriov_vf_unref:
+ * @vf: the #NMSriovVF
+ *
+ * Decreases the reference count of the object.  If the reference count
+ * reaches zero, the object will be destroyed.
+ *
+ * Since: 1.14
+ **/
+void
+nm_sriov_vf_unref (NMSriovVF *vf)
+{
+	g_return_if_fail (vf);
+	g_return_if_fail (vf->refcount > 0);
+
+	vf->refcount--;
+	if (vf->refcount == 0) {
+		g_hash_table_unref (vf->attributes);
+		if (vf->vlans)
+			g_hash_table_unref (vf->vlans);
+		g_free (vf->vlan_ids);
+		g_slice_free (NMSriovVF, vf);
+	}
+}
+
+/**
+ * nm_sriov_vf_equal:
+ * @vf: the #NMSriovVF
+ * @other: the #NMSriovVF to compare @vf to.
+ *
+ * Determines if two #NMSriovVF objects have the same index,
+ * attributes and VLANs.
+ *
+ * Returns: %TRUE if the objects contain the same values, %FALSE
+ *    if they do not.
+ *
+ * Since: 1.14
+ **/
+gboolean
+nm_sriov_vf_equal (const NMSriovVF *vf, const NMSriovVF *other)
+{
+	GHashTableIter iter;
+	const char *key;
+	GVariant *value, *value2;
+	VFVlan *vlan, *vlan2;
+	guint n_vlans;
+
+	g_return_val_if_fail (vf, FALSE);
+	g_return_val_if_fail (vf->refcount > 0, FALSE);
+	g_return_val_if_fail (other, FALSE);
+	g_return_val_if_fail (other->refcount > 0, FALSE);
+
+	if (vf == other)
+		return TRUE;
+
+	if (vf->index != other->index)
+		return FALSE;
+
+	if (g_hash_table_size (vf->attributes) != g_hash_table_size (other->attributes))
+		return FALSE;
+	g_hash_table_iter_init (&iter, vf->attributes);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) {
+		value2 = g_hash_table_lookup (other->attributes, key);
+		if (!value2)
+			return FALSE;
+		if (!g_variant_equal (value, value2))
+			return FALSE;
+	}
+
+	n_vlans = vf->vlans ? g_hash_table_size (vf->vlans) : 0u;
+	if (n_vlans != (other->vlans ? g_hash_table_size (other->vlans) : 0u))
+		return FALSE;
+	if (n_vlans > 0) {
+		g_hash_table_iter_init (&iter, vf->vlans);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &vlan, NULL)) {
+			vlan2 = g_hash_table_lookup (other->vlans, vlan);
+			if (!vlan2)
+				return FALSE;
+			if (   vlan->qos != vlan2->qos
+			    || vlan->protocol != vlan2->protocol)
+				return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static void
+vf_add_vlan (NMSriovVF *vf,
+             guint vlan_id,
+             guint qos,
+             NMSriovVFVlanProtocol protocol)
+{
+	VFVlan *vlan;
+
+	vlan = g_slice_new0 (VFVlan);
+	vlan->id = vlan_id;
+	vlan->qos = qos;
+	vlan->protocol = protocol;
+
+	if (!vf->vlans)
+		vf->vlans = _vf_vlan_create_hash ();
+
+	g_hash_table_add (vf->vlans, vlan);
+	g_clear_pointer (&vf->vlan_ids, g_free);
+}
+
+/**
+ * nm_sriov_vf_dup:
+ * @vf: the #NMSriovVF
+ *
+ * Creates a copy of @vf.
+ *
+ * Returns: (transfer full): a copy of @vf
+ *
+ * Since: 1.14
+ **/
+NMSriovVF *
+nm_sriov_vf_dup (const NMSriovVF *vf)
+{
+	NMSriovVF *copy;
+	GHashTableIter iter;
+	const char *name;
+	GVariant *variant;
+	VFVlan *vlan;
+
+	g_return_val_if_fail (vf, NULL);
+	g_return_val_if_fail (vf->refcount > 0, NULL);
+
+	copy = nm_sriov_vf_new (vf->index);
+
+	g_hash_table_iter_init (&iter, vf->attributes);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant))
+		nm_sriov_vf_set_attribute (copy, name, variant);
+
+	if (vf->vlans) {
+		g_hash_table_iter_init (&iter, vf->vlans);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &vlan, NULL))
+			vf_add_vlan (copy, vlan->id, vlan->qos, vlan->protocol);
+	}
+
+	return copy;
+}
+
+/**
+ * nm_sriov_vf_get_index:
+ * @vf: the #NMSriovVF
+ *
+ * Gets the index property of this VF object.
+ *
+ * Returns: the VF index
+ *
+ * Since: 1.14
+ **/
+guint
+nm_sriov_vf_get_index (const NMSriovVF *vf)
+{
+	g_return_val_if_fail (vf, 0);
+	g_return_val_if_fail (vf->refcount > 0, 0);
+
+	return vf->index;
+}
+
+/**
+ * nm_sriov_vf_set_attribute:
+ * @vf: the #NMSriovVF
+ * @name: the name of a route attribute
+ * @value: (transfer none) (allow-none): the value
+ *
+ * Sets the named attribute on @vf to the given value.
+ *
+ * Since: 1.14
+ **/
+void
+nm_sriov_vf_set_attribute (NMSriovVF *vf, const char *name, GVariant *value)
+{
+	g_return_if_fail (vf);
+	g_return_if_fail (vf->refcount > 0);
+	g_return_if_fail (name && *name != '\0');
+	g_return_if_fail (!nm_streq (name, "index"));
+
+	if (value) {
+		g_hash_table_insert (vf->attributes,
+		                     g_strdup (name),
+		                     g_variant_ref_sink (value));
+	} else
+		g_hash_table_remove (vf->attributes, name);
+}
+
+/**
+ * nm_sriov_vf_get_attribute_names:
+ * @vf: the #NMSriovVF
+ *
+ * Gets an array of attribute names defined on @vf.
+ *
+ * Returns: (transfer container): a %NULL-terminated array of attribute names
+ *
+ * Since: 1.14
+ **/
+const char **
+nm_sriov_vf_get_attribute_names (const NMSriovVF *vf)
+{
+	g_return_val_if_fail (vf, NULL);
+	g_return_val_if_fail (vf->refcount > 0, NULL);
+
+	return nm_utils_strdict_get_keys (vf->attributes, TRUE, NULL);
+}
+
+/**
+ * nm_sriov_vf_get_attribute:
+ * @vf: the #NMSriovVF
+ * @name: the name of a VF attribute
+ *
+ * Gets the value of the attribute with name @name on @vf
+ *
+ * Returns: (transfer none): the value of the attribute with name @name on
+ *   @vf, or %NULL if @vf has no such attribute.
+ *
+ * Since: 1.14
+ **/
+GVariant *
+nm_sriov_vf_get_attribute (const NMSriovVF *vf, const char *name)
+{
+	g_return_val_if_fail (vf, NULL);
+	g_return_val_if_fail (vf->refcount > 0, NULL);
+	g_return_val_if_fail (name && *name != '\0', NULL);
+
+	return g_hash_table_lookup (vf->attributes, name);
+}
+
+#define SRIOV_ATTR_SPEC_PTR(name, type, str_type) \
+	&(NMVariantAttributeSpec) { name, type, FALSE, FALSE, FALSE, FALSE, str_type }
+
+const NMVariantAttributeSpec * const _nm_sriov_vf_attribute_spec[] = {
+	SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_MAC,          G_VARIANT_TYPE_STRING,  'm'),
+	SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK,  G_VARIANT_TYPE_BOOLEAN,  0),
+	SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_TRUST,        G_VARIANT_TYPE_BOOLEAN,  0),
+	SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE,  G_VARIANT_TYPE_UINT32,   0),
+	SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE,  G_VARIANT_TYPE_UINT32,   0),
+	/* D-Bus only, synthetic attributes */
+	SRIOV_ATTR_SPEC_PTR ("vlans",                            G_VARIANT_TYPE_STRING,  'd'),
+	NULL,
+};
+
+/**
+ * nm_sriov_vf_attribute_validate:
+ * @name: the attribute name
+ * @value: the attribute value
+ * @known: (out): on return, whether the attribute name is a known one
+ * @error: (allow-none): return location for a #GError, or %NULL
+ *
+ * Validates a VF attribute, i.e. checks that the attribute is a known one,
+ * the value is of the correct type and well-formed.
+ *
+ * Returns: %TRUE if the attribute is valid, %FALSE otherwise
+ *
+ * Since: 1.14
+ */
+gboolean
+nm_sriov_vf_attribute_validate  (const char *name,
+                                 GVariant *value,
+                                 gboolean *known,
+                                 GError **error)
+{
+	const NMVariantAttributeSpec *const *iter;
+	const NMVariantAttributeSpec *spec = NULL;
+
+	g_return_val_if_fail (name, FALSE);
+	g_return_val_if_fail (value, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+
+	for (iter = _nm_sriov_vf_attribute_spec; *iter; iter++) {
+		if (nm_streq (name, (*iter)->name)) {
+			spec = *iter;
+			break;
+		}
+	}
+
+	if (!spec || spec->str_type == 'd') {
+		NM_SET_OUT (known, FALSE);
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_FAILED,
+		                     _("unknown attribute"));
+		return FALSE;
+	}
+
+	NM_SET_OUT (known, TRUE);
+
+	if (!g_variant_is_of_type (value, spec->type)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_FAILED,
+		             _("invalid attribute type '%s'"),
+		             g_variant_get_type_string (value));
+		return FALSE;
+	}
+
+	if (spec->type == G_VARIANT_TYPE_STRING) {
+		const char *string;
+
+		switch (spec->str_type) {
+		case 'm': /* MAC address */
+			string = g_variant_get_string (value, NULL);
+			if (!nm_utils_hwaddr_valid (string, -1)) {
+				g_set_error (error,
+				             NM_CONNECTION_ERROR,
+				             NM_CONNECTION_ERROR_FAILED,
+				             _("'%s' is not a valid MAC address"),
+				             string);
+				return FALSE;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	return TRUE;
+}
+
+gboolean
+_nm_sriov_vf_attribute_validate_all (const NMSriovVF *vf, GError **error)
+{
+	GHashTableIter iter;
+	const char *name;
+	GVariant *variant;
+	GVariant *min, *max;
+
+	g_return_val_if_fail (vf, FALSE);
+	g_return_val_if_fail (vf->refcount > 0, FALSE);
+
+	g_hash_table_iter_init (&iter, vf->attributes);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) {
+		if (!nm_sriov_vf_attribute_validate (name, variant, NULL, error)) {
+			g_prefix_error (error, "attribute '%s':", name);
+			return FALSE;
+		}
+	}
+
+	min = g_hash_table_lookup (vf->attributes, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE);
+	max = g_hash_table_lookup (vf->attributes, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE);
+	if (   min
+	    && max
+	    && g_variant_get_uint32 (min) > g_variant_get_uint32 (max)) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_FAILED,
+		             "min_tx_rate is greater than max_tx_rate");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/**
+ * nm_sriov_vf_add_vlan:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ *
+ * Adds a VLAN to the VF.
+ *
+ * Returns: %TRUE if the VLAN was added; %FALSE if it already existed
+ *
+ * Since: 1.14
+ **/
+gboolean
+nm_sriov_vf_add_vlan (NMSriovVF *vf, guint vlan_id)
+{
+	g_return_val_if_fail (vf, FALSE);
+	g_return_val_if_fail (vf->refcount > 0, FALSE);
+
+	if (   vf->vlans
+	    && g_hash_table_contains (vf->vlans, &vlan_id))
+		return FALSE;
+
+	vf_add_vlan (vf, vlan_id, 0, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+
+	return TRUE;
+}
+
+/**
+ * nm_sriov_vf_remove_vlan:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ *
+ * Removes a VLAN from a VF.
+ *
+ * Returns: %TRUE if the VLAN was removed, %FALSE if the VLAN @vlan_id
+ *     did not belong to the VF.
+ *
+ * Since: 1.14
+ */
+gboolean
+nm_sriov_vf_remove_vlan (NMSriovVF *vf, guint vlan_id)
+{
+	g_return_val_if_fail (vf, FALSE);
+	g_return_val_if_fail (vf->refcount > 0, FALSE);
+
+	if (   !vf->vlans
+	    || !g_hash_table_remove (vf->vlans, &vlan_id))
+		return FALSE;
+
+	g_clear_pointer (&vf->vlan_ids, g_free);
+	return TRUE;
+}
+
+static int
+vlan_id_compare (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+	guint id_a  = *(guint *) a;
+	guint id_b  = *(guint *) b;
+
+	if (id_a < id_b)
+		return -1;
+	else if (id_a > id_b)
+		return 1;
+	else return 0;
+}
+
+/**
+ * nm_sriov_vf_get_vlan_ids:
+ * @vf: the #NMSriovVF
+ * @length: (out) (allow-none): on return, the number of VLANs configured
+ *
+ * Returns the VLANs currently configured on the VF.
+ *
+ * Returns: (transfer none): a list of VLAN ids configured on the VF.
+ *
+ * Since: 1.14
+ */
+const guint *
+nm_sriov_vf_get_vlan_ids (const NMSriovVF *vf, guint *length)
+{
+	GHashTableIter iter;
+	VFVlan *vlan;
+	guint num, i;
+
+	g_return_val_if_fail (vf, NULL);
+	g_return_val_if_fail (vf->refcount > 0, NULL);
+
+	num = vf->vlans ? g_hash_table_size (vf->vlans) : 0u;
+	NM_SET_OUT (length, num);
+
+	if (vf->vlan_ids)
+		return vf->vlan_ids;
+	if (num == 0)
+		return NULL;
+
+	/* vf is const, however, vlan_ids is a mutable field caching the
+	 * result ("mutable" in C++ terminology) */
+	((NMSriovVF *) vf)->vlan_ids = g_new0 (guint, num);
+
+	i = 0;
+	g_hash_table_iter_init (&iter, vf->vlans);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &vlan, NULL))
+		vf->vlan_ids[i++] = vlan->id;
+
+	nm_assert (num == i);
+
+	g_qsort_with_data (vf->vlan_ids, num, sizeof (guint), vlan_id_compare, NULL);
+
+	return vf->vlan_ids;
+}
+
+/**
+ * nm_sriov_vf_set_vlan_qos:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ * @qos: a QoS (priority) value
+ *
+ * Sets a QoS value for the given VLAN.
+ *
+ * Since: 1.14
+ */
+void
+nm_sriov_vf_set_vlan_qos (NMSriovVF *vf, guint vlan_id, guint32 qos)
+{
+	VFVlan *vlan;
+
+	g_return_if_fail (vf);
+	g_return_if_fail (vf->refcount > 0);
+
+	if (   !vf->vlans
+	    || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id)))
+		g_return_if_reached ();
+
+	vlan->qos = qos;
+}
+
+/**
+ * nm_sriov_vf_set_vlan_protocol:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ * @protocol: the VLAN protocol
+ *
+ * Sets the protocol for the given VLAN.
+ *
+ * Since: 1.14
+ */
+void
+nm_sriov_vf_set_vlan_protocol (NMSriovVF *vf, guint vlan_id, NMSriovVFVlanProtocol protocol)
+{
+	VFVlan *vlan;
+
+	g_return_if_fail (vf);
+	g_return_if_fail (vf->refcount > 0);
+
+	if (   !vf->vlans
+	    || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id)))
+		g_return_if_reached ();
+
+	vlan->protocol = protocol;
+}
+
+/**
+ * nm_sriov_vf_get_vlan_qos:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ *
+ * Returns the QoS value for the given VLAN.
+ *
+ * Returns: the QoS value
+ *
+ * Since: 1.14
+ */
+guint32
+nm_sriov_vf_get_vlan_qos (const NMSriovVF *vf, guint vlan_id)
+{
+	VFVlan *vlan;
+
+	g_return_val_if_fail (vf, 0);
+	g_return_val_if_fail (vf->refcount > 0, 0);
+
+	if (   !vf->vlans
+	    || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id)))
+		g_return_val_if_reached (0);
+
+	return vlan->qos;
+}
+
+/*
+ * nm_sriov_vf_get_vlan_protocol:
+ * @vf: the #NMSriovVF
+ * @vlan_id: the VLAN id
+ *
+ * Returns the configured protocol for the given VLAN.
+ *
+ * Returns: the configured protocol
+ *
+ * Since: 1.14
+ */
+NMSriovVFVlanProtocol
+nm_sriov_vf_get_vlan_protocol (const NMSriovVF *vf, guint vlan_id)
+{
+	VFVlan *vlan;
+
+	g_return_val_if_fail (vf, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+	g_return_val_if_fail (vf->refcount > 0, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+
+	if (   !vf->vlans
+	    || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id)))
+		g_return_val_if_reached (NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+
+	return vlan->protocol;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_sriov_new:
+ *
+ * Creates a new #NMSettingSriov object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingSriov object
+ *
+ * Since: 1.14
+ **/
+NMSetting *
+nm_setting_sriov_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_SRIOV, NULL);
+}
+
+/**
+ * nm_setting_sriov_get_total_vfs:
+ * @setting: the #NMSettingSriov
+ *
+ * Returns the value contained in the #NMSettingSriov:total-vfs
+ * property.
+ *
+ * Returns: the total number of SR-IOV virtual functions to create
+ *
+ * Since: 1.14
+ **/
+guint
+nm_setting_sriov_get_total_vfs (NMSettingSriov *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), 0);
+
+	return setting->total_vfs;
+}
+
+/**
+ * nm_setting_sriov_get_num_vfs:
+ * @setting: the #NMSettingSriov
+ *
+ * Returns: the number of configured VFs
+ *
+ * Since: 1.14
+ **/
+guint
+nm_setting_sriov_get_num_vfs (NMSettingSriov *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), 0);
+
+	return setting->vfs->len;
+}
+
+/**
+ * nm_setting_sriov_get_vf:
+ * @setting: the #NMSettingSriov
+ * @idx: index number of the VF to return
+ *
+ * Returns: (transfer none): the VF at index @idx
+ *
+ * Since: 1.14
+ **/
+NMSriovVF *
+nm_setting_sriov_get_vf (NMSettingSriov *setting, guint idx)
+{
+	g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), NULL);
+	g_return_val_if_fail (idx < setting->vfs->len, NULL);
+
+	return setting->vfs->pdata[idx];
+}
+
+/**
+ * nm_setting_sriov_add_vf:
+ * @setting: the #NMSettingSriov
+ * @vf: the VF to add
+ *
+ * Appends a new VF and associated information to the setting.  The
+ * given VF is duplicated internally and is not changed by this function.
+ *
+ * Since: 1.14
+ **/
+void
+nm_setting_sriov_add_vf (NMSettingSriov *setting, NMSriovVF *vf)
+{
+	g_return_if_fail (NM_IS_SETTING_SRIOV (setting));
+	g_return_if_fail (vf);
+	g_return_if_fail (vf->refcount > 0);
+
+	g_ptr_array_add (setting->vfs, nm_sriov_vf_dup (vf));
+	g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+}
+
+/**
+ * nm_setting_sriov_remove_vf:
+ * @setting: the #NMSettingSriov
+ * @idx: index number of the VF
+ *
+ * Removes the VF at index @idx.
+ *
+ * Since: 1.14
+ **/
+void
+nm_setting_sriov_remove_vf (NMSettingSriov *setting, guint idx)
+{
+	g_return_if_fail (NM_IS_SETTING_SRIOV (setting));
+	g_return_if_fail (idx < setting->vfs->len);
+
+	g_ptr_array_remove_index (setting->vfs, idx);
+	g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+}
+
+/**
+ * nm_setting_sriov_remove_vf_by_index:
+ * @setting: the #NMSettingSriov
+ * @index: the VF index of the VF to remove
+ *
+ * Removes the VF with VF index @index.
+ *
+ * Returns: %TRUE if the VF was found and removed; %FALSE if it was not
+ *
+ * Since: 1.14
+ **/
+gboolean
+nm_setting_sriov_remove_vf_by_index (NMSettingSriov *setting,
+                                     guint index)
+{
+	guint i;
+
+	g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), FALSE);
+
+	for (i = 0; i < setting->vfs->len; i++) {
+		if (nm_sriov_vf_get_index  (setting->vfs->pdata[i]) == index) {
+			g_ptr_array_remove_index (setting->vfs, i);
+			g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+/**
+ * nm_setting_sriov_clear_vfs:
+ * @setting: the #NMSettingSriov
+ *
+ * Removes all configured VFs.
+ *
+ * Since: 1.14
+ **/
+void
+nm_setting_sriov_clear_vfs (NMSettingSriov *setting)
+{
+	g_return_if_fail (NM_IS_SETTING_SRIOV (setting));
+
+	if (setting->vfs->len != 0) {
+		g_ptr_array_set_size (setting->vfs, 0);
+		g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+	}
+}
+
+/**
+ * nm_setting_sriov_get_autoprobe_drivers:
+ * @setting: the #NMSettingSriov
+ *
+ * Returns the value contained in the #NMSettingSriov:autoprobe-drivers
+ * property.
+ *
+ * Returns: the autoprobe-drivers property value
+ *
+ * Since: 1.14
+ **/
+NMTernary
+nm_setting_sriov_get_autoprobe_drivers (NMSettingSriov *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), NM_TERNARY_DEFAULT);
+
+	return setting->autoprobe_drivers;
+}
+
+static gint
+vf_index_compare (gconstpointer a, gconstpointer b)
+{
+	NMSriovVF *vf_a = *(NMSriovVF **) a;
+	NMSriovVF *vf_b = *(NMSriovVF **) b;
+
+	if (vf_a->index < vf_b->index)
+		return -1;
+	else if (vf_a->index > vf_b->index)
+		return 1;
+	else
+		return 0;
+}
+
+gboolean
+_nm_setting_sriov_sort_vfs (NMSettingSriov *setting)
+{
+	gboolean need_sort = FALSE;
+	guint i;
+
+	for (i = 1; i < setting->vfs->len; i++) {
+		NMSriovVF *vf_prev = setting->vfs->pdata[i - 1];
+		NMSriovVF *vf = setting->vfs->pdata[i];
+
+		if (vf->index <= vf_prev->index) {
+			need_sort = TRUE;
+			break;
+		}
+	}
+
+	if (need_sort)
+		g_ptr_array_sort (setting->vfs, vf_index_compare);
+
+	return need_sort;
+}
+
+/*****************************************************************************/
+
+static GVariant *
+vfs_to_dbus (NMSetting *setting, const char *property)
+{
+	gs_unref_ptrarray GPtrArray *vfs = NULL;
+	GVariantBuilder builder;
+	guint i;
+
+	g_object_get (setting, NM_SETTING_SRIOV_VFS, &vfs, NULL);
+	g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+
+	if (vfs) {
+		for (i = 0; i < vfs->len; i++) {
+			gs_free const char **attr_names = NULL;
+			NMSriovVF *vf = vfs->pdata[i];
+			GVariantBuilder vf_builder;
+			const guint *vlan_ids;
+			const char **name;
+			guint num_vlans;
+
+			g_variant_builder_init (&vf_builder, G_VARIANT_TYPE_VARDICT);
+			g_variant_builder_add (&vf_builder, "{sv}", "index",
+			                       g_variant_new_uint32 (nm_sriov_vf_get_index (vf)));
+
+			attr_names = nm_utils_strdict_get_keys (vf->attributes, TRUE, NULL);
+			if (attr_names) {
+				for (name = attr_names; *name; name++) {
+					g_variant_builder_add (&vf_builder,
+					                       "{sv}",
+					                       *name,
+					                       nm_sriov_vf_get_attribute (vf, *name));
+				}
+			}
+
+			/* VLANs are translated into an array of maps, where each map has
+			 * keys 'id', 'qos' and 'proto'. This guarantees enough flexibility
+			 * to accomodate any future new option. */
+			vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
+			if (num_vlans) {
+				GVariantBuilder vlans_builder;
+				guint j;
+
+				g_variant_builder_init (&vlans_builder, G_VARIANT_TYPE ("aa{sv}"));
+				for (j = 0; j < num_vlans; j++) {
+					GVariantBuilder vlan_builder;
+
+					g_variant_builder_init (&vlan_builder, G_VARIANT_TYPE ("a{sv}"));
+					g_variant_builder_add (&vlan_builder,
+					                       "{sv}", "id",
+					                       g_variant_new_uint32 (vlan_ids[j]));
+					g_variant_builder_add (&vlan_builder,
+					                       "{sv}", "qos",
+					                       g_variant_new_uint32 (nm_sriov_vf_get_vlan_qos (vf,
+					                                                                       vlan_ids[j])));
+					g_variant_builder_add (&vlan_builder,
+					                       "{sv}", "protocol",
+					                       g_variant_new_uint32 (nm_sriov_vf_get_vlan_protocol (vf,
+					                                                                            vlan_ids[j])));
+					g_variant_builder_add (&vlans_builder,
+					                       "a{sv}",
+					                       &vlan_builder);
+				}
+				g_variant_builder_add (&vf_builder , "{sv}", "vlans", g_variant_builder_end (&vlans_builder));
+			}
+			g_variant_builder_add (&builder, "a{sv}", &vf_builder);
+		}
+	}
+
+	return g_variant_builder_end (&builder);
+}
+
+static gboolean
+vfs_from_dbus (NMSetting *setting,
+               GVariant *connection_dict,
+               const char *property,
+               GVariant *value,
+               NMSettingParseFlags parse_flags,
+               GError **error)
+{
+	GPtrArray *vfs;
+	GVariantIter vf_iter;
+	GVariant *vf_var;
+
+	g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), FALSE);
+
+	vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+	g_variant_iter_init (&vf_iter, value);
+	while (g_variant_iter_next (&vf_iter, "@a{sv}", &vf_var)) {
+		NMSriovVF *vf;
+		guint32 index;
+		GVariantIter attr_iter;
+		const char *attr_name;
+		GVariant *attr_var, *vlans_var;
+
+		if (!g_variant_lookup (vf_var, "index", "u", &index))
+			goto next;
+
+		vf = nm_sriov_vf_new (index);
+
+		g_variant_iter_init (&attr_iter, vf_var);
+		while (g_variant_iter_next (&attr_iter, "{&sv}", &attr_name, &attr_var)) {
+			if (!NM_IN_STRSET (attr_name, "index", "vlans"))
+				nm_sriov_vf_set_attribute (vf, attr_name, attr_var);
+			g_variant_unref (attr_var);
+		}
+
+		if (g_variant_lookup (vf_var, "vlans", "@aa{sv}", &vlans_var)) {
+			GVariantIter vlan_iter;
+			GVariant *vlan_var;
+
+			g_variant_iter_init (&vlan_iter, vlans_var);
+			while (g_variant_iter_next (&vlan_iter, "@a{sv}", &vlan_var)) {
+				NMSriovVFVlanProtocol proto = NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q;
+				gint64 vlan_id = -1;
+				guint qos = 0;
+
+				g_variant_iter_init (&attr_iter, vlan_var);
+				while (g_variant_iter_next (&attr_iter, "{&sv}", &attr_name, &attr_var)) {
+					if (   nm_streq (attr_name, "id")
+					    && g_variant_is_of_type (attr_var, G_VARIANT_TYPE_UINT32))
+						vlan_id = g_variant_get_uint32 (attr_var);
+					else if (   nm_streq (attr_name, "qos")
+					         && g_variant_is_of_type (attr_var, G_VARIANT_TYPE_UINT32))
+						qos = g_variant_get_uint32 (attr_var);
+					else if (   nm_streq (attr_name, "protocol")
+					         && g_variant_is_of_type (attr_var, G_VARIANT_TYPE_UINT32))
+						proto = g_variant_get_uint32 (attr_var);
+					g_variant_unref (attr_var);
+				}
+				if (vlan_id != -1)
+					vf_add_vlan (vf, vlan_id, qos, proto);
+				g_variant_unref (vlan_var);
+			}
+			g_variant_unref (vlans_var);
+		}
+
+		g_ptr_array_add (vfs, vf);
+next:
+		g_variant_unref (vf_var);
+	}
+
+	g_object_set (setting, NM_SETTING_SRIOV_VFS, vfs, NULL);
+	g_ptr_array_unref (vfs);
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+static gboolean
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	NMSettingSriov *self = NM_SETTING_SRIOV (setting);
+	guint i;
+
+	if (self->vfs->len) {
+		gs_unref_hashtable GHashTable *h = NULL;
+
+		h = g_hash_table_new (nm_direct_hash, NULL);
+		for (i = 0; i < self->vfs->len; i++) {
+			NMSriovVF *vf = self->vfs->pdata[i];
+			gs_free_error GError *local = NULL;
+
+			if (vf->index >= self->total_vfs) {
+				g_set_error (error,
+				             NM_CONNECTION_ERROR,
+				             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				             _("VF with index %u, but the total number of VFs is %u"),
+				             vf->index, self->total_vfs);
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME,
+				                NM_SETTING_SRIOV_VFS);
+				return FALSE;
+			}
+
+			if (!_nm_sriov_vf_attribute_validate_all (vf, &local)) {
+				g_set_error (error,
+				             NM_CONNECTION_ERROR,
+				             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				             _("invalid VF %u: %s"),
+				             vf->index,
+				             local->message);
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME,
+				                NM_SETTING_SRIOV_VFS);
+				return FALSE;
+			}
+
+			if (g_hash_table_contains (h, GUINT_TO_POINTER (vf->index))) {
+				g_set_error (error,
+				             NM_CONNECTION_ERROR,
+				             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				             _("duplicate VF index %u"), vf->index);
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME,
+				                NM_SETTING_SRIOV_VFS);
+				return FALSE;
+			}
+
+			g_hash_table_add (h, GUINT_TO_POINTER (vf->index));
+		}
+	}
+
+	/* Failures from here on are NORMALIZABLE... */
+
+	if (self->vfs->len) {
+		for (i = 1; i < self->vfs->len; i++) {
+			NMSriovVF *vf_prev = self->vfs->pdata[i - 1];
+			NMSriovVF *vf = self->vfs->pdata[i];
+
+			if (vf->index <= vf_prev->index) {
+				g_set_error (error,
+				             NM_CONNECTION_ERROR,
+				             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				             _("VFs %d and %d are not sorted by ascending index"),
+				             vf_prev->index, vf->index);
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME,
+				                NM_SETTING_SRIOV_VFS);
+				return NM_SETTING_VERIFY_NORMALIZABLE;
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettingSriov *self = NM_SETTING_SRIOV (object);
+
+	switch (prop_id) {
+	case PROP_TOTAL_VFS:
+		self->total_vfs = g_value_get_uint (value);
+		break;
+	case PROP_VFS:
+		g_ptr_array_unref (self->vfs);
+		self->vfs = _nm_utils_copy_array (g_value_get_boxed (value),
+		                                  (NMUtilsCopyFunc) nm_sriov_vf_dup,
+		                                  (GDestroyNotify) nm_sriov_vf_unref);
+		break;
+	case PROP_AUTOPROBE_DRIVERS:
+		self->autoprobe_drivers = g_value_get_enum (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingSriov *self = NM_SETTING_SRIOV (object);
+
+	switch (prop_id) {
+	case PROP_TOTAL_VFS:
+		g_value_set_uint (value, self->total_vfs);
+		break;
+	case PROP_VFS:
+		g_value_take_boxed (value, _nm_utils_copy_array (self->vfs,
+		                                                 (NMUtilsCopyFunc) nm_sriov_vf_dup,
+		                                                 (GDestroyNotify) nm_sriov_vf_unref));
+		break;
+	case PROP_AUTOPROBE_DRIVERS:
+		g_value_set_enum (value, self->autoprobe_drivers);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static gboolean
+compare_property (NMSetting *setting,
+                  NMSetting *other,
+                  const GParamSpec *prop_spec,
+                  NMSettingCompareFlags flags)
+{
+	NMSettingSriov *a = NM_SETTING_SRIOV (setting);
+	NMSettingSriov *b = NM_SETTING_SRIOV (other);
+	NMSettingClass *setting_class;
+	guint i;
+
+	if (nm_streq (prop_spec->name, NM_SETTING_SRIOV_VFS)) {
+		if (a->vfs->len != b->vfs->len)
+			return FALSE;
+		for (i = 0; i < a->vfs->len; i++) {
+			if (!nm_sriov_vf_equal (a->vfs->pdata[i], b->vfs->pdata[i]))
+				return FALSE;
+		}
+		return TRUE;
+	}
+
+	setting_class = NM_SETTING_CLASS (nm_setting_sriov_parent_class);
+	return setting_class->compare_property (setting, other, prop_spec, flags);
+}
+
+static void
+nm_setting_sriov_init (NMSettingSriov *setting)
+{
+	setting->vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingSriov *self = NM_SETTING_SRIOV (object);
+
+	g_ptr_array_unref (self->vfs);
+
+	G_OBJECT_CLASS (nm_setting_sriov_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_sriov_class_init (NMSettingSriovClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
+
+	object_class->get_property     = get_property;
+	object_class->set_property     = set_property;
+	object_class->finalize         = finalize;
+
+	setting_class->compare_property = compare_property;
+	setting_class->verify           = verify;
+
+	/**
+	 * NMSettingSriov:total-vfs
+	 *
+	 * The total number of virtual functions to create.
+	 *
+	 * Since: 1.14
+	 **/
+	/* ---ifcfg-rh---
+	 * property: total-vfs
+	 * variable: SRIOV_TOTAL_VFS(+)
+	 * description: The total number of virtual functions to create
+	 * example: SRIOV_TOTAL_VFS=16
+	 * ---end---
+	 */
+	g_object_class_install_property
+		(object_class, PROP_TOTAL_VFS,
+		 g_param_spec_uint (NM_SETTING_SRIOV_TOTAL_VFS, "", "",
+		                    0, G_MAXUINT32, 0,
+		                    NM_SETTING_PARAM_FUZZY_IGNORE |
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_CONSTRUCT |
+		                    G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingSriov:vfs: (type GPtrArray(NMSriovVF))
+	 *
+	 * Array of virtual function descriptors.
+	 *
+	 * Each VF descriptor is a dictionary mapping attribute names
+	 * to GVariant values. The 'index' entry is mandatory for
+	 * each VF.
+	 *
+	 * When represented as string a VF is in the form:
+	 *
+	 *   "INDEX [ATTR=VALUE[ ATTR=VALUE]...]".
+	 *
+	 * for example:
+	 *
+	 *   "2 mac=00:11:22:33:44:55 spoof-check=true".
+	 *
+	 * The "vlans" attribute is represented as a semicolor-separated
+	 * list of VLAN descriptors, where each descriptor has the form
+	 *
+	 *   "ID[.PRIORITY[.PROTO]]".
+	 *
+	 * PROTO can be either 'q' for 802.1Q (the default) or 'ad' for
+	 * 802.1ad.
+	 *
+	 * Since: 1.14
+	 **/
+	/* ---ifcfg-rh---
+	 * property: vfs
+	 * variable: SRIOV_VF1(+), SRIOV_VF2(+), ...
+	 * description: SR-IOV virtual function descriptors
+	 * example: SRIOV_VF10="mac=00:11:22:33:44:55", ...
+	 * ---end---
+	 */
+	g_object_class_install_property
+		(object_class, PROP_VFS,
+		 g_param_spec_boxed (NM_SETTING_SRIOV_VFS, "", "",
+		                     G_TYPE_PTR_ARRAY,
+		                     G_PARAM_READWRITE |
+		                     NM_SETTING_PARAM_INFERRABLE |
+		                     G_PARAM_STATIC_STRINGS));
+
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_SRIOV_VFS),
+	                                   G_VARIANT_TYPE ("aa{sv}"),
+	                                   vfs_to_dbus,
+	                                   vfs_from_dbus,
+	                                   NULL);
+
+	/**
+	 * NMSettingSriov:autoprobe-drivers
+	 *
+	 * Whether to autoprobe virtual functions by a compatible driver.
+	 *
+	 * If set to %NM_TERNARY_TRUE, the kernel will try to bind VFs to
+	 * a compatible driver and if this succeeds a new network
+	 * interface will be instantiated for each VF.
+	 *
+	 * If set to %NM_TERNARY_FALSE, VFs will not be claimed and no
+	 * network interfaces will be created for them.
+	 *
+	 * When set to %NM_TERNARY_DEFAULT, the global default is used; in
+	 * case the global default is unspecified it is assumed to be
+	 * %NM_TERNARY_TRUE.
+	 *
+	 * Since: 1.14
+	 **/
+	/* ---ifcfg-rh---
+	 * property: autoprobe-drivers
+	 * variable: SRIOV_AUTOPROBE_DRIVERS(+)
+	 * default: missing variable means global default
+	 * description: Whether to autoprobe virtual functions by a compatible driver
+	 * example: SRIOV_AUTOPROBE_DRIVERS=0,1
+	 * ---end---
+	 */
+	g_object_class_install_property
+		(object_class, PROP_AUTOPROBE_DRIVERS,
+		 g_param_spec_enum (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, "", "",
+		                    nm_ternary_get_type (),
+		                    NM_TERNARY_DEFAULT,
+		                    NM_SETTING_PARAM_FUZZY_IGNORE |
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_CONSTRUCT |
+		                    G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_SRIOV,
+	                               NULL, properties_override);
+}
diff --git a/libnm-core/nm-setting-sriov.h b/libnm-core/nm-setting-sriov.h
new file mode 100644
index 00000000..2e209964
--- /dev/null
+++ b/libnm-core/nm-setting-sriov.h
@@ -0,0 +1,123 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_SRIOV_H
+#define NM_SETTING_SRIOV_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_SRIOV            (nm_setting_sriov_get_type ())
+#define NM_SETTING_SRIOV(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_SRIOV, NMSettingSriov))
+#define NM_SETTING_SRIOV_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_SRIOV, NMSettingSriovClass))
+#define NM_IS_SETTING_SRIOV(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_SRIOV))
+#define NM_IS_SETTING_SRIOV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_SRIOV))
+#define NM_SETTING_SRIOV_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_SRIOV, NMSettingSriovClass))
+
+#define NM_SETTING_SRIOV_SETTING_NAME          "sriov"
+
+#define NM_SETTING_SRIOV_TOTAL_VFS             "total-vfs"
+#define NM_SETTING_SRIOV_VFS                   "vfs"
+#define NM_SETTING_SRIOV_AUTOPROBE_DRIVERS     "autoprobe-drivers"
+
+#define NM_SRIOV_VF_ATTRIBUTE_MAC              "mac"
+#define NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK      "spoof-check"
+#define NM_SRIOV_VF_ATTRIBUTE_TRUST            "trust"
+#define NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE      "min-tx-rate"
+#define NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE      "max-tx-rate"
+
+typedef struct _NMSettingSriovClass NMSettingSriovClass;
+typedef struct _NMSriovVF NMSriovVF;
+
+/**
+ * NMSriovVFVlanProtocol:
+ * @NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q:  use 802.1Q
+ * @NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD: use 802.1ad
+ *
+ * #NMSriovVFVlanProtocol indicates the VLAN protocol to use.
+ *
+ * Since: 1.14
+ */
+typedef enum {
+	NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q = 0,
+	NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD = 1,
+} NMSriovVFVlanProtocol;
+
+NM_AVAILABLE_IN_1_14
+GType nm_setting_sriov_get_type (void);
+NM_AVAILABLE_IN_1_14
+NMSetting *nm_setting_sriov_new (void);
+NM_AVAILABLE_IN_1_14
+guint nm_setting_sriov_get_total_vfs (NMSettingSriov *setting);
+NM_AVAILABLE_IN_1_14
+guint nm_setting_sriov_get_num_vfs (NMSettingSriov *setting);
+NM_AVAILABLE_IN_1_14
+NMSriovVF *nm_setting_sriov_get_vf (NMSettingSriov *setting, guint idx);
+NM_AVAILABLE_IN_1_14
+void nm_setting_sriov_add_vf (NMSettingSriov *setting, NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+void nm_setting_sriov_remove_vf (NMSettingSriov *setting, guint idx);
+NM_AVAILABLE_IN_1_14
+gboolean nm_setting_sriov_remove_vf_by_index (NMSettingSriov *setting, guint index);
+NM_AVAILABLE_IN_1_14
+void nm_setting_sriov_clear_vfs (NMSettingSriov *setting);
+NM_AVAILABLE_IN_1_14
+NMTernary nm_setting_sriov_get_autoprobe_drivers (NMSettingSriov *setting);
+
+NM_AVAILABLE_IN_1_14
+gboolean nm_sriov_vf_add_vlan (NMSriovVF *vf, guint vlan_id);
+NM_AVAILABLE_IN_1_14
+gboolean nm_sriov_vf_remove_vlan (NMSriovVF *vf, guint vlan_id);
+NM_AVAILABLE_IN_1_14
+const guint *nm_sriov_vf_get_vlan_ids (const NMSriovVF *vf, guint *length);
+NM_AVAILABLE_IN_1_14
+void nm_sriov_vf_set_vlan_qos (NMSriovVF *vf, guint vlan_id, guint32 qos);
+NM_AVAILABLE_IN_1_14
+void nm_sriov_vf_set_vlan_protocol (NMSriovVF *vf, guint vlan_id, NMSriovVFVlanProtocol protocol);
+NM_AVAILABLE_IN_1_14
+guint32 nm_sriov_vf_get_vlan_qos (const NMSriovVF *vf, guint vlan_id);
+NM_AVAILABLE_IN_1_14
+NMSriovVFVlanProtocol nm_sriov_vf_get_vlan_protocol (const NMSriovVF *vf, guint vlan_id);
+
+NM_AVAILABLE_IN_1_14
+GType nm_sriov_vf_get_type (void);
+NM_AVAILABLE_IN_1_14
+NMSriovVF *nm_sriov_vf_new (guint index);
+NM_AVAILABLE_IN_1_14
+void nm_sriov_vf_ref (NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+void nm_sriov_vf_unref (NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+gboolean nm_sriov_vf_equal (const NMSriovVF *vf, const NMSriovVF *other);
+NM_AVAILABLE_IN_1_14
+NMSriovVF *nm_sriov_vf_dup (const NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+guint nm_sriov_vf_get_index (const NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+void nm_sriov_vf_set_attribute (NMSriovVF *vf, const char *name, GVariant *value);
+NM_AVAILABLE_IN_1_14
+const char **nm_sriov_vf_get_attribute_names (const NMSriovVF *vf);
+NM_AVAILABLE_IN_1_14
+GVariant *nm_sriov_vf_get_attribute (const NMSriovVF *vf, const char *name);
+NM_AVAILABLE_IN_1_14
+gboolean nm_sriov_vf_attribute_validate  (const char *name, GVariant *value, gboolean *known, GError **error);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_SRIOV_H */
diff --git a/libnm-core/nm-setting-tc-config.c b/libnm-core/nm-setting-tc-config.c
index f50969fb..43015d37 100644
--- a/libnm-core/nm-setting-tc-config.c
+++ b/libnm-core/nm-setting-tc-config.c
@@ -820,23 +820,21 @@ enum {
 /**
  * NMSettingTCConfig:
  *
- * Linux Traffic Contril Settings.
+ * Linux Traffic Control Settings.
  *
  * Since: 1.12
  */
 struct _NMSettingTCConfig {
-        NMSetting parent;
+	NMSetting parent;
 	GPtrArray *qdiscs;
 	GPtrArray *tfilters;
 };
 
 struct _NMSettingTCConfigClass {
-        NMSettingClass parent;
+	NMSettingClass parent;
 };
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingTCConfig, nm_setting_tc_config, NM_TYPE_SETTING,
-                         _nm_register_setting (TC_CONFIG, NM_SETTING_PRIORITY_IP))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TC_CONFIG)
+G_DEFINE_TYPE (NMSettingTCConfig, nm_setting_tc_config, NM_TYPE_SETTING)
 
 /**
  * nm_setting_tc_config_new:
@@ -983,8 +981,10 @@ nm_setting_tc_config_clear_qdiscs (NMSettingTCConfig *self)
 {
 	g_return_if_fail (NM_IS_SETTING_TC_CONFIG (self));
 
-	g_ptr_array_set_size (self->qdiscs, 0);
-	g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS);
+	if (self->qdiscs->len != 0) {
+		g_ptr_array_set_size (self->qdiscs, 0);
+		g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS);
+	}
 }
 
 /*****************************************************************************/
@@ -1117,8 +1117,10 @@ nm_setting_tc_config_clear_tfilters (NMSettingTCConfig *self)
 {
 	g_return_if_fail (NM_IS_SETTING_TC_CONFIG (self));
 
-	g_ptr_array_set_size (self->tfilters, 0);
-	g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS);
+	if (self->tfilters->len != 0) {
+		g_ptr_array_set_size (self->tfilters, 0);
+		g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS);
+	}
 }
 
 /*****************************************************************************/
@@ -1239,7 +1241,7 @@ compare_property (NMSetting *setting,
 {
 	NMSettingTCConfig *a_tc_config = NM_SETTING_TC_CONFIG (setting);
 	NMSettingTCConfig *b_tc_config = NM_SETTING_TC_CONFIG (other);
-	NMSettingClass *parent_class;
+	NMSettingClass *setting_class;
 	guint i;
 
 	if (nm_streq (prop_spec->name, NM_SETTING_TC_CONFIG_QDISCS)) {
@@ -1262,9 +1264,8 @@ compare_property (NMSetting *setting,
 		return TRUE;
 	}
 
-	/* Otherwise chain up to parent to handle generic compare */
-	parent_class = NM_SETTING_CLASS (nm_setting_tc_config_parent_class);
-	return parent_class->compare_property (setting, other, prop_spec, flags);
+	setting_class = NM_SETTING_CLASS (nm_setting_tc_config_parent_class);
+	return setting_class->compare_property (setting, other, prop_spec, flags);
 }
 
 static void
@@ -1586,28 +1587,27 @@ tc_tfilters_set (NMSetting *setting,
 }
 
 static void
-nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class)
+nm_setting_tc_config_class_init (NMSettingTCConfigClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	/* virtual methods */
 	object_class->set_property     = set_property;
 	object_class->get_property     = get_property;
 	object_class->finalize         = finalize;
-	parent_class->compare_property = compare_property;
-	parent_class->verify           = verify;
 
-	/* Properties */
+	setting_class->compare_property = compare_property;
+	setting_class->verify           = verify;
 
 	/**
 	 * NMSettingTCConfig:qdiscs: (type GPtrArray(NMTCQdisc))
 	 *
-	 * Array of TC queuening disciplines.
+	 * Array of TC queueing disciplines.
 	 **/
 	/* ---ifcfg-rh---
 	 * property: qdiscs
-	 * variable: QDISC1, QDISC2, ...
+	 * variable: QDISC1(+), QDISC2(+), ...
 	 * description: Queueing disciplines
 	 * example: QDISC1=ingress, QDISC2="root handle 1234: fq_codel"
 	 * ---end---
@@ -1620,12 +1620,13 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class)
 		                     NM_SETTING_PARAM_INFERRABLE |
 		                     G_PARAM_STATIC_STRINGS));
 
-	_nm_setting_class_override_property (parent_class,
-	                                     NM_SETTING_TC_CONFIG_QDISCS,
-	                                     G_VARIANT_TYPE ("aa{sv}"),
-	                                     tc_qdiscs_get,
-	                                     tc_qdiscs_set,
-	                                     NULL);
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_TC_CONFIG_QDISCS),
+	                                   G_VARIANT_TYPE ("aa{sv}"),
+	                                   tc_qdiscs_get,
+	                                   tc_qdiscs_set,
+	                                   NULL);
 
 	/**
 	 * NMSettingTCConfig:tfilters: (type GPtrArray(NMTCTfilter))
@@ -1634,7 +1635,7 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class)
 	 **/
 	/* ---ifcfg-rh---
 	 * property: qdiscs
-	 * variable: FILTER1, FILTER2, ...
+	 * variable: FILTER1(+), FILTER2(+), ...
 	 * description: Traffic filters
 	 * example: FILTER1="parent ffff: matchall action simple sdata Input", ...
 	 * ---end---
@@ -1647,10 +1648,14 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class)
 		                     NM_SETTING_PARAM_INFERRABLE |
 		                     G_PARAM_STATIC_STRINGS));
 
-	_nm_setting_class_override_property (parent_class,
-	                                     NM_SETTING_TC_CONFIG_TFILTERS,
-	                                     G_VARIANT_TYPE ("aa{sv}"),
-	                                     tc_tfilters_get,
-	                                     tc_tfilters_set,
-	                                     NULL);
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_TC_CONFIG_TFILTERS),
+	                                   G_VARIANT_TYPE ("aa{sv}"),
+	                                   tc_tfilters_get,
+	                                   tc_tfilters_set,
+	                                   NULL);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_TC_CONFIG,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c
index b902b822..ee84aaf7 100644
--- a/libnm-core/nm-setting-team-port.c
+++ b/libnm-core/nm-setting-team-port.c
@@ -39,9 +39,7 @@
  * optional properties that apply to team ports.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingTeamPort, nm_setting_team_port, NM_TYPE_SETTING,
-                         _nm_register_setting (TEAM_PORT, NM_SETTING_PRIORITY_AUX))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM_PORT)
+G_DEFINE_TYPE (NMSettingTeamPort, nm_setting_team_port, NM_TYPE_SETTING)
 
 #define NM_SETTING_TEAM_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPortPrivate))
 
@@ -115,7 +113,7 @@ nm_setting_team_port_get_config (NMSettingTeamPort *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_port_get_queue_id (NMSettingTeamPort *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), -1);
@@ -131,7 +129,7 @@ nm_setting_team_port_get_queue_id (NMSettingTeamPort *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_port_get_prio (NMSettingTeamPort *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0);
@@ -163,7 +161,7 @@ nm_setting_team_port_get_sticky (NMSettingTeamPort *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0);
@@ -179,7 +177,7 @@ nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0);
@@ -323,8 +321,10 @@ nm_setting_team_port_clear_link_watchers (NMSettingTeamPort *setting)
 
 	g_return_if_fail (NM_IS_SETTING_TEAM_PORT (setting));
 
-	g_ptr_array_set_size (priv->link_watchers, 0);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS);
+	if (priv->link_watchers->len != 0) {
+		g_ptr_array_set_size (priv->link_watchers, 0);
+		g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS);
+	}
 }
 
 static GVariant *
@@ -407,7 +407,7 @@ compare_property (NMSetting *setting,
                   const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	NMSettingClass *parent_class;
+	NMSettingClass *setting_class;
 	NMSettingTeamPortPrivate *a_priv, *b_priv;
 	guint i, j;
 
@@ -441,9 +441,8 @@ compare_property (NMSetting *setting,
 		return TRUE;
 	}
 
-	/* Otherwise chain up to parent to handle generic compare */
-	parent_class = NM_SETTING_CLASS (nm_setting_team_port_parent_class);
-	return parent_class->compare_property (setting, other, prop_spec, flags);
+	setting_class = NM_SETTING_CLASS (nm_setting_team_port_parent_class);
+	return setting_class->compare_property (setting, other, prop_spec, flags);
 }
 
 static void
@@ -586,21 +585,21 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
+nm_setting_team_port_class_init (NMSettingTeamPortClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingTeamPortPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingTeamPortPrivate));
 
-	/* virtual methods */
 	object_class->set_property     = set_property;
 	object_class->get_property     = get_property;
 	object_class->finalize         = finalize;
-	parent_class->compare_property = compare_property;
-	parent_class->verify           = verify;
 
-	/* Properties */
+	setting_class->compare_property = compare_property;
+	setting_class->verify           = verify;
+
 	/**
 	 * NMSettingTeamPort:config:
 	 *
@@ -714,10 +713,14 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
 		                     G_TYPE_PTR_ARRAY,
 		                     G_PARAM_READWRITE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class,
-	                                      NM_SETTING_TEAM_PORT_LINK_WATCHERS,
-	                                      G_VARIANT_TYPE ("aa{sv}"),
-	                                      team_link_watchers_to_dbus,
-	                                      team_link_watchers_from_dbus);
 
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_TEAM_PORT_LINK_WATCHERS),
+	                                    G_VARIANT_TYPE ("aa{sv}"),
+	                                    team_link_watchers_to_dbus,
+	                                    team_link_watchers_from_dbus);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_TEAM_PORT,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-team-port.h b/libnm-core/nm-setting-team-port.h
index 4ecb1838..57611750 100644
--- a/libnm-core/nm-setting-team-port.h
+++ b/libnm-core/nm-setting-team-port.h
@@ -72,15 +72,15 @@ NMSetting *  nm_setting_team_port_new (void);
 
 const char * nm_setting_team_port_get_config (NMSettingTeamPort *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_port_get_queue_id (NMSettingTeamPort *setting);
+int nm_setting_team_port_get_queue_id (NMSettingTeamPort *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_port_get_prio (NMSettingTeamPort *setting);
+int nm_setting_team_port_get_prio (NMSettingTeamPort *setting);
 NM_AVAILABLE_IN_1_12
 gboolean nm_setting_team_port_get_sticky (NMSettingTeamPort *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting);
+int nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting);
+int nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting);
 NM_AVAILABLE_IN_1_12
 guint nm_setting_team_port_get_num_link_watchers (NMSettingTeamPort *setting);
 NM_AVAILABLE_IN_1_12
diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c
index 195e509c..e6737e48 100644
--- a/libnm-core/nm-setting-team.c
+++ b/libnm-core/nm-setting-team.c
@@ -118,8 +118,8 @@ struct NMTeamLinkWatcher {
  * Since: 1.12
  **/
 NMTeamLinkWatcher *
-nm_team_link_watcher_new_ethtool (gint delay_up,
-                                  gint delay_down,
+nm_team_link_watcher_new_ethtool (int delay_up,
+                                  int delay_down,
                                   GError **error)
 {
 	NMTeamLinkWatcher *watcher;
@@ -161,9 +161,9 @@ nm_team_link_watcher_new_ethtool (gint delay_up,
  * Since: 1.12
  **/
 NMTeamLinkWatcher *
-nm_team_link_watcher_new_nsna_ping (gint init_wait,
-                                    gint interval,
-                                    gint missed_max,
+nm_team_link_watcher_new_nsna_ping (int init_wait,
+                                    int interval,
+                                    int missed_max,
                                     const char *target_host,
                                     GError **error)
 {
@@ -225,9 +225,9 @@ nm_team_link_watcher_new_nsna_ping (gint init_wait,
  * Since: 1.12
  **/
 NMTeamLinkWatcher *
-nm_team_link_watcher_new_arp_ping (gint init_wait,
-                                   gint interval,
-                                   gint missed_max,
+nm_team_link_watcher_new_arp_ping (int init_wait,
+                                   int interval,
+                                   int missed_max,
                                    const char *target_host,
                                    const char *source_host,
                                    NMTeamLinkWatcherArpPingFlags flags,
@@ -557,27 +557,25 @@ nm_team_link_watcher_get_flags (NMTeamLinkWatcher *watcher)
 
 /*****************************************************************************/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING,
-                         _nm_register_setting (TEAM, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM)
+G_DEFINE_TYPE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING)
 
 #define NM_SETTING_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM, NMSettingTeamPrivate))
 
 typedef struct {
 	char *config;
-	gint notify_peers_count;
-	gint notify_peers_interval;
-	gint mcast_rejoin_count;
-	gint mcast_rejoin_interval;
+	int notify_peers_count;
+	int notify_peers_interval;
+	int mcast_rejoin_count;
+	int mcast_rejoin_interval;
 	char *runner;
 	char *runner_hwaddr_policy;
 	GPtrArray *runner_tx_hash;
 	char *runner_tx_balancer;
-	gint runner_tx_balancer_interval;
+	int runner_tx_balancer_interval;
 	gboolean runner_active;
 	gboolean runner_fast_rate;
-	gint runner_sys_prio;
-	gint runner_min_ports;
+	int runner_sys_prio;
+	int runner_min_ports;
 	char *runner_agg_select_policy;
 	GPtrArray *link_watchers; /* Array of NMTeamLinkWatcher */
 } NMSettingTeamPrivate;
@@ -661,7 +659,7 @@ nm_setting_team_get_config (NMSettingTeam *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_get_notify_peers_count (NMSettingTeam *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0);
@@ -677,7 +675,7 @@ nm_setting_team_get_notify_peers_count (NMSettingTeam *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_get_notify_peers_interval (NMSettingTeam *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0);
@@ -693,7 +691,7 @@ nm_setting_team_get_notify_peers_interval (NMSettingTeam *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_get_mcast_rejoin_count (NMSettingTeam *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0);
@@ -709,7 +707,7 @@ nm_setting_team_get_mcast_rejoin_count (NMSettingTeam *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_get_mcast_rejoin_interval (NMSettingTeam *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0);
@@ -773,7 +771,7 @@ nm_setting_team_get_runner_tx_balancer (NMSettingTeam *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_get_runner_tx_balancer_interval (NMSettingTeam *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0);
@@ -821,7 +819,7 @@ nm_setting_team_get_runner_fast_rate (NMSettingTeam *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_get_runner_sys_prio (NMSettingTeam *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0);
@@ -837,7 +835,7 @@ nm_setting_team_get_runner_sys_prio (NMSettingTeam *setting)
  *
  * Since: 1.12
  **/
-gint
+int
 nm_setting_team_get_runner_min_ports (NMSettingTeam *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0);
@@ -1121,8 +1119,10 @@ nm_setting_team_clear_link_watchers (NMSettingTeam *setting) {
 
 	g_return_if_fail (NM_IS_SETTING_TEAM (setting));
 
-	g_ptr_array_set_size (priv->link_watchers, 0);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS);
+	if (priv->link_watchers->len != 0) {
+		g_ptr_array_set_size (priv->link_watchers, 0);
+		g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS);
+	}
 }
 
 static GVariant *
@@ -1237,7 +1237,7 @@ compare_property (NMSetting *setting,
                   NMSettingCompareFlags flags)
 {
 	NMSettingTeamPrivate *a_priv, *b_priv;
-	NMSettingClass *parent_class;
+	NMSettingClass *setting_class;
 	guint i, j;
 
 	/* If we are trying to match a connection in order to assume it (and thus
@@ -1270,9 +1270,8 @@ compare_property (NMSetting *setting,
 		return TRUE;
 	}
 
-	/* Otherwise chain up to parent to handle generic compare */
-	parent_class = NM_SETTING_CLASS (nm_setting_team_parent_class);
-	return parent_class->compare_property (setting, other, prop_spec, flags);
+	setting_class = NM_SETTING_CLASS (nm_setting_team_parent_class);
+	return setting_class->compare_property (setting, other, prop_spec, flags);
 }
 
 static void
@@ -1557,21 +1556,21 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_team_class_init (NMSettingTeamClass *setting_class)
+nm_setting_team_class_init (NMSettingTeamClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingTeamPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingTeamPrivate));
 
-	/* virtual methods */
 	object_class->set_property     = set_property;
 	object_class->get_property     = get_property;
 	object_class->finalize         = finalize;
-	parent_class->compare_property = compare_property;
-	parent_class->verify           = verify;
 
-	/* Properties */
+	setting_class->compare_property = compare_property;
+	setting_class->verify           = verify;
+
 	/**
 	 * NMSettingTeam:config:
 	 *
@@ -1817,11 +1816,13 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 		                     G_TYPE_PTR_ARRAY,
 		                     G_PARAM_READWRITE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class,
-	                                     NM_SETTING_TEAM_LINK_WATCHERS,
-	                                     G_VARIANT_TYPE ("aa{sv}"),
-	                                     team_link_watchers_to_dbus,
-	                                     team_link_watchers_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_TEAM_LINK_WATCHERS),
+	                                    G_VARIANT_TYPE ("aa{sv}"),
+	                                    team_link_watchers_to_dbus,
+	                                    team_link_watchers_from_dbus);
 
 	/* ---dbus---
 	 * property: interface-name
@@ -1831,8 +1832,12 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *   team's interface name.
 	 * ---end---
 	 */
-	_nm_setting_class_add_dbus_only_property (parent_class, "interface-name",
-	                                          G_VARIANT_TYPE_STRING,
-	                                          _nm_setting_get_deprecated_virtual_interface_name,
-	                                          NULL);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "interface-name",
+	                                    G_VARIANT_TYPE_STRING,
+	                                    _nm_setting_get_deprecated_virtual_interface_name,
+	                                    NULL);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_TEAM,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-team.h b/libnm-core/nm-setting-team.h
index cd766d8e..a600053a 100644
--- a/libnm-core/nm-setting-team.h
+++ b/libnm-core/nm-setting-team.h
@@ -57,19 +57,19 @@ typedef struct NMTeamLinkWatcher NMTeamLinkWatcher;
 GType nm_team_link_watcher_get_type              (void);
 
 NM_AVAILABLE_IN_1_12
-NMTeamLinkWatcher *nm_team_link_watcher_new_ethtool (gint delay_up,
-                                                     gint delay_down,
+NMTeamLinkWatcher *nm_team_link_watcher_new_ethtool (int delay_up,
+                                                     int delay_down,
                                                      GError **error);
 NM_AVAILABLE_IN_1_12
-NMTeamLinkWatcher *nm_team_link_watcher_new_nsna_ping (gint init_wait,
-                                                       gint interval,
-                                                       gint missed_max,
+NMTeamLinkWatcher *nm_team_link_watcher_new_nsna_ping (int init_wait,
+                                                       int interval,
+                                                       int missed_max,
                                                        const char *target_host,
                                                        GError **error);
 NM_AVAILABLE_IN_1_12
-NMTeamLinkWatcher *nm_team_link_watcher_new_arp_ping (gint init_wait,
-                                                      gint interval,
-                                                      gint missed_max,
+NMTeamLinkWatcher *nm_team_link_watcher_new_arp_ping (int init_wait,
+                                                      int interval,
+                                                      int missed_max,
                                                       const char *target_host,
                                                       const char *source_host,
                                                       NMTeamLinkWatcherArpPingFlags flags,
@@ -174,13 +174,13 @@ NMSetting *  nm_setting_team_new                (void);
 
 const char * nm_setting_team_get_config (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_get_notify_peers_count (NMSettingTeam *setting);
+int nm_setting_team_get_notify_peers_count (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_get_notify_peers_interval (NMSettingTeam *setting);
+int nm_setting_team_get_notify_peers_interval (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_get_mcast_rejoin_count (NMSettingTeam *setting);
+int nm_setting_team_get_mcast_rejoin_count (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_get_mcast_rejoin_interval (NMSettingTeam *setting);
+int nm_setting_team_get_mcast_rejoin_interval (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
 const char * nm_setting_team_get_runner (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
@@ -188,15 +188,15 @@ const char * nm_setting_team_get_runner_hwaddr_policy (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
 const char * nm_setting_team_get_runner_tx_balancer (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_get_runner_tx_balancer_interval (NMSettingTeam *setting);
+int nm_setting_team_get_runner_tx_balancer_interval (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
 gboolean nm_setting_team_get_runner_active (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
 gboolean nm_setting_team_get_runner_fast_rate (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_get_runner_sys_prio (NMSettingTeam *setting);
+int nm_setting_team_get_runner_sys_prio (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
-gint nm_setting_team_get_runner_min_ports (NMSettingTeam *setting);
+int nm_setting_team_get_runner_min_ports (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
 const char * nm_setting_team_get_runner_agg_select_policy (NMSettingTeam *setting);
 NM_AVAILABLE_IN_1_12
diff --git a/libnm-core/nm-setting-tun.c b/libnm-core/nm-setting-tun.c
index edcb3ffd..3e033e25 100644
--- a/libnm-core/nm-setting-tun.c
+++ b/libnm-core/nm-setting-tun.c
@@ -38,9 +38,7 @@
  * necessary for connection to TUN/TAP interfaces.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingTun, nm_setting_tun, NM_TYPE_SETTING,
-                         _nm_register_setting (TUN, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TUN)
+G_DEFINE_TYPE (NMSettingTun, nm_setting_tun, NM_TYPE_SETTING)
 
 #define NM_SETTING_TUN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TUN, NMSettingTunPrivate))
 
@@ -292,20 +290,19 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_tun_class_init (NMSettingTunClass *setting_class)
+nm_setting_tun_class_init (NMSettingTunClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingTunPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingTunPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify = verify;
+
 	/**
 	 * NMSettingTun:mode:
 	 *
@@ -406,4 +403,6 @@ nm_setting_tun_class_init (NMSettingTunClass *setting_class)
 		                       G_PARAM_READWRITE |
 		                       NM_SETTING_PARAM_INFERRABLE |
 		                       G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_TUN);
 }
diff --git a/libnm-core/nm-setting-user.c b/libnm-core/nm-setting-user.c
index ccc030aa..d72c9094 100644
--- a/libnm-core/nm-setting-user.c
+++ b/libnm-core/nm-setting-user.c
@@ -63,9 +63,7 @@ struct _NMSettingUserClass {
 	NMSettingClass parent;
 };
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingUser, nm_setting_user, NM_TYPE_SETTING,
-                         _nm_register_setting (USER, NM_SETTING_PRIORITY_USER))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_USER)
+G_DEFINE_TYPE (NMSettingUser, nm_setting_user, NM_TYPE_SETTING)
 
 #define NM_SETTING_USER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMSettingUser, NM_IS_SETTING_USER)
 
@@ -398,29 +396,6 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static gboolean
-hash_table_equal (GHashTable *a, GHashTable *b)
-{
-	guint n;
-	GHashTableIter iter;
-	const char *key, *value, *valu2;
-
-	n = a ? g_hash_table_size (a) : 0;
-	if (n != (b ? g_hash_table_size (b) : 0))
-		return FALSE;
-	if (n > 0) {
-		g_hash_table_iter_init (&iter, a);
-		while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) {
-			if (!g_hash_table_lookup_extended (b, key, NULL, (gpointer *) &valu2))
-				return FALSE;
-			if (!nm_streq (value, valu2))
-				return FALSE;
-		}
-	}
-	return TRUE;
-
-}
-
-static gboolean
 compare_property (NMSetting *setting,
                   NMSetting *other,
                   const GParamSpec *prop_spec,
@@ -437,10 +412,10 @@ compare_property (NMSetting *setting,
 	priv = NM_SETTING_USER_GET_PRIVATE (NM_SETTING_USER (setting));
 	pri2 = NM_SETTING_USER_GET_PRIVATE (NM_SETTING_USER (other));
 
-	if (!hash_table_equal (priv->data, pri2->data))
+	if (!nm_utils_hash_table_equal (priv->data, pri2->data, TRUE, g_str_equal))
 		return FALSE;
 
-	if (!hash_table_equal (priv->data_invalid, pri2->data_invalid))
+	if (!nm_utils_hash_table_equal (priv->data_invalid, pri2->data_invalid, TRUE, g_str_equal))
 		return FALSE;
 
 	return TRUE;
@@ -572,12 +547,14 @@ nm_setting_user_class_init (NMSettingUserClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
+
 	setting_class->compare_property = compare_property;
-	setting_class->verify = verify;
+	setting_class->verify           = verify;
 
 	/**
 	 * NMSettingUser:data: (type GHashTable(utf8,utf8))
@@ -608,8 +585,13 @@ nm_setting_user_class_init (NMSettingUserClass *klass)
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-	_nm_setting_class_transform_property (setting_class, NM_SETTING_USER_DATA,
-	                                      G_VARIANT_TYPE ("a{ss}"),
-	                                      _nm_utils_strdict_to_dbus,
-	                                      _nm_utils_strdict_from_dbus);
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_USER_DATA),
+	                                    G_VARIANT_TYPE ("a{ss}"),
+	                                    _nm_utils_strdict_to_dbus,
+	                                    _nm_utils_strdict_from_dbus);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_USER,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-vlan.c b/libnm-core/nm-setting-vlan.c
index f9c6e02d..1b61da55 100644
--- a/libnm-core/nm-setting-vlan.c
+++ b/libnm-core/nm-setting-vlan.c
@@ -41,9 +41,7 @@
  * necessary for connection to VLAN interfaces.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingVlan, nm_setting_vlan, NM_TYPE_SETTING,
-                         _nm_register_setting (VLAN, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VLAN)
+G_DEFINE_TYPE (NMSettingVlan, nm_setting_vlan, NM_TYPE_SETTING)
 
 #define NM_SETTING_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VLAN, NMSettingVlanPrivate))
 
@@ -134,7 +132,7 @@ static NMVlanQosMapping *
 priority_map_new_from_str (NMVlanPriorityMap map, const char *str)
 {
 	NMVlanQosMapping *p = NULL;
-	gchar **t = NULL;
+	char **t = NULL;
 	guint32 len;
 	guint64 from, to;
 
@@ -176,7 +174,7 @@ get_map (NMSettingVlan *self, NMVlanPriorityMap map)
 	return NULL;
 }
 
-static gint
+static int
 prio_map_compare (gconstpointer p_a, gconstpointer p_b)
 {
 	const NMVlanQosMapping *a = p_a;
@@ -831,20 +829,19 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
+nm_setting_vlan_class_init (NMSettingVlanClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingVlanPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingVlanPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify = verify;
 
 	/**
 	 * NMSettingVlan:parent:
@@ -921,11 +918,14 @@ nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
 		                     G_PARAM_CONSTRUCT |
 		                     NM_SETTING_PARAM_INFERRABLE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_override_property (parent_class, NM_SETTING_VLAN_FLAGS,
-	                                     NULL,
-	                                     _override_flags_get,
-	                                     NULL,
-	                                     _override_flags_not_set);
+
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_VLAN_FLAGS),
+	                                   NULL,
+	                                   _override_flags_get,
+	                                   NULL,
+	                                   _override_flags_not_set);
 
 	/**
 	 * NMSettingVlan:ingress-priority-map:
@@ -987,8 +987,12 @@ nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
 	 *   vlan's interface name.
 	 * ---end---
 	 */
-	_nm_setting_class_add_dbus_only_property (parent_class, "interface-name",
-	                                          G_VARIANT_TYPE_STRING,
-	                                          _nm_setting_get_deprecated_virtual_interface_name,
-	                                          NULL);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "interface-name",
+	                                    G_VARIANT_TYPE_STRING,
+	                                    _nm_setting_get_deprecated_virtual_interface_name,
+	                                    NULL);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_VLAN,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c
index a921f51f..fcbeec68 100644
--- a/libnm-core/nm-setting-vpn.c
+++ b/libnm-core/nm-setting-vpn.c
@@ -25,6 +25,8 @@
 #include <errno.h>
 #include <stdlib.h>
 
+#include "nm-utils/nm-secret-utils.h"
+
 #include "nm-setting-vpn.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
@@ -43,9 +45,7 @@
  * properties.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingVpn, nm_setting_vpn, NM_TYPE_SETTING,
-                         _nm_register_setting (VPN, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VPN)
+G_DEFINE_TYPE (NMSettingVpn, nm_setting_vpn, NM_TYPE_SETTING)
 
 #define NM_SETTING_VPN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VPN, NMSettingVpnPrivate))
 
@@ -481,6 +481,7 @@ static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+	NMSettingConnection *s_con;
 
 	if (!priv->service_type) {
 		g_set_error_literal (error,
@@ -510,6 +511,16 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
+	if (   connection
+	    && (s_con = nm_connection_get_setting_connection (connection))
+	    && nm_setting_connection_get_multi_connect (s_con) != NM_CONNECTION_MULTI_CONNECT_DEFAULT) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("cannot set connection.multi-connect for VPN setting"));
+		return FALSE;
+	}
+
 	return TRUE;
 }
 
@@ -878,27 +889,26 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_vpn_class_init (NMSettingVpnClass *setting_class)
+nm_setting_vpn_class_init (NMSettingVpnClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingVpnPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingVpnPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
 
-	parent_class->verify            = verify;
-	parent_class->update_one_secret = update_one_secret;
-	parent_class->get_secret_flags  = get_secret_flags;
-	parent_class->set_secret_flags  = set_secret_flags;
-	parent_class->need_secrets      = need_secrets;
-	parent_class->compare_property  = compare_property;
-	parent_class->clear_secrets_with_flags = clear_secrets_with_flags;
+	setting_class->verify                   = verify;
+	setting_class->update_one_secret        = update_one_secret;
+	setting_class->get_secret_flags         = get_secret_flags;
+	setting_class->set_secret_flags         = set_secret_flags;
+	setting_class->need_secrets             = need_secrets;
+	setting_class->compare_property         = compare_property;
+	setting_class->clear_secrets_with_flags = clear_secrets_with_flags;
 
-	/* Properties */
 	/**
 	 * NMSettingVpn:service-type:
 	 *
@@ -964,10 +974,13 @@ nm_setting_vpn_class_init (NMSettingVpnClass *setting_class)
 		                     G_TYPE_HASH_TABLE,
 		                     G_PARAM_READWRITE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_VPN_DATA,
-	                                      G_VARIANT_TYPE ("a{ss}"),
-	                                      _nm_utils_strdict_to_dbus,
-	                                      _nm_utils_strdict_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_VPN_DATA),
+	                                    G_VARIANT_TYPE ("a{ss}"),
+	                                    _nm_utils_strdict_to_dbus,
+	                                    _nm_utils_strdict_from_dbus);
 
 	/**
 	 * NMSettingVpn:secrets: (type GHashTable(utf8,utf8)):
@@ -990,10 +1003,13 @@ nm_setting_vpn_class_init (NMSettingVpnClass *setting_class)
 		                     G_PARAM_READWRITE |
 		                     NM_SETTING_PARAM_SECRET |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_VPN_SECRETS,
-	                                      G_VARIANT_TYPE ("a{ss}"),
-	                                      _nm_utils_strdict_to_dbus,
-	                                      _nm_utils_strdict_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_VPN_SECRETS),
+	                                    G_VARIANT_TYPE ("a{ss}"),
+	                                    _nm_utils_strdict_to_dbus,
+	                                    _nm_utils_strdict_from_dbus);
 
 	/**
 	 * NMSettingVpn:timeout:
@@ -1012,4 +1028,7 @@ nm_setting_vpn_class_init (NMSettingVpnClass *setting_class)
 		                    0, G_MAXUINT32, 0,
 		                    G_PARAM_READWRITE |
 		                    G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_VPN,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-vxlan.c b/libnm-core/nm-setting-vxlan.c
index 4be6a236..d00c5a7a 100644
--- a/libnm-core/nm-setting-vxlan.c
+++ b/libnm-core/nm-setting-vxlan.c
@@ -36,9 +36,7 @@
  * necessary for connection to VXLAN interfaces.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingVxlan, nm_setting_vxlan, NM_TYPE_SETTING,
-                         _nm_register_setting (VXLAN, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VXLAN)
+G_DEFINE_TYPE (NMSettingVxlan, nm_setting_vxlan, NM_TYPE_SETTING)
 
 #define NM_SETTING_VXLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VXLAN, NMSettingVxlanPrivate))
 
@@ -565,20 +563,18 @@ finalize (GObject *object)
 }
 
 static void
-nm_setting_vxlan_class_init (NMSettingVxlanClass *setting_class)
+nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingVxlanPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingVxlanPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify = verify;
 
 	/**
 	 * NMSettingVxlan:parent:
@@ -840,4 +836,6 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *setting_class)
 		                       G_PARAM_CONSTRUCT |
 		                       NM_SETTING_PARAM_INFERRABLE |
 		                       G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_VXLAN);
 }
diff --git a/libnm-core/nm-setting-wimax.c b/libnm-core/nm-setting-wimax.c
index dbfb46e4..4c1a572e 100644
--- a/libnm-core/nm-setting-wimax.c
+++ b/libnm-core/nm-setting-wimax.c
@@ -42,9 +42,7 @@
  * connections cannot be activated.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingWimax, nm_setting_wimax, NM_TYPE_SETTING,
-                         _nm_register_setting (WIMAX, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIMAX)
+G_DEFINE_TYPE (NMSettingWimax, nm_setting_wimax, NM_TYPE_SETTING)
 
 #define NM_SETTING_WIMAX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIMAX, NMSettingWimaxPrivate))
 
@@ -207,20 +205,20 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_wimax_class_init (NMSettingWimaxClass *setting_class)
+nm_setting_wimax_class_init (NMSettingWimaxClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingWimaxPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingWimaxPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	parent_class->verify       = verify;
 
-	/* Properties */
+	setting_class->verify = verify;
+
 	/**
 	 * NMSettingWimax:network-name:
 	 *
@@ -251,8 +249,14 @@ nm_setting_wimax_class_init (NMSettingWimaxClass *setting_class)
 		                      NULL,
 		                      G_PARAM_READWRITE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class, NM_SETTING_WIMAX_MAC_ADDRESS,
-	                                      G_VARIANT_TYPE_BYTESTRING,
-	                                      _nm_utils_hwaddr_to_dbus,
-	                                      _nm_utils_hwaddr_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_WIMAX_MAC_ADDRESS),
+	                                    G_VARIANT_TYPE_BYTESTRING,
+	                                    _nm_utils_hwaddr_to_dbus,
+	                                    _nm_utils_hwaddr_from_dbus);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_WIMAX,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c
index 5da9ce7d..baa40cc4 100644
--- a/libnm-core/nm-setting-wired.c
+++ b/libnm-core/nm-setting-wired.c
@@ -40,9 +40,7 @@
  * necessary for connection to Ethernet networks.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingWired, nm_setting_wired, NM_TYPE_SETTING,
-                         _nm_register_setting (WIRED, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRED)
+G_DEFINE_TYPE (NMSettingWired, nm_setting_wired, NM_TYPE_SETTING)
 
 #define NM_SETTING_WIRED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRED, NMSettingWiredPrivate))
 
@@ -766,8 +764,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	    || ((!priv->speed) && (priv->duplex))) {
 
 		g_set_error_literal (error,
-				     NM_CONNECTION_ERROR,
-				     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
 		                     priv->auto_negotiate ?
 		                       _("both speed and duplex should have a valid value or both should be unset")
 		                     : _("both speed and duplex are required for static link configuration"));
@@ -783,15 +781,15 @@ compare_property (NMSetting *setting,
                   const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	NMSettingClass *parent_class;
+	NMSettingClass *setting_class;
 
 	if (nm_streq (prop_spec->name, NM_SETTING_WIRED_CLONED_MAC_ADDRESS)) {
 		return nm_streq0 (NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address,
 		                  NM_SETTING_WIRED_GET_PRIVATE (other)->cloned_mac_address);
 	}
 
-	parent_class = NM_SETTING_CLASS (nm_setting_wired_parent_class);
-	return parent_class->compare_property (setting, other, prop_spec, flags);
+	setting_class = NM_SETTING_CLASS (nm_setting_wired_parent_class);
+	return setting_class->compare_property (setting, other, prop_spec, flags);
 }
 
 static GVariant *
@@ -978,21 +976,21 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
+nm_setting_wired_class_init (NMSettingWiredClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_wired_class);
-	NMSettingClass *setting_class = NM_SETTING_CLASS (setting_wired_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_wired_class, sizeof (NMSettingWiredPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingWiredPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	setting_class->verify      = verify;
+
+	setting_class->verify           = verify;
 	setting_class->compare_property = compare_property;
 
-	/* Properties */
 	/**
 	 * NMSettingWired:port:
 	 *
@@ -1103,12 +1101,14 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 		                       G_PARAM_READWRITE |
 		                       G_PARAM_CONSTRUCT |
 		                       G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_override_property (setting_class,
-	                                     NM_SETTING_WIRED_AUTO_NEGOTIATE,
-	                                     G_VARIANT_TYPE_BOOLEAN,
-	                                     _override_autoneg_get,
-	                                     NULL,
-	                                     NULL);
+
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_WIRED_AUTO_NEGOTIATE),
+	                                   G_VARIANT_TYPE_BOOLEAN,
+	                                   _override_autoneg_get,
+	                                   NULL,
+	                                   NULL);
 
 	/**
 	 * NMSettingWired:mac-address:
@@ -1141,10 +1141,13 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 		                      G_PARAM_READWRITE |
 		                      NM_SETTING_PARAM_INFERRABLE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (setting_class, NM_SETTING_WIRED_MAC_ADDRESS,
-	                                      G_VARIANT_TYPE_BYTESTRING,
-	                                      _nm_utils_hwaddr_to_dbus,
-	                                      _nm_utils_hwaddr_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_WIRED_MAC_ADDRESS),
+	                                    G_VARIANT_TYPE_BYTESTRING,
+	                                    _nm_utils_hwaddr_to_dbus,
+	                                    _nm_utils_hwaddr_from_dbus);
 
 	/**
 	 * NMSettingWired:cloned-mac-address:
@@ -1196,8 +1199,10 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 		                      G_PARAM_READWRITE |
 		                      NM_SETTING_PARAM_INFERRABLE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_override_property (setting_class,
-	                                     NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
+
+	_properties_override_add_override (properties_override,
+	                                     g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                   NM_SETTING_WIRED_CLONED_MAC_ADDRESS),
 	                                     G_VARIANT_TYPE_BYTESTRING,
 	                                     _nm_utils_hwaddr_cloned_get,
 	                                     _nm_utils_hwaddr_cloned_set,
@@ -1215,11 +1220,11 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 	 *   "cloned-mac-address".
 	 * ---end---
 	 */
-	_nm_setting_class_add_dbus_only_property (setting_class,
-	                                          "assigned-mac-address",
-	                                          G_VARIANT_TYPE_STRING,
-	                                          _nm_utils_hwaddr_cloned_data_synth,
-	                                          _nm_utils_hwaddr_cloned_data_set);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "assigned-mac-address",
+	                                    G_VARIANT_TYPE_STRING,
+	                                    _nm_utils_hwaddr_cloned_data_synth,
+	                                    _nm_utils_hwaddr_cloned_data_set);
 
 	/**
 	 * NMSettingWired:generate-mac-address-mask:
@@ -1389,10 +1394,13 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 		                     G_PARAM_READWRITE |
 		                     NM_SETTING_PARAM_INFERRABLE |
 		                     G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (setting_class, NM_SETTING_WIRED_S390_OPTIONS,
-	                                      G_VARIANT_TYPE ("a{ss}"),
-	                                      _nm_utils_strdict_to_dbus,
-	                                      _nm_utils_strdict_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_WIRED_S390_OPTIONS),
+	                                    G_VARIANT_TYPE ("a{ss}"),
+	                                    _nm_utils_strdict_to_dbus,
+	                                    _nm_utils_strdict_from_dbus);
 
 	/**
 	 * NMSettingWired:wake-on-lan:
@@ -1431,4 +1439,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 		                      NULL,
 		                      G_PARAM_READWRITE |
 		                      G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_WIRED,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-wireless-security.c b/libnm-core/nm-setting-wireless-security.c
index 31e386f3..d1527821 100644
--- a/libnm-core/nm-setting-wireless-security.c
+++ b/libnm-core/nm-setting-wireless-security.c
@@ -53,9 +53,7 @@
  *       ISBN: 978-1587051548
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingWirelessSecurity, nm_setting_wireless_security, NM_TYPE_SETTING,
-                         _nm_register_setting (WIRELESS_SECURITY, NM_SETTING_PRIORITY_HW_AUX))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRELESS_SECURITY)
+G_DEFINE_TYPE (NMSettingWirelessSecurity, nm_setting_wireless_security, NM_TYPE_SETTING)
 
 #define NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityPrivate))
 
@@ -1431,25 +1429,24 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *setting_class)
+nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
-	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_class, sizeof (NMSettingWirelessSecurityPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingWirelessSecurityPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
 
-	parent_class->verify           = verify;
-	parent_class->verify_secrets   = verify_secrets;
-	parent_class->need_secrets     = need_secrets;
-	parent_class->get_secret_flags = get_secret_flags;
-	parent_class->set_secret_flags = set_secret_flags;
+	setting_class->verify           = verify;
+	setting_class->verify_secrets   = verify_secrets;
+	setting_class->need_secrets     = need_secrets;
+	setting_class->get_secret_flags = get_secret_flags;
+	setting_class->set_secret_flags = set_secret_flags;
 
-	/* Properties */
 	/**
 	 * NMSettingWirelessSecurity:key-mgmt:
 	 *
@@ -1856,11 +1853,13 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *setting
 		                    G_PARAM_READWRITE |
 		                    G_PARAM_CONSTRUCT |
 		                    G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (parent_class,
-	                                      NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE,
-	                                      G_VARIANT_TYPE_UINT32,
-	                                      wep_key_type_to_dbus,
-	                                      NULL);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE),
+	                                    G_VARIANT_TYPE_UINT32,
+	                                    wep_key_type_to_dbus,
+	                                    NULL);
 	/**
 	 * NMSettingWirelessSecurity:wps-method:
 	 *
@@ -1922,4 +1921,7 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *setting
 		                   G_PARAM_CONSTRUCT |
 		                   NM_SETTING_PARAM_FUZZY_IGNORE |
 		                   G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_WIRELESS_SECURITY,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c
index 89a2df8e..a9cae6c0 100644
--- a/libnm-core/nm-setting-wireless.c
+++ b/libnm-core/nm-setting-wireless.c
@@ -40,9 +40,7 @@
  * necessary for connection to 802.11 Wi-Fi networks.
  **/
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingWireless, nm_setting_wireless, NM_TYPE_SETTING,
-                         _nm_register_setting (WIRELESS, NM_SETTING_PRIORITY_HW_BASE))
-NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRELESS)
+G_DEFINE_TYPE (NMSettingWireless, nm_setting_wireless, NM_TYPE_SETTING)
 
 #define NM_SETTING_WIRELESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessPrivate))
 
@@ -937,15 +935,15 @@ compare_property (NMSetting *setting,
                   const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	NMSettingClass *parent_class;
+	NMSettingClass *setting_class;
 
 	if (nm_streq (prop_spec->name, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS)) {
 		return nm_streq0 (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->cloned_mac_address,
 		                  NM_SETTING_WIRELESS_GET_PRIVATE (other)->cloned_mac_address);
 	}
 
-	parent_class = NM_SETTING_CLASS (nm_setting_wireless_parent_class);
-	return parent_class->compare_property (setting, other, prop_spec, flags);
+	setting_class = NM_SETTING_CLASS (nm_setting_wireless_parent_class);
+	return setting_class->compare_property (setting, other, prop_spec, flags);
 }
 
 /*****************************************************************************/
@@ -1176,21 +1174,21 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
-nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class)
+nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (setting_wireless_class);
-	NMSettingClass *setting_class = NM_SETTING_CLASS (setting_wireless_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_type_class_add_private (setting_wireless_class, sizeof (NMSettingWirelessPrivate));
+	g_type_class_add_private (klass, sizeof (NMSettingWirelessPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
-	setting_class->verify      = verify;
+
+	setting_class->verify           = verify;
 	setting_class->compare_property = compare_property;
 
-	/* Properties */
 	/**
 	 * NMSettingWireless:ssid:
 	 *
@@ -1308,10 +1306,13 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class)
 		                      NULL,
 		                      G_PARAM_READWRITE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (setting_class, NM_SETTING_WIRELESS_BSSID,
-	                                      G_VARIANT_TYPE_BYTESTRING,
-	                                      _nm_utils_hwaddr_to_dbus,
-	                                      _nm_utils_hwaddr_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_WIRELESS_BSSID),
+	                                    G_VARIANT_TYPE_BYTESTRING,
+	                                    _nm_utils_hwaddr_to_dbus,
+	                                    _nm_utils_hwaddr_from_dbus);
 
 	/**
 	 * NMSettingWireless:rate:
@@ -1388,10 +1389,13 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class)
 		                      NULL,
 		                      G_PARAM_READWRITE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_transform_property (setting_class, NM_SETTING_WIRELESS_MAC_ADDRESS,
-	                                      G_VARIANT_TYPE_BYTESTRING,
-	                                      _nm_utils_hwaddr_to_dbus,
-	                                      _nm_utils_hwaddr_from_dbus);
+
+	_properties_override_add_transform (properties_override,
+	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                  NM_SETTING_WIRELESS_MAC_ADDRESS),
+	                                    G_VARIANT_TYPE_BYTESTRING,
+	                                    _nm_utils_hwaddr_to_dbus,
+	                                    _nm_utils_hwaddr_from_dbus);
 
 	/**
 	 * NMSettingWireless:cloned-mac-address:
@@ -1441,12 +1445,14 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class)
 		                      NULL,
 		                      G_PARAM_READWRITE |
 		                      G_PARAM_STATIC_STRINGS));
-	_nm_setting_class_override_property (setting_class,
-	                                     NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
-	                                     G_VARIANT_TYPE_BYTESTRING,
-	                                     _nm_utils_hwaddr_cloned_get,
-	                                     _nm_utils_hwaddr_cloned_set,
-	                                     _nm_utils_hwaddr_cloned_not_set);
+
+	_properties_override_add_override (properties_override,
+	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
+	                                                                 NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS),
+	                                   G_VARIANT_TYPE_BYTESTRING,
+	                                   _nm_utils_hwaddr_cloned_get,
+	                                   _nm_utils_hwaddr_cloned_set,
+	                                   _nm_utils_hwaddr_cloned_not_set);
 
 	/* ---dbus---
 	 * property: assigned-mac-address
@@ -1460,11 +1466,11 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class)
 	 *   "cloned-mac-address".
 	 * ---end---
 	 */
-	_nm_setting_class_add_dbus_only_property (setting_class,
-	                                          "assigned-mac-address",
-	                                          G_VARIANT_TYPE_STRING,
-	                                          _nm_utils_hwaddr_cloned_data_synth,
-	                                          _nm_utils_hwaddr_cloned_data_set);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "assigned-mac-address",
+	                                    G_VARIANT_TYPE_STRING,
+	                                    _nm_utils_hwaddr_cloned_data_synth,
+	                                    _nm_utils_hwaddr_cloned_data_set);
 
 	/**
 	 * NMSettingWireless:generate-mac-address-mask:
@@ -1679,9 +1685,11 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class)
 	 *   NetworkManager daemons.
 	 * ---end---
 	 */
-	_nm_setting_class_add_dbus_only_property (setting_class, "security",
-	                                          G_VARIANT_TYPE_STRING,
-	                                          nm_setting_wireless_get_security, NULL);
+	_properties_override_add_dbus_only (properties_override,
+	                                    "security",
+	                                    G_VARIANT_TYPE_STRING,
+	                                    nm_setting_wireless_get_security,
+	                                    NULL);
 
 	/**
 	 * NMSettingWireless:wake-on-wlan:
@@ -1708,4 +1716,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class)
 		                    G_PARAM_CONSTRUCT |
 		                    G_PARAM_READWRITE |
 		                    G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_WIRELESS,
+	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-wpan.c b/libnm-core/nm-setting-wpan.c
new file mode 100644
index 00000000..6e1aaadc
--- /dev/null
+++ b/libnm-core/nm-setting-wpan.c
@@ -0,0 +1,274 @@
+/*
+ * 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 2018 Lubomir Rintel <lkundrak@v3.sk>
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-wpan.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+#include "nm-utils-private.h"
+
+/* Ideally we'll be able to get this from a public header. */
+#ifndef IEEE802154_ADDR_LEN
+#define IEEE802154_ADDR_LEN 8
+#endif
+
+enum {
+	PROP_0,
+	PROP_MAC_ADDRESS,
+	PROP_PAN_ID,
+	PROP_SHORT_ADDRESS,
+};
+
+typedef struct {
+	char *mac_address;
+	guint16 pan_id;
+	guint16 short_address;
+} NMSettingWpanPrivate;
+
+/**
+ * NMSettingWpan:
+ *
+ * IEEE 802.15.4 (WPAN) MAC Settings
+ */
+struct _NMSettingWpan {
+        NMSetting parent;
+};
+
+struct _NMSettingWpanClass {
+        NMSettingClass parent;
+};
+
+/**
+ * SECTION:nm-setting-wpan
+ * @short_description: Describes connection properties for IEEE 802.15.4 (WPAN) MAC
+ *
+ * The #NMSettingWpan object is a #NMSetting subclass that describes properties
+ * necessary for configuring IEEE 802.15.4 (WPAN) MAC layer devices.
+ **/
+
+G_DEFINE_TYPE (NMSettingWpan, nm_setting_wpan, NM_TYPE_SETTING)
+
+#define NM_SETTING_WPAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WPAN, NMSettingWpanPrivate))
+
+/**
+ * nm_setting_wpan_get_mac_address:
+ * @setting: the #NMSettingWpan
+ *
+ * Returns: the #NMSettingWpan:mac-address property of the setting
+ *
+ * Since: 1.14
+ **/
+const char *
+nm_setting_wpan_get_mac_address (NMSettingWpan *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WPAN (setting), NULL);
+
+	return NM_SETTING_WPAN_GET_PRIVATE (setting)->mac_address;
+}
+
+/**
+ * nm_setting_wpan_get_pan_id:
+ * @setting: the #NMSettingWpan
+ *
+ * Returns: the #NMSettingWpan:pan-id property of the setting
+ *
+ * Since: 1.14
+ **/
+guint16
+nm_setting_wpan_get_pan_id (NMSettingWpan *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WPAN (setting), G_MAXUINT16);
+
+	return NM_SETTING_WPAN_GET_PRIVATE (setting)->pan_id;
+}
+
+/**
+ * nm_setting_wpan_get_short_address:
+ * @setting: the #NMSettingWpan
+ *
+ * Returns: the #NMSettingWpan:short-address property of the setting
+ *
+ * Since: 1.14
+ **/
+guint16
+nm_setting_wpan_get_short_address (NMSettingWpan *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WPAN (setting), G_MAXUINT16);
+
+	return NM_SETTING_WPAN_GET_PRIVATE (setting)->short_address;
+}
+
+static gboolean
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	NMSettingWpanPrivate *priv = NM_SETTING_WPAN_GET_PRIVATE (setting);
+
+	if (priv->mac_address && !nm_utils_hwaddr_valid (priv->mac_address, IEEE802154_ADDR_LEN)) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("property is invalid"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WPAN_SETTING_NAME, NM_SETTING_WPAN_MAC_ADDRESS);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	NMSettingWpan *setting = NM_SETTING_WPAN (object);
+
+	switch (prop_id) {
+	case PROP_MAC_ADDRESS:
+		g_value_set_string (value, nm_setting_wpan_get_mac_address (setting));
+		break;
+	case PROP_PAN_ID:
+		g_value_set_uint (value, nm_setting_wpan_get_pan_id (setting));
+		break;
+	case PROP_SHORT_ADDRESS:
+		g_value_set_uint (value, nm_setting_wpan_get_short_address (setting));
+		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)
+{
+	NMSettingWpanPrivate *priv = NM_SETTING_WPAN_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MAC_ADDRESS:
+		g_free (priv->mac_address);
+		priv->mac_address = _nm_utils_hwaddr_canonical_or_invalid (g_value_get_string (value),
+		                                                           IEEE802154_ADDR_LEN);
+		break;
+	case PROP_PAN_ID:
+		priv->pan_id = g_value_get_uint (value);
+		break;
+	case PROP_SHORT_ADDRESS:
+		priv->short_address = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_wpan_init (NMSettingWpan *setting)
+{
+	NMSettingWpanPrivate *priv = NM_SETTING_WPAN_GET_PRIVATE (setting);
+
+	priv->short_address = G_MAXUINT16;
+	priv->pan_id = G_MAXUINT16;
+}
+
+/**
+ * nm_setting_wpan_new:
+ *
+ * Creates a new #NMSettingWpan object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingWpan object
+ *
+ * Since: 1.14
+ **/
+NMSetting *
+nm_setting_wpan_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WPAN, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingWpanPrivate *priv = NM_SETTING_WPAN_GET_PRIVATE (object);
+
+	g_free (priv->mac_address);
+
+	G_OBJECT_CLASS (nm_setting_wpan_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_wpan_class_init (NMSettingWpanClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingWpanPrivate));
+
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+
+	setting_class->verify = verify;
+
+	/**
+	 * NMSettingWpan:mac-address:
+	 *
+	 * If specified, this connection will only apply to the IEEE 802.15.4 (WPAN)
+	 * MAC layer device whose permanent MAC address matches.
+	 **/
+	/* ---keyfile---
+	 * property: mac-address
+	 * format: usual hex-digits-and-colons notation
+	 * description: MAC address in hex-digits-and-colons notation
+	 *   (e.g. 76:d8:9b:87:66:60:84:ee).
+	 * ---end---
+	 */
+	g_object_class_install_property
+	        (object_class, PROP_MAC_ADDRESS,
+	         g_param_spec_string (NM_SETTING_WPAN_MAC_ADDRESS, "", "",
+	                              NULL,
+	                              G_PARAM_READWRITE |
+	                              G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingWpan:pan-id:
+	 *
+	 * IEEE 802.15.4 Personal Area Network (PAN) identifier.
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_PAN_ID,
+	         g_param_spec_uint (NM_SETTING_WPAN_PAN_ID, "", "",
+	                            0, G_MAXUINT16, G_MAXUINT16,
+	                            G_PARAM_READWRITE |
+	                            G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NMSettingWpan:short-address:
+	 *
+	 * Short IEEE 802.15.4 address to be used within a restricted environment.
+	 **/
+	g_object_class_install_property
+	        (object_class, PROP_SHORT_ADDRESS,
+	         g_param_spec_uint (NM_SETTING_WPAN_SHORT_ADDRESS, "", "",
+	                            0, G_MAXUINT16, G_MAXUINT16,
+	                            G_PARAM_READWRITE |
+	                            G_PARAM_STATIC_STRINGS));
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_WPAN);
+}
diff --git a/libnm-core/nm-setting-wpan.h b/libnm-core/nm-setting-wpan.h
new file mode 100644
index 00000000..a39e377e
--- /dev/null
+++ b/libnm-core/nm-setting-wpan.h
@@ -0,0 +1,59 @@
+/*
+ * 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 2018 Lubomir Rintel <lkundrak@v3.sk>
+ */
+
+#ifndef __NM_SETTING_WPAN_H__
+#define __NM_SETTING_WPAN_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_WPAN            (nm_setting_wpan_get_type ())
+#define NM_SETTING_WPAN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WPAN, NMSettingWpan))
+#define NM_SETTING_WPAN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WPANCONFIG, NMSettingWpanClass))
+#define NM_IS_SETTING_WPAN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WPAN))
+#define NM_IS_SETTING_WPAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WPAN))
+#define NM_SETTING_WPAN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WPAN, NMSettingWpanClass))
+
+#define NM_SETTING_WPAN_SETTING_NAME       "wpan"
+#define NM_SETTING_WPAN_MAC_ADDRESS        "mac-address"
+#define NM_SETTING_WPAN_PAN_ID             "pan-id"
+#define NM_SETTING_WPAN_SHORT_ADDRESS      "short-address"
+
+typedef struct _NMSettingWpanClass NMSettingWpanClass;
+
+NM_AVAILABLE_IN_1_14
+GType nm_setting_wpan_get_type (void);
+NM_AVAILABLE_IN_1_14
+NMSetting *nm_setting_wpan_new (void);
+
+NM_AVAILABLE_IN_1_14
+const char *nm_setting_wpan_get_mac_address        (NMSettingWpan *setting);
+NM_AVAILABLE_IN_1_14
+guint16     nm_setting_wpan_get_pan_id             (NMSettingWpan *setting);
+NM_AVAILABLE_IN_1_14
+guint16     nm_setting_wpan_get_short_address      (NMSettingWpan *setting);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_WPAN_H__ */
diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c
index 218f0dad..0cec5165 100644
--- a/libnm-core/nm-setting.c
+++ b/libnm-core/nm-setting.c
@@ -53,9 +53,13 @@
  * of properties and allowed values.
  */
 
-G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)
+/*****************************************************************************/
 
-#define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate))
+typedef struct {
+	GHashTable *hash;
+	const char **names;
+	GVariant **values;
+} GenData;
 
 typedef struct {
 	const char *name;
@@ -63,10 +67,6 @@ typedef struct {
 	NMSettingPriority priority;
 } SettingInfo;
 
-typedef struct {
-	const SettingInfo *info;
-} NMSettingPrivate;
-
 enum {
 	PROP_0,
 	PROP_NAME,
@@ -74,136 +74,66 @@ enum {
 	PROP_LAST
 };
 
-/*****************************************************************************/
-
-static GHashTable *registered_settings = NULL;
-static GHashTable *registered_settings_by_type = NULL;
-
-static gboolean
-_nm_gtype_equal (gconstpointer v1, gconstpointer v2)
-{
-	return *((const GType *) v1) == *((const GType *) v2);
-}
-static guint
-_nm_gtype_hash (gconstpointer v)
-{
-	return *((const GType *) v);
-}
-
-static void
-_ensure_registered (void)
-{
-	if (G_UNLIKELY (registered_settings == NULL)) {
-		registered_settings = g_hash_table_new (nm_str_hash, g_str_equal);
-		registered_settings_by_type = g_hash_table_new (_nm_gtype_hash, _nm_gtype_equal);
-	}
-}
+typedef struct {
+	GenData *gendata;
+} NMSettingPrivate;
 
-static void __attribute__((constructor))
-_ensure_registered_constructor (void)
-{
-	_ensure_registered ();
-}
+G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)
 
-#define _ensure_setting_info(self, priv) \
-	G_STMT_START { \
-		NMSettingPrivate *_priv_esi = (priv); \
-		if (G_UNLIKELY (!_priv_esi->info)) { \
-			_priv_esi->info = _nm_setting_lookup_setting_by_type (G_OBJECT_TYPE (self)); \
-			g_assert (_priv_esi->info); \
-		} \
-	} G_STMT_END
+#define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate))
 
 /*****************************************************************************/
 
-/*
- * _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 #NMSettingPriority
- *
- * INTERNAL ONLY: registers a setting's internal properties with libnm.
- */
-void
-_nm_register_setting_impl (const char *name,
-                           GType type,
-                           NMSettingPriority priority)
-{
-	SettingInfo *info;
-
-	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 ();
-
-	nm_assert (!g_hash_table_lookup (registered_settings, name));
-	nm_assert (!g_hash_table_lookup (registered_settings_by_type, &type));
-
-	nm_assert (   priority != NM_SETTING_PRIORITY_CONNECTION
-	           || nm_streq (name, NM_SETTING_CONNECTION_SETTING_NAME));
+static GenData *_gendata_hash (NMSetting *setting, gboolean create_if_necessary);
 
-	info = g_slice_new0 (SettingInfo);
-	info->type = type;
-	info->priority = priority;
-	info->name = name;
-	g_hash_table_insert (registered_settings, (void *) info->name, info);
-	g_hash_table_insert (registered_settings_by_type, &info->type, info);
-}
-
-static const SettingInfo *
-_nm_setting_lookup_setting_by_type (GType type)
-{
-	_ensure_registered ();
-	return g_hash_table_lookup (registered_settings_by_type, &type);
-}
+/*****************************************************************************/
 
 static NMSettingPriority
-_get_setting_type_priority (GType type)
+_get_base_type_priority (const NMMetaSettingInfo *setting_info,
+                         GType gtype)
 {
-	const SettingInfo *info;
+	/* 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.
+	 */
 
-	g_return_val_if_fail (g_type_is_a (type, NM_TYPE_SETTING), G_MAXUINT32);
+	if (setting_info) {
+		if (   NM_IN_SET (setting_info->setting_priority,
+		                  NM_SETTING_PRIORITY_HW_BASE,
+		                  NM_SETTING_PRIORITY_HW_NON_BASE)
+		    || gtype == NM_TYPE_SETTING_PPPOE)
+			return setting_info->setting_priority;
+	}
 
-	info = _nm_setting_lookup_setting_by_type (type);
-	return info->priority;
+	return NM_SETTING_PRIORITY_INVALID;
 }
 
 NMSettingPriority
 _nm_setting_get_setting_priority (NMSetting *setting)
 {
-	NMSettingPrivate *priv;
+	const NMMetaSettingInfo *setting_info;
 
-	g_return_val_if_fail (NM_IS_SETTING (setting), G_MAXUINT32);
-	priv = NM_SETTING_GET_PRIVATE (setting);
-	_ensure_setting_info (setting, priv);
-	return priv->info->priority;
+	g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_PRIORITY_INVALID);
+
+	setting_info = NM_SETTING_GET_CLASS (setting)->setting_info;
+	return setting_info ? setting_info->setting_priority : NM_SETTING_PRIORITY_INVALID;
 }
 
 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.
-	 */
-	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;
+	return _get_base_type_priority (nm_meta_setting_infos_by_gtype (type),
+	                                type);
 }
 
 NMSettingPriority
 _nm_setting_get_base_type_priority (NMSetting *setting)
 {
-	return _nm_setting_type_get_base_type_priority (G_OBJECT_TYPE (setting));
+	g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_PRIORITY_INVALID);
+
+	return _get_base_type_priority (NM_SETTING_GET_CLASS (setting)->setting_info,
+	                                G_OBJECT_TYPE (setting));
 }
 
 /**
@@ -218,17 +148,15 @@ _nm_setting_get_base_type_priority (NMSetting *setting)
 GType
 nm_setting_lookup_type (const char *name)
 {
-	const SettingInfo *info;
+	const NMMetaSettingInfo *setting_info;
 
 	g_return_val_if_fail (name, G_TYPE_INVALID);
 
-	_ensure_registered ();
-
-	info = g_hash_table_lookup (registered_settings, name);
-	return info ? info->type : G_TYPE_INVALID;
+	setting_info = nm_meta_setting_infos_by_name (name);
+	return setting_info ? setting_info->get_setting_gtype () : G_TYPE_INVALID;
 }
 
-gint
+int
 _nm_setting_compare_priority (gconstpointer a, gconstpointer b)
 {
 	NMSettingPriority prio_a, prio_b;
@@ -273,90 +201,53 @@ _nm_setting_slave_type_is_valid (const char *slave_type, const char **out_port_t
 
 /*****************************************************************************/
 
-typedef struct {
-	const char *name;
-	GParamSpec *param_spec;
-	const GVariantType *dbus_type;
-
-	NMSettingPropertyGetFunc get_func;
-	NMSettingPropertySynthFunc synth_func;
-	NMSettingPropertySetFunc set_func;
-	NMSettingPropertyNotSetFunc not_set_func;
-
-	NMSettingPropertyTransformToFunc to_dbus;
-	NMSettingPropertyTransformFromFunc from_dbus;
-} NMSettingProperty;
-
-static NM_CACHED_QUARK_FCN ("nm-setting-property-overrides", setting_property_overrides_quark)
-static NM_CACHED_QUARK_FCN ("nm-setting-properties", setting_properties_quark)
-
-static NMSettingProperty *
-find_property (GArray *properties, const char *name)
+static const NMSettInfoProperty *
+_nm_sett_info_property_find_in_array (const NMSettInfoProperty *properties, guint len, const char *name)
 {
-	NMSettingProperty *property;
-	int i;
-
-	if (!properties)
-		return NULL;
+	guint i;
 
-	for (i = 0; i < properties->len; i++) {
-		property = &g_array_index (properties, NMSettingProperty, i);
-		if (strcmp (name, property->name) == 0)
-			return property;
+	for (i = 0; i < len; i++) {
+		if (nm_streq (name, properties[i].name))
+			return &properties[i];
 	}
-
 	return NULL;
 }
 
-static void
-add_property_override (NMSettingClass *setting_class,
-                       const char *property_name,
-                       GParamSpec *param_spec,
-                       const GVariantType *dbus_type,
-                       NMSettingPropertyGetFunc get_func,
-                       NMSettingPropertySynthFunc synth_func,
-                       NMSettingPropertySetFunc set_func,
-                       NMSettingPropertyNotSetFunc not_set_func,
-                       NMSettingPropertyTransformToFunc to_dbus,
-                       NMSettingPropertyTransformFromFunc from_dbus)
+void
+_properties_override_add_struct (GArray *properties_override,
+                                 const NMSettInfoProperty *prop_info)
 {
-	GType setting_type = G_TYPE_FROM_CLASS (setting_class);
-	GArray *overrides;
-	NMSettingProperty override;
-
-	g_return_if_fail (g_type_get_qdata (setting_type, setting_properties_quark ()) == NULL);
-
-	memset (&override, 0, sizeof (override));
-	override.name = property_name;
-	override.param_spec = param_spec;
-	override.dbus_type = dbus_type;
-	override.get_func = get_func;
-	override.synth_func = synth_func;
-	override.set_func = set_func;
-	override.not_set_func = not_set_func;
-	override.to_dbus = to_dbus;
-	override.from_dbus = from_dbus;
-
-	overrides = g_type_get_qdata (setting_type, setting_property_overrides_quark ());
-	if (!overrides) {
-		overrides = g_array_new (FALSE, FALSE, sizeof (NMSettingProperty));
-		g_type_set_qdata (setting_type, setting_property_overrides_quark (), overrides);
+	nm_assert (properties_override);
+	nm_assert (prop_info);
+	nm_assert (prop_info->name || prop_info->param_spec);
+	nm_assert (!prop_info->param_spec || !prop_info->name || nm_streq0 (prop_info->name, prop_info->param_spec->name));
+	nm_assert (!_nm_sett_info_property_find_in_array ((NMSettInfoProperty *) properties_override->data,
+	                                                  properties_override->len,
+	                                                  prop_info->name ?: prop_info->param_spec->name));
+
+	nm_assert (!prop_info->from_dbus || prop_info->dbus_type);
+	nm_assert (!prop_info->set_func || prop_info->dbus_type);
+
+	g_array_append_vals (properties_override, prop_info, 1);
+
+	if (!prop_info->name) {
+		/* for convenience, allow omitting "name" if "param_spec" is given. */
+		g_array_index (properties_override,
+		               NMSettInfoProperty,
+		               properties_override->len - 1).name = prop_info->param_spec->name;
 	}
-	g_return_if_fail (find_property (overrides, property_name) == NULL);
-
-	g_array_append_val (overrides, override);
 }
 
 /**
- * _nm_setting_class_add_dbus_only_property:
- * @setting_class: the setting class
+ * _properties_override_add_dbus_only:
+ * @properties_override: an array collecting the overrides
  * @property_name: the name of the property to override
  * @dbus_type: the type of the property (in its D-Bus representation)
  * @synth_func: (allow-none): function to call to synthesize a value for the property
  * @set_func: (allow-none): function to call to set the value of the property
  *
  * Registers a property named @property_name, which will be used in the D-Bus
- * serialization of objects of @setting_class, but which does not correspond to
+ * serialization of objects of this setting type, but which does not correspond to
  * a #GObject property.
  *
  * When serializing a setting to D-Bus, @synth_func will be called to synthesize
@@ -369,35 +260,30 @@ add_property_override (NMSettingClass *setting_class,
  * then the property will be ignored when deserializing.)
  */
 void
-_nm_setting_class_add_dbus_only_property (NMSettingClass *setting_class,
-                                          const char *property_name,
-                                          const GVariantType *dbus_type,
-                                          NMSettingPropertySynthFunc synth_func,
-                                          NMSettingPropertySetFunc set_func)
+_properties_override_add_dbus_only (GArray *properties_override,
+                                    const char *property_name,
+                                    const GVariantType *dbus_type,
+                                    NMSettingPropertySynthFunc synth_func,
+                                    NMSettingPropertySetFunc set_func)
 {
-	g_return_if_fail (NM_IS_SETTING_CLASS (setting_class));
-	g_return_if_fail (property_name != NULL);
-
-	/* Must not match any GObject property. */
-	g_return_if_fail (!g_object_class_find_property (G_OBJECT_CLASS (setting_class), property_name));
-
-	add_property_override (setting_class,
-	                       property_name, NULL, dbus_type,
-	                       NULL, synth_func, set_func, NULL,
-	                       NULL, NULL);
+	_properties_override_add (properties_override,
+	                          .name = property_name,
+	                          .dbus_type = dbus_type,
+	                          .synth_func = synth_func,
+	                          .set_func = set_func);
 }
 
 /**
- * _nm_setting_class_override_property:
- * @setting_class: the setting class
- * @property_name: the name of the property to override
+ * _properties_override_add_override:
+ * @properties_override: an array collecting the overrides
+ * @param_spec: the name of the property to override
  * @dbus_type: the type of the property (in its D-Bus representation)
  * @get_func: (allow-none): function to call to get the value of the property
  * @set_func: (allow-none): function to call to set the value of the property
  * @not_set_func: (allow-none): function to call to indicate the property was not set
  *
- * Overrides the D-Bus representation of the #GObject property named
- * @property_name on @setting_class.
+ * Overrides the D-Bus representation of the #GObject property that shares the
+ * same name as @param_spec.
  *
  * When serializing a setting to D-Bus, if @get_func is non-%NULL, then it will
  * be called to get the property's value. If it returns a #GVariant, the
@@ -406,38 +292,38 @@ _nm_setting_class_add_dbus_only_property (NMSettingClass *setting_class,
  * with g_object_get_property(), and added to the hash if it is not the default
  * value.)
  *
- * When deserializing a D-Bus representation into a setting, if @property_name
- * is present, then @set_func will be called to set it. (If @set_func is %NULL
- * then the property will be set normally with g_object_set_property().)
+ * When deserializing a D-Bus representation into a setting, if a value with
+ * the name of @param_spec is present, then @set_func will be called to set it.
+ * (If @set_func is %NULL then the property will be set normally with
+ * g_object_set_property().)
  *
  * If @not_set_func is non-%NULL, then it will be called when deserializing a
- * representation that does NOT contain @property_name. This can be used, eg, if
- * a new property needs to be initialized from some older deprecated property
+ * representation that does NOT contain a value for the property. This can be used,
+ * eg, if a new property needs to be initialized from some older deprecated property
  * when it is not present.
  */
 void
-_nm_setting_class_override_property (NMSettingClass *setting_class,
-                                     const char *property_name,
-                                     const GVariantType *dbus_type,
-                                     NMSettingPropertyGetFunc get_func,
-                                     NMSettingPropertySetFunc set_func,
-                                     NMSettingPropertyNotSetFunc not_set_func)
+_properties_override_add_override (GArray *properties_override,
+                                   GParamSpec *param_spec,
+                                   const GVariantType *dbus_type,
+                                   NMSettingPropertyGetFunc get_func,
+                                   NMSettingPropertySetFunc set_func,
+                                   NMSettingPropertyNotSetFunc not_set_func)
 {
-	GParamSpec *param_spec;
-
-	param_spec = g_object_class_find_property (G_OBJECT_CLASS (setting_class), property_name);
-	g_return_if_fail (param_spec != NULL);
-
-	add_property_override (setting_class,
-	                       property_name, param_spec, dbus_type,
-	                       get_func, NULL, set_func, not_set_func,
-	                       NULL, NULL);
+	nm_assert (param_spec);
+
+	_properties_override_add (properties_override,
+	                          .param_spec = param_spec,
+	                          .dbus_type = dbus_type,
+	                          .get_func = get_func,
+	                          .set_func = set_func,
+	                          .not_set_func = not_set_func);
 }
 
 /**
- * _nm_setting_class_transform_property:
- * @setting_class: the setting class
- * @property: the name of the property to transform
+ * _properties_override_add_transform:
+ * @properties_override: an array collecting the overrides
+ * @param_spec: the param spec of the property to transform.
  * @dbus_type: the type of the property (in its D-Bus representation)
  * @to_dbus: function to convert from object to D-Bus format
  * @from_dbus: function to convert from D-Bus to object format
@@ -451,23 +337,143 @@ _nm_setting_class_override_property (NMSettingClass *setting_class,
  * nm_property_compare() recognizes, as long as it recognizes @dbus_type.
  */
 void
-_nm_setting_class_transform_property (NMSettingClass *setting_class,
-                                      const char *property,
-                                      const GVariantType *dbus_type,
-                                      NMSettingPropertyTransformToFunc to_dbus,
-                                      NMSettingPropertyTransformFromFunc from_dbus)
+_properties_override_add_transform (GArray *properties_override,
+                                    GParamSpec *param_spec,
+                                    const GVariantType *dbus_type,
+                                    NMSettingPropertyTransformToFunc to_dbus,
+                                    NMSettingPropertyTransformFromFunc from_dbus)
+{
+	nm_assert (param_spec);
+
+	_properties_override_add (properties_override,
+	                          .param_spec = param_spec,
+	                          .dbus_type = dbus_type,
+	                          .to_dbus = to_dbus,
+	                          .from_dbus = from_dbus);
+}
+
+static NMSettInfoSetting _sett_info_settings[_NM_META_SETTING_TYPE_NUM];
+
+void
+_nm_setting_class_commit_full (NMSettingClass *setting_class,
+                               NMMetaSettingType meta_type,
+                               const NMSettInfoSettDetail *detail,
+                               GArray *properties_override)
+{
+	NMSettInfoSetting *sett_info;
+	gs_free GParamSpec **property_specs = NULL;
+	guint i, n_property_specs, override_len;
+
+	nm_assert (NM_IS_SETTING_CLASS (setting_class));
+	nm_assert (!setting_class->setting_info);
+
+	nm_assert (meta_type < G_N_ELEMENTS (_sett_info_settings));
+
+	sett_info = &_sett_info_settings[meta_type];
+
+	nm_assert (!sett_info->setting_class);
+	nm_assert (!sett_info->property_infos_len);
+	nm_assert (!sett_info->property_infos);
+
+	if (!properties_override) {
+		override_len = 0;
+		properties_override = _nm_sett_info_property_override_create_array ();
+	} else
+		override_len = properties_override->len;
+
+	property_specs = g_object_class_list_properties (G_OBJECT_CLASS (setting_class),
+	                                                 &n_property_specs);
+
+#if NM_MORE_ASSERTS > 10
+	/* assert that properties_override is constructed consistently. */
+	for (i = 0; i < override_len; i++) {
+		guint j;
+		const NMSettInfoProperty *p = &g_array_index (properties_override, NMSettInfoProperty, i);
+
+		nm_assert (!_nm_sett_info_property_find_in_array ((NMSettInfoProperty *) properties_override->data,
+		                                                  i,
+		                                                  p->name));
+		for (j = 0; j < n_property_specs; j++) {
+			if (nm_streq (property_specs[j]->name, p->name)) {
+				nm_assert (p->param_spec == property_specs[j]);
+				break;
+			}
+		}
+		nm_assert ((j == n_property_specs) == (p->param_spec == NULL));
+	}
+#endif
+
+	for (i = 0; i < n_property_specs; i++) {
+		const char *name = property_specs[i]->name;
+		NMSettInfoProperty *p;
+
+		if (_nm_sett_info_property_find_in_array ((NMSettInfoProperty *) properties_override->data,
+		                                           override_len,
+		                                           name))
+			continue;
+
+		g_array_set_size (properties_override, properties_override->len + 1);
+		p = &g_array_index (properties_override, NMSettInfoProperty, properties_override->len - 1);
+		memset (p, 0, sizeof (*p));
+		p->name = name;
+		p->param_spec = property_specs[i];
+	}
+
+	G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMSettInfoProperty, name) == 0);
+	g_array_sort (properties_override, nm_strcmp_p);
+
+	setting_class->setting_info = &nm_meta_setting_infos[meta_type];
+	sett_info->setting_class = setting_class;
+	if (detail)
+		sett_info->detail = *detail;
+	sett_info->property_infos_len = properties_override->len;
+	sett_info->property_infos = (const NMSettInfoProperty *) g_array_free (properties_override,
+	                                                                       properties_override->len == 0);
+}
+
+const NMSettInfoSetting *
+_nm_sett_info_setting_get (NMSettingClass *setting_class)
 {
-	GParamSpec *param_spec;
+	if (   NM_IS_SETTING_CLASS (setting_class)
+	    && setting_class->setting_info) {
+		nm_assert (setting_class->setting_info->meta_type < G_N_ELEMENTS (_sett_info_settings));
+		return &_sett_info_settings[setting_class->setting_info->meta_type];
+	}
+	return NULL;
+}
+
+const NMSettInfoProperty *
+_nm_sett_info_property_get (NMSettingClass *setting_class,
+                            const char *property_name)
+{
+	const NMSettInfoSetting *sett_info = _nm_sett_info_setting_get (setting_class);
+	const NMSettInfoProperty *property;
+	gssize idx;
+
+	if (!sett_info)
+		return NULL;
+
+	G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMSettInfoProperty, name) == 0);
+	idx = nm_utils_array_find_binary_search (sett_info->property_infos,
+	                                         sizeof (NMSettInfoProperty),
+	                                         sett_info->property_infos_len,
+	                                         &property_name,
+	                                         nm_strcmp_p_with_data,
+	                                         NULL);
+
+	if (idx < 0)
+		return NULL;
+
+	property = &sett_info->property_infos[idx];
 
-	param_spec = g_object_class_find_property (G_OBJECT_CLASS (setting_class), property);
-	g_return_if_fail (param_spec != NULL);
+	nm_assert (idx == 0 || strcmp (property[-1].name, property[0].name) < 0);
+	nm_assert (idx == sett_info->property_infos_len - 1 || strcmp (property[0].name, property[1].name) < 0);
 
-	add_property_override (setting_class,
-	                       property, param_spec, dbus_type,
-	                       NULL, NULL, NULL, NULL,
-	                       to_dbus, from_dbus);
+	return property;
 }
 
+/*****************************************************************************/
+
 gboolean
 _nm_setting_use_legacy_property (NMSetting *setting,
                                  GVariant *connection_dict,
@@ -504,77 +510,6 @@ _nm_setting_use_legacy_property (NMSetting *setting,
 		return FALSE;
 }
 
-static GArray *
-nm_setting_class_ensure_properties (NMSettingClass *setting_class)
-{
-	GType type = G_TYPE_FROM_CLASS (setting_class), otype;
-	NMSettingProperty property, *override;
-	GArray *overrides, *type_overrides, *properties;
-	GParamSpec **property_specs;
-	guint n_property_specs, i;
-
-	properties = g_type_get_qdata (type, setting_properties_quark ());
-	if (properties)
-		return properties;
-
-	/* Build overrides array from @setting_class and its superclasses */
-	overrides = g_array_new (FALSE, FALSE, sizeof (NMSettingProperty));
-	for (otype = type; otype != G_TYPE_OBJECT; otype = g_type_parent (otype)) {
-		type_overrides = g_type_get_qdata (otype, setting_property_overrides_quark ());
-		if (type_overrides)
-			g_array_append_vals (overrides, (NMSettingProperty *)type_overrides->data, type_overrides->len);
-	}
-
-	/* Build the properties array from the GParamSpecs, obeying overrides */
-	properties = g_array_new (FALSE, FALSE, sizeof (NMSettingProperty));
-
-	property_specs = g_object_class_list_properties (G_OBJECT_CLASS (setting_class),
-	                                                 &n_property_specs);
-	for (i = 0; i < n_property_specs; i++) {
-		override = find_property (overrides, property_specs[i]->name);
-		if (override)
-			property = *override;
-		else {
-			memset (&property, 0, sizeof (property));
-			property.name = property_specs[i]->name;
-			property.param_spec = property_specs[i];
-		}
-		g_array_append_val (properties, property);
-	}
-	g_free (property_specs);
-
-	/* Add any remaining overrides not corresponding to GObject properties */
-	for (i = 0; i < overrides->len; i++) {
-		override = &g_array_index (overrides, NMSettingProperty, i);
-		if (!g_object_class_find_property (G_OBJECT_CLASS (setting_class), override->name))
-			g_array_append_val (properties, *override);
-	}
-	g_array_unref (overrides);
-
-	g_type_set_qdata (type, setting_properties_quark (), properties);
-	return properties;
-}
-
-static const NMSettingProperty *
-nm_setting_class_get_properties (NMSettingClass *setting_class, guint *n_properties)
-{
-	GArray *properties;
-
-	properties = nm_setting_class_ensure_properties (setting_class);
-
-	*n_properties = properties->len;
-	return (NMSettingProperty *) properties->data;
-}
-
-static const NMSettingProperty *
-nm_setting_class_find_property (NMSettingClass *setting_class, const char *property_name)
-{
-	GArray *properties;
-
-	properties = nm_setting_class_ensure_properties (setting_class);
-	return find_property (properties, property_name);
-}
-
 /*****************************************************************************/
 
 static const GVariantType *
@@ -610,7 +545,7 @@ variant_type_for_gtype (GType type)
 
 static GVariant *
 get_property_for_dbus (NMSetting *setting,
-                       const NMSettingProperty *property,
+                       const NMSettInfoProperty *property,
                        gboolean ignore_default)
 {
 	GValue prop_value = { 0, };
@@ -638,7 +573,7 @@ get_property_for_dbus (NMSetting *setting,
 	else if (g_type_is_a (prop_value.g_type, G_TYPE_FLAGS))
 		dbus_value = g_variant_new_uint32 (g_value_get_flags (&prop_value));
 	else if (prop_value.g_type == G_TYPE_BYTES)
-		dbus_value = _nm_utils_bytes_to_dbus (&prop_value);
+		dbus_value = nm_utils_gbytes_to_variant_ay (g_value_get_boxed (&prop_value));
 	else
 		dbus_value = g_dbus_gvalue_to_gvariant (&prop_value, variant_type_for_gtype (prop_value.g_type));
 	g_value_unset (&prop_value);
@@ -647,7 +582,7 @@ get_property_for_dbus (NMSetting *setting,
 }
 
 static gboolean
-set_property_from_dbus (const NMSettingProperty *property,
+set_property_from_dbus (const NMSettInfoProperty *property,
                         GVariant *src_value,
                         GValue *dst_value)
 {
@@ -698,40 +633,57 @@ set_property_from_dbus (const NMSettingProperty *property,
 GVariant *
 _nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionSerializationFlags flags)
 {
+	NMSettingPrivate *priv;
 	GVariantBuilder builder;
 	GVariant *dbus_value;
-	const NMSettingProperty *properties;
+	const NMSettInfoSetting *sett_info;
 	guint n_properties, i;
+	const char *const*gendata_keys;
 
 	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
 
-	properties = nm_setting_class_get_properties (NM_SETTING_GET_CLASS (setting), &n_properties);
+	priv = NM_SETTING_GET_PRIVATE (setting);
 
 	g_variant_builder_init (&builder, NM_VARIANT_TYPE_SETTING);
 
+	n_properties = _nm_setting_gendata_get_all (setting, &gendata_keys, NULL);
 	for (i = 0; i < n_properties; i++) {
-		const NMSettingProperty *property = &properties[i];
+		g_variant_builder_add (&builder,
+		                       "{sv}",
+		                       gendata_keys[i],
+		                       g_hash_table_lookup (priv->gendata->hash, gendata_keys[i]));
+	}
+
+	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+	for (i = 0; i < sett_info->property_infos_len; i++) {
+		const NMSettInfoProperty *property = &sett_info->property_infos[i];
 		GParamSpec *prop_spec = property->param_spec;
 
-		if (!prop_spec && !property->synth_func) {
-			/* D-Bus-only property with no synth_func, so we skip it. */
-			continue;
-		}
+		if (!prop_spec) {
+			if (!property->synth_func)
+				continue;
 
-		if (prop_spec && !(prop_spec->flags & G_PARAM_WRITABLE))
-			continue;
+			if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+				continue;
+		} else {
+			if (!(prop_spec->flags & G_PARAM_WRITABLE))
+				continue;
 
-		if (   prop_spec && (prop_spec->flags & NM_SETTING_PARAM_LEGACY)
-		    && !_nm_utils_is_manager_process)
-			continue;
+			if (NM_FLAGS_ANY (prop_spec->flags, NM_SETTING_PARAM_GENDATA_BACKED))
+				continue;
 
-		if (   (flags & NM_CONNECTION_SERIALIZE_NO_SECRETS)
-		    && (prop_spec && (prop_spec->flags & NM_SETTING_PARAM_SECRET)))
-			continue;
+			if (   (prop_spec->flags & NM_SETTING_PARAM_LEGACY)
+			    && !_nm_utils_is_manager_process)
+				continue;
 
-		if (   (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
-		    && !(prop_spec && (prop_spec->flags & NM_SETTING_PARAM_SECRET)))
-			continue;
+			if (   (flags & NM_CONNECTION_SERIALIZE_NO_SECRETS)
+			    && (prop_spec->flags & NM_SETTING_PARAM_SECRET))
+				continue;
+
+			if (   (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+			    && !(prop_spec->flags & NM_SETTING_PARAM_SECRET))
+				continue;
+		}
 
 		if (property->synth_func)
 			dbus_value = property->synth_func (setting, connection, property->name);
@@ -778,8 +730,8 @@ _nm_setting_new_from_dbus (GType setting_type,
 {
 	gs_unref_object NMSetting *setting = NULL;
 	gs_unref_hashtable GHashTable *keys = NULL;
-	const NMSettingProperty *properties;
-	guint i, n_properties;
+	const NMSettInfoSetting *sett_info;
+	guint i;
 
 	g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL);
 	g_return_val_if_fail (g_variant_is_of_type (setting_dict, NM_VARIANT_TYPE_SETTING), NULL);
@@ -823,9 +775,29 @@ _nm_setting_new_from_dbus (GType setting_type,
 		}
 	}
 
-	properties = nm_setting_class_get_properties (NM_SETTING_GET_CLASS (setting), &n_properties);
-	for (i = 0; i < n_properties; i++) {
-		const NMSettingProperty *property = &properties[i];
+	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+
+	if (sett_info->detail.gendata_info) {
+		GHashTable *hash;
+		GVariantIter iter;
+		char *key;
+		GVariant *val;
+
+		hash = _gendata_hash (setting, TRUE)->hash;
+
+		g_variant_iter_init (&iter, setting_dict);
+		while (g_variant_iter_next (&iter, "{sv}", &key, &val)) {
+			g_hash_table_insert (hash,
+			                     key,
+			                     val);
+		}
+
+		_nm_setting_gendata_notify (setting, TRUE);
+		return g_steal_pointer (&setting);
+	}
+
+	for (i = 0; i < sett_info->property_infos_len; i++) {
+		const NMSettInfoProperty *property = &sett_info->property_infos[i];
 		gs_unref_variant GVariant *value = NULL;
 		gs_free_error GError *local = NULL;
 
@@ -944,12 +916,12 @@ const GVariantType *
 nm_setting_get_dbus_property_type (NMSetting *setting,
                                    const char *property_name)
 {
-	const NMSettingProperty *property;
+	const NMSettInfoProperty *property;
 
 	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
 	g_return_val_if_fail (property_name != NULL, NULL);
 
-	property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), property_name);
+	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), property_name);
 	g_return_val_if_fail (property != NULL, NULL);
 
 	if (property->dbus_type)
@@ -961,14 +933,32 @@ nm_setting_get_dbus_property_type (NMSetting *setting,
 gboolean
 _nm_setting_get_property (NMSetting *setting, const char *property_name, GValue *value)
 {
+	const NMSettInfoSetting *sett_info;
 	GParamSpec *prop_spec;
 
 	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
 	g_return_val_if_fail (property_name, FALSE);
 	g_return_val_if_fail (value, FALSE);
 
-	prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
+	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+
+	if (sett_info->detail.gendata_info) {
+		GVariant *variant;
+		GenData *gendata = _gendata_hash (setting, FALSE);
+
+		variant = gendata ? g_hash_table_lookup (gendata->hash, property_name) : NULL;
+
+		if (!variant) {
+			g_value_unset (value);
+			return FALSE;
+		}
+
+		g_value_init (value, G_TYPE_VARIANT);
+		g_value_set_variant (value, variant);
+		return TRUE;
+	}
 
+	prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
 	if (!prop_spec) {
 		g_value_unset (value);
 		return FALSE;
@@ -1002,16 +992,37 @@ duplicate_setting (NMSetting *setting,
 NMSetting *
 nm_setting_duplicate (NMSetting *setting)
 {
+	const NMSettInfoSetting *sett_info;
 	GObject *dup;
 
 	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
 
 	dup = g_object_new (G_OBJECT_TYPE (setting), NULL);
 
-	g_object_freeze_notify (dup);
-	nm_setting_enumerate_values (setting, duplicate_setting, dup);
-	g_object_thaw_notify (dup);
+	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+
+	if (sett_info->detail.gendata_info) {
+		GenData *gendata = _gendata_hash (setting, FALSE);
+
+		if (   gendata
+		    && g_hash_table_size (gendata->hash) > 0) {
+			GHashTableIter iter;
+			GHashTable *h = _gendata_hash (NM_SETTING (dup), TRUE)->hash;
+			const char *key;
+			GVariant *val;
 
+			g_hash_table_iter_init (&iter, gendata->hash);
+			while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
+				g_hash_table_insert (h,
+				                     g_strdup (key),
+				                     g_variant_ref (val));
+			}
+		}
+	} else {
+		g_object_freeze_notify (dup);
+		nm_setting_enumerate_values (setting, duplicate_setting, dup);
+		g_object_thaw_notify (dup);
+	}
 	return NM_SETTING (dup);
 }
 
@@ -1027,12 +1038,12 @@ nm_setting_duplicate (NMSetting *setting)
 const char *
 nm_setting_get_name (NMSetting *setting)
 {
-	NMSettingPrivate *priv;
+	const NMMetaSettingInfo *setting_info;
 
 	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
-	priv = NM_SETTING_GET_PRIVATE (setting);
-	_ensure_setting_info (setting, priv);
-	return priv->info->name;
+
+	setting_info = NM_SETTING_GET_CLASS (setting)->setting_info;
+	return setting_info ? setting_info->setting_name : NULL;
 }
 
 /**
@@ -1126,7 +1137,7 @@ compare_property (NMSetting *setting,
                   const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	const NMSettingProperty *property;
+	const NMSettInfoProperty *property;
 	GVariant *value1, *value2;
 	int cmp;
 
@@ -1158,7 +1169,7 @@ compare_property (NMSetting *setting,
 			return TRUE;
 	}
 
-	property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), prop_spec->name);
+	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), prop_spec->name);
 	g_return_val_if_fail (property != NULL, FALSE);
 
 	value1 = get_property_for_dbus (setting, property, TRUE);
@@ -1191,9 +1202,10 @@ nm_setting_compare (NMSetting *a,
                     NMSetting *b,
                     NMSettingCompareFlags flags)
 {
+	const NMSettInfoSetting *sett_info;
 	GParamSpec **property_specs;
 	guint n_property_specs;
-	gint same = TRUE;
+	int same = TRUE;
 	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
@@ -1203,6 +1215,18 @@ nm_setting_compare (NMSetting *a,
 	if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
 		return FALSE;
 
+	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a));
+
+	if (sett_info->detail.gendata_info) {
+		GenData *a_gendata = _gendata_hash (a, FALSE);
+		GenData *b_gendata = _gendata_hash (b, FALSE);
+
+		return nm_utils_hash_table_equal (a_gendata ? a_gendata->hash : NULL,
+		                                  b_gendata ? b_gendata->hash : NULL,
+		                                  TRUE,
+		                                  g_variant_equal);
+	}
+
 	/* And now all properties */
 	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
 	for (i = 0; i < n_property_specs && same; i++) {
@@ -1288,6 +1312,21 @@ should_compare_prop (NMSetting *setting,
 	return TRUE;
 }
 
+static void
+_setting_diff_add_result (GHashTable *results, const char *prop_name, NMSettingDiffResult r)
+{
+	void *p;
+
+	if (r == NM_SETTING_DIFF_RESULT_UNKNOWN)
+		return;
+
+	if (g_hash_table_lookup_extended (results, prop_name, NULL, &p)) {
+		if (!NM_FLAGS_ALL ((guint) r, GPOINTER_TO_UINT (p)))
+			g_hash_table_insert (results, g_strdup (prop_name), GUINT_TO_POINTER (((guint) r) | GPOINTER_TO_UINT (p)));
+	} else
+		g_hash_table_insert (results, g_strdup (prop_name), GUINT_TO_POINTER (r));
+}
+
 /**
  * nm_setting_diff:
  * @a: a #NMSetting
@@ -1316,8 +1355,7 @@ nm_setting_diff (NMSetting *a,
                  gboolean invert_results,
                  GHashTable **results)
 {
-	GParamSpec **property_specs;
-	guint n_property_specs;
+	const NMSettInfoSetting *sett_info;
 	guint i;
 	NMSettingDiffResult a_result = NM_SETTING_DIFF_RESULT_IN_A;
 	NMSettingDiffResult b_result = NM_SETTING_DIFF_RESULT_IN_B;
@@ -1361,78 +1399,117 @@ nm_setting_diff (NMSetting *a,
 		results_created = TRUE;
 	}
 
-	/* And now all properties */
-	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
+	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a));
 
-	for (i = 0; i < n_property_specs; i++) {
-		GParamSpec *prop_spec = property_specs[i];
-		NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN;
-
-		/* Handle compare flags */
-		if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags))
-			continue;
-		if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
-			continue;
-
-		compared_any = TRUE;
-
-		if (b) {
-			gboolean different;
-
-			different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
-			if (different) {
-				gboolean a_is_default, b_is_default;
+	if (sett_info->detail.gendata_info) {
+		const char *key;
+		GVariant *val, *val2;
+		GHashTableIter iter;
+		GenData *a_gendata = _gendata_hash (a, FALSE);
+		GenData *b_gendata = b ? _gendata_hash (b, FALSE) : NULL;
+
+		if (!a_gendata || !b_gendata) {
+			if (a_gendata || b_gendata) {
+				NMSettingDiffResult one_sided_result;
+
+				one_sided_result = a_gendata ? a_result : b_result;
+				g_hash_table_iter_init (&iter, a_gendata ? a_gendata->hash : b_gendata->hash);
+				while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
+					diff_found = TRUE;
+					_setting_diff_add_result (*results, key, one_sided_result);
+				}
+			}
+		} else {
+			g_hash_table_iter_init (&iter, a_gendata->hash);
+			while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
+				val2 = b_gendata ? g_hash_table_lookup (b_gendata->hash, key) : NULL;
+				compared_any = TRUE;
+				if (   !val2
+				    || !g_variant_equal (val, val2)) {
+					diff_found = TRUE;
+					_setting_diff_add_result (*results, key, a_result);
+				}
+			}
+			g_hash_table_iter_init (&iter, b_gendata->hash);
+			while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
+				val2 = a_gendata ? g_hash_table_lookup (a_gendata->hash, key) : NULL;
+				compared_any = TRUE;
+				if (   !val2
+				    || !g_variant_equal (val, val2)) {
+					diff_found = TRUE;
+					_setting_diff_add_result (*results, key, b_result);
+				}
+			}
+		}
+	} else {
+		gs_free GParamSpec **property_specs = NULL;
+		guint n_property_specs;
+
+		property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
+
+		for (i = 0; i < n_property_specs; i++) {
+			GParamSpec *prop_spec = property_specs[i];
+			NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN;
+
+			/* Handle compare flags */
+			if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags))
+				continue;
+			if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
+				continue;
+
+			compared_any = TRUE;
+
+			if (b) {
+				gboolean different;
+
+				different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
+				if (different) {
+					gboolean a_is_default, b_is_default;
+					GValue value = G_VALUE_INIT;
+
+					g_value_init (&value, prop_spec->value_type);
+					g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
+					a_is_default = g_param_value_defaults (prop_spec, &value);
+
+					g_value_reset (&value);
+					g_object_get_property (G_OBJECT (b), prop_spec->name, &value);
+					b_is_default = g_param_value_defaults (prop_spec, &value);
+
+					g_value_unset (&value);
+					if ((flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT) == 0) {
+						if (!a_is_default)
+							r |= a_result;
+						if (!b_is_default)
+							r |= b_result;
+					} else {
+						r |= a_result | b_result;
+						if (a_is_default)
+							r |= a_result_default;
+						if (b_is_default)
+							r |= b_result_default;
+					}
+				}
+			} else if ((flags & (NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) == 0)
+				r = a_result;  /* only in A */
+			else {
 				GValue value = G_VALUE_INIT;
 
 				g_value_init (&value, prop_spec->value_type);
 				g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
-				a_is_default = g_param_value_defaults (prop_spec, &value);
-
-				g_value_reset (&value);
-				g_object_get_property (G_OBJECT (b), prop_spec->name, &value);
-				b_is_default = g_param_value_defaults (prop_spec, &value);
+				if (!g_param_value_defaults (prop_spec, &value))
+					r |= a_result;
+				else if (flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT)
+					r |= a_result | a_result_default;
 
 				g_value_unset (&value);
-				if ((flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT) == 0) {
-					if (!a_is_default)
-						r |= a_result;
-					if (!b_is_default)
-						r |= b_result;
-				} else {
-					r |= a_result | b_result;
-					if (a_is_default)
-						r |= a_result_default;
-					if (b_is_default)
-						r |= b_result_default;
-				}
 			}
-		} else if ((flags & (NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) == 0)
-			r = a_result;  /* only in A */
-		else {
-			GValue value = G_VALUE_INIT;
 
-			g_value_init (&value, prop_spec->value_type);
-			g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
-			if (!g_param_value_defaults (prop_spec, &value))
-				r |= a_result;
-			else if (flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT)
-				r |= a_result | a_result_default;
-
-			g_value_unset (&value);
-		}
-
-		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)));
-			} else
-				g_hash_table_insert (*results, g_strdup (prop_spec->name), GUINT_TO_POINTER (r));
+			if (r != NM_SETTING_DIFF_RESULT_UNKNOWN) {
+				diff_found = TRUE;
+				_setting_diff_add_result (*results, prop_spec->name, r);
+			}
 		}
 	}
-	g_free (property_specs);
 
 	if (!compared_any && !b) {
 		/* special case: the setting has no properties, and the opposite
@@ -1443,7 +1520,7 @@ nm_setting_diff (NMSetting *a,
 
 	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. */
+		 * have allocated a new @results hash, and return it to the caller. */
 		return FALSE;
 	} else {
 		if (results_created) {
@@ -1499,23 +1576,57 @@ nm_setting_enumerate_values (NMSetting *setting,
                              NMSettingValueIterFn func,
                              gpointer user_data)
 {
+	const NMSettInfoSetting *sett_info;
 	GParamSpec **property_specs;
-	guint n_property_specs;
-	int i;
+	guint n_properties;
+	guint i;
 	GType type;
 
 	g_return_if_fail (NM_IS_SETTING (setting));
 	g_return_if_fail (func != NULL);
 
-	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+
+	if (sett_info->detail.gendata_info) {
+		const char *const*names;
+
+		/* the properties of this setting are not real GObject properties.
+		 * Hence, this API makes little sense (or does it?). Still, call
+		 * @func with each value. */
+		n_properties = _nm_setting_gendata_get_all (setting, &names, NULL);
+		if (n_properties > 0) {
+			gs_strfreev char **keys = g_strdupv ((char **) names);
+			GHashTable *h = _gendata_hash (setting, FALSE)->hash;
+
+			for (i = 0; i < n_properties; i++) {
+				GValue value = G_VALUE_INIT;
+				GVariant *val = g_hash_table_lookup (h, keys[i]);
+
+				if (!val) {
+					/* was deleted in the meantime? Skip */
+					continue;
+				}
+
+				g_value_init (&value, G_TYPE_VARIANT);
+				g_value_set_variant (&value, val);
+				/* call it will GParamFlags 0. It shall indicate that this
+				 * is not a "real" GObject property. */
+				func (setting, keys[i], &value, 0, user_data);
+				g_value_unset (&value);
+			}
+		}
+		return;
+	}
+
+	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_properties);
 
 	/* sort the properties. This has an effect on the order in which keyfile
 	 * prints them. */
 	type = G_OBJECT_TYPE (setting);
-	g_qsort_with_data (property_specs, n_property_specs, sizeof (gpointer),
+	g_qsort_with_data (property_specs, n_properties, sizeof (gpointer),
 	                   (GCompareDataFunc) _enumerate_values_sort, &type);
 
-	for (i = 0; i < n_property_specs; i++) {
+	for (i = 0; i < n_properties; i++) {
 		GParamSpec *prop_spec = property_specs[i];
 		GValue value = G_VALUE_INIT;
 
@@ -1541,7 +1652,7 @@ nm_setting_enumerate_values (NMSetting *setting,
 gboolean
 _nm_setting_clear_secrets (NMSetting *setting)
 {
-	GParamSpec **property_specs;
+	gs_free GParamSpec **property_specs = NULL;
 	guint n_property_specs;
 	guint i;
 	gboolean changed = FALSE;
@@ -1549,7 +1660,6 @@ _nm_setting_clear_secrets (NMSetting *setting)
 	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
 
 	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
-
 	for (i = 0; i < n_property_specs; i++) {
 		GParamSpec *prop_spec = property_specs[i];
 
@@ -1566,9 +1676,6 @@ _nm_setting_clear_secrets (NMSetting *setting)
 			g_value_unset (&value);
 		}
 	}
-
-	g_free (property_specs);
-
 	return changed;
 }
 
@@ -1619,7 +1726,7 @@ _nm_setting_clear_secrets_with_flags (NMSetting *setting,
                                       NMSettingClearSecretsWithFlagsFn func,
                                       gpointer user_data)
 {
-	GParamSpec **property_specs;
+	gs_free GParamSpec **property_specs = NULL;
 	guint n_property_specs;
 	guint i;
 	gboolean changed = FALSE;
@@ -1637,8 +1744,6 @@ _nm_setting_clear_secrets_with_flags (NMSetting *setting,
 			                                                                     user_data);
 		}
 	}
-
-	g_free (property_specs);
 	return changed;
 }
 
@@ -1672,11 +1777,11 @@ _nm_setting_need_secrets (NMSetting *setting)
 static int
 update_one_secret (NMSetting *setting, const char *key, GVariant *value, GError **error)
 {
-	const NMSettingProperty *property;
+	const NMSettInfoProperty *property;
 	GParamSpec *prop_spec;
 	GValue prop_value = { 0, };
 
-	property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), key);
+	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), key);
 	if (!property) {
 		g_set_error_literal (error,
 		                     NM_CONNECTION_ERROR,
@@ -1765,10 +1870,10 @@ _nm_setting_update_secrets (NMSetting *setting, GVariant *secrets, GError **erro
 static gboolean
 is_secret_prop (NMSetting *setting, const char *secret_name, GError **error)
 {
-	const NMSettingProperty *property;
+	const NMSettInfoProperty *property;
 	GParamSpec *pspec;
 
-	property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), secret_name);
+	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), secret_name);
 	if (!property) {
 		g_set_error_literal (error,
 		                     NM_CONNECTION_ERROR,
@@ -1888,59 +1993,40 @@ nm_setting_set_secret_flags (NMSetting *setting,
  * nm_setting_to_string:
  * @setting: the #NMSetting
  *
- * Convert the setting into a string.  For debugging purposes ONLY, should NOT
- * be used for serialization of the setting, or machine-parsed in any way. The
- * output format is not guaranteed to be stable and may change at any time.
+ * Convert the setting (including secrets!) into a string. For debugging
+ * purposes ONLY, should NOT be used for serialization of the setting,
+ * or machine-parsed in any way. The output format is not guaranteed to
+ * be stable and may change at any time.
  *
  * Returns: an allocated string containing a textual representation of the
- * setting's properties and values (including secrets!), which the caller should
+ * setting's properties and values, which the caller should
  * free with g_free()
  **/
 char *
 nm_setting_to_string (NMSetting *setting)
 {
 	GString *string;
-	GParamSpec **property_specs;
-	guint n_property_specs;
-	guint i;
-
-	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
-
-	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+	gs_unref_variant GVariant *variant = NULL;
+	GVariant *child;
+	GVariantIter iter;
 
 	string = g_string_new (nm_setting_get_name (setting));
 	g_string_append_c (string, '\n');
 
-	for (i = 0; i < n_property_specs; i++) {
-		GParamSpec *prop_spec = property_specs[i];
-		GValue value = G_VALUE_INIT;
-		char *value_str;
-		gboolean is_default;
-
-		if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
-			continue;
-
-		g_value_init (&value, prop_spec->value_type);
-		g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
+	variant = _nm_setting_to_dbus (setting, NULL, NM_CONNECTION_SERIALIZE_ALL);
 
-		value_str = g_strdup_value_contents (&value);
-		g_string_append_printf (string, "\t%s : %s", prop_spec->name, value_str);
-		g_free (value_str);
+	g_variant_iter_init (&iter, variant);
+	while ((child = g_variant_iter_next_value (&iter))) {
+		gs_free char *name = NULL;
+		gs_free char *value_str = NULL;
+		gs_unref_variant GVariant *value = NULL;
 
-		is_default = g_param_value_defaults (prop_spec, &value);
-		g_value_unset (&value);
+		g_variant_get (child, "{sv}", &name, &value);
+		value_str = g_variant_print (value, FALSE);
 
-		g_string_append (string, " (");
-		g_string_append_c (string, 's');
-		if (is_default)
-			g_string_append_c (string, 'd');
-		g_string_append_c (string, ')');
-		g_string_append_c (string, '\n');
+		g_string_append_printf (string, "\t%s : %s\n", name, value_str);
 	}
 
-	g_free (property_specs);
-	g_string_append_c (string, '\n');
-
 	return g_string_free (string, FALSE);
 }
 
@@ -1962,17 +2048,257 @@ _nm_setting_get_deprecated_virtual_interface_name (NMSetting *setting,
 
 /*****************************************************************************/
 
-static void
-nm_setting_init (NMSetting *setting)
+static GenData *
+_gendata_hash (NMSetting *setting, gboolean create_if_necessary)
 {
+	NMSettingPrivate *priv;
+
+	nm_assert (NM_IS_SETTING (setting));
+
+	priv = NM_SETTING_GET_PRIVATE (setting);
+
+	if (G_UNLIKELY (!priv->gendata)) {
+		if (!create_if_necessary)
+			return NULL;
+		priv->gendata = g_slice_new (GenData);
+		priv->gendata->hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
+		priv->gendata->names = NULL;
+		priv->gendata->values = NULL;
+	}
+
+	return priv->gendata;
 }
 
-static void
-constructed (GObject *object)
+GHashTable *
+_nm_setting_gendata_hash (NMSetting *setting, gboolean create_if_necessary)
 {
-	_ensure_setting_info (object, NM_SETTING_GET_PRIVATE (object));
+	GenData *gendata;
 
-	G_OBJECT_CLASS (nm_setting_parent_class)->constructed (object);
+	gendata = _gendata_hash (setting, create_if_necessary);
+	return gendata ? gendata->hash : NULL;
+}
+
+void
+_nm_setting_gendata_notify (NMSetting *setting,
+                            gboolean names_changed)
+{
+	GenData *gendata;
+
+	gendata = _gendata_hash (setting, FALSE);
+	if (!gendata)
+		return;
+
+	nm_clear_g_free (&gendata->values);
+
+	if (names_changed) {
+		/* if only the values changed, it's sufficient to invalidate the
+		 * values cache. Otherwise, the names cache must be invalidated too. */
+		nm_clear_g_free (&gendata->names);
+	}
+
+	/* Note, that currently there is now way to notify the subclass when gendata changed.
+	 * gendata is only changed in two situations:
+	 *   1) from within NMSetting itself, for example when creating a NMSetting instance
+	 *      from keyfile or a D-Bus GVariant.
+	 *   2) actively from the subclass itself
+	 * For 2), we don't need the notification, because the subclass knows that something
+	 * changed.
+	 * For 1), we currently don't need the notification either, because all that the subclass
+	 * currently would do, is emit a g_object_notify() signal. However, 1) only happens when
+	 * the setting instance is newly created, at that point, nobody listens to the signal.
+	 *
+	 * If we ever need it, then we would need to call a virtual function to notify the subclass
+	 * that gendata changed. */
+}
+
+GVariant *
+nm_setting_gendata_get (NMSetting *setting,
+                        const char *name)
+{
+	GenData *gendata;
+
+	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+	g_return_val_if_fail (name, NULL);
+
+	gendata = _gendata_hash (setting, FALSE);
+	return gendata ? g_hash_table_lookup (gendata->hash, name) : NULL;
+}
+
+guint
+_nm_setting_gendata_get_all (NMSetting *setting,
+                             const char *const**out_names,
+                             GVariant *const**out_values)
+{
+	GenData *gendata;
+	GHashTable *hash;
+	guint i, len;
+
+	nm_assert (NM_IS_SETTING (setting));
+
+	gendata = _gendata_hash (setting, FALSE);
+	if (!gendata)
+		goto out_zero;
+
+	hash = gendata->hash;
+	len = g_hash_table_size (hash);
+	if (len == 0)
+		goto out_zero;
+
+	if (!out_names && !out_values)
+		return len;
+
+	if (G_UNLIKELY (!gendata->names)) {
+		gendata->names = nm_utils_strdict_get_keys (hash,
+		                                            TRUE,
+		                                            NULL);
+	}
+
+	if (out_values) {
+		if (G_UNLIKELY (!gendata->values)) {
+			gendata->values = g_new (GVariant *, len + 1);
+			for (i = 0; i < len; i++)
+				gendata->values[i] = g_hash_table_lookup (hash, gendata->names[i]);
+			gendata->values[i] = NULL;
+		}
+		*out_values = gendata->values;
+	}
+
+	NM_SET_OUT (out_names, (const char *const*) gendata->names);
+	return len;
+
+out_zero:
+	NM_SET_OUT (out_names, NULL);
+	NM_SET_OUT (out_values, NULL);
+	return 0;
+}
+
+/**
+ * nm_setting_gendata_get_all_names:
+ * @setting: the #NMSetting
+ * @out_len: (allow-none): (out):
+ *
+ * Gives the number of generic data elements and optionally returns all their
+ * key names and values. This API is low level access and unless you know what you
+ * are doing, it might not be what you want.
+ *
+ * Returns: (array length=out_len zero-terminated=1) (transfer none):
+ *   A %NULL terminated array of key names. If no names are present, this returns
+ *   %NULL. The returned array and the names are owned by %NMSetting and might be invalidated
+ *   soon.
+ *
+ * Since: 1.14
+ **/
+const char *const*
+nm_setting_gendata_get_all_names (NMSetting *setting,
+                                  guint *out_len)
+{
+	const char *const*names;
+	guint len;
+
+	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+	len = _nm_setting_gendata_get_all (setting, &names, NULL);
+	NM_SET_OUT (out_len, len);
+	return names;
+}
+
+/**
+ * nm_setting_gendata_get_all_values:
+ * @setting: the #NMSetting
+ *
+ * Gives the number of generic data elements and optionally returns all their
+ * key names and values. This API is low level access and unless you know what you
+ * are doing, it might not be what you want.
+ *
+ * Returns: (array zero-terminated=1) (transfer none):
+ *   A %NULL terminated array of #GVariant. If no data is present, this returns
+ *   %NULL. The returned array and the variants are owned by %NMSetting and might be invalidated
+ *   soon. The sort order of nm_setting_gendata_get_all_names() and nm_setting_gendata_get_all_values()
+ *   is consistent. That means, the nth value has the nth name returned by nm_setting_gendata_get_all_names().
+ *
+ * Since: 1.14
+ **/
+GVariant *const*
+nm_setting_gendata_get_all_values (NMSetting *setting)
+{
+	GVariant *const*values;
+
+	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+	_nm_setting_gendata_get_all (setting, NULL, &values);
+	return values;
+}
+
+void
+_nm_setting_gendata_to_gvalue (NMSetting *setting,
+                                GValue *value)
+{
+	GenData *gendata;
+	GHashTable *new;
+	const char *key;
+	GVariant *val;
+	GHashTableIter iter;
+
+	nm_assert (NM_IS_SETTING (setting));
+	nm_assert (value);
+	nm_assert (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_HASH_TABLE));
+
+	new = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
+
+	gendata = _gendata_hash (setting, FALSE);
+	if (gendata) {
+		g_hash_table_iter_init (&iter, gendata->hash);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val))
+			g_hash_table_insert (new, g_strdup (key), g_variant_ref (val));
+	}
+
+	g_value_take_boxed (value, new);
+}
+
+gboolean
+_nm_setting_gendata_reset_from_hash (NMSetting *setting,
+                                     GHashTable *new)
+{
+	GenData *gendata;
+	GHashTableIter iter;
+	const char *key;
+	GVariant *val;
+	guint num;
+
+	nm_assert (NM_IS_SETTING (setting));
+	nm_assert (new);
+
+	num = new ? g_hash_table_size (new) : 0;
+
+	gendata = _gendata_hash (setting, num > 0);
+
+	if (num == 0) {
+		if (   !gendata
+		    || g_hash_table_size (gendata->hash) == 0)
+			return FALSE;
+
+		g_hash_table_remove_all (gendata->hash);
+		_nm_setting_gendata_notify (setting, TRUE);
+		return TRUE;
+	}
+
+	/* let's not bother to find out whether the new hash has any different
+	 * content the the current gendata. Just replace it. */
+	g_hash_table_remove_all (gendata->hash);
+	if (num > 0) {
+		g_hash_table_iter_init (&iter, new);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val))
+			g_hash_table_insert (gendata->hash, g_strdup (key), g_variant_ref (val));
+	}
+	_nm_setting_gendata_notify (setting, TRUE);
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_init (NMSetting *setting)
+{
 }
 
 static void
@@ -1992,15 +2318,44 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
+finalize (GObject *object)
+{
+	NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object);
+
+	if (priv->gendata) {
+		g_free (priv->gendata->names);
+		g_free (priv->gendata->values);
+		g_hash_table_unref (priv->gendata->hash);
+		g_slice_free (GenData, priv->gendata);
+	}
+
+	G_OBJECT_CLASS (nm_setting_parent_class)->finalize (object);
+}
+
+static void
 nm_setting_class_init (NMSettingClass *setting_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	GModule *self_module;
+	gpointer func;
+
+	/* loading libnm and legacy libraries libnm-util/libnm-glib at the same
+	 * time is not supported. The reason is, that both libraries use the same
+	 * glib type names ("NMSetting"), and glib does not support namespacing
+	 * to allow for that.
+	 *
+	 * Arbitrarily, add a check here, see whether a known symbol from libnm-util
+	 * is present. If it is, it indicates that the process is borked and we
+	 * abort. */
+	self_module = g_module_open (NULL, 0);
+	if (g_module_symbol (self_module, "nm_util_get_private", &func))
+		g_error ("libnm-util symbols detected; Mixing libnm with libnm-util/libnm-glib is not supported");
+	g_module_close (self_module);
 
 	g_type_class_add_private (setting_class, sizeof (NMSettingPrivate));
 
-	/* virtual methods */
-	object_class->constructed  = constructed;
 	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
 
 	setting_class->update_one_secret = update_one_secret;
 	setting_class->get_secret_flags = get_secret_flags;
@@ -2008,14 +2363,12 @@ nm_setting_class_init (NMSettingClass *setting_class)
 	setting_class->compare_property = compare_property;
 	setting_class->clear_secrets_with_flags = clear_secrets_with_flags;
 
-	/* Properties */
-
 	/**
 	 * NMSetting:name:
 	 *
 	 * The setting's name, which uniquely identifies the setting within the
 	 * connection.  Each setting type has a name unique to that type, for
-	 * example "ppp" or "wireless" or "wired".
+	 * example "ppp" or "802-11-wireless" or "802-3-ethernet".
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_NAME,
diff --git a/libnm-core/nm-setting.h b/libnm-core/nm-setting.h
index 47a2fdc1..a7a0f81f 100644
--- a/libnm-core/nm-setting.h
+++ b/libnm-core/nm-setting.h
@@ -168,11 +168,13 @@ typedef gboolean (*NMSettingClearSecretsWithFlagsFn) (NMSetting *setting,
                                                       NMSettingSecretFlags flags,
                                                       gpointer user_data);
 
+struct _NMMetaSettingInfo;
+
 typedef struct {
 	GObjectClass parent;
 
 	/* Virtual functions */
-	gint        (*verify)            (NMSetting     *setting,
+	int         (*verify)            (NMSetting     *setting,
 	                                  NMConnection  *connection,
 	                                  GError       **error);
 
@@ -211,7 +213,10 @@ typedef struct {
 	                                  NMSettingCompareFlags flags);
 
 	/*< private >*/
-	gpointer padding[7];
+	const struct _NMMetaSettingInfo *setting_info;
+
+	/*< private >*/
+	gpointer padding[6];
 } NMSettingClass;
 
 /**
@@ -282,7 +287,8 @@ void        nm_setting_enumerate_values (NMSetting *setting,
 
 char       *nm_setting_to_string      (NMSetting *setting);
 
-/* Secrets */
+/*****************************************************************************/
+
 gboolean    nm_setting_get_secret_flags (NMSetting *setting,
                                          const char *secret_name,
                                          NMSettingSecretFlags *out_flags,
@@ -293,10 +299,13 @@ gboolean    nm_setting_set_secret_flags (NMSetting *setting,
                                          NMSettingSecretFlags flags,
                                          GError **error);
 
-/* Properties */
+/*****************************************************************************/
+
 const GVariantType *nm_setting_get_dbus_property_type (NMSetting *setting,
                                                        const char *property_name);
 
+/*****************************************************************************/
+
 G_END_DECLS
 
 #endif /* __NM_SETTING_H__ */
diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h
index 5d0f8386..b886730a 100644
--- a/libnm-core/nm-utils-private.h
+++ b/libnm-core/nm-utils-private.h
@@ -74,7 +74,6 @@ GVariant *  _nm_utils_strdict_to_dbus   (const GValue *prop_value);
 void        _nm_utils_strdict_from_dbus (GVariant *dbus_value,
                                          GValue *prop_value);
 
-GVariant *  _nm_utils_bytes_to_dbus     (const GValue *prop_value);
 void        _nm_utils_bytes_from_dbus   (GVariant *dbus_value,
                                          GValue *prop_value);
 
@@ -83,6 +82,13 @@ char *      _nm_utils_hwaddr_canonical_or_invalid (const char *mac, gssize lengt
 GPtrArray * _nm_utils_team_link_watchers_from_variant (GVariant *value);
 GVariant *  _nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers);
 
+void        _nm_utils_format_variant_attributes_full (GString *str,
+                                                      const NMUtilsNamedValue *values,
+                                                      guint num_values,
+                                                      char attr_separator,
+                                                      char key_value_separator);
+gboolean    _nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **error);
+
 /* JSON to GValue conversion macros */
 
 typedef struct {
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index b700c7f5..47ec7a4e 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -43,7 +43,7 @@
 #include "nm-common-macros.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
-#include "crypto.h"
+#include "nm-crypto.h"
 #include "nm-setting-bond.h"
 #include "nm-setting-bridge.h"
 #include "nm-setting-infiniband.h"
@@ -230,31 +230,33 @@ get_system_encodings (void)
 	return cached_encodings;
 }
 
-/* init libnm */
-
-static gboolean initialized = FALSE;
+/*****************************************************************************/
 
 static void __attribute__((constructor))
 _nm_utils_init (void)
 {
-	GModule *self;
-	gpointer func;
+	static int initialized = 0;
 
-	if (initialized)
+	if (g_atomic_int_get (&initialized) != 0)
 		return;
-	initialized = TRUE;
 
-	self = g_module_open (NULL, 0);
-	if (g_module_symbol (self, "nm_util_get_private", &func))
-		g_error ("libnm-util symbols detected; Mixing libnm with libnm-util/libnm-glib is not supported");
-	g_module_close (self);
+	/* we don't expect this code to run multiple times, nor on multiple threads.
+	 *
+	 * In practice, it would not be a problem if two threads concurrently try to
+	 * run the initialization code below, all code below itself is thread-safe,
+	 * Hence, a poor-man guard "initialized" above is more than sufficient,
+	 * although it does not guarantee that the code is not run concurrently. */
 
 	bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR);
 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 
 	_nm_dbus_errors_init ();
+
+	g_atomic_int_set (&initialized, 1);
 }
 
+/*****************************************************************************/
+
 gboolean _nm_utils_is_manager_process;
 
 /* ssid helpers */
@@ -300,19 +302,19 @@ nm_utils_ssid_to_utf8 (const guint8 *ssid, gsize len)
 
 	g_return_val_if_fail (ssid != NULL, NULL);
 
-	if (g_utf8_validate ((const gchar *) ssid, len, NULL))
-		return g_strndup ((const gchar *) ssid, len);
+	if (g_utf8_validate ((const char *) ssid, len, NULL))
+		return g_strndup ((const char *) ssid, len);
 
 	encodings = get_system_encodings ();
 
 	for (e = encodings; *e; e++) {
-		converted = g_convert ((const gchar *) ssid, len, "UTF-8", *e, NULL, NULL, NULL);
+		converted = g_convert ((const char *) ssid, len, "UTF-8", *e, NULL, NULL, NULL);
 		if (converted)
 			break;
 	}
 
 	if (!converted) {
-		converted = g_convert_with_fallback ((const gchar *) ssid, len,
+		converted = g_convert_with_fallback ((const char *) ssid, len,
 		                                     "UTF-8", encodings[0], "?", NULL, NULL, NULL);
 	}
 
@@ -323,7 +325,7 @@ nm_utils_ssid_to_utf8 (const guint8 *ssid, gsize len)
 		 */
 
 		/* Use the printable range of 0x20-0x7E */
-		gchar *valid_chars = " !\"#$%&'()*+,-./0123456789:;<=>?@"
+		char *valid_chars = " !\"#$%&'()*+,-./0123456789:;<=>?@"
 		                     "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
 		                     "abcdefghijklmnopqrstuvwxyz{|}~";
 
@@ -334,6 +336,18 @@ nm_utils_ssid_to_utf8 (const guint8 *ssid, gsize len)
 	return converted;
 }
 
+char *
+_nm_utils_ssid_to_utf8 (GBytes *ssid)
+{
+	const guint8 *p;
+	gsize l;
+
+	g_return_val_if_fail (ssid, NULL);
+
+	p = g_bytes_get_data (ssid, &l);
+	return nm_utils_ssid_to_utf8 (p, l);
+}
+
 /* Shamelessly ripped from the Linux kernel ieee80211 stack */
 /**
  * nm_utils_is_empty_ssid:
@@ -361,6 +375,18 @@ nm_utils_is_empty_ssid (const guint8 *ssid, gsize len)
 	return TRUE;
 }
 
+gboolean
+_nm_utils_is_empty_ssid (GBytes *ssid)
+{
+	const guint8 *p;
+	gsize l;
+
+	g_return_val_if_fail (ssid, FALSE);
+
+	p = g_bytes_get_data (ssid, &l);
+	return nm_utils_is_empty_ssid (p, l);
+}
+
 #define ESSID_MAX_SIZE 32
 
 /**
@@ -402,6 +428,37 @@ nm_utils_escape_ssid (const guint8 *ssid, gsize len)
 	return escaped;
 }
 
+char *
+_nm_utils_ssid_to_string_arr (const guint8 *ssid, gsize len)
+{
+	char *s_copy;
+	const char *s_cnst;
+
+	if (len == 0)
+		return g_strdup ("(empty)");
+
+	s_cnst = nm_utils_buf_utf8safe_escape (ssid, len, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, &s_copy);
+	nm_assert (s_cnst);
+
+	if (nm_utils_is_empty_ssid (ssid, len))
+		return g_strdup_printf ("\"%s\" (hidden)", s_cnst);
+
+	return g_strdup_printf ("\"%s\"", s_cnst);
+}
+
+char *
+_nm_utils_ssid_to_string (GBytes *ssid)
+{
+	gconstpointer p;
+	gsize l;
+
+	if (!ssid)
+		return g_strdup ("(none)");
+
+	p = g_bytes_get_data (ssid, &l);
+	return _nm_utils_ssid_to_string_arr (p, l);
+}
+
 /**
  * nm_utils_same_ssid:
  * @ssid1: (array length=len1): the first SSID to compare
@@ -566,39 +623,6 @@ _nm_utils_copy_strdict (GHashTable *strdict)
 }
 
 GPtrArray *
-_nm_utils_copy_slist_to_array (const GSList *list,
-                               NMUtilsCopyFunc copy_func,
-                               GDestroyNotify unref_func)
-{
-	const GSList *iter;
-	GPtrArray *array;
-
-	array = g_ptr_array_new_with_free_func (unref_func);
-	for (iter = list; iter; iter = iter->next)
-		g_ptr_array_add (array, copy_func ? copy_func (iter->data) : iter->data);
-	return array;
-}
-
-GSList *
-_nm_utils_copy_array_to_slist (const GPtrArray *array,
-                               NMUtilsCopyFunc copy_func)
-{
-	GSList *slist = NULL;
-	gpointer item;
-	int i;
-
-	if (!array)
-		return NULL;
-
-	for (i = 0; i < array->len; i++) {
-		item = array->pdata[i];
-		slist = g_slist_prepend (slist, copy_func (item));
-	}
-
-	return g_slist_reverse (slist);
-}
-
-GPtrArray *
 _nm_utils_copy_array (const GPtrArray *array,
                       NMUtilsCopyFunc copy_func,
                       GDestroyNotify free_func)
@@ -645,136 +669,6 @@ _nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpointer ne
 	return -1;
 }
 
-gssize
-_nm_utils_ptrarray_find_binary_search (gconstpointer *list,
-                                       gsize len,
-                                       gconstpointer needle,
-                                       GCompareDataFunc cmpfcn,
-                                       gpointer user_data,
-                                       gssize *out_idx_first,
-                                       gssize *out_idx_last)
-{
-	gssize imin, imax, imid, i2min, i2max, i2mid;
-	int cmp;
-
-	g_return_val_if_fail (list || !len, ~((gssize) 0));
-	g_return_val_if_fail (cmpfcn, ~((gssize) 0));
-
-	imin = 0;
-	if (len > 0) {
-		imax = len - 1;
-
-		while (imin <= imax) {
-			imid = imin + (imax - imin) / 2;
-
-			cmp = cmpfcn (list[imid], needle, user_data);
-			if (cmp == 0) {
-				/* we found a matching entry at index imid.
-				 *
-				 * Does the caller request the first/last index as well (in case that
-				 * there are multiple entries which compare equal). */
-
-				if (out_idx_first) {
-					i2min = imin;
-					i2max = imid + 1;
-					while (i2min <= i2max) {
-						i2mid = i2min + (i2max - i2min) / 2;
-
-						cmp = cmpfcn (list[i2mid], needle, user_data);
-						if (cmp == 0)
-							i2max = i2mid -1;
-						else {
-							nm_assert (cmp < 0);
-							i2min = i2mid + 1;
-						}
-					}
-					*out_idx_first = i2min;
-				}
-				if (out_idx_last) {
-					i2min = imid + 1;
-					i2max = imax;
-					while (i2min <= i2max) {
-						i2mid = i2min + (i2max - i2min) / 2;
-
-						cmp = cmpfcn (list[i2mid], needle, user_data);
-						if (cmp == 0)
-							i2min = i2mid + 1;
-						else {
-							nm_assert (cmp > 0);
-							i2max = i2mid - 1;
-						}
-					}
-					*out_idx_last = i2min - 1;
-				}
-				return imid;
-			}
-
-			if (cmp < 0)
-				imin = imid + 1;
-			else
-				imax = imid - 1;
-		}
-	}
-
-	/* return the inverse of @imin. This is a negative number, but
-	 * also is ~imin the position where the value should be inserted. */
-	imin = ~imin;
-	NM_SET_OUT (out_idx_first, imin);
-	NM_SET_OUT (out_idx_last, imin);
-	return imin;
-}
-
-gssize
-_nm_utils_array_find_binary_search (gconstpointer list, gsize elem_size, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data)
-{
-	gssize imin, imax, imid;
-	int cmp;
-
-	g_return_val_if_fail (list || !len, ~((gssize) 0));
-	g_return_val_if_fail (cmpfcn, ~((gssize) 0));
-	g_return_val_if_fail (elem_size > 0, ~((gssize) 0));
-
-	imin = 0;
-	if (len == 0)
-		return ~imin;
-
-	imax = len - 1;
-
-	while (imin <= imax) {
-		imid = imin + (imax - imin) / 2;
-
-		cmp = cmpfcn (&((const char *) list)[elem_size * imid], needle, user_data);
-		if (cmp == 0)
-			return imid;
-
-		if (cmp < 0)
-			imin = imid + 1;
-		else
-			imax = imid - 1;
-	}
-
-	/* return the inverse of @imin. This is a negative number, but
-	 * also is ~imin the position where the value should be inserted. */
-	return ~imin;
-}
-
-GVariant *
-_nm_utils_bytes_to_dbus (const GValue *prop_value)
-{
-	GBytes *bytes = g_value_get_boxed (prop_value);
-
-	if (bytes) {
-		return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
-		                                  g_bytes_get_data (bytes, NULL),
-		                                  g_bytes_get_size (bytes),
-		                                  1);
-	} else {
-		return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
-		                                  NULL, 0,
-		                                  1);
-	}
-}
-
 void
 _nm_utils_bytes_from_dbus (GVariant *dbus_value,
                            GValue *prop_value)
@@ -2648,6 +2542,249 @@ nm_utils_tc_tfilter_from_str (const char *str, GError **error)
 
 /*****************************************************************************/
 
+extern const NMVariantAttributeSpec *const _nm_sriov_vf_attribute_spec[];
+
+/**
+ * nm_utils_sriov_vf_to_str:
+ * @vf: the %NMSriovVF
+ * @omit_index: if %TRUE, the VF index will be omitted from output string
+ * @error: (out) (allow-none): location to store the error on failure
+ *
+ * Converts a SR-IOV virtual function object to its string representation.
+ *
+ * Returns: a newly allocated string or %NULL on error
+ *
+ * Since: 1.14
+ */
+char *
+nm_utils_sriov_vf_to_str (const NMSriovVF *vf, gboolean omit_index, GError **error)
+{
+	gs_free NMUtilsNamedValue *values = NULL;
+	gs_free const char **names = NULL;
+	const guint *vlan_ids;
+	guint num_vlans, num_attrs;
+	guint i;
+	GString *str;
+
+	str = g_string_new ("");
+	if (!omit_index)
+		g_string_append_printf (str, "%u", nm_sriov_vf_get_index (vf));
+
+	names = nm_sriov_vf_get_attribute_names (vf);
+	num_attrs = names ? g_strv_length ((char **) names) : 0;
+	values = g_new0 (NMUtilsNamedValue, num_attrs);
+
+	for (i = 0; i < num_attrs; i++) {
+		values[i].name = names[i];
+		values[i].value_ptr = nm_sriov_vf_get_attribute (vf, names[i]);
+	}
+
+	if (num_attrs > 0) {
+		if (!omit_index)
+			g_string_append_c (str, ' ');
+		_nm_utils_format_variant_attributes_full (str, values, num_attrs, ' ', '=');
+	}
+
+	vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
+	if (num_vlans != 0) {
+		g_string_append (str, " vlans");
+		for (i = 0; i < num_vlans; i++) {
+			guint32 qos;
+			NMSriovVFVlanProtocol protocol;
+
+			qos = nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]);
+			protocol = nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]);
+
+			g_string_append_c (str, i == 0 ? '=' : ';');
+
+			g_string_append_printf (str, "%u", vlan_ids[i]);
+
+			if (   qos != 0
+			    || protocol != NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q) {
+				g_string_append_printf (str,
+				                        ".%u%s",
+				                        (unsigned) qos,
+				                        protocol == NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q ? "" : ".ad");
+			}
+		}
+	}
+
+	return g_string_free (str, FALSE);
+}
+
+gboolean
+_nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **error)
+{
+	gs_free const char **vlans = NULL;
+	guint i;
+
+	vlans = nm_utils_strsplit_set (str, ";", FALSE);
+	if (!vlans) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_FAILED,
+		                     "empty VF VLAN");
+		return FALSE;
+	}
+
+	for (i = 0; vlans[i]; i++) {
+		gs_strfreev char **params = NULL;
+		guint id = G_MAXUINT;
+		gint64 qos = -1;
+
+		/* we accept leading/trailing whitespace around vlans[1]. Hence
+		 * the nm_str_skip_leading_spaces() and g_strchomp() below.
+		 *
+		 * However, we don't accept any whitespace inside the specifier.
+		 * Hence the NM_STRCHAR_ALL() checks. */
+
+		params = g_strsplit (nm_str_skip_leading_spaces (vlans[i]), ".", 3);
+		if (!params || !params[0] || *params[0] == '\0') {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_FAILED,
+			                     "empty VF VLAN");
+			return FALSE;
+		}
+
+		if (!params[1])
+			g_strchomp (params[0]);
+		if (NM_STRCHAR_ALL (params[0], ch, ch == 'x' || g_ascii_isdigit (ch)))
+			id = _nm_utils_ascii_str_to_int64 (params[0], 0, 0, 4095, G_MAXUINT);
+		if (id == G_MAXUINT) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_FAILED,
+			             "invalid VF VLAN id '%s'",
+			             params[0]);
+			return FALSE;
+		}
+		if (!nm_sriov_vf_add_vlan (vf, id)) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_FAILED,
+			             "duplicate VLAN id %u",
+			             id);
+			return FALSE;
+		}
+
+		if (!params[1])
+			continue;
+
+		if (!params[2])
+			g_strchomp (params[1]);
+		if (NM_STRCHAR_ALL (params[1], ch, ch == 'x' || g_ascii_isdigit (ch)))
+			qos = _nm_utils_ascii_str_to_int64 (params[1], 0, 0, G_MAXUINT32, -1);
+		if (qos == -1) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_FAILED,
+			             "invalid VF VLAN QoS '%s'",
+			             params[1]);
+			return FALSE;
+		}
+		nm_sriov_vf_set_vlan_qos (vf, id, qos);
+
+		if (!params[2])
+			continue;
+
+		g_strchomp (params[2]);
+
+		if (nm_streq (params[2], "ad"))
+			nm_sriov_vf_set_vlan_protocol (vf, id, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+		else if (nm_streq (params[2], "q"))
+			nm_sriov_vf_set_vlan_protocol (vf, id, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+		else {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_FAILED,
+			             "invalid VF VLAN protocol '%s'",
+			             params[2]);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+/**
+ * nm_utils_sriov_vf_from_str:
+ * @str: the input string
+ * @error: (out) (allow-none): location to store the error on failure
+ *
+ * Converts a string to a SR-IOV virtual function object.
+ *
+ * Returns: (transfer full): the virtual function object
+ *
+ * Since: 1.14
+ */
+NMSriovVF *
+nm_utils_sriov_vf_from_str (const char *str, GError **error)
+{
+	gs_free char *index_free = NULL;
+	const char *detail;
+
+	g_return_val_if_fail (str, NULL);
+	g_return_val_if_fail (!error || !*error, NULL);
+
+	while (*str == ' ')
+		str++;
+
+	detail = strchr (str, ' ');
+	if (detail) {
+		index_free = g_strndup (str, detail - str);
+		str = index_free;
+		detail++;
+	}
+
+	return _nm_utils_sriov_vf_from_strparts (str, detail, error);
+}
+
+NMSriovVF *
+_nm_utils_sriov_vf_from_strparts (const char *index, const char *detail, GError **error)
+{
+	NMSriovVF *vf;
+	guint32 n_index;
+	GHashTableIter iter;
+	char *key;
+	GVariant *variant;
+	gs_unref_hashtable GHashTable *ht = NULL;
+
+	n_index = _nm_utils_ascii_str_to_int64 (index, 10, 0, G_MAXUINT32, 0);
+	if (errno) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_FAILED,
+		                     "invalid index");
+		return NULL;
+	}
+
+	vf = nm_sriov_vf_new (n_index);
+	if (detail) {
+		ht = nm_utils_parse_variant_attributes (detail, ' ', '=', TRUE, _nm_sriov_vf_attribute_spec, error);
+		if (!ht) {
+			nm_sriov_vf_unref (vf);
+			return NULL;
+		}
+
+		if ((variant = g_hash_table_lookup (ht, "vlans"))) {
+			if (!_nm_sriov_vf_parse_vlans (vf, g_variant_get_string (variant, NULL), error)) {
+				nm_sriov_vf_unref (vf);
+				return NULL;
+			}
+			g_hash_table_remove (ht, "vlans");
+		}
+
+		g_hash_table_iter_init (&iter, ht);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &variant))
+			nm_sriov_vf_set_attribute (vf, key, g_variant_ref_sink (variant));
+	}
+
+	return vf;
+}
+
+/*****************************************************************************/
+
 /**
  * nm_utils_uuid_generate_buf_:
  * @buf: input buffer, must contain at least 37 bytes
@@ -2701,9 +2838,17 @@ nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, g
 	g_return_val_if_fail (uuid_type == NM_UTILS_UUID_TYPE_LEGACY || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL);
 	g_return_val_if_fail (!type_args || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL);
 
+	if (slen < 0)
+		slen = s ? strlen (s) : 0;
+
 	switch (uuid_type) {
 	case NM_UTILS_UUID_TYPE_LEGACY:
-		crypto_md5_hash (NULL, 0, s, slen, (char *) uuid, sizeof (uuid));
+		nm_crypto_md5_hash (NULL,
+		                    0,
+		                    (guint8 *) s,
+		                    slen,
+		                    (guint8 *) uuid,
+		                    sizeof (uuid));
 		break;
 	case NM_UTILS_UUID_TYPE_VARIANT3: {
 		uuid_t ns_uuid = { 0 };
@@ -2714,7 +2859,12 @@ nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, g
 				g_return_val_if_reached (NULL);
 		}
 
-		crypto_md5_hash (s, slen, (char *) ns_uuid, sizeof (ns_uuid), (char *) uuid, sizeof (uuid));
+		nm_crypto_md5_hash ((guint8 *) s,
+		                    slen,
+		                    (guint8 *) ns_uuid,
+		                    sizeof (ns_uuid),
+		                    (guint8 *) uuid,
+		                    sizeof (uuid));
 
 		uuid[6] = (uuid[6] & 0x0F) | 0x30;
 		uuid[8] = (uuid[8] & 0x3F) | 0x80;
@@ -2774,110 +2924,6 @@ _nm_utils_uuid_generate_from_strings (const char *string1, ...)
 
 /*****************************************************************************/
 
-/**
- * nm_utils_rsa_key_encrypt:
- * @data: (array length=len): RSA private key data to be encrypted
- * @len: length of @data
- * @in_password: (allow-none): existing password to use, if any
- * @out_password: (out) (allow-none): if @in_password was %NULL, a random
- *  password will be generated and returned in this argument
- * @error: detailed error information on return, if an error occurred
- *
- * Encrypts the given RSA private key data with the given password (or generates
- * a password if no password was given) and converts the data to PEM format
- * suitable for writing to a file. It uses Triple DES cipher for the encryption.
- *
- * Returns: (transfer full): on success, PEM-formatted data suitable for writing
- * to a PEM-formatted certificate/private key file.
- **/
-GByteArray *
-nm_utils_rsa_key_encrypt (const guint8 *data,
-                          gsize len,
-                          const char *in_password,
-                          char **out_password,
-                          GError **error)
-{
-	char salt[16];
-	int salt_len;
-	char *key = NULL, *enc = NULL, *pw_buf[32];
-	gsize key_len = 0, enc_len = 0;
-	GString *pem = NULL;
-	char *tmp, *tmp_password = NULL;
-	int left;
-	const char *p;
-	GByteArray *ret = NULL;
-
-	g_return_val_if_fail (data != NULL, NULL);
-	g_return_val_if_fail (len > 0, NULL);
-	if (out_password)
-		g_return_val_if_fail (*out_password == NULL, NULL);
-
-	/* Make the password if needed */
-	if (!in_password) {
-		if (!crypto_randomize (pw_buf, sizeof (pw_buf), error))
-			return NULL;
-		in_password = tmp_password = nm_utils_bin2hexstr (pw_buf, sizeof (pw_buf), -1);
-	}
-
-	salt_len = 8;
-	if (!crypto_randomize (salt, salt_len, error))
-		goto out;
-
-	key = crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], salt_len, in_password, &key_len, NULL);
-	if (!key)
-		g_return_val_if_reached (NULL);
-
-	enc = crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, salt_len, key, key_len, &enc_len, error);
-	if (!enc)
-		goto out;
-
-	pem = g_string_sized_new (enc_len * 2 + 100);
-	g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n");
-	g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n");
-
-	/* Convert the salt to a hex string */
-	tmp = nm_utils_bin2hexstr (salt, salt_len, salt_len * 2);
-	g_string_append_printf (pem, "DEK-Info: %s,%s\n\n", CIPHER_DES_EDE3_CBC, tmp);
-	g_free (tmp);
-
-	/* Convert the encrypted key to a base64 string */
-	p = tmp = g_base64_encode ((const guchar *) enc, enc_len);
-	left = strlen (tmp);
-	while (left > 0) {
-		g_string_append_len (pem, p, (left < 64) ? left : 64);
-		g_string_append_c (pem, '\n');
-		left -= 64;
-		p += 64;
-	}
-	g_free (tmp);
-
-	g_string_append (pem, "-----END RSA PRIVATE KEY-----\n");
-
-	ret = g_byte_array_sized_new (pem->len);
-	g_byte_array_append (ret, (const unsigned char *) pem->str, pem->len);
-	if (tmp_password && out_password)
-		*out_password = g_strdup (tmp_password);
-
-out:
-	if (key) {
-		memset (key, 0, key_len);
-		g_free (key);
-	}
-	if (enc) {
-		memset (enc, 0, enc_len);
-		g_free (enc);
-	}
-	if (pem)
-		g_string_free (pem, TRUE);
-
-	if (tmp_password) {
-		memset (tmp_password, 0, strlen (tmp_password));
-		g_free (tmp_password);
-	}
-
-	return ret;
-}
-
 static gboolean
 file_has_extension (const char *filename, const char *extensions[])
 {
@@ -2910,18 +2956,15 @@ gboolean
 nm_utils_file_is_certificate (const char *filename)
 {
 	const char *extensions[] = { ".der", ".pem", ".crt", ".cer", NULL };
-	NMCryptoFileFormat file_format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	GByteArray *cert;
+	NMCryptoFileFormat file_format;
 
 	g_return_val_if_fail (filename != NULL, FALSE);
 
 	if (!file_has_extension (filename, extensions))
 		return FALSE;
 
-	cert = crypto_load_and_verify_certificate (filename, &file_format, NULL);
-	if (cert)
-		g_byte_array_unref (cert);
-
+	if (!nm_crypto_load_and_verify_certificate (filename, &file_format, NULL, NULL))
+		return FALSE;
 	return file_format = NM_CRYPTO_FILE_FORMAT_X509;
 }
 
@@ -2947,7 +2990,7 @@ nm_utils_file_is_private_key (const char *filename, gboolean *out_encrypted)
 	if (!file_has_extension (filename, extensions))
 		return FALSE;
 
-	return crypto_verify_private_key (filename, NULL, out_encrypted, NULL) != NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	return nm_crypto_verify_private_key (filename, NULL, out_encrypted, NULL) != NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 }
 
 /**
@@ -2963,7 +3006,7 @@ nm_utils_file_is_pkcs12 (const char *filename)
 {
 	g_return_val_if_fail (filename != NULL, FALSE);
 
-	return crypto_is_pkcs12_file (filename, NULL);
+	return nm_crypto_is_pkcs12_file (filename, NULL);
 }
 
 /*****************************************************************************/
@@ -3483,13 +3526,13 @@ nm_utils_hwaddr_len (int type)
 	g_return_val_if_reached (0);
 }
 
-static guint8 *
-_str2bin (const char *asc,
-          gboolean delimiter_required,
-          const char *delimiter_candidates,
-          guint8 *buffer,
-          gsize buffer_length,
-          gsize *out_len)
+guint8 *
+_nm_utils_str2bin_full (const char *asc,
+                        gboolean delimiter_required,
+                        const char *delimiter_candidates,
+                        guint8 *buffer,
+                        gsize buffer_length,
+                        gsize *out_len)
 {
 	const char *in = asc;
 	guint8 *out = buffer;
@@ -3559,7 +3602,7 @@ _str2bin (const char *asc,
 	return buffer;
 }
 
-#define hwaddr_aton(asc, buffer, buffer_length, out_len) _str2bin ((asc), TRUE, ":-", (buffer), (buffer_length), (out_len))
+#define hwaddr_aton(asc, buffer, buffer_length, out_len) _nm_utils_str2bin_full ((asc), TRUE, ":-", (buffer), (buffer_length), (out_len))
 
 /**
  * nm_utils_hexstr2bin:
@@ -3585,7 +3628,7 @@ nm_utils_hexstr2bin (const char *hex)
 
 	buffer_length = strlen (hex) / 2 + 3;
 	buffer = g_malloc (buffer_length);
-	if (!_str2bin (hex, FALSE, ":", buffer, buffer_length, &len)) {
+	if (!_nm_utils_str2bin_full (hex, FALSE, ":", buffer, buffer_length, &len)) {
 		g_free (buffer);
 		return NULL;
 	}
@@ -4290,6 +4333,8 @@ nm_utils_is_uuid (const char *str)
 	const char *p = str;
 	int num_dashes = 0;
 
+	g_return_val_if_fail (str, FALSE);
+
 	while (*p) {
 		if (*p == '-')
 			num_dashes++;
@@ -4465,7 +4510,7 @@ _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin)
 		return TRUE;
 	}
 
-	if (_str2bin (duid, FALSE, ":", duid_arr, sizeof (duid_arr), &duid_len)) {
+	if (_nm_utils_str2bin_full (duid, FALSE, ":", duid_arr, sizeof (duid_arr), &duid_len)) {
 		/* MAX DUID length is 128 octects + the type code (2 octects). */
 		if (   duid_len > 2
 		    && duid_len <= (128 + 2)) {
@@ -4886,7 +4931,7 @@ nm_utils_enum_from_str (GType type, const char *str,
  *
  * Since: 1.2
  */
-const char **nm_utils_enum_get_values (GType type, gint from, gint to)
+const char **nm_utils_enum_get_values (GType type, int from, int to)
 {
 	return _nm_utils_enum_get_values (type, from, to);
 }
@@ -5539,7 +5584,7 @@ _nm_utils_team_config_get (const char *conf,
 				g_ptr_array_free (data, TRUE);
 
 		} else if (json_is_array (json_element)) {
-			GPtrArray *data = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+			GPtrArray *data = g_ptr_array_new_with_free_func (g_free);
 			json_t *str_element;
 			int index;
 
@@ -6147,44 +6192,21 @@ next:
 	return g_steal_pointer (&ht);
 }
 
-/*
- * nm_utils_format_variant_attributes:
- * @attributes: (element-type utf8 GVariant): a #GHashTable mapping attribute names to #GVariant values
- * @attr_separator: the attribute separator character
- * @key_value_separator: character separating key and values
- *
- * Format attributes to a string.
- *
- * Returns: (transfer full): the string representing attributes, or %NULL
- *    in case there are no attributes
- *
- * Since: 1.8
- */
-char *
-nm_utils_format_variant_attributes (GHashTable *attributes,
-                                    char attr_separator,
-                                    char key_value_separator)
+void
+_nm_utils_format_variant_attributes_full (GString *str,
+                                          const NMUtilsNamedValue *values,
+                                          guint num_values,
+                                          char attr_separator,
+                                          char key_value_separator)
 {
-	GString *str = NULL;
-	GVariant *variant;
-	char sep = 0;
 	const char *name, *value;
+	GVariant *variant;
 	char *escaped;
 	char buf[64];
-	gs_free NMUtilsNamedValue *values = NULL;
-	guint i, len;
-
-	g_return_val_if_fail (attr_separator, NULL);
-	g_return_val_if_fail (key_value_separator, NULL);
-
-	if (!attributes || !g_hash_table_size (attributes))
-		return NULL;
-
-	values = nm_utils_named_values_from_str_dict (attributes, &len);
-
-	str = g_string_new ("");
+	char sep = 0;
+	guint i;
 
-	for (i = 0; i < len; i++) {
+	for (i = 0; i < num_values; i++) {
 		name = values[i].name;
 		variant = (GVariant *) values[i].value_ptr;
 		value = NULL;
@@ -6217,7 +6239,44 @@ nm_utils_format_variant_attributes (GHashTable *attributes,
 
 		sep = attr_separator;
 	}
+}
+
+/*
+ * nm_utils_format_variant_attributes:
+ * @attributes: (element-type utf8 GVariant): a #GHashTable mapping attribute names to #GVariant values
+ * @attr_separator: the attribute separator character
+ * @key_value_separator: character separating key and values
+ *
+ * Format attributes to a string.
+ *
+ * Returns: (transfer full): the string representing attributes, or %NULL
+ *    in case there are no attributes
+ *
+ * Since: 1.8
+ */
+char *
+nm_utils_format_variant_attributes (GHashTable *attributes,
+                                    char attr_separator,
+                                    char key_value_separator)
+{
+	GString *str = NULL;
+	gs_free NMUtilsNamedValue *values = NULL;
+	guint len;
 
+	g_return_val_if_fail (attr_separator, NULL);
+	g_return_val_if_fail (key_value_separator, NULL);
+
+	if (!attributes || !g_hash_table_size (attributes))
+		return NULL;
+
+	values = nm_utils_named_values_from_str_dict (attributes, &len);
+
+	str = g_string_new ("");
+	_nm_utils_format_variant_attributes_full (str,
+	                                          values,
+	                                          len,
+	                                          attr_separator,
+	                                          key_value_separator);
 	return g_string_free (str, FALSE);
 }
 
diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h
index 70245869..cf9572b9 100644
--- a/libnm-core/nm-utils.h
+++ b/libnm-core/nm-utils.h
@@ -34,8 +34,9 @@
 #include <linux/if_infiniband.h>
 
 #include "nm-core-enum-types.h"
-#include "nm-setting-wireless-security.h"
+#include "nm-setting-sriov.h"
 #include "nm-setting-tc-config.h"
+#include "nm-setting-wireless-security.h"
 
 G_BEGIN_DECLS
 
@@ -213,7 +214,7 @@ NM_AVAILABLE_IN_1_2
 gboolean nm_utils_enum_from_str (GType type, const char *str, int *out_value, char **err_token);
 
 NM_AVAILABLE_IN_1_2
-const char **nm_utils_enum_get_values (GType type, gint from, gint to);
+const char **nm_utils_enum_get_values (GType type, int from, int to);
 
 NM_AVAILABLE_IN_1_6
 guint nm_utils_version (void);
@@ -250,9 +251,17 @@ char *nm_utils_tc_tfilter_to_str           (NMTCTfilter *tfilter, GError **error
 
 /*****************************************************************************/
 
+NM_AVAILABLE_IN_1_14
+char *nm_utils_sriov_vf_to_str (const NMSriovVF *vf, gboolean omit_index, GError **error);
+NM_AVAILABLE_IN_1_14
+NMSriovVF *nm_utils_sriov_vf_from_str (const char *str, GError **error);
+
+/*****************************************************************************/
+
 NM_AVAILABLE_IN_1_12
 gint64 nm_utils_get_timestamp_msec         (void);
 
+
 G_END_DECLS
 
 #endif /* __NM_UTILS_H__ */
diff --git a/libnm-core/nm-version.h b/libnm-core/nm-version.h
index ee6dbc0b..59482d4e 100644
--- a/libnm-core/nm-version.h
+++ b/libnm-core/nm-version.h
@@ -160,10 +160,18 @@
 # define NM_AVAILABLE_IN_1_12
 #endif
 
-#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_12_2
-# define NM_AVAILABLE_IN_1_12_2          G_UNAVAILABLE(1,12,2)
+#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_14
+# define NM_DEPRECATED_IN_1_14           G_DEPRECATED
+# define NM_DEPRECATED_IN_1_14_FOR(f)    G_DEPRECATED_FOR(f)
 #else
-# define NM_AVAILABLE_IN_1_12_2
+# define NM_DEPRECATED_IN_1_14
+# define NM_DEPRECATED_IN_1_14_FOR(f)
+#endif
+
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_14
+# define NM_AVAILABLE_IN_1_14            G_UNAVAILABLE(1,14)
+#else
+# define NM_AVAILABLE_IN_1_14
 #endif
 
 #endif  /* NM_VERSION_H */
diff --git a/libnm-core/tests/certs/test-aes-key.pem b/libnm-core/tests/certs/test-aes-128-key.pem
index aab8f463..aab8f463 100644
--- a/libnm-core/tests/certs/test-aes-key.pem
+++ b/libnm-core/tests/certs/test-aes-128-key.pem
diff --git a/libnm-core/tests/certs/test-aes-256-key.pem b/libnm-core/tests/certs/test-aes-256-key.pem
new file mode 100644
index 00000000..e51bafd3
--- /dev/null
+++ b/libnm-core/tests/certs/test-aes-256-key.pem
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,5FF6BD2D4E57E8933D4A6814DEF5305A
+
+9Br+xw6XOg7qUqfeE5PJ4g/PAm7eTcPMb4FzSKkaEosLo6oj4f37TwXuojJZeAmi
+1EytpqM1vdYHCLdjg+qYaTIq6mzMZIyoaREokcOhcNrq5S0J39gJLVV9LjiXhCAH
+GQgDBnbRT6HGz70AyTRLcW9aj6uBzTv/m92sLUw2txFeBXK8n2AA1oHJTgsFNYjf
+/ZvTCE1VMQHDPx31Vn5WXSUHNc0hx4MTIwpHqWI17ohr8IiWCs5HXVfVaqrNeNEw
+haD7fg8oNxjLs46/4dDWmfWXhDsMFSweZv03gZdyVjwn1IOqeVGmTdLpllfgOW7E
++XE8Y/d55s5nkOxu6eXNMtWgjclKBGr2iMxxnODmEsUt2WcV98cPS+25o3hOfy3s
+NIcfxtWVRFUtjqf3ragyGLuXFqATkj1slj4LVMeewRJ1g+Z6ti0mwBN+ZrYtKdec
+FRNb4zr5FW+3SqkIIJVfxJEYJDB4zODhMg8tySEHLKuT0uz42YQ4aoOHTzO5WDBY
+2BI7TjRppXcExPnkAk5jqbKA6BjT9KcAVyypfxDKvCeXKdjDcL6ISOBSm6cQBh8D
+HxsFzMy9PF6kKNeiNiEsVPnKYvhvs1hTBtp+IAgJ6KZnCDKplZFxo/mBAlV2KyCT
+x+Mhmme3fXdLJkvxlVJAoAhwgXvomVCVTGI3JhcQIqVgxPIKYpqlHVFC7JjG+yQX
+tvzCPtr9G9+Ofrm6zXjlDD7zNyl/KfFtEWhO2ePHkQlCEuKJnsnRIf/wQ0viG0yY
+MH31Z/84o2pKLBKY5fq8+eYuYoP9Rk4W2LpjGMvdkKhEHL26kZofeFyqD+JcaxHc
+kQh7/SbWAsREGb9Jp7I2q1mo749mse1oSFIQa5gN3jB0mgHZd6edRYeW2Up+rqEK
+k6Xd6uqs7bZd5W9sP7Cf6yJOFEjqFVLQEVEXWSchgeta/JNrjGr3UzLFN2S+vhvX
+XgDa41y2UdXHRqj2s864u0ZDPyGXYZnVbvQn/8xHQ7rvxHowpTn+XXUEf0AQnk3j
+9h++3McwP8GuVxkwc6o9TfOL+ell5jup7F3SekwEiE3hqY8x87g6X2zD5VSnfCy3
+0t0LmPGI1b3LABeYjA1WEdhoTlHrNLkwOR4gsudrJ5nxIzfGy+IHaloXLJy4YKfX
+pJ+qyGRUR42YD9IhiEmmmO1VoJgVEYfBiz50Jg8emddku6eKdmv9IKjiSb2pTbDS
+4oUYKg109OOn+krk67dNXofAXrBa8v7QusC0yz9N25H05Xyou1iqpGk+uBrTqEO6
+lW9lWQo57BQU9og40xMKH/xQgIxfQRktUKsPizj8mKil4izo5KgjPSqBeEbj+Q3c
+0FKlrpTXQlXfX5Z5esqMuCSiwQEzoJR+V+SUaSVcg1av0k/CJMin4Cr8roai+OjK
+lhaQIvx35Bzd02yERYsfpDjmQCXmIeiDm8JtB6znbQPUJ4d8kzWR+5ACOZW/dUss
+YhWJRkZpkIwTY+/sDU4mnP2R37MNo+OH4CwZyUDHjlkRPGW+6JBEpnnlI9a/1Vb1
+pjAGpi/8u/luvZGTzCzxQG2dZc5YQR869U+wFsFbLRiD0aP2SpdOH0QxxPOcdR8+
+HWyL01BJBKyK/wZWJhe+63zlk1L5CA0XYpoNkYpMlPNZkcqR7QzUOATfuBgI2aPM
+AXaweaAWhpPCDsc2RypIs9DhTiCCkt8tq8Au15hVUKAoshLeewPtv0t75MEC0hVB
+z6FVnNlqq0cqqcSVqvUG6JUGtFOGgG3ifEMXggq5k12+wGzY63DLR8dFPNpOL6/1
+nocOayHJIU9M8PP817PzhAUAePRRUKRg8kkbKKeZnCJxoF7O15AFVEJnl9Vyokkz
+bULYhzYVx3xh8THMi+5jsnKWPJyMeYHbHH3C658SIw6Ff9fgEWscv5ZkGYdKMg+l
+8hBn+++SoqIO+F3lOGco+s8qlYox106lUwJEtORXcBxmkaHSo/X2AVO8Owt4vYli
+mjWnY6V9vooBgOuCMcY780pcoj2lSf9JPHDYK0j8t5VumDUSLyLt+tCj0yv/vl5L
+9L++vbu2akZRC9ChijYpfhTvXoG36ePhoT7AGGnhpFjjw1VqG80GY4XSODKzH86w
+kUcZoErb8swUPYOtsybtuPb+6c/YofQ8GfpVosPZgSRD4+U7v+zA3/z8xF2B0xt6
+uV8hXbropuni8KmbFuKrPZK3p2v2aZ8F0+GITwS75/hbT6D7ruUSr5q4V0VKeE8G
+k3QSI0s6+74stPv3S/ByCxu8q51ffYqVw00wzPpEc4SmHEa0R7IczJKXupmDdZZM
+1rASSBNzS5TZDBXP6S7npYQ8nHhgXTdCFO7eM3bp24B/i2o0s7+gkKrz0DkEbv9I
+UrCJjTL8OIIP4qSLMILzZ8pB28c+zyM482ZqFY/2b7j6WlTiqa9P1adrD1gLxTQ0
+Sw9xY+sY3PAJqcnPA5NjDZL/h5plgHhCqDa9pEtdBVG2Mxcl9bXbphwD1MIzj4gr
+xtlW1HUJ/iOhFcXldOJ1MCt++Bm5av4mL5adQ/oUnL5Q0oZZFwqT09k7xe7lZ98N
+uj2Lfl8NN7N3ama9KatgbX5g6IALuk/rJN/4KEiiu24m+lR7c5L0pg/cG6LIFjmk
+HlTsc0ANCgeZBhDJ8kvjcXDhFOqoYE/+D2VO6ZEHRsDibQ+kjpaH+DiD01/gh0N0
+HM6GGtm3GbOyZUhw5OFz04xzcyFYo2xaqzgaZieAOcrt2s6XyPVf1gww08/HtTMR
+gLg14MUQvRXV6kPJfdu4OLZ//b6J0KnzVyLDRdOrWIj2raLWmKwQN9qv05/yskcD
+Y6x7wq3v6iZpFjDc53sslhwp2XRsoWT9X5alVspz8WvP/kqgkTdzpPFdp1vIovOQ
+kRXdzzKICDGDJUIcTL8cJ3Dv4XqNR/sVyuB4dfndzQQApbdYTDNpwX0VJDBjMkQy
+Up6aiUknxa6Cbp7b1ZfUQY8yNBAIZL+R8dmobT3nAHW61DaASHSxn+elCD2Ja/6b
+EiWikskyN6crMAv35ILr5ySsZK97ttNNmRoGFbt8bTjRd83Ie+UfH445kCKsY83x
+aDCvWm+bbV6M9rSgjhJ3bWOudiw+EBMGvSamSnS7CYnRmwq4t+4bM2sh2nYKY0qw
+-----END RSA PRIVATE KEY-----
diff --git a/libnm-core/tests/meson.build b/libnm-core/tests/meson.build
index 2eeadd88..627e6b53 100644
--- a/libnm-core/tests/meson.build
+++ b/libnm-core/tests/meson.build
@@ -37,8 +37,7 @@ foreach test_unit: test_units
     c_args: [
         '-DNETWORKMANAGER_COMPILATION_TEST',
         '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE',
-      ] +
-      nm_build_cflags,
+      ],
     link_with: libnm_core
   )
   test(
diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c
index fb99ffea..6b63e6fd 100644
--- a/libnm-core/tests/test-crypto.c
+++ b/libnm-core/tests/test-crypto.c
@@ -28,7 +28,7 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "crypto.h"
+#include "nm-crypto-impl.h"
 #include "nm-utils.h"
 #include "nm-errors.h"
 #include "nm-core-internal.h"
@@ -99,37 +99,20 @@ static void
 test_cert (gconstpointer test_data)
 {
 	gs_free char *path = NULL;
-	GByteArray *array;
+	gs_unref_bytes GBytes *cert = NULL;
 	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 	GError *error = NULL;
+	gboolean success;
 
 	path = g_build_filename (TEST_CERT_DIR, (const char *) test_data, NULL);
 
-	array = crypto_load_and_verify_certificate (path, &format, &error);
-	g_assert_no_error (error);
+	success = nm_crypto_load_and_verify_certificate (path, &format, &cert, &error);
+	nmtst_assert_success (success, error);
 	g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_X509);
 
-	g_byte_array_free (array, TRUE);
-
 	g_assert (nm_utils_file_is_certificate (path));
 }
 
-static GByteArray *
-file_to_byte_array (const char *filename)
-{
-	char *contents;
-	GByteArray *array = NULL;
-	gsize length = 0;
-
-	if (g_file_get_contents (filename, &contents, &length, NULL)) {
-		array = g_byte_array_sized_new (length);
-		g_byte_array_append (array, (guint8 *) contents, length);
-		g_assert (array->len == length);
-		g_free (contents);
-	}
-	return array;
-}
-
 static void
 test_load_private_key (const char *path,
                        const char *password,
@@ -138,13 +121,13 @@ test_load_private_key (const char *path,
 {
 	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
 	gboolean is_encrypted = FALSE;
-	GByteArray *array, *decrypted;
+	gs_unref_bytes GBytes *array = NULL;
 	GError *error = NULL;
 
 	g_assert (nm_utils_file_is_private_key (path, &is_encrypted));
 	g_assert (is_encrypted);
 
-	array = crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
+	array = nmtst_crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
 	/* Even if the password is wrong, we should determine the key type */
 	g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA);
 
@@ -164,16 +147,14 @@ test_load_private_key (const char *path,
 	g_assert (array != NULL);
 
 	if (decrypted_path) {
-		/* Compare the crypto decrypted key against a known-good decryption */
-		decrypted = file_to_byte_array (decrypted_path);
-		g_assert (decrypted != NULL);
-		g_assert (decrypted->len == array->len);
-		g_assert (memcmp (decrypted->data, array->data, array->len) == 0);
+		gs_free char *contents = NULL;
+		gsize length;
 
-		g_byte_array_free (decrypted, TRUE);
+		/* Compare the crypto decrypted key against a known-good decryption */
+		if (!g_file_get_contents (decrypted_path, &contents, &length, NULL))
+			g_assert_not_reached ();
+		g_assert (nm_utils_gbytes_equal_mem (array, contents, length));
 	}
-
-	g_byte_array_free (array, TRUE);
 }
 
 static void
@@ -187,7 +168,7 @@ test_load_pkcs12 (const char *path,
 
 	g_assert (nm_utils_file_is_private_key (path, NULL));
 
-	format = crypto_verify_private_key (path, password, &is_encrypted, &error);
+	format = nm_crypto_verify_private_key (path, password, &is_encrypted, &error);
 	if (expected_error != -1) {
 		g_assert_error (error, NM_CRYPTO_ERROR, expected_error);
 		g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
@@ -209,7 +190,7 @@ test_load_pkcs12_no_password (const char *path)
 	g_assert (nm_utils_file_is_private_key (path, NULL));
 
 	/* We should still get a valid returned crypto file format */
-	format = crypto_verify_private_key (path, NULL, &is_encrypted, &error);
+	format = nm_crypto_verify_private_key (path, NULL, &is_encrypted, &error);
 	g_assert_no_error (error);
 	g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_PKCS12);
 	g_assert (is_encrypted);
@@ -221,7 +202,7 @@ test_is_pkcs12 (const char *path, gboolean expect_fail)
 	gboolean is_pkcs12;
 	GError *error = NULL;
 
-	is_pkcs12 = crypto_is_pkcs12_file (path, &error);
+	is_pkcs12 = nm_crypto_is_pkcs12_file (path, &error);
 
 	if (expect_fail) {
 		g_assert_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA);
@@ -244,7 +225,7 @@ test_load_pkcs8 (const char *path,
 
 	g_assert (nm_utils_file_is_private_key (path, NULL));
 
-	format = crypto_verify_private_key (path, password, &is_encrypted, &error);
+	format = nm_crypto_verify_private_key (path, password, &is_encrypted, &error);
 	if (expected_error != -1) {
 		g_assert_error (error, NM_CRYPTO_ERROR, expected_error);
 		g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_UNKNOWN);
@@ -261,34 +242,35 @@ test_encrypt_private_key (const char *path,
                           const char *password)
 {
 	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
-	GByteArray *array, *encrypted, *re_decrypted;
+	gs_unref_bytes GBytes *array = NULL;
+	gs_unref_bytes GBytes *encrypted = NULL;
+	gs_unref_bytes GBytes *re_decrypted = NULL;
 	GError *error = NULL;
 
-	array = crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
-	g_assert_no_error (error);
-	g_assert (array != NULL);
+	array = nmtst_crypto_decrypt_openssl_private_key (path, password, &key_type, &error);
+	nmtst_assert_success (array, error);
 	g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA);
 
 	/* Now re-encrypt the private key */
-	encrypted = nm_utils_rsa_key_encrypt (array->data, array->len, password, NULL, &error);
-	g_assert_no_error (error);
-	g_assert (encrypted != NULL);
+	encrypted = nmtst_crypto_rsa_key_encrypt (g_bytes_get_data (array, NULL),
+	                                          g_bytes_get_size (array),
+	                                          password,
+	                                          NULL,
+	                                          &error);
+	nmtst_assert_success (encrypted, error);
 
 	/* Then re-decrypt the private key */
 	key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
-	re_decrypted = crypto_decrypt_openssl_private_key_data (encrypted->data, encrypted->len,
-	                                                        password, &key_type, &error);
-	g_assert_no_error (error);
-	g_assert (re_decrypted != NULL);
+	re_decrypted = nmtst_crypto_decrypt_openssl_private_key_data (g_bytes_get_data (encrypted, NULL),
+	                                                              g_bytes_get_size (encrypted),
+	                                                              password,
+	                                                              &key_type,
+	                                                              &error);
+	nmtst_assert_success (re_decrypted, error);
 	g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA);
 
 	/* Compare the original decrypted key with the re-decrypted key */
-	g_assert_cmpint (array->len, ==, re_decrypted->len);
-	g_assert (!memcmp (array->data, re_decrypted->data, array->len));
-
-	g_byte_array_free (re_decrypted, TRUE);
-	g_byte_array_free (encrypted, TRUE);
-	g_byte_array_free (array, TRUE);
+	g_assert (g_bytes_equal (array, re_decrypted));
 }
 
 static void
@@ -417,15 +399,16 @@ test_md5 (void)
 
 	for (i = 0; i < G_N_ELEMENTS (md5_tests); i++) {
 		memset (digest, 0, sizeof (digest));
-		crypto_md5_hash (md5_tests[i].salt,
-		                 /* crypto_md5_hash() used to clamp salt_len to 8.  It
-		                  * doesn't any more, so we need to do it here now to
-		                  * get output that matches md5_tests[i].result.
-		                  */
-		                 md5_tests[i].salt ? 8 : 0,
-		                 md5_tests[i].password,
-		                 strlen (md5_tests[i].password),
-		                 digest, md5_tests[i].digest_size);
+		nm_crypto_md5_hash ((const guint8 *) md5_tests[i].salt,
+		                    /* nm_crypto_md5_hash() used to clamp salt_len to 8.  It
+		                     * doesn't any more, so we need to do it here now to
+		                     * get output that matches md5_tests[i].result.
+		                     */
+		                    md5_tests[i].salt ? 8 : 0,
+		                    (const guint8 *) md5_tests[i].password,
+		                    strlen (md5_tests[i].password),
+		                    (guint8 *) digest,
+		                    md5_tests[i].digest_size);
 
 		hex = nm_utils_bin2hexstr (digest, md5_tests[i].digest_size, -1);
 		g_assert_cmpstr (hex, ==, md5_tests[i].result);
@@ -444,7 +427,7 @@ main (int argc, char **argv)
 
 	nmtst_init (&argc, &argv, TRUE);
 
-	success = crypto_init (&error);
+	success = _nm_crypto_init (&error);
 	g_assert_no_error (error);
 	g_assert (success);
 
@@ -476,8 +459,11 @@ main (int argc, char **argv)
 	g_test_add_data_func ("/libnm/crypto/key/padding-8",
 	                      "test2_key_and_cert.pem, 12345testing",
 	                      test_key);
-	g_test_add_data_func ("/libnm/crypto/key/aes",
-	                      "test-aes-key.pem, test-aes-password",
+	g_test_add_data_func ("/libnm/crypto/key/aes-128",
+	                      "test-aes-128-key.pem, test-aes-password",
+	                      test_key);
+	g_test_add_data_func ("/libnm/crypto/key/aes-256",
+	                      "test-aes-256-key.pem, test-aes-password",
 	                      test_key);
 	g_test_add_data_func ("/libnm/crypto/key/decrypted",
 	                      "test-key-only-decrypted.pem",
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 3b9debd9..38531248 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2008 - 2011 Red Hat, Inc.
+ * Copyright 2008 - 2018 Red Hat, Inc.
  *
  */
 
@@ -43,6 +43,7 @@
 #include "nm-setting-bridge-port.h"
 #include "nm-setting-cdma.h"
 #include "nm-setting-connection.h"
+#include "nm-setting-ethtool.h"
 #include "nm-setting-generic.h"
 #include "nm-setting-gsm.h"
 #include "nm-setting-infiniband.h"
@@ -61,9 +62,11 @@
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless.h"
 #include "nm-setting-wireless-security.h"
+#include "nm-setting-wpan.h"
 #include "nm-simple-connection.h"
 #include "nm-keyfile-internal.h"
 #include "nm-utils/nm-dedup-multi.h"
+#include "nm-ethtool-utils.h"
 
 #include "test-general-enums.h"
 
@@ -222,8 +225,8 @@ test_nm_g_slice_free_fcn (void)
 	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 (int);
+	(nm_g_slice_free_fcn (int)) (p);
 
 	p = g_slice_new (gint64);
 	nm_g_slice_free_fcn_gint64 (p);
@@ -235,7 +238,7 @@ test_nm_g_slice_free_fcn (void)
 /*****************************************************************************/
 
 static void
-_do_test_nm_utils_strsplit_set (const char *str, ...)
+_do_test_nm_utils_strsplit_set (gboolean escape, const char *str, ...)
 {
 	gs_unref_ptrarray GPtrArray *args_array = g_ptr_array_new ();
 	const char *const*args;
@@ -252,7 +255,7 @@ _do_test_nm_utils_strsplit_set (const char *str, ...)
 
 	args = (const char *const*) args_array->pdata;
 
-	words = nm_utils_strsplit_set (str, " \t\n");
+	words = nm_utils_strsplit_set (str, " \t\n", escape);
 
 	if (!args[0]) {
 		g_assert (!words);
@@ -265,7 +268,7 @@ _do_test_nm_utils_strsplit_set (const char *str, ...)
 		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 (escape || NM_STRCHAR_ALL (args[i], ch, !NM_IN_SET (ch, ' ', '\t', '\n')));
 		g_assert_cmpstr (args[i], ==, words[i]);
 	}
 }
@@ -276,21 +279,29 @@ _do_test_nm_utils_strsplit_set (const char *str, ...)
 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 "
+	do_test_nm_utils_strsplit_set (FALSE, NULL);
+	do_test_nm_utils_strsplit_set (FALSE, "");
+	do_test_nm_utils_strsplit_set (FALSE, "\t");
+	do_test_nm_utils_strsplit_set (FALSE, " \t\n");
+	do_test_nm_utils_strsplit_set (FALSE, "a", "a");
+	do_test_nm_utils_strsplit_set (FALSE, "a b", "a", "b");
+	do_test_nm_utils_strsplit_set (FALSE, "a\rb", "a\rb");
+	do_test_nm_utils_strsplit_set (FALSE, "  a\rb  ", "a\rb");
+	do_test_nm_utils_strsplit_set (FALSE, "  a bbbd afds ere", "a", "bbbd", "afds", "ere");
+	do_test_nm_utils_strsplit_set (FALSE,
+	                               "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");
+	do_test_nm_utils_strsplit_set (TRUE, "\\", "\\");
+	do_test_nm_utils_strsplit_set (TRUE, "\\ ", "\\ ");
+	do_test_nm_utils_strsplit_set (TRUE, "\\\\", "\\\\");
+	do_test_nm_utils_strsplit_set (TRUE, "\\\t", "\\\t");
+	do_test_nm_utils_strsplit_set (TRUE, "foo\\", "foo\\");
+	do_test_nm_utils_strsplit_set (TRUE, "bar foo\\", "bar", "foo\\");
+	do_test_nm_utils_strsplit_set (TRUE, "\\ a b\\ \\  c", "\\ a", "b\\ \\ ", "c");
 }
 
 /*****************************************************************************/
@@ -2594,6 +2605,7 @@ test_connection_diff_a_only (void)
 			{ NM_SETTING_CONNECTION_AUTOCONNECT,          NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,  NM_SETTING_DIFF_RESULT_IN_A },
+			{ NM_SETTING_CONNECTION_MULTI_CONNECT,        NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_READ_ONLY,            NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_PERMISSIONS,          NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_ZONE,                 NM_SETTING_DIFF_RESULT_IN_A },
@@ -2606,6 +2618,7 @@ test_connection_diff_a_only (void)
 			{ NM_SETTING_CONNECTION_LLDP,                 NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_AUTH_RETRIES,         NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_MDNS,                 NM_SETTING_DIFF_RESULT_IN_A },
+			{ NM_SETTING_CONNECTION_LLMNR,                NM_SETTING_DIFF_RESULT_IN_A },
 			{ NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }
 		} },
 		{ NM_SETTING_WIRED_SETTING_NAME, {
@@ -5328,18 +5341,17 @@ test_hexstr2bin (void)
 		{ "aab:ccc:ddd" },
 		{ "aab::ccc:ddd" },
 	};
-	GBytes *b;
 	guint i;
 
 	for (i = 0; i < G_N_ELEMENTS (items); i++) {
+		gs_unref_bytes GBytes *b = NULL;
+
 		b = nm_utils_hexstr2bin (items[i].str);
-		if (items[i].expected_len) {
+		if (items[i].expected_len)
 			g_assert (b);
-			g_assert_cmpint (g_bytes_get_size (b), ==, items[i].expected_len);
-			g_assert (memcmp (g_bytes_get_data (b, NULL), items[i].expected, g_bytes_get_size (b)) == 0);
-			g_bytes_unref (b);
-		} else
-			g_assert (b == NULL);
+		else
+			g_assert (!b);
+		g_assert (nm_utils_gbytes_equal_mem (b, items[i].expected, items[i].expected_len));
 	}
 }
 
@@ -6033,7 +6045,7 @@ static void
 test_nm_utils_is_power_of_two (void)
 {
 	guint64 xyes, xno;
-	gint i, j;
+	int i, j;
 	GRand *rand = nmtst_get_rand ();
 	int numbits;
 
@@ -6124,7 +6136,7 @@ _test_find_binary_search_do (const int *array, gsize len)
 
 	expected_result = _nm_utils_ptrarray_find_first (parray, len, pneedle);
 
-	idx = _nm_utils_ptrarray_find_binary_search (parray, len, pneedle, _test_find_binary_search_cmp, NULL, &idx_first, &idx_last);
+	idx = nm_utils_ptrarray_find_binary_search (parray, len, pneedle, _test_find_binary_search_cmp, NULL, &idx_first, &idx_last);
 	if (expected_result >= 0) {
 		g_assert_cmpint (expected_result, ==, idx);
 	} else {
@@ -6186,12 +6198,12 @@ _test_find_binary_search_do_uint32 (const int *int_array, gsize len)
 			expected_result = idx;
 	}
 
-	idx = _nm_utils_array_find_binary_search (array,
-	                                          sizeof (guint32),
-	                                          len,
-	                                          &NEEDLE,
-	                                          nm_cmp_uint32_p_with_data,
-	                                          NULL);
+	idx = nm_utils_array_find_binary_search (array,
+	                                         sizeof (guint32),
+	                                         len,
+	                                         &NEEDLE,
+	                                         nm_cmp_uint32_p_with_data,
+	                                         NULL);
 	if (expected_result >= 0)
 		g_assert_cmpint (expected_result, ==, idx);
 	else {
@@ -6291,11 +6303,11 @@ test_nm_utils_ptrarray_find_binary_search_with_duplicates (void)
 			for (i = 0; i < i_len + BIN_SEARCH_W_DUPS_JITTER; i++) {
 				gconstpointer p = GINT_TO_POINTER (i);
 
-				idx = _nm_utils_ptrarray_find_binary_search (arr, i_len, p, _test_bin_search2_cmp, NULL, &idx_first, &idx_last);
+				idx = nm_utils_ptrarray_find_binary_search (arr, i_len, p, _test_bin_search2_cmp, NULL, &idx_first, &idx_last);
 
 				idx_first2 = _nm_utils_ptrarray_find_first (arr, i_len, p);
 
-				idx2 = _nm_utils_array_find_binary_search (arr, sizeof (gpointer), i_len, &p, _test_bin_search2_cmp_p, NULL);
+				idx2 = nm_utils_array_find_binary_search (arr, sizeof (gpointer), i_len, &p, _test_bin_search2_cmp_p, NULL);
 				g_assert_cmpint (idx, ==, idx2);
 
 				if (idx_first2 < 0) {
@@ -6547,74 +6559,161 @@ test_nm_utils_enum (void)
 /*****************************************************************************/
 
 static void
-do_test_utils_str_utf8safe (const char *str, const char *expected, NMUtilsStrUtf8SafeFlags flags)
+_do_test_utils_str_utf8safe_unescape (const char *str, const char *expected, gsize expected_len)
 {
-	const char *str_safe, *s;
-	gs_free char *str2 = NULL;
-	gs_free char *str3 = NULL;
+	gsize l;
+	const char *s;
+	gs_free gpointer buf_free_1 = NULL;
+	gs_free char *str_free_1 = NULL;
 
-	str_safe = nm_utils_str_utf8safe_escape (str, flags, &str2);
+	s = nm_utils_buf_utf8safe_unescape (str, &l, &buf_free_1);
+	g_assert_cmpint (expected_len, ==, l);
+	g_assert_cmpstr (s, ==, expected);
 
-	str3 = nm_utils_str_utf8safe_escape_cp (str, flags);
-	g_assert_cmpstr (str3, ==, str_safe);
-	g_assert ((!str && !str3) || (str != str3));
-	g_clear_pointer (&str3, g_free);
+	if (str == NULL) {
+		g_assert (!s);
+		g_assert (!buf_free_1);
+		g_assert_cmpint (l, ==, 0);
+	} else {
+		g_assert (s);
+		if (!strchr (str, '\\')) {
+			g_assert (!buf_free_1);
+			g_assert (s == str);
+			g_assert_cmpint (l, ==, strlen (str));
+		} else {
+			g_assert (buf_free_1);
+			g_assert (s == buf_free_1);
+			g_assert (memcmp (s, expected, expected_len) == 0);
+		}
+	}
+
+	if (   expected
+	    && l == strlen (expected)) {
+		/* there are no embeeded NULs. Check that nm_utils_str_utf8safe_unescape() yields the same result. */
+		s = nm_utils_str_utf8safe_unescape (str, &str_free_1);
+		g_assert_cmpstr (s, ==, expected);
+		if (strchr (str, '\\')) {
+			g_assert (str_free_1 != str);
+			g_assert (s == str_free_1);
+		} else
+			g_assert (s == str);
+	}
+}
+
+#define do_test_utils_str_utf8safe_unescape(str, expected) \
+	_do_test_utils_str_utf8safe_unescape (""str"", expected, NM_STRLEN (expected))
+
+static void
+_do_test_utils_str_utf8safe (const char *str, gsize str_len, const char *expected, NMUtilsStrUtf8SafeFlags flags)
+{
+	const char *str_safe;
+	const char *buf_safe;
+	const char *s;
+	gs_free gpointer buf_free_1 = NULL;
+	gs_free char *str_free_1 = NULL;
+	gs_free char *str_free_2 = NULL;
+	gs_free char *str_free_3 = NULL;
+	gs_free char *str_free_4 = NULL;
+	gs_free char *str_free_5 = NULL;
+	gs_free char *str_free_6 = NULL;
+	gs_free char *str_free_7 = NULL;
+	gs_free char *str_free_8 = NULL;
+	gboolean str_has_nul = FALSE;
+
+	buf_safe = nm_utils_buf_utf8safe_escape (str, str_len, flags, &str_free_1);
+
+	str_safe = nm_utils_str_utf8safe_escape (str, flags, &str_free_2);
+
+	if (str_len == 0) {
+		g_assert (buf_safe == NULL);
+		g_assert (str_free_1 == NULL);
+		g_assert (str_safe == str);
+		g_assert (str == NULL || str[0] == '\0');
+		g_assert (str_free_2 == NULL);
+	} else if (str_len == strlen (str)) {
+		g_assert (buf_safe);
+		g_assert_cmpstr (buf_safe, ==, str_safe);
+
+		/* nm_utils_buf_utf8safe_escape() can only return a pointer equal to the input string,
+		 * if and only if str_len is negative. Otherwise, the input str won't be NUL terminated
+		 * and cannot be returned. */
+		g_assert (buf_safe != str);
+		g_assert (buf_safe == str_free_1);
+	} else
+		str_has_nul = TRUE;
+
+	str_free_3 = nm_utils_str_utf8safe_escape_cp (str, flags);
+	g_assert_cmpstr (str_free_3, ==, str_safe);
+	g_assert ((!str && !str_free_3) || (str != str_free_3));
+
+	if (str_len > 0)
+		_do_test_utils_str_utf8safe_unescape (buf_safe, str, str_len);
 
 	if (expected == NULL) {
+		g_assert (!str_has_nul);
+
 		g_assert (str_safe == str);
-		g_assert (!str2);
+		g_assert (!str_free_2);
 		if (str) {
 			g_assert (!strchr (str, '\\'));
 			g_assert (g_utf8_validate (str, -1, NULL));
 		}
 
-		g_assert (str == nm_utils_str_utf8safe_unescape (str_safe, &str3));
-		g_assert (!str3);
+		g_assert (str == nm_utils_str_utf8safe_unescape (str_safe, &str_free_4));
+		g_assert (!str_free_4);
 
-		str3 = nm_utils_str_utf8safe_unescape_cp (str_safe);
+		str_free_5 = nm_utils_str_utf8safe_unescape_cp (str_safe);
 		if (str) {
-			g_assert (str3 != str);
-			g_assert_cmpstr (str3, ==, str);
+			g_assert (str_free_5 != str);
+			g_assert_cmpstr (str_free_5, ==, str);
 		} else
-			g_assert (!str3);
-		g_clear_pointer (&str3, g_free);
+			g_assert (!str_free_5);
 		return;
 	}
 
-	g_assert (str);
-	g_assert (str_safe != str);
-	g_assert (str_safe == str2);
-	g_assert (   strchr (str, '\\')
-	          || !g_utf8_validate (str, -1, NULL)
-	          || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII)
-	              && NM_STRCHAR_ANY (str, ch, (guchar) ch >= 127))
-	          || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)
-	              && NM_STRCHAR_ANY (str, ch, (guchar) ch < ' ')));
-	g_assert (g_utf8_validate (str_safe, -1, NULL));
+	if (!str_has_nul) {
+		g_assert (str);
+		g_assert (str_safe != str);
+		g_assert (str_safe == str_free_2);
+		g_assert (   strchr (str, '\\')
+		          || !g_utf8_validate (str, -1, NULL)
+		          || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII)
+		              && NM_STRCHAR_ANY (str, ch, (guchar) ch >= 127))
+		          || (   NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)
+		              && NM_STRCHAR_ANY (str, ch, (guchar) ch < ' ')));
+		g_assert (g_utf8_validate (str_safe, -1, NULL));
 
-	str3 = g_strcompress (str_safe);
-	g_assert_cmpstr (str, ==, str3);
-	g_clear_pointer (&str3, g_free);
+		str_free_6 = g_strcompress (str_safe);
+		g_assert_cmpstr (str, ==, str_free_6);
 
-	str3 = nm_utils_str_utf8safe_unescape_cp (str_safe);
-	g_assert (str3 != str);
-	g_assert_cmpstr (str3, ==, str);
-	g_clear_pointer (&str3, g_free);
+		str_free_7 = nm_utils_str_utf8safe_unescape_cp (str_safe);
+		g_assert (str_free_7 != str);
+		g_assert_cmpstr (str_free_7, ==, str);
 
-	s = nm_utils_str_utf8safe_unescape (str_safe, &str3);
-	g_assert (str3 != str);
-	g_assert (s == str3);
-	g_assert_cmpstr (str3, ==, str);
-	g_clear_pointer (&str3, g_free);
+		s = nm_utils_str_utf8safe_unescape (str_safe, &str_free_8);
+		g_assert (str_free_8 != str);
+		g_assert (s == str_free_8);
+		g_assert_cmpstr (str_free_8, ==, str);
+
+		g_assert_cmpstr (str_safe, ==, expected);
+
+		return;
+	}
+
+	g_assert_cmpstr (buf_safe, ==, expected);
 
-	g_assert_cmpstr (str_safe, ==, expected);
 }
+#define do_test_utils_str_utf8safe(str, expected, flags) \
+	_do_test_utils_str_utf8safe (""str"", NM_STRLEN (str), expected, flags)
 
 static void
 test_utils_str_utf8safe (void)
 {
-	do_test_utils_str_utf8safe (NULL, NULL,                                       NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	_do_test_utils_str_utf8safe (NULL, 0, NULL,                                   NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+
 	do_test_utils_str_utf8safe ("", NULL,                                         NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\\", "\\\\",                                     NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\\a", "\\\\a",                                   NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
 	do_test_utils_str_utf8safe ("\314", "\\314",                                  NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
 	do_test_utils_str_utf8safe ("\314\315x\315\315x", "\\314\\315x\\315\\315x",   NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
 	do_test_utils_str_utf8safe ("\314\315xx", "\\314\\315xx",                     NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
@@ -6636,6 +6735,18 @@ test_utils_str_utf8safe (void)
 	do_test_utils_str_utf8safe ("㈞abä㈞b", NULL,                                 NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
 	do_test_utils_str_utf8safe ("abäb", "ab\\303\\244b",                          NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII);
 	do_test_utils_str_utf8safe ("ab\ab", "ab\\007b",                              NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL);
+
+	do_test_utils_str_utf8safe ("\0", "\\000",                                    NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\0a\0", "\\000a\\000",                           NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\\\0", "\\\\\\000",                              NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\n\0", "\n\\000",                                NM_UTILS_STR_UTF8_SAFE_FLAG_NONE);
+	do_test_utils_str_utf8safe ("\n\0", "\\012\\000",                             NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL);
+
+	do_test_utils_str_utf8safe_unescape ("\n\\0", "\n\0");
+	do_test_utils_str_utf8safe_unescape ("\n\\01", "\n\01");
+	do_test_utils_str_utf8safe_unescape ("\n\\012", "\n\012");
+	do_test_utils_str_utf8safe_unescape ("\n\\.", "\n.");
+	do_test_utils_str_utf8safe_unescape ("\\n\\.3\\r", "\n.3\r");
 }
 
 /*****************************************************************************/
@@ -6951,7 +7062,7 @@ test_route_attributes_format (void)
 /*****************************************************************************/
 
 static gboolean
-do_test_nm_set_out_called (gint *call_count)
+do_test_nm_set_out_called (int *call_count)
 {
 	(*call_count)++;
 	return TRUE;
@@ -7005,6 +7116,120 @@ test_get_start_time_for_pid (void)
 
 /*****************************************************************************/
 
+static void
+test_nm_va_args_macros (void)
+{
+#define GET_NARG_1(...) \
+	NM_NARG (__VA_ARGS__)
+
+	g_assert_cmpint ( 0, ==, GET_NARG_1 ());
+	g_assert_cmpint ( 1, ==, GET_NARG_1 (x));
+	g_assert_cmpint ( 2, ==, GET_NARG_1 ( ,  ));
+	g_assert_cmpint ( 2, ==, GET_NARG_1 ( , x));
+	g_assert_cmpint ( 2, ==, GET_NARG_1 (x,  ));
+	g_assert_cmpint ( 2, ==, GET_NARG_1 (x, x));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 ( ,  ,  ));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 ( ,  , x));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 ( , x,  ));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 ( , x, x));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 (x,  ,  ));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 (x,  , x));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 (x, x,  ));
+	g_assert_cmpint ( 3, ==, GET_NARG_1 (x, x, x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( ,  ,  ,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( ,  ,  , x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( ,  , x,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( ,  , x, x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( , x,  ,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( , x,  , x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( , x, x,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 ( , x, x, x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x,  ,  ,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x,  ,  , x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x,  , x,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x,  , x, x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x, x,  ,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x, x,  , x));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x, x, x,  ));
+	g_assert_cmpint ( 4, ==, GET_NARG_1 (x, x, x, x));
+
+	g_assert_cmpint ( 5, ==, GET_NARG_1 (x, x, x, x, x));
+	g_assert_cmpint ( 6, ==, GET_NARG_1 (x, x, x, x, x, x));
+	g_assert_cmpint ( 7, ==, GET_NARG_1 (x, x, x, x, x, x, x));
+	g_assert_cmpint ( 8, ==, GET_NARG_1 (x, x, x, x, x, x, x, x));
+	g_assert_cmpint ( 9, ==, GET_NARG_1 (x, x, x, x, x, x, x, x, x));
+	g_assert_cmpint (10, ==, GET_NARG_1 (x, x, x, x, x, x, x, x, x, x));
+
+	G_STATIC_ASSERT_EXPR (0 == GET_NARG_1 ());
+	G_STATIC_ASSERT_EXPR (1 == GET_NARG_1 (x));
+	G_STATIC_ASSERT_EXPR (2 == GET_NARG_1 (x, x));
+}
+
+/*****************************************************************************/
+
+static void
+test_ethtool_offload (void)
+{
+	const NMEthtoolData *d;
+
+	g_assert_cmpint (nm_ethtool_id_get_by_name ("invalid"),    ==, NM_ETHTOOL_ID_UNKNOWN);
+	g_assert_cmpint (nm_ethtool_id_get_by_name ("feature-rx"), ==, NM_ETHTOOL_ID_FEATURE_RX);
+
+	d = nm_ethtool_data_get_by_optname (NM_ETHTOOL_OPTNAME_FEATURE_RXHASH);
+	g_assert (d);
+	g_assert_cmpint (d->id, ==, NM_ETHTOOL_ID_FEATURE_RXHASH);
+	g_assert_cmpstr (d->optname, ==, NM_ETHTOOL_OPTNAME_FEATURE_RXHASH);
+}
+
+static void
+test_nm_utils_escape_spaces (void)
+{
+	char *to_free;
+
+	g_assert_cmpstr (_nm_utils_escape_spaces (NULL, &to_free), ==, NULL);
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces ("", &to_free), ==, "");
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces (" ", &to_free), ==, "\\ ");
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces ("\t ", &to_free), ==, "\\\t\\ ");
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces ("abc", &to_free), ==, "abc");
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces ("abc def", &to_free), ==, "abc\\ def");
+	g_free (to_free);
+
+	g_assert_cmpstr (_nm_utils_escape_spaces ("abc\tdef", &to_free), ==, "abc\\\tdef");
+	g_free (to_free);
+}
+
+static void
+test_nm_utils_unescape_spaces (void)
+{
+#define CHECK_STR(in, out) \
+	G_STMT_START { \
+		gs_free char *str = g_strdup (in); \
+		\
+		g_assert_cmpstr (_nm_utils_unescape_spaces (str), ==, out); \
+	} G_STMT_END
+
+	CHECK_STR ("\\a", "\\a");
+	CHECK_STR ("foobar", "foobar");
+	CHECK_STR ("foo bar", "foo bar");
+	CHECK_STR ("foo\\ bar", "foo bar");
+	CHECK_STR ("foo\\", "foo\\");
+	CHECK_STR ("\\\\\t", "\\\t");
+
+#undef CHECK_STR
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int main (int argc, char **argv)
@@ -7144,8 +7369,8 @@ int main (int argc, char **argv)
 
 	g_test_add_func ("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
 	g_test_add_func ("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two);
-	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_ptrarray_find_binary_search_with_duplicates", test_nm_utils_ptrarray_find_binary_search_with_duplicates);
+	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_ptrarray_find_binary_search_with_duplicates", test_nm_utils_ptrarray_find_binary_search_with_duplicates);
 	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);
 
@@ -7153,12 +7378,16 @@ int main (int argc, char **argv)
 	g_test_add_func ("/core/general/_nm_utils_dns_option_find_idx", test_nm_utils_dns_option_find_idx);
 	g_test_add_func ("/core/general/_nm_utils_validate_json", test_nm_utils_check_valid_json);
 	g_test_add_func ("/core/general/_nm_utils_team_config_equal", test_nm_utils_team_config_equal);
+	g_test_add_func ("/core/general/_nm_utils_escape_spaces", test_nm_utils_escape_spaces);
+	g_test_add_func ("/core/general/_nm_utils_unescape_spaces", test_nm_utils_unescape_spaces);
 	g_test_add_func ("/core/general/test_nm_utils_enum", test_nm_utils_enum);
 	g_test_add_func ("/core/general/nm-set-out", test_nm_set_out);
 	g_test_add_func ("/core/general/route_attributes/parse", test_route_attributes_parse);
 	g_test_add_func ("/core/general/route_attributes/format", test_route_attributes_format);
 
 	g_test_add_func ("/core/general/get_start_time_for_pid", test_get_start_time_for_pid);
+	g_test_add_func ("/core/general/test_nm_va_args_macros", test_nm_va_args_macros);
+	g_test_add_func ("/core/general/test_ethtool_offload", test_ethtool_offload);
 
 	return g_test_run ();
 }
diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c
index 672c72b6..d941fa22 100644
--- a/libnm-core/tests/test-keyfile.c
+++ b/libnm-core/tests/test-keyfile.c
@@ -127,10 +127,8 @@ _assert_gbytes (GBytes *bytes, gconstpointer data, gssize len)
 
 	if (!len)
 		g_assert (!bytes);
-	else {
-		g_assert_cmpint (g_bytes_get_size (bytes), ==, len);
-		g_assert (memcmp (g_bytes_get_data (bytes, NULL), data, len) == 0);
-	}
+
+	g_assert (nm_utils_gbytes_equal_mem (bytes, data, len));
 }
 
 static GKeyFile *
@@ -277,8 +275,9 @@ _keyfile_convert (NMConnection **con,
 
 				b1 = nm_setting_802_1x_get_ca_cert_blob (s1);
 				b2 = nm_setting_802_1x_get_ca_cert_blob (s2);
-				g_assert_cmpint (g_bytes_get_size (b1), ==, g_bytes_get_size (b2));
-				g_assert (memcmp (g_bytes_get_data (b1, NULL), g_bytes_get_data (b2, NULL), g_bytes_get_size (b1)) == 0);
+				g_assert (b1);
+				g_assert (b2);
+				g_assert (g_bytes_equal (b1, b2));
 				break;
 			}
 			default:
@@ -344,8 +343,7 @@ _test_8021x_cert_check (NMConnection *con,
 		}
 
 		g_assert (blob);
-		g_assert_cmpint (g_bytes_get_size (blob), ==, val_len);
-		g_assert (!memcmp (g_bytes_get_data (blob, NULL), value, val_len));
+		g_assert (nm_utils_gbytes_equal_mem (blob, value, val_len));
 
 		kval = g_key_file_get_string (keyfile, "802-1x", "ca-cert", NULL);
 		g_assert (kval);
diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c
index e71d6b7c..c8eda381 100644
--- a/libnm-core/tests/test-setting.c
+++ b/libnm-core/tests/test-setting.c
@@ -23,9 +23,12 @@
 #include <string.h>
 
 #include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-core-internal.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-bond.h"
 #include "nm-setting-dcb.h"
+#include "nm-setting-ethtool.h"
 #include "nm-setting-team.h"
 #include "nm-setting-team-port.h"
 #include "nm-setting-tc-config.h"
@@ -34,6 +37,7 @@
 #include "nm-simple-connection.h"
 #include "nm-setting-connection.h"
 #include "nm-errors.h"
+#include "nm-keyfile-internal.h"
 
 #include "nm-utils/nm-test-utils.h"
 
@@ -46,7 +50,7 @@ compare_blob_data (const char *test,
                    const char *key_path,
                    GBytes *key)
 {
-	char *contents = NULL;
+	gs_free char *contents = NULL;
 	gsize len = 0;
 	GError *error = NULL;
 	gboolean success;
@@ -57,18 +61,18 @@ compare_blob_data (const char *test,
 	nmtst_assert_success (success, error);
 
 	g_assert_cmpmem (contents, len, g_bytes_get_data (key, NULL), g_bytes_get_size (key));
-
-	g_free (contents);
 }
 
 static void
 check_scheme_path (GBytes *value, const char *path)
 {
 	const guint8 *p;
+	gsize l;
 
 	g_assert (value);
 
-	p = g_bytes_get_data (value, NULL);
+	p = g_bytes_get_data (value, &l);
+	g_assert_cmpint (l, ==, strlen (path) + NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1);
 	g_assert (memcmp (p, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0);
 	p += strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
 	g_assert (memcmp (p, path, strlen (path)) == 0);
@@ -998,7 +1002,7 @@ test_runner_loadbalance_sync_from_config (void)
 {
 	gs_unref_ptrarray GPtrArray *tx_hash = NULL;
 
-	tx_hash = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+	tx_hash = g_ptr_array_new_with_free_func (g_free);
 	g_ptr_array_add (tx_hash, g_strdup ("eth"));
 	g_ptr_array_add (tx_hash, g_strdup ("ipv4"));
 	g_ptr_array_add (tx_hash, g_strdup ("ipv6"));
@@ -1035,7 +1039,7 @@ test_runner_lacp_sync_from_config (void)
 {
 	gs_unref_ptrarray GPtrArray *tx_hash = NULL;
 
-	tx_hash = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+	tx_hash = g_ptr_array_new_with_free_func (g_free);
 	g_ptr_array_add (tx_hash, g_strdup ("eth"));
 	g_ptr_array_add (tx_hash, g_strdup ("ipv4"));
 	g_ptr_array_add (tx_hash, g_strdup ("ipv6"));
@@ -1263,6 +1267,307 @@ test_team_port_full_config (void)
 /*****************************************************************************/
 
 static void
+test_ethtool_1 (void)
+{
+	gs_unref_object NMConnection *con = NULL;
+	gs_unref_object NMConnection *con2 = NULL;
+	gs_unref_object NMConnection *con3 = NULL;
+	gs_unref_variant GVariant *variant = NULL;
+	gs_free_error GError *error = NULL;
+	gs_unref_keyfile GKeyFile *keyfile = NULL;
+	NMSettingConnection *s_con;
+	NMSettingEthtool *s_ethtool;
+	NMSettingEthtool *s_ethtool2;
+	NMSettingEthtool *s_ethtool3;
+
+	con = nmtst_create_minimal_connection ("ethtool-1",
+	                                        NULL,
+	                                        NM_SETTING_WIRED_SETTING_NAME,
+	                                        &s_con);
+	s_ethtool = NM_SETTING_ETHTOOL (nm_setting_ethtool_new ());
+	nm_connection_add_setting (con, NM_SETTING (s_ethtool));
+
+	nm_setting_ethtool_set_feature (s_ethtool,
+	                                NM_ETHTOOL_OPTNAME_FEATURE_RX,
+	                                NM_TERNARY_TRUE);
+	nm_setting_ethtool_set_feature (s_ethtool,
+	                                NM_ETHTOOL_OPTNAME_FEATURE_LRO,
+	                                NM_TERNARY_FALSE);
+
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RX), ==, NM_TERNARY_TRUE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_SG),  ==, NM_TERNARY_DEFAULT);
+
+	nmtst_connection_normalize (con);
+
+	variant = nm_connection_to_dbus (con, NM_CONNECTION_SERIALIZE_ALL);
+
+	con2 = nm_simple_connection_new_from_dbus (variant, &error);
+	nmtst_assert_success (con2, error);
+
+	s_ethtool2 = NM_SETTING_ETHTOOL (nm_connection_get_setting (con2, NM_TYPE_SETTING_ETHTOOL));
+
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_RX),  ==, NM_TERNARY_TRUE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_SG),  ==, NM_TERNARY_DEFAULT);
+
+	nmtst_assert_connection_verifies_without_normalization (con2);
+
+	nmtst_assert_connection_equals (con, FALSE, con2, FALSE);
+
+	keyfile = nm_keyfile_write (con, NULL, NULL, &error);
+	nmtst_assert_success (keyfile, error);
+
+	con3 = nm_keyfile_read (keyfile,
+	                        "ethtool-keyfile-name",
+	                        NULL,
+	                        NULL,
+	                        NULL,
+	                        &error);
+	nmtst_assert_success (con3, error);
+
+	nmtst_connection_normalize (con3);
+
+	nmtst_assert_connection_equals (con, FALSE, con3, FALSE);
+
+	s_ethtool3 = NM_SETTING_ETHTOOL (nm_connection_get_setting (con3, NM_TYPE_SETTING_ETHTOOL));
+
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_RX),  ==, NM_TERNARY_TRUE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_SG),  ==, NM_TERNARY_DEFAULT);
+}
+
+/*****************************************************************************/
+
+static void
+test_sriov_vf (void)
+{
+	NMSriovVF *vf1, *vf2;
+	GError *error = NULL;
+	char *str;
+
+	vf1 = nm_sriov_vf_new (1);
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("00:11:22:33:44:55"));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (TRUE));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, g_variant_new_uint32 (100));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, g_variant_new_uint32 (500));
+
+	str = nm_utils_sriov_vf_to_str (vf1, FALSE, &error);
+	g_assert_no_error (error);
+	g_assert_cmpstr (str, ==, "1 mac=00:11:22:33:44:55 max-tx-rate=500 min-tx-rate=100 spoof-check=true trust=false");
+	g_free (str);
+
+	vf2 = nm_utils_sriov_vf_from_str (" 1  mac=00:11:22:33:44:55  max-tx-rate=500 min-tx-rate=100", &error);
+	nmtst_assert_success (vf2, error);
+	nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (FALSE));
+	nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (TRUE));
+	nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (TRUE));
+	nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, NULL);
+	nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
+
+	g_assert (nm_sriov_vf_equal (vf1, vf2));
+
+	nm_sriov_vf_unref (vf1);
+	nm_sriov_vf_unref (vf2);
+}
+
+static void
+test_sriov_vf_dup (void)
+{
+	NMSriovVF *vf1, *vf2;
+
+	vf1 = nm_sriov_vf_new (1);
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("foobar"));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, g_variant_new_uint32 (10));
+	nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, g_variant_new_uint32 (1000));
+	nm_sriov_vf_add_vlan (vf1, 80);
+	nm_sriov_vf_set_vlan_qos (vf1, 80, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+	vf2 = nm_sriov_vf_dup (vf1);
+	g_assert (nm_sriov_vf_equal (vf1, vf2));
+
+	nm_sriov_vf_unref (vf1);
+	nm_sriov_vf_unref (vf2);
+}
+
+static void
+test_sriov_vf_vlan (void)
+{
+	NMSriovVF *vf;
+	const guint *vlan_ids;
+	guint num;
+	GError *error = NULL;
+	gs_free char *str = NULL;
+
+	vf = nm_sriov_vf_new (19);
+	nm_sriov_vf_set_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("00:11:22"));
+	g_assert (nm_sriov_vf_add_vlan (vf, 80));
+	g_assert (!nm_sriov_vf_add_vlan (vf, 80));
+	g_assert (nm_sriov_vf_add_vlan (vf, 82));
+	g_assert (nm_sriov_vf_add_vlan (vf, 83));
+	g_assert (nm_sriov_vf_add_vlan (vf, 81));
+	g_assert (!nm_sriov_vf_remove_vlan (vf, 100));
+	g_assert (nm_sriov_vf_remove_vlan (vf, 82));
+	nm_sriov_vf_set_vlan_qos (vf, 81, 0xabba);
+	nm_sriov_vf_set_vlan_protocol (vf, 81, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+	vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num);
+	g_assert (vlan_ids);
+	g_assert_cmpint (num, ==, 3);
+	g_assert_cmpint (vlan_ids[0], ==, 80);
+	g_assert_cmpint (vlan_ids[1], ==, 81);
+	g_assert_cmpint (vlan_ids[2], ==, 83);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 80), ==, 0x0);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 80), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 81), ==, 0xabba);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 81), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+	nm_sriov_vf_unref (vf);
+
+	vf = nm_utils_sriov_vf_from_str ("20 spoof-check=false vlans=85.0.q;4000.0x20.ad;81.10;83", &error);
+	nmtst_assert_success (vf, error);
+	vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num);
+	g_assert (vlan_ids);
+	g_assert_cmpint (num, ==, 4);
+	g_assert_cmpint (vlan_ids[0], ==, 81);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 81), ==, 10);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 81), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+	g_assert_cmpint (vlan_ids[1], ==, 83);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 83), ==, 0);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 83), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+	g_assert_cmpint (vlan_ids[2], ==, 85);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 85), ==, 0);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 85), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+	g_assert_cmpint (vlan_ids[3], ==, 4000);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 4000), ==, 0x20);
+	g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 4000), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
+
+	str = nm_utils_sriov_vf_to_str (vf, FALSE, &error);
+	nmtst_assert_success (str, error);
+	g_assert_cmpstr (str, ==, "20 spoof-check=false vlans=81.10;83;85;4000.32.ad");
+
+	nm_sriov_vf_unref (vf);
+}
+
+static void
+test_sriov_setting (void)
+{
+	gs_unref_object NMConnection *con = NULL;
+	NMSettingConnection *s_con;
+	NMSettingSriov *s_sriov = NULL;
+	NMSriovVF *vf1, *vf2, *vf3;
+	GError *error = NULL;
+	gboolean success;
+
+	con = nm_simple_connection_new ();
+
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	nm_connection_add_setting (con, NM_SETTING (s_con));
+
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test SR-IOV connection",
+	              NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0",
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	              NULL);
+
+	nm_connection_add_setting (con, nm_setting_wired_new ());
+
+	s_sriov = (NMSettingSriov *) nm_setting_sriov_new ();
+	nm_connection_add_setting (con, NM_SETTING (s_sriov));
+
+	g_object_set (s_sriov, NM_SETTING_SRIOV_TOTAL_VFS, 16, NULL);
+	nm_setting_sriov_add_vf (s_sriov, (vf1 = nm_sriov_vf_new (0)));
+	nm_setting_sriov_add_vf (s_sriov, (vf2 = nm_sriov_vf_new (4)));
+	nm_setting_sriov_add_vf (s_sriov, (vf3 = nm_sriov_vf_new (10)));
+	g_assert (nm_setting_sriov_remove_vf_by_index (s_sriov, 4));
+	nm_sriov_vf_unref (vf2);
+	nm_setting_sriov_add_vf (s_sriov, (vf2 = nm_sriov_vf_new (2)));
+
+	nmtst_assert_connection_verifies_and_normalizable (con);
+	nmtst_connection_normalize (con);
+	success = nm_setting_verify ((NMSetting *) s_sriov, con, &error);
+	nmtst_assert_success (success, error);
+
+	g_assert_cmpint (nm_setting_sriov_get_num_vfs (s_sriov), ==, 3);
+	g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 0)), ==, 0);
+	g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 1)), ==, 2);
+	g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 2)), ==, 10);
+
+	nm_sriov_vf_unref (vf1);
+	nm_sriov_vf_unref (vf2);
+	nm_sriov_vf_unref (vf3);
+}
+
+typedef struct {
+	guint id;
+	guint qos;
+	bool  proto_ad;
+} VlanData;
+
+static void
+_test_sriov_parse_vlan_one (const char *string, gboolean exp_res, VlanData *data, guint data_length)
+{
+	NMSriovVF *vf;
+	gboolean res;
+	guint i, num_vlans;
+	const guint *vlan_ids;
+
+	vf = nm_sriov_vf_new (1);
+	g_assert (vf);
+
+	res = _nm_sriov_vf_parse_vlans (vf, string, NULL);
+	g_assert_cmpint (res, ==, exp_res);
+
+	if (exp_res) {
+		vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
+		g_assert_cmpint (num_vlans, ==, data_length);
+		for (i = 0; i < num_vlans; i++) {
+			g_assert_cmpint (vlan_ids[i], ==, data[i].id);
+			g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]), ==, data[i].qos);
+			g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]),
+			                 ==,
+			                 data[i].proto_ad ? NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD: NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
+		}
+	}
+
+	nm_sriov_vf_unref (vf);
+}
+
+#define test_sriov_parse_vlan_one(string, result, ...) \
+	{ \
+		VlanData _data[] = { __VA_ARGS__ }; \
+		guint _length = G_N_ELEMENTS (_data); \
+		\
+		_test_sriov_parse_vlan_one (string, result, _data, _length); \
+	}
+
+static void
+test_sriov_parse_vlans (void)
+{
+	test_sriov_parse_vlan_one ("",  FALSE, {});
+	test_sriov_parse_vlan_one ("1", TRUE, {1, 0, 0});
+	test_sriov_parse_vlan_one ("1;2", TRUE, {1, 0, 0}, {2, 0, 0});
+	test_sriov_parse_vlan_one ("4095;;2", TRUE, {2, 0, 0}, {4095, 0, 0});
+	test_sriov_parse_vlan_one ("1 2", FALSE, {});
+	test_sriov_parse_vlan_one ("4096", FALSE, {});
+	test_sriov_parse_vlan_one ("1.10", TRUE, {1, 10, 0});
+	test_sriov_parse_vlan_one ("1.20.ad", TRUE, {1, 20, 1});
+	test_sriov_parse_vlan_one ("1.21.q", TRUE, {1, 21, 0});
+	test_sriov_parse_vlan_one ("9.20.foo", FALSE, {});
+	test_sriov_parse_vlan_one ("1.20.ad.12", FALSE, {});
+	test_sriov_parse_vlan_one ("1;1.10", FALSE, {});
+	test_sriov_parse_vlan_one ("1..1;2", FALSE, {});
+	test_sriov_parse_vlan_one ("1..ad;2", FALSE, {});
+	test_sriov_parse_vlan_one ("1.2.ad;2.0.q;5;3", TRUE, {1, 2, 1}, {2, 0, 0}, {3, 0, 0}, {5, 0, 0});
+}
+
+/*****************************************************************************/
+
+static void
 test_tc_config_qdisc (void)
 {
 	NMTCQdisc *qdisc1, *qdisc2;
@@ -1669,6 +1974,14 @@ main (int argc, char **argv)
 	g_test_add_func ("/libnm/settings/dcb/priorities", test_dcb_priorities_valid);
 	g_test_add_func ("/libnm/settings/dcb/bandwidth-sums", test_dcb_bandwidth_sums);
 
+	g_test_add_func ("/libnm/settings/ethtool/1", test_ethtool_1);
+
+	g_test_add_func ("/libnm/settings/sriov/vf", test_sriov_vf);
+	g_test_add_func ("/libnm/settings/sriov/vf-dup", test_sriov_vf_dup);
+	g_test_add_func ("/libnm/settings/sriov/vf-vlan", test_sriov_vf_vlan);
+	g_test_add_func ("/libnm/settings/sriov/setting", test_sriov_setting);
+	g_test_add_func ("/libnm/settings/sriov/vlans", test_sriov_parse_vlans);
+
 	g_test_add_func ("/libnm/settings/tc_config/qdisc", test_tc_config_qdisc);
 	g_test_add_func ("/libnm/settings/tc_config/action", test_tc_config_action);
 	g_test_add_func ("/libnm/settings/tc_config/tfilter", test_tc_config_tfilter);