about summary refs log tree commit diff
path: root/libnm-core/nm-setting.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-core/nm-setting.c')
-rw-r--r--libnm-core/nm-setting.c1303
1 files changed, 475 insertions, 828 deletions
diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c
index 0cec5165..218f0dad 100644
--- a/libnm-core/nm-setting.c
+++ b/libnm-core/nm-setting.c
@@ -53,13 +53,9 @@
  * of properties and allowed values.
  */
 
-/*****************************************************************************/
+G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)
 
-typedef struct {
-	GHashTable *hash;
-	const char **names;
-	GVariant **values;
-} GenData;
+#define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate))
 
 typedef struct {
 	const char *name;
@@ -67,6 +63,10 @@ typedef struct {
 	NMSettingPriority priority;
 } SettingInfo;
 
+typedef struct {
+	const SettingInfo *info;
+} NMSettingPrivate;
+
 enum {
 	PROP_0,
 	PROP_NAME,
@@ -74,66 +74,136 @@ enum {
 	PROP_LAST
 };
 
-typedef struct {
-	GenData *gendata;
-} NMSettingPrivate;
+/*****************************************************************************/
 
-G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)
+static GHashTable *registered_settings = NULL;
+static GHashTable *registered_settings_by_type = NULL;
 
-#define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate))
+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);
+	}
+}
 
-static GenData *_gendata_hash (NMSetting *setting, gboolean create_if_necessary);
+static void __attribute__((constructor))
+_ensure_registered_constructor (void)
+{
+	_ensure_registered ();
+}
+
+#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
 
 /*****************************************************************************/
 
+/*
+ * _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));
+
+	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_base_type_priority (const NMMetaSettingInfo *setting_info,
-                         GType gtype)
+_get_setting_type_priority (GType type)
 {
-	/* 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.
-	 */
+	const SettingInfo *info;
 
-	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;
-	}
+	g_return_val_if_fail (g_type_is_a (type, NM_TYPE_SETTING), G_MAXUINT32);
 
-	return NM_SETTING_PRIORITY_INVALID;
+	info = _nm_setting_lookup_setting_by_type (type);
+	return info->priority;
 }
 
 NMSettingPriority
 _nm_setting_get_setting_priority (NMSetting *setting)
 {
-	const NMMetaSettingInfo *setting_info;
-
-	g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_PRIORITY_INVALID);
+	NMSettingPrivate *priv;
 
-	setting_info = NM_SETTING_GET_CLASS (setting)->setting_info;
-	return setting_info ? setting_info->setting_priority : NM_SETTING_PRIORITY_INVALID;
+	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;
 }
 
 NMSettingPriority
 _nm_setting_type_get_base_type_priority (GType type)
 {
-	return _get_base_type_priority (nm_meta_setting_infos_by_gtype (type),
-	                                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;
 }
 
 NMSettingPriority
 _nm_setting_get_base_type_priority (NMSetting *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));
+	return _nm_setting_type_get_base_type_priority (G_OBJECT_TYPE (setting));
 }
 
 /**
@@ -148,15 +218,17 @@ _nm_setting_get_base_type_priority (NMSetting *setting)
 GType
 nm_setting_lookup_type (const char *name)
 {
-	const NMMetaSettingInfo *setting_info;
+	const SettingInfo *info;
 
 	g_return_val_if_fail (name, G_TYPE_INVALID);
 
-	setting_info = nm_meta_setting_infos_by_name (name);
-	return setting_info ? setting_info->get_setting_gtype () : G_TYPE_INVALID;
+	_ensure_registered ();
+
+	info = g_hash_table_lookup (registered_settings, name);
+	return info ? info->type : G_TYPE_INVALID;
 }
 
-int
+gint
 _nm_setting_compare_priority (gconstpointer a, gconstpointer b)
 {
 	NMSettingPriority prio_a, prio_b;
@@ -201,53 +273,90 @@ _nm_setting_slave_type_is_valid (const char *slave_type, const char **out_port_t
 
 /*****************************************************************************/
 
-static const NMSettInfoProperty *
-_nm_sett_info_property_find_in_array (const NMSettInfoProperty *properties, guint len, const char *name)
+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)
 {
-	guint i;
+	NMSettingProperty *property;
+	int i;
 
-	for (i = 0; i < len; i++) {
-		if (nm_streq (name, properties[i].name))
-			return &properties[i];
+	if (!properties)
+		return NULL;
+
+	for (i = 0; i < properties->len; i++) {
+		property = &g_array_index (properties, NMSettingProperty, i);
+		if (strcmp (name, property->name) == 0)
+			return property;
 	}
+
 	return NULL;
 }
 
-void
-_properties_override_add_struct (GArray *properties_override,
-                                 const NMSettInfoProperty *prop_info)
+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)
 {
-	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;
+	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);
 	}
+	g_return_if_fail (find_property (overrides, property_name) == NULL);
+
+	g_array_append_val (overrides, override);
 }
 
 /**
- * _properties_override_add_dbus_only:
- * @properties_override: an array collecting the overrides
+ * _nm_setting_class_add_dbus_only_property:
+ * @setting_class: the setting class
  * @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 this setting type, but which does not correspond to
+ * serialization of objects of @setting_class, but which does not correspond to
  * a #GObject property.
  *
  * When serializing a setting to D-Bus, @synth_func will be called to synthesize
@@ -260,30 +369,35 @@ _properties_override_add_struct (GArray *properties_override,
  * then the property will be ignored when deserializing.)
  */
 void
-_properties_override_add_dbus_only (GArray *properties_override,
-                                    const char *property_name,
-                                    const GVariantType *dbus_type,
-                                    NMSettingPropertySynthFunc synth_func,
-                                    NMSettingPropertySetFunc set_func)
+_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 (properties_override,
-	                          .name = property_name,
-	                          .dbus_type = dbus_type,
-	                          .synth_func = synth_func,
-	                          .set_func = 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_override:
- * @properties_override: an array collecting the overrides
- * @param_spec: the name of the property to override
+ * _nm_setting_class_override_property:
+ * @setting_class: the setting class
+ * @property_name: 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 that shares the
- * same name as @param_spec.
+ * Overrides the D-Bus representation of the #GObject property named
+ * @property_name on @setting_class.
  *
  * 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
@@ -292,38 +406,38 @@ _properties_override_add_dbus_only (GArray *properties_override,
  * 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 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().)
+ * 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().)
  *
  * If @not_set_func is non-%NULL, then it will be called when deserializing a
- * 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
+ * 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
  * when it is not present.
  */
 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)
+_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)
 {
-	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);
+	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);
 }
 
 /**
- * _properties_override_add_transform:
- * @properties_override: an array collecting the overrides
- * @param_spec: the param spec of the property to transform.
+ * _nm_setting_class_transform_property:
+ * @setting_class: the setting class
+ * @property: the name 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
@@ -337,143 +451,23 @@ _properties_override_add_override (GArray *properties_override,
  * nm_property_compare() recognizes, as long as it recognizes @dbus_type.
  */
 void
-_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)
+_nm_setting_class_transform_property (NMSettingClass *setting_class,
+                                      const char *property,
+                                      const GVariantType *dbus_type,
+                                      NMSettingPropertyTransformToFunc to_dbus,
+                                      NMSettingPropertyTransformFromFunc from_dbus)
 {
-	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];
+	GParamSpec *param_spec;
 
-	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);
+	param_spec = g_object_class_find_property (G_OBJECT_CLASS (setting_class), property);
+	g_return_if_fail (param_spec != NULL);
 
-	return property;
+	add_property_override (setting_class,
+	                       property, param_spec, dbus_type,
+	                       NULL, NULL, NULL, NULL,
+	                       to_dbus, from_dbus);
 }
 
-/*****************************************************************************/
-
 gboolean
 _nm_setting_use_legacy_property (NMSetting *setting,
                                  GVariant *connection_dict,
@@ -510,6 +504,77 @@ _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 *
@@ -545,7 +610,7 @@ variant_type_for_gtype (GType type)
 
 static GVariant *
 get_property_for_dbus (NMSetting *setting,
-                       const NMSettInfoProperty *property,
+                       const NMSettingProperty *property,
                        gboolean ignore_default)
 {
 	GValue prop_value = { 0, };
@@ -573,7 +638,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_gbytes_to_variant_ay (g_value_get_boxed (&prop_value));
+		dbus_value = _nm_utils_bytes_to_dbus (&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);
@@ -582,7 +647,7 @@ get_property_for_dbus (NMSetting *setting,
 }
 
 static gboolean
-set_property_from_dbus (const NMSettInfoProperty *property,
+set_property_from_dbus (const NMSettingProperty *property,
                         GVariant *src_value,
                         GValue *dst_value)
 {
@@ -633,57 +698,40 @@ set_property_from_dbus (const NMSettInfoProperty *property,
 GVariant *
 _nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionSerializationFlags flags)
 {
-	NMSettingPrivate *priv;
 	GVariantBuilder builder;
 	GVariant *dbus_value;
-	const NMSettInfoSetting *sett_info;
+	const NMSettingProperty *properties;
 	guint n_properties, i;
-	const char *const*gendata_keys;
 
 	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
 
-	priv = NM_SETTING_GET_PRIVATE (setting);
+	properties = nm_setting_class_get_properties (NM_SETTING_GET_CLASS (setting), &n_properties);
 
 	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++) {
-		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];
+		const NMSettingProperty *property = &properties[i];
 		GParamSpec *prop_spec = property->param_spec;
 
-		if (!prop_spec) {
-			if (!property->synth_func)
-				continue;
-
-			if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
-				continue;
-		} else {
-			if (!(prop_spec->flags & G_PARAM_WRITABLE))
-				continue;
+		if (!prop_spec && !property->synth_func) {
+			/* D-Bus-only property with no synth_func, so we skip it. */
+			continue;
+		}
 
-			if (NM_FLAGS_ANY (prop_spec->flags, NM_SETTING_PARAM_GENDATA_BACKED))
-				continue;
+		if (prop_spec && !(prop_spec->flags & G_PARAM_WRITABLE))
+			continue;
 
-			if (   (prop_spec->flags & NM_SETTING_PARAM_LEGACY)
-			    && !_nm_utils_is_manager_process)
-				continue;
+		if (   prop_spec && (prop_spec->flags & NM_SETTING_PARAM_LEGACY)
+		    && !_nm_utils_is_manager_process)
+			continue;
 
-			if (   (flags & NM_CONNECTION_SERIALIZE_NO_SECRETS)
-			    && (prop_spec->flags & NM_SETTING_PARAM_SECRET))
-				continue;
+		if (   (flags & NM_CONNECTION_SERIALIZE_NO_SECRETS)
+		    && (prop_spec && (prop_spec->flags & NM_SETTING_PARAM_SECRET)))
+			continue;
 
-			if (   (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
-			    && !(prop_spec->flags & NM_SETTING_PARAM_SECRET))
-				continue;
-		}
+		if (   (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		    && !(prop_spec && (prop_spec->flags & NM_SETTING_PARAM_SECRET)))
+			continue;
 
 		if (property->synth_func)
 			dbus_value = property->synth_func (setting, connection, property->name);
@@ -730,8 +778,8 @@ _nm_setting_new_from_dbus (GType setting_type,
 {
 	gs_unref_object NMSetting *setting = NULL;
 	gs_unref_hashtable GHashTable *keys = NULL;
-	const NMSettInfoSetting *sett_info;
-	guint i;
+	const NMSettingProperty *properties;
+	guint i, n_properties;
 
 	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);
@@ -775,29 +823,9 @@ _nm_setting_new_from_dbus (GType setting_type,
 		}
 	}
 
-	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];
+	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];
 		gs_unref_variant GVariant *value = NULL;
 		gs_free_error GError *local = NULL;
 
@@ -916,12 +944,12 @@ const GVariantType *
 nm_setting_get_dbus_property_type (NMSetting *setting,
                                    const char *property_name)
 {
-	const NMSettInfoProperty *property;
+	const NMSettingProperty *property;
 
 	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
 	g_return_val_if_fail (property_name != NULL, NULL);
 
-	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), property_name);
+	property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), property_name);
 	g_return_val_if_fail (property != NULL, NULL);
 
 	if (property->dbus_type)
@@ -933,32 +961,14 @@ 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);
 
-	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;
@@ -992,37 +1002,16 @@ 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);
 
-	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_object_freeze_notify (dup);
+	nm_setting_enumerate_values (setting, duplicate_setting, dup);
+	g_object_thaw_notify (dup);
 
-			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);
 }
 
@@ -1038,12 +1027,12 @@ nm_setting_duplicate (NMSetting *setting)
 const char *
 nm_setting_get_name (NMSetting *setting)
 {
-	const NMMetaSettingInfo *setting_info;
+	NMSettingPrivate *priv;
 
 	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
-
-	setting_info = NM_SETTING_GET_CLASS (setting)->setting_info;
-	return setting_info ? setting_info->setting_name : NULL;
+	priv = NM_SETTING_GET_PRIVATE (setting);
+	_ensure_setting_info (setting, priv);
+	return priv->info->name;
 }
 
 /**
@@ -1137,7 +1126,7 @@ compare_property (NMSetting *setting,
                   const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	const NMSettInfoProperty *property;
+	const NMSettingProperty *property;
 	GVariant *value1, *value2;
 	int cmp;
 
@@ -1169,7 +1158,7 @@ compare_property (NMSetting *setting,
 			return TRUE;
 	}
 
-	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), prop_spec->name);
+	property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), prop_spec->name);
 	g_return_val_if_fail (property != NULL, FALSE);
 
 	value1 = get_property_for_dbus (setting, property, TRUE);
@@ -1202,10 +1191,9 @@ nm_setting_compare (NMSetting *a,
                     NMSetting *b,
                     NMSettingCompareFlags flags)
 {
-	const NMSettInfoSetting *sett_info;
 	GParamSpec **property_specs;
 	guint n_property_specs;
-	int same = TRUE;
+	gint same = TRUE;
 	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
@@ -1215,18 +1203,6 @@ 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++) {
@@ -1312,21 +1288,6 @@ 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
@@ -1355,7 +1316,8 @@ nm_setting_diff (NMSetting *a,
                  gboolean invert_results,
                  GHashTable **results)
 {
-	const NMSettInfoSetting *sett_info;
+	GParamSpec **property_specs;
+	guint n_property_specs;
 	guint i;
 	NMSettingDiffResult a_result = NM_SETTING_DIFF_RESULT_IN_A;
 	NMSettingDiffResult b_result = NM_SETTING_DIFF_RESULT_IN_B;
@@ -1399,117 +1361,78 @@ nm_setting_diff (NMSetting *a,
 		results_created = TRUE;
 	}
 
-	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a));
+	/* And now all properties */
+	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
 
-	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 {
+	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);
-				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;
+				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;
 
-			if (r != NM_SETTING_DIFF_RESULT_UNKNOWN) {
-				diff_found = TRUE;
-				_setting_diff_add_result (*results, prop_spec->name, r);
-			}
+			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));
 		}
 	}
+	g_free (property_specs);
 
 	if (!compared_any && !b) {
 		/* special case: the setting has no properties, and the opposite
@@ -1520,7 +1443,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 it to the caller. */
+		 * have allocated a new @results hash, and return if to the caller. */
 		return FALSE;
 	} else {
 		if (results_created) {
@@ -1576,57 +1499,23 @@ nm_setting_enumerate_values (NMSetting *setting,
                              NMSettingValueIterFn func,
                              gpointer user_data)
 {
-	const NMSettInfoSetting *sett_info;
 	GParamSpec **property_specs;
-	guint n_properties;
-	guint i;
+	guint n_property_specs;
+	int i;
 	GType type;
 
 	g_return_if_fail (NM_IS_SETTING (setting));
 	g_return_if_fail (func != NULL);
 
-	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);
+	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
 
 	/* 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_properties, sizeof (gpointer),
+	g_qsort_with_data (property_specs, n_property_specs, sizeof (gpointer),
 	                   (GCompareDataFunc) _enumerate_values_sort, &type);
 
-	for (i = 0; i < n_properties; i++) {
+	for (i = 0; i < n_property_specs; i++) {
 		GParamSpec *prop_spec = property_specs[i];
 		GValue value = G_VALUE_INIT;
 
@@ -1652,7 +1541,7 @@ nm_setting_enumerate_values (NMSetting *setting,
 gboolean
 _nm_setting_clear_secrets (NMSetting *setting)
 {
-	gs_free GParamSpec **property_specs = NULL;
+	GParamSpec **property_specs;
 	guint n_property_specs;
 	guint i;
 	gboolean changed = FALSE;
@@ -1660,6 +1549,7 @@ _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];
 
@@ -1676,6 +1566,9 @@ _nm_setting_clear_secrets (NMSetting *setting)
 			g_value_unset (&value);
 		}
 	}
+
+	g_free (property_specs);
+
 	return changed;
 }
 
@@ -1726,7 +1619,7 @@ _nm_setting_clear_secrets_with_flags (NMSetting *setting,
                                       NMSettingClearSecretsWithFlagsFn func,
                                       gpointer user_data)
 {
-	gs_free GParamSpec **property_specs = NULL;
+	GParamSpec **property_specs;
 	guint n_property_specs;
 	guint i;
 	gboolean changed = FALSE;
@@ -1744,6 +1637,8 @@ _nm_setting_clear_secrets_with_flags (NMSetting *setting,
 			                                                                     user_data);
 		}
 	}
+
+	g_free (property_specs);
 	return changed;
 }
 
@@ -1777,11 +1672,11 @@ _nm_setting_need_secrets (NMSetting *setting)
 static int
 update_one_secret (NMSetting *setting, const char *key, GVariant *value, GError **error)
 {
-	const NMSettInfoProperty *property;
+	const NMSettingProperty *property;
 	GParamSpec *prop_spec;
 	GValue prop_value = { 0, };
 
-	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), key);
+	property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), key);
 	if (!property) {
 		g_set_error_literal (error,
 		                     NM_CONNECTION_ERROR,
@@ -1870,10 +1765,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 NMSettInfoProperty *property;
+	const NMSettingProperty *property;
 	GParamSpec *pspec;
 
-	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), secret_name);
+	property = nm_setting_class_find_property (NM_SETTING_GET_CLASS (setting), secret_name);
 	if (!property) {
 		g_set_error_literal (error,
 		                     NM_CONNECTION_ERROR,
@@ -1993,40 +1888,59 @@ nm_setting_set_secret_flags (NMSetting *setting,
  * nm_setting_to_string:
  * @setting: the #NMSetting
  *
- * 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.
+ * 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.
  *
  * Returns: an allocated string containing a textual representation of the
- * setting's properties and values, which the caller should
+ * setting's properties and values (including secrets!), which the caller should
  * free with g_free()
  **/
 char *
 nm_setting_to_string (NMSetting *setting)
 {
 	GString *string;
-	gs_unref_variant GVariant *variant = NULL;
-	GVariant *child;
-	GVariantIter iter;
+	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);
 
 	string = g_string_new (nm_setting_get_name (setting));
 	g_string_append_c (string, '\n');
 
-	variant = _nm_setting_to_dbus (setting, NULL, NM_CONNECTION_SERIALIZE_ALL);
+	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_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;
+		g_value_init (&value, prop_spec->value_type);
+		g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
 
-		g_variant_get (child, "{sv}", &name, &value);
-		value_str = g_variant_print (value, FALSE);
+		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_string_append_printf (string, "\t%s : %s\n", name, value_str);
+		is_default = g_param_value_defaults (prop_spec, &value);
+		g_value_unset (&value);
+
+		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_free (property_specs);
+	g_string_append_c (string, '\n');
+
 	return g_string_free (string, FALSE);
 }
 
@@ -2048,257 +1962,17 @@ _nm_setting_get_deprecated_virtual_interface_name (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;
-}
-
-GHashTable *
-_nm_setting_gendata_hash (NMSetting *setting, gboolean create_if_necessary)
-{
-	GenData *gendata;
-
-	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)
+static void
+nm_setting_init (NMSetting *setting)
 {
-	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)
+constructed (GObject *object)
 {
+	_ensure_setting_info (object, NM_SETTING_GET_PRIVATE (object));
+
+	G_OBJECT_CLASS (nm_setting_parent_class)->constructed (object);
 }
 
 static void
@@ -2318,44 +1992,15 @@ 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;
@@ -2363,12 +2008,14 @@ 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 "802-11-wireless" or "802-3-ethernet".
+	 * example "ppp" or "wireless" or "wired".
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_NAME,