summary refs log tree commit diff
path: root/clients/tui
diff options
context:
space:
mode:
Diffstat (limited to 'clients/tui')
-rw-r--r--clients/tui/newt/nmt-newt-entry-numeric.c96
-rw-r--r--clients/tui/newt/nmt-newt-entry-numeric.h9
-rw-r--r--clients/tui/nm-editor-bindings.c131
-rw-r--r--clients/tui/nmt-connect-connection-list.c8
-rw-r--r--clients/tui/nmt-ip-entry.c33
-rw-r--r--clients/tui/nmt-page-bond.c14
-rw-r--r--clients/tui/nmt-page-bridge.c6
-rw-r--r--clients/tui/nmt-page-ip4.c6
-rw-r--r--clients/tui/nmt-page-ip6.c6
-rw-r--r--clients/tui/nmt-page-vlan.c29
-rw-r--r--clients/tui/nmt-page-wifi.c30
-rw-r--r--clients/tui/nmt-route-entry.c2
-rw-r--r--clients/tui/nmt-route-table.c4
-rw-r--r--clients/tui/nmtui-connect.c74
14 files changed, 236 insertions, 212 deletions
diff --git a/clients/tui/newt/nmt-newt-entry-numeric.c b/clients/tui/newt/nmt-newt-entry-numeric.c
index b79f056a..a52fe868 100644
--- a/clients/tui/newt/nmt-newt-entry-numeric.c
+++ b/clients/tui/newt/nmt-newt-entry-numeric.c
@@ -37,13 +37,15 @@ G_DEFINE_TYPE (NmtNewtEntryNumeric, nmt_newt_entry_numeric, NMT_TYPE_NEWT_ENTRY)
 #define NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericPrivate))
 
 typedef struct {
-	int min, max;
+	gint64 min, max;
+	bool optional;
 } NmtNewtEntryNumericPrivate;
 
 enum {
 	PROP_0,
 	PROP_MINIMUM,
 	PROP_MAXIMUM,
+	PROP_OPTIONAL,
 
 	LAST_PROP
 };
@@ -61,13 +63,38 @@ enum {
  */
 NmtNewtWidget *
 nmt_newt_entry_numeric_new (int width,
-                            int min,
-                            int max)
+                            gint64 min,
+                            gint64 max)
+{
+	return nmt_newt_entry_numeric_new_full (width,
+	                                        min,
+	                                        max,
+	                                        FALSE);
+}
+
+/**
+ * nmt_newt_entry_numeric_new_full:
+ * @width: the entry's width in characters
+ * @min: the minimum valid value
+ * @max: the maximum valid value
+ * @optional: whether an empty entry is valid
+ *
+ * Creates a new #NmtNewtEntryNumeric, accepting values in the
+ * indicated range.
+ *
+ * Returns: a new #NmtNewtEntryNumeric
+ */
+NmtNewtWidget *
+nmt_newt_entry_numeric_new_full (int width,
+                                 gint64 min,
+                                 gint64 max,
+                                 gboolean optional)
 {
 	return g_object_new (NMT_TYPE_NEWT_ENTRY_NUMERIC,
 	                     "width", width,
 	                     "minimum", min,
 	                     "maximum", max,
+	                     "optional", optional,
 	                     NULL);
 }
 
@@ -95,19 +122,13 @@ newt_entry_numeric_validate (NmtNewtEntry *entry,
                              gpointer      user_data)
 {
 	NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (entry);
-	int val;
-	char *end;
+	gint64 val;
 
 	if (!*text)
-		return FALSE;
+		return priv->optional ? TRUE : FALSE;
 
-	val = strtoul (text, &end, 10);
-	if (*end)
-		return FALSE;
-	if (val < priv->min || val > priv->max)
-		return FALSE;
-
-	return TRUE;
+	val = _nm_utils_ascii_str_to_int64 (text, 10, priv->min, priv->max, 0);
+	return val != 0 || errno == 0;
 }
 
 static void
@@ -125,7 +146,7 @@ nmt_newt_entry_numeric_constructed (GObject *object)
 	if (!*nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object))) {
 		char buf[32];
 
-		g_snprintf (buf, sizeof (buf), "%d", priv->min);
+		g_snprintf (buf, sizeof (buf), "%lld", (long long) priv->min);
 		nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), buf);
 	}
 
@@ -142,10 +163,13 @@ nmt_newt_entry_numeric_set_property (GObject      *object,
 
 	switch (prop_id) {
 	case PROP_MINIMUM:
-		priv->min = g_value_get_int (value);
+		priv->min = g_value_get_int64 (value);
 		break;
 	case PROP_MAXIMUM:
-		priv->max = g_value_get_int (value);
+		priv->max = g_value_get_int64 (value);
+		break;
+	case PROP_OPTIONAL:
+		priv->optional = g_value_get_boolean (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -163,10 +187,13 @@ nmt_newt_entry_numeric_get_property (GObject    *object,
 
 	switch (prop_id) {
 	case PROP_MINIMUM:
-		g_value_set_int (value, priv->min);
+		g_value_set_int64 (value, priv->min);
 		break;
 	case PROP_MAXIMUM:
-		g_value_set_int (value, priv->max);
+		g_value_set_int64 (value, priv->max);
+		break;
+	case PROP_OPTIONAL:
+		g_value_set_boolean (value, priv->optional);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -195,11 +222,11 @@ nmt_newt_entry_numeric_class_init (NmtNewtEntryNumericClass *entry_class)
 	 */
 	g_object_class_install_property
 		(object_class, PROP_MINIMUM,
-		 g_param_spec_int ("minimum", "", "",
-		                   G_MININT, G_MAXINT, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT_ONLY |
-		                   G_PARAM_STATIC_STRINGS));
+		 g_param_spec_int64 ("minimum", "", "",
+		                     G_MININT64, G_MAXINT64, 0,
+		                     G_PARAM_READWRITE |
+		                     G_PARAM_CONSTRUCT_ONLY |
+		                     G_PARAM_STATIC_STRINGS));
 	/**
 	 * NmtNewtEntryNumeric:maximum:
 	 *
@@ -207,9 +234,22 @@ nmt_newt_entry_numeric_class_init (NmtNewtEntryNumericClass *entry_class)
 	 */
 	g_object_class_install_property
 		(object_class, PROP_MAXIMUM,
-		 g_param_spec_int ("maximum", "", "",
-		                   G_MININT, G_MAXINT, G_MAXINT,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT_ONLY |
-		                   G_PARAM_STATIC_STRINGS));
+		 g_param_spec_int64 ("maximum", "", "",
+		                     G_MININT64, G_MAXINT64, G_MAXINT64,
+		                     G_PARAM_READWRITE |
+		                     G_PARAM_CONSTRUCT_ONLY |
+		                     G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtEntryNumeric:optional:
+	 *
+	 * If %TRUE, allow empty string to indicate some default value.
+	 * It means the property is optional and can be left at the default
+	 */
+	g_object_class_install_property
+		(object_class, PROP_OPTIONAL,
+		 g_param_spec_boolean ("optional", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
 }
diff --git a/clients/tui/newt/nmt-newt-entry-numeric.h b/clients/tui/newt/nmt-newt-entry-numeric.h
index 00e19739..bfcabd88 100644
--- a/clients/tui/newt/nmt-newt-entry-numeric.h
+++ b/clients/tui/newt/nmt-newt-entry-numeric.h
@@ -41,7 +41,12 @@ typedef struct {
 GType nmt_newt_entry_numeric_get_type (void);
 
 NmtNewtWidget *nmt_newt_entry_numeric_new (int width,
-                                           int min,
-                                           int max);
+                                           gint64 min,
+                                           gint64 max);
+
+NmtNewtWidget *nmt_newt_entry_numeric_new_full (int width,
+                                                gint64 min,
+                                                gint64 max,
+                                                gboolean optional);
 
 #endif /* NMT_NEWT_ENTRY_NUMERIC_H */
diff --git a/clients/tui/nm-editor-bindings.c b/clients/tui/nm-editor-bindings.c
index 3753aaa9..647ac8df 100644
--- a/clients/tui/nm-editor-bindings.c
+++ b/clients/tui/nm-editor-bindings.c
@@ -71,45 +71,6 @@ nm_editor_bindings_init (void)
 }
 
 static gboolean
-parse_addr_prefix (const char  *text,
-                   int          family,
-                   char       **addr,
-                   guint32     *prefix)
-{
-	const char *slash;
-	char *addrstr, *end;
-	gboolean valid;
-
-	slash = strchr (text, '/');
-
-	if (slash)
-		addrstr = g_strndup (text, slash - text);
-	else
-		addrstr = g_strdup (text);
-	valid = nm_utils_ipaddr_valid (family, addrstr);
-
-	if (slash) {
-		*prefix = strtoul (slash + 1, &end, 10);
-		if (   *end
-		    || *prefix == 0
-		    || (family == AF_INET && *prefix > 32)
-		    || (family == AF_INET6 && *prefix > 128))
-			valid = FALSE;
-	} else if (prefix) {
-		if (family == AF_INET)
-			*prefix = 32;
-		else
-			*prefix = 128;
-	}
-
-	if (addr && valid)
-		*addr = addrstr;
-	else
-		g_free (addrstr);
-	return valid;
-}
-
-static gboolean
 ip_addresses_with_prefix_to_strv (GBinding     *binding,
                                   const GValue *source_value,
                                   GValue       *target_value,
@@ -146,12 +107,12 @@ ip_addresses_with_prefix_from_strv (GBinding     *binding,
                                     GValue       *target_value,
                                     gpointer      user_data)
 {
-	int family = GPOINTER_TO_INT (user_data);
+	int addr_family = GPOINTER_TO_INT (user_data);
 	char **strings;
 	GPtrArray *addrs;
 	NMIPAddress *addr;
 	char *addrstr;
-	guint32 prefix;
+	int prefix;
 	int i;
 
 	strings = g_value_get_boxed (source_value);
@@ -162,7 +123,7 @@ ip_addresses_with_prefix_from_strv (GBinding     *binding,
 
 	for (i = 0; strings[i]; i++) {
 		if (i >= addrs->len) {
-			if (family == AF_INET)
+			if (addr_family == AF_INET)
 				addr = nm_ip_address_new (AF_INET, "0.0.0.0", 32, NULL);
 			else
 				addr = nm_ip_address_new (AF_INET6, "::", 128, NULL);
@@ -170,11 +131,24 @@ ip_addresses_with_prefix_from_strv (GBinding     *binding,
 		} else
 			addr = addrs->pdata[i];
 
-		if (!parse_addr_prefix (strings[i], family, &addrstr, &prefix)) {
+		if (!nm_utils_parse_inaddr_prefix (addr_family, strings[i], &addrstr, &prefix)) {
 			g_ptr_array_unref (addrs);
 			return FALSE;
 		}
 
+		if (prefix == -1) {
+			if (addr_family == AF_INET) {
+				in_addr_t v4;
+
+				inet_pton (addr_family, addrstr, &v4);
+				if (nm_utils_ip_is_site_local (AF_INET, &v4))
+					prefix = nm_utils_ip4_get_default_prefix (v4);
+				else
+					prefix = 32;
+			} else
+				prefix = 64;
+		}
+
 		nm_ip_address_set_address (addr, addrstr);
 		nm_ip_address_set_prefix (addr, prefix);
 		g_free (addrstr);
@@ -187,7 +161,7 @@ ip_addresses_with_prefix_from_strv (GBinding     *binding,
 
 /**
  * nm_editor_bind_ip_addresses_with_prefix_to_strv:
- * @family: the IP address family
+ * @addr_family: the IP address family
  * @source: the source object (eg, an #NMSettingIP4Config)
  * @source_property: the property on @source to bind (eg,
  *   %NM_SETTING_IP4_CONFIG_ADDRESSES)
@@ -204,7 +178,7 @@ ip_addresses_with_prefix_from_strv (GBinding     *binding,
  * vice versa if %G_BINDING_BIDIRECTIONAL) is specified.
  */
 void
-nm_editor_bind_ip_addresses_with_prefix_to_strv (int            family,
+nm_editor_bind_ip_addresses_with_prefix_to_strv (int            addr_family,
                                                  gpointer       source,
                                                  const gchar   *source_property,
                                                  gpointer       target,
@@ -216,7 +190,7 @@ nm_editor_bind_ip_addresses_with_prefix_to_strv (int            family,
 	                             flags,
 	                             ip_addresses_with_prefix_to_strv,
 	                             ip_addresses_with_prefix_from_strv,
-	                             GINT_TO_POINTER (family), NULL);
+	                             GINT_TO_POINTER (addr_family), NULL);
 }
 
 static gboolean
@@ -225,14 +199,14 @@ ip_addresses_check_and_copy (GBinding     *binding,
                              GValue       *target_value,
                              gpointer      user_data)
 {
-	int family = GPOINTER_TO_INT (user_data);
+	int addr_family = GPOINTER_TO_INT (user_data);
 	char **strings;
 	int i;
 
 	strings = g_value_get_boxed (source_value);
 
 	for (i = 0; strings[i]; i++) {
-		if (!nm_utils_ipaddr_valid (family, strings[i]))
+		if (!nm_utils_ipaddr_valid (addr_family, strings[i]))
 			return FALSE;
 	}
 
@@ -242,7 +216,7 @@ ip_addresses_check_and_copy (GBinding     *binding,
 
 /**
  * nm_editor_bind_ip_addresses_to_strv:
- * @family: the IP address family
+ * @addr_family: the IP address family
  * @source: the source object (eg, an #NMSettingIP4Config)
  * @source_property: the property on @source to bind (eg,
  *   %NM_SETTING_IP4_CONFIG_DNS)
@@ -253,10 +227,10 @@ ip_addresses_check_and_copy (GBinding     *binding,
  *
  * Binds the %G_TYPE_STRV property @source_property on @source to the
  * %G_TYPE_STRV property @target_property on @target, verifying that
- * each string is a valid address of type @family when copying.
+ * each string is a valid address of type @addr_family when copying.
  */
 void
-nm_editor_bind_ip_addresses_to_strv (int            family,
+nm_editor_bind_ip_addresses_to_strv (int            addr_family,
                                      gpointer       source,
                                      const gchar   *source_property,
                                      gpointer       target,
@@ -268,7 +242,7 @@ nm_editor_bind_ip_addresses_to_strv (int            family,
 	                             flags,
 	                             ip_addresses_check_and_copy,
 	                             ip_addresses_check_and_copy,
-	                             GINT_TO_POINTER (family), NULL);
+	                             GINT_TO_POINTER (addr_family), NULL);
 }
 
 static gboolean
@@ -287,11 +261,11 @@ ip_gateway_from_string (GBinding     *binding,
                         GValue       *target_value,
                         gpointer      user_data)
 {
-	int family = GPOINTER_TO_INT (user_data);
+	int addr_family = GPOINTER_TO_INT (user_data);
 	const char *gateway;
 
 	gateway = g_value_get_string (source_value);
-	if (gateway && !nm_utils_ipaddr_valid (family, gateway))
+	if (gateway && !nm_utils_ipaddr_valid (addr_family, gateway))
 		gateway = NULL;
 
 	g_value_set_string (target_value, gateway);
@@ -329,7 +303,7 @@ ip_addresses_to_sensitivity (GBinding     *binding,
 
 /**
  * nm_editor_bind_ip_gateway_to_string:
- * @family: the IP address family
+ * @addr_family: the IP address family
  * @source: the source #NMSettingIPConfig
  * @target: the target object (eg, an #NmtIPEntry)
  * @target_property: the property on @target to bind (eg, "text")
@@ -351,7 +325,7 @@ ip_addresses_to_sensitivity (GBinding     *binding,
  * address.
  */
 void
-nm_editor_bind_ip_gateway_to_string (int                family,
+nm_editor_bind_ip_gateway_to_string (int                addr_family,
                                      NMSettingIPConfig *source,
                                      gpointer           target,
                                      const gchar       *target_property,
@@ -363,7 +337,7 @@ nm_editor_bind_ip_gateway_to_string (int                family,
 	                             flags,
 	                             ip_gateway_to_string,
 	                             ip_gateway_from_string,
-	                             GINT_TO_POINTER (family), NULL);
+	                             GINT_TO_POINTER (addr_family), NULL);
 	g_object_bind_property_full (source, "addresses",
 	                             source, "gateway",
 	                             (flags & G_BINDING_SYNC_CREATE),
@@ -447,14 +421,14 @@ ip_route_transform_from_dest_string (GBinding     *binding,
                                      GValue       *target_value,
                                      gpointer      user_data)
 {
-	int family = GPOINTER_TO_INT (user_data);
+	int addr_family = GPOINTER_TO_INT (user_data);
 	NMIPRoute *route;
 	const char *text;
 	char *addrstr;
-	guint32 prefix;
+	int prefix;
 
 	text = g_value_get_string (source_value);
-	if (!parse_addr_prefix (text, family, &addrstr, &prefix))
+	if (!nm_utils_parse_inaddr_prefix (addr_family, text, &addrstr, &prefix))
 		return FALSE;
 
 	/* Fetch the original property value */
@@ -462,6 +436,21 @@ ip_route_transform_from_dest_string (GBinding     *binding,
 	              g_binding_get_source_property (binding), &route,
 	              NULL);
 
+	if (prefix == -1) {
+		if (addr_family == AF_INET) {
+			in_addr_t v4;
+
+			inet_pton (addr_family, addrstr, &v4);
+			if (nm_utils_ip_is_site_local (AF_INET, &v4)) {
+				prefix = nm_utils_ip4_get_default_prefix (v4);
+				if (v4 & (~nm_utils_ip4_prefix_to_netmask (prefix)))
+					prefix = 32;
+			} else
+				prefix = 32;
+		} else
+			prefix = 64;
+	}
+
 	nm_ip_route_set_dest (route, addrstr);
 	nm_ip_route_set_prefix (route, prefix);
 	g_free (addrstr);
@@ -476,13 +465,13 @@ ip_route_transform_from_next_hop_string (GBinding     *binding,
                                          GValue       *target_value,
                                          gpointer      user_data)
 {
-	int family = GPOINTER_TO_INT (user_data);
+	int addr_family = GPOINTER_TO_INT (user_data);
 	NMIPRoute *route;
 	const char *text;
 
 	text = g_value_get_string (source_value);
 	if (*text) {
-		if (!nm_utils_ipaddr_valid (family, text))
+		if (!nm_utils_ipaddr_valid (addr_family, text))
 			return FALSE;
 	} else
 		text = NULL;
@@ -509,10 +498,7 @@ ip_route_transform_from_metric_string (GBinding     *binding,
 	gint64 metric;
 
 	text = g_value_get_string (source_value);
-	if (*text)
-		metric = strtoul (text, NULL, 10);
-	else
-		metric = -1;
+	metric = _nm_utils_ascii_str_to_int64 (text, 10, 0, G_MAXUINT32, -1);
 
 	/* Fetch the original property value */
 	g_object_get (g_binding_get_source (binding),
@@ -527,7 +513,7 @@ ip_route_transform_from_metric_string (GBinding     *binding,
 
 /**
  * nm_editor_bind_ip_route_to_strings:
- * @family: the IP address family
+ * @addr_family: the IP address family
  * @source: the source object
  * @source_property: the source property
  * @dest_target: the target object for the route's destionation
@@ -547,7 +533,7 @@ ip_route_transform_from_metric_string (GBinding     *binding,
  * is a plain IP address, and @metric_target_property is a number.
  */
 void
-nm_editor_bind_ip_route_to_strings (int            family,
+nm_editor_bind_ip_route_to_strings (int            addr_family,
                                     gpointer       source,
                                     const gchar   *source_property,
                                     gpointer       dest_target,
@@ -563,19 +549,19 @@ nm_editor_bind_ip_route_to_strings (int            family,
 	                             flags,
 	                             ip_route_transform_to_dest_string,
 	                             ip_route_transform_from_dest_string,
-	                             GINT_TO_POINTER (family), NULL);
+	                             GINT_TO_POINTER (addr_family), NULL);
 	g_object_bind_property_full (source, source_property,
 	                             next_hop_target, next_hop_target_property,
 	                             flags,
 	                             ip_route_transform_to_next_hop_string,
 	                             ip_route_transform_from_next_hop_string,
-	                             GINT_TO_POINTER (family), NULL);
+	                             GINT_TO_POINTER (addr_family), NULL);
 	g_object_bind_property_full (source, source_property,
 	                             metric_target, metric_target_property,
 	                             flags,
 	                             ip_route_transform_to_metric_string,
 	                             ip_route_transform_from_metric_string,
-	                             GINT_TO_POINTER (family), NULL);
+	                             GINT_TO_POINTER (addr_family), NULL);
 }
 
 /* Wireless security method binding */
@@ -599,6 +585,9 @@ get_security_type (NMEditorWirelessSecurityMethodBinding *binding)
 		return "none";
 
 	key_mgmt = nm_setting_wireless_security_get_key_mgmt (binding->s_wsec);
+	if (!key_mgmt)
+		return "none";
+
 	auth_alg = nm_setting_wireless_security_get_auth_alg (binding->s_wsec);
 
 	/* No IEEE 802.1x */
diff --git a/clients/tui/nmt-connect-connection-list.c b/clients/tui/nmt-connect-connection-list.c
index 86286129..6e69a04b 100644
--- a/clients/tui/nmt-connect-connection-list.c
+++ b/clients/tui/nmt-connect-connection-list.c
@@ -30,6 +30,8 @@
 
 #include "NetworkManager.h"
 
+#include "nm-utils/nm-hash-utils.h"
+
 #include "nmtui.h"
 #include "nmt-connect-connection-list.h"
 
@@ -88,6 +90,7 @@ nmt_connect_connection_free (NmtConnectConnection *nmtconn)
 	g_clear_object (&nmtconn->ap);
 	g_clear_object (&nmtconn->active);
 	g_free (nmtconn->ssid);
+	g_slice_free (NmtConnectConnection, nmtconn);
 }
 
 static void
@@ -97,6 +100,7 @@ nmt_connect_device_free (NmtConnectDevice *nmtdev)
 	g_clear_object (&nmtdev->device);
 
 	g_slist_free_full (nmtdev->conns, (GDestroyNotify) nmt_connect_connection_free);
+	g_slice_free (NmtConnectDevice, nmtdev);
 }
 
 static const char *device_sort_order[] = {
@@ -272,7 +276,7 @@ add_connections_for_aps (NmtConnectDevice *nmtdev,
 	if (!aps->len)
 		return;
 
-	seen_ssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	seen_ssids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
 
 	for (i = 0; i < aps->len; i++) {
 		ap = aps->pdata[i];
@@ -360,7 +364,7 @@ append_nmt_devices_for_virtual_devices (GSList          *nmt_devices,
 	NmtConnectConnection *nmtconn;
 	int sort_order;
 
-	devices_by_name = g_hash_table_new (g_str_hash, g_str_equal);
+	devices_by_name = g_hash_table_new (nm_str_hash, g_str_equal);
 
 	for (i = 0; i < connections->len; i++) {
 		conn = connections->pdata[i];
diff --git a/clients/tui/nmt-ip-entry.c b/clients/tui/nmt-ip-entry.c
index 8d8f888f..66838680 100644
--- a/clients/tui/nmt-ip-entry.c
+++ b/clients/tui/nmt-ip-entry.c
@@ -123,39 +123,12 @@ ip_entry_validate (NmtNewtEntry *entry,
                    gpointer      user_data)
 {
 	NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (entry);
-	guchar buf[16];
-	guint32 prefix;
-	const char *slash;
-	char *addrstr, *end;
-	gboolean valid;
 
 	if (!*text)
 		return priv->optional;
-
-	slash = strchr (text, '/');
-
-	if (slash) {
-		if (!priv->prefix)
-			return FALSE;
-		addrstr = g_strndup (text, slash - text);
-	} else
-		addrstr = g_strdup (text);
-	valid = (inet_pton (priv->family, addrstr, buf) == 1);
-	g_free (addrstr);
-
-	if (!valid)
-		return FALSE;
-
-	if (slash) {
-		prefix = strtoul (slash + 1, &end, 10);
-		if (   *end
-		    || prefix == 0
-		    || (priv->family == AF_INET && prefix > 32)
-		    || (priv->family == AF_INET6 && prefix > 128))
-			valid = FALSE;
-	}
-
-	return valid;
+	if (priv->prefix)
+		return nm_utils_parse_inaddr_prefix (priv->family, text, NULL, NULL);
+	return nm_utils_parse_inaddr (priv->family, text, NULL);
 }
 
 static void
diff --git a/clients/tui/nmt-page-bond.c b/clients/tui/nmt-page-bond.c
index 48070dbf..e39a4c96 100644
--- a/clients/tui/nmt-page-bond.c
+++ b/clients/tui/nmt-page-bond.c
@@ -29,6 +29,7 @@
 
 #include "nmt-page-bond.h"
 
+#include "nmt-mac-entry.h"
 #include "nmt-address-list.h"
 #include "nmt-slave-list.h"
 
@@ -336,6 +337,7 @@ nmt_page_bond_constructed (GObject *object)
 	NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond);
 	NmtEditorSection *section;
 	NmtEditorGrid *grid;
+	NMSettingWired *s_wired;
 	NMSettingBond *s_bond;
 	NmtNewtWidget *widget, *label;
 	NMConnection *conn;
@@ -348,6 +350,12 @@ nmt_page_bond_constructed (GObject *object)
 	}
 	priv->s_bond = s_bond;
 
+	s_wired = nm_connection_get_setting_wired (conn);
+	if (!s_wired) {
+		nm_connection_add_setting (conn, nm_setting_wired_new ());
+		s_wired = nm_connection_get_setting_wired (conn);
+	}
+
 	section = nmt_editor_section_new (_("BOND"), NULL, TRUE);
 	grid = nmt_editor_section_get_body (section);
 
@@ -413,6 +421,12 @@ nmt_page_bond_constructed (GObject *object)
 	nmt_editor_grid_append (grid, _("ARP targets"), widget, NULL);
 	priv->arp_ip_target = NMT_ADDRESS_LIST (widget);
 
+	widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED);
+	g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
+	                        widget, "mac-address",
+	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, _("Cloned MAC address"), widget, NULL);
+
 	g_signal_connect (s_bond, "notify::" NM_SETTING_BOND_OPTIONS,
 	                  G_CALLBACK (bond_options_changed), bond);
 	bond_options_changed (G_OBJECT (s_bond), NULL, bond);
diff --git a/clients/tui/nmt-page-bridge.c b/clients/tui/nmt-page-bridge.c
index 08526db4..cf141f59 100644
--- a/clients/tui/nmt-page-bridge.c
+++ b/clients/tui/nmt-page-bridge.c
@@ -147,6 +147,12 @@ nmt_page_bridge_constructed (GObject *object)
 	label = nmt_newt_label_new (_("seconds"));
 	nmt_editor_grid_append (grid, _("Max age"), widget, label);
 
+	widget = nmt_newt_entry_numeric_new (10, 0, 65535);
+	g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_GROUP_FORWARD_MASK,
+	                        widget, "text",
+	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, _("Group forward mask"), widget, NULL);
+
 	nmt_editor_page_add_section (NMT_EDITOR_PAGE (bridge), section);
 
 	G_OBJECT_CLASS (nmt_page_bridge_parent_class)->constructed (object);
diff --git a/clients/tui/nmt-page-ip4.c b/clients/tui/nmt-page-ip4.c
index deb0f509..cfeb2d1e 100644
--- a/clients/tui/nmt-page-ip4.c
+++ b/clients/tui/nmt-page-ip4.c
@@ -182,6 +182,12 @@ nmt_page_ip4_constructed (GObject *object)
 	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
 	nmt_editor_grid_append (grid, NULL, widget, NULL);
 
+	widget = nmt_newt_checkbox_new (_("Ignore automatically obtained DNS parameters"));
+	g_object_bind_property (s_ip4, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS,
+	                        widget, "active",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
 	nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
 
 	widget = nmt_newt_checkbox_new (_("Require IPv4 addressing for this connection"));
diff --git a/clients/tui/nmt-page-ip6.c b/clients/tui/nmt-page-ip6.c
index 14802ace..2444a93d 100644
--- a/clients/tui/nmt-page-ip6.c
+++ b/clients/tui/nmt-page-ip6.c
@@ -180,6 +180,12 @@ nmt_page_ip6_constructed (GObject *object)
 	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
 	nmt_editor_grid_append (grid, NULL, widget, NULL);
 
+	widget = nmt_newt_checkbox_new (_("Ignore automatically obtained DNS parameters"));
+	g_object_bind_property (s_ip6, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS,
+	                        widget, "active",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
 	nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
 
 	widget = nmt_newt_checkbox_new (_("Require IPv6 addressing for this connection"));
diff --git a/clients/tui/nmt-page-vlan.c b/clients/tui/nmt-page-vlan.c
index 9cd65421..5ff3fa58 100644
--- a/clients/tui/nmt-page-vlan.c
+++ b/clients/tui/nmt-page-vlan.c
@@ -32,13 +32,6 @@
 
 G_DEFINE_TYPE (NmtPageVlan, nmt_page_vlan, NMT_TYPE_EDITOR_PAGE_DEVICE)
 
-#define NMT_PAGE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_VLAN, NmtPageVlanPrivate))
-
-typedef struct {
-	NMSettingWired *s_wired;
-
-} NmtPageVlanPrivate;
-
 NmtEditorPage *
 nmt_page_vlan_new (NMConnection   *conn,
                    NmtDeviceEntry *deventry)
@@ -67,7 +60,6 @@ static void
 nmt_page_vlan_constructed (GObject *object)
 {
 	NmtPageVlan *vlan = NMT_PAGE_VLAN (object);
-	NmtPageVlanPrivate *priv = NMT_PAGE_VLAN_GET_PRIVATE (vlan);
 	NmtEditorSection *section;
 	NmtEditorGrid *grid;
 	NMSettingWired *s_wired;
@@ -83,13 +75,9 @@ nmt_page_vlan_constructed (GObject *object)
 	}
 	s_wired = nm_connection_get_setting_wired (conn);
 	if (!s_wired) {
-		/* It makes things simpler if we always have a NMSettingWired;
-		 * we'll hold a ref on one, and add it to and remove it from
-		 * the connection as needed.
-		 */
-		s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
+		nm_connection_add_setting (conn, nm_setting_wired_new ());
+		s_wired = nm_connection_get_setting_wired (conn);
 	}
-	priv->s_wired = g_object_ref_sink (s_wired);
 
 	section = nmt_editor_section_new (_("VLAN"), NULL, TRUE);
 	grid = nmt_editor_section_get_body (section);
@@ -133,23 +121,10 @@ nmt_page_vlan_constructed (GObject *object)
 }
 
 static void
-nmt_page_vlan_finalize (GObject *object)
-{
-	NmtPageVlanPrivate *priv = NMT_PAGE_VLAN_GET_PRIVATE (object);
-
-	g_clear_object (&priv->s_wired);
-
-	G_OBJECT_CLASS (nmt_page_vlan_parent_class)->finalize (object);
-}
-
-static void
 nmt_page_vlan_class_init (NmtPageVlanClass *vlan_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (vlan_class);
 
-	g_type_class_add_private (vlan_class, sizeof (NmtPageVlanPrivate));
-
 	/* virtual methods */
 	object_class->constructed = nmt_page_vlan_constructed;
-	object_class->finalize    = nmt_page_vlan_finalize;
 }
diff --git a/clients/tui/nmt-page-wifi.c b/clients/tui/nmt-page-wifi.c
index 35625fe4..f846345c 100644
--- a/clients/tui/nmt-page-wifi.c
+++ b/clients/tui/nmt-page-wifi.c
@@ -38,13 +38,6 @@
 
 G_DEFINE_TYPE (NmtPageWifi, nmt_page_wifi, NMT_TYPE_EDITOR_PAGE_DEVICE)
 
-#define NMT_PAGE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_WIFI, NmtPageWifiPrivate))
-
-typedef struct {
-	NMSettingWirelessSecurity *s_wsec;
-
-} NmtPageWifiPrivate;
-
 NmtEditorPage *
 nmt_page_wifi_new (NMConnection   *conn,
                    NmtDeviceEntry *deventry)
@@ -177,7 +170,6 @@ ssid_transform_from_entry (GBinding     *binding,
 static void
 nmt_page_wifi_constructed (GObject *object)
 {
-	NmtPageWifiPrivate *priv = NMT_PAGE_WIFI_GET_PRIVATE (object);
 	NmtPageWifi *wifi = NMT_PAGE_WIFI (object);
 	NmtDeviceEntry *deventry;
 	NmtEditorSection *section;
@@ -198,13 +190,9 @@ nmt_page_wifi_constructed (GObject *object)
 
 	s_wsec = nm_connection_get_setting_wireless_security (conn);
 	if (!s_wsec) {
-		/* It makes things simpler if we always have a
-		 * NMSettingWirelessSecurity; we'll hold a ref on one, and add
-		 * it to and remove it from the connection as needed.
-		 */
-		s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
+		nm_connection_add_setting (conn, nm_setting_wireless_security_new ());
+		s_wsec = nm_connection_get_setting_wireless_security (conn);
 	}
-	priv->s_wsec = g_object_ref_sink (s_wsec);
 
 	deventry = nmt_editor_page_device_get_device_entry (NMT_EDITOR_PAGE_DEVICE (object));
 	g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS,
@@ -375,23 +363,9 @@ nmt_page_wifi_constructed (GObject *object)
 }
 
 static void
-nmt_page_wifi_finalize (GObject *object)
-{
-	NmtPageWifiPrivate *priv = NMT_PAGE_WIFI_GET_PRIVATE (object);
-
-	g_clear_object (&priv->s_wsec);
-
-	G_OBJECT_CLASS (nmt_page_wifi_parent_class)->finalize (object);
-}
-
-
-static void
 nmt_page_wifi_class_init (NmtPageWifiClass *wifi_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (wifi_class);
 
-	g_type_class_add_private (wifi_class, sizeof (NmtPageWifiPrivate));
-
 	object_class->constructed = nmt_page_wifi_constructed;
-	object_class->finalize    = nmt_page_wifi_finalize;
 }
diff --git a/clients/tui/nmt-route-entry.c b/clients/tui/nmt-route-entry.c
index f7c75e0a..1b37389f 100644
--- a/clients/tui/nmt-route-entry.c
+++ b/clients/tui/nmt-route-entry.c
@@ -126,7 +126,7 @@ nmt_route_entry_constructed (GObject *object)
 
 	priv->dest = nmt_ip_entry_new (priv->ip_entry_width, priv->family, TRUE, FALSE);
 	priv->next_hop = nmt_ip_entry_new (priv->ip_entry_width, priv->family, FALSE, TRUE);
-	priv->metric = nmt_newt_entry_numeric_new (priv->metric_entry_width, 0, 65535);
+	priv->metric = nmt_newt_entry_numeric_new_full (priv->metric_entry_width, 0, G_MAXUINT32, TRUE);
 
 	nmt_newt_grid_add (grid, priv->dest, 0, 0);
 	warning_label = create_warning_label (priv->dest);
diff --git a/clients/tui/nmt-route-table.c b/clients/tui/nmt-route-table.c
index 7005e8ad..a63f5205 100644
--- a/clients/tui/nmt-route-table.c
+++ b/clients/tui/nmt-route-table.c
@@ -149,9 +149,9 @@ add_route (NmtWidgetList *list,
 	NMIPRoute *route;
 
 	if (priv->family == AF_INET)
-		route = nm_ip_route_new (AF_INET, "0.0.0.0", 32, NULL, 0, NULL);
+		route = nm_ip_route_new (AF_INET, "0.0.0.0", 32, NULL, -1, NULL);
 	else
-		route = nm_ip_route_new (AF_INET6, "::", 128, NULL, 0, NULL);
+		route = nm_ip_route_new (AF_INET6, "::", 128, NULL, -1, NULL);
 	g_ptr_array_add (priv->routes, route);
 	nmt_widget_list_set_length (list, priv->routes->len);
 	g_object_notify (table, "routes");
diff --git a/clients/tui/nmtui-connect.c b/clients/tui/nmtui-connect.c
index ddabcd72..086e4bd3 100644
--- a/clients/tui/nmtui-connect.c
+++ b/clients/tui/nmtui-connect.c
@@ -36,6 +36,7 @@
 #include "nmt-password-dialog.h"
 #include "nm-secret-agent-simple.h"
 #include "nm-vpn-helpers.h"
+#include "nm-client-utils.h"
 #include "nmt-utils.h"
 
 /**
@@ -148,38 +149,60 @@ secrets_requested (NMSecretAgentSimple *agent,
 	g_object_unref (form);
 }
 
+typedef struct {
+	NMDevice *device;
+	NMActiveConnection *active;
+	NmtSyncOp *op;
+} ActivateConnectionInfo;
+
 static void
 connect_cancelled (NmtNewtForm *form,
                    gpointer     user_data)
 {
-	NmtSyncOp *op = user_data;
+	ActivateConnectionInfo *info = user_data;
 	GError *error = NULL;
 
 	error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
-	nmt_sync_op_complete_boolean (op, FALSE, error);
+	nmt_sync_op_complete_boolean (info->op, FALSE, error);
 	g_clear_error (&error);
 }
 
 static void
-activate_ac_state_changed (GObject    *object,
-                           GParamSpec *pspec,
-                           gpointer    user_data)
+check_activated (ActivateConnectionInfo *info)
 {
-	NmtSyncOp *op = user_data;
-	NMActiveConnectionState state;
-	GError *error = NULL;
-
-	state = nm_active_connection_get_state (NM_ACTIVE_CONNECTION (object));
-	if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING)
+	NMActiveConnectionState ac_state;
+	const char *reason = NULL;
+	gs_free_error GError *error = NULL;
+
+	ac_state = nmc_activation_get_effective_state (info->active, info->device, &reason);
+	if (!NM_IN_SET (ac_state,
+	                NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+	                NM_ACTIVE_CONNECTION_STATE_DEACTIVATED))
 		return;
 
-	if (state != NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-		error = g_error_new_literal (NM_CLIENT_ERROR, NM_CLIENT_ERROR_FAILED,
-		                             _("Activation failed"));
+	if (ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
+		nm_assert (reason);
+		error = g_error_new (NM_CLIENT_ERROR, NM_CLIENT_ERROR_FAILED,
+		                     _("Activation failed: %s"), reason);
 	}
 
-	nmt_sync_op_complete_boolean (op, error == NULL, error);
-	g_clear_error (&error);
+	nmt_sync_op_complete_boolean (info->op, error == NULL, error);
+}
+
+static void
+activate_ac_state_changed (GObject    *object,
+                           GParamSpec *pspec,
+                           gpointer    user_data)
+{
+	check_activated (user_data);
+}
+
+static void
+activate_device_state_changed (GObject    *object,
+                               GParamSpec *pspec,
+                               gpointer    user_data)
+{
+	check_activated (user_data);
 }
 
 static void
@@ -226,6 +249,7 @@ activate_connection (NMConnection *connection,
 	const char *specific_object_path;
 	NMActiveConnection *ac;
 	GError *error = NULL;
+	ActivateConnectionInfo info = { };
 
 	form = g_object_new (NMT_TYPE_NEWT_FORM,
 	                     "escape-exits", TRUE,
@@ -290,12 +314,18 @@ activate_connection (NMConnection *connection,
 	/* Now wait for the connection to actually reach the ACTIVATED state,
 	 * allowing the user to cancel if it takes too long.
 	 */
-
 	nmt_sync_op_init (&op);
+	info.active = ac;
+	info.device = device;
+	info.op = &op;
 
-	g_signal_connect (form, "quit", G_CALLBACK (connect_cancelled), &op);
+	g_signal_connect (form, "quit", G_CALLBACK (connect_cancelled), &info);
 	g_signal_connect (ac, "notify::" NM_ACTIVE_CONNECTION_STATE,
-	                  G_CALLBACK (activate_ac_state_changed), &op);
+	                  G_CALLBACK (activate_ac_state_changed), &info);
+	if (device) {
+		g_signal_connect (device, "notify::" NM_DEVICE_STATE,
+		                  G_CALLBACK (activate_device_state_changed), &info);
+	}
 
 	if (!nmt_sync_op_wait_boolean (&op, &error)) {
 		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
@@ -303,8 +333,10 @@ activate_connection (NMConnection *connection,
 		g_clear_error (&error);
 	}
 
-	g_signal_handlers_disconnect_by_func (form, G_CALLBACK (connect_cancelled), &op);
-	g_signal_handlers_disconnect_by_func (ac, G_CALLBACK (activate_ac_state_changed), &op);
+	g_signal_handlers_disconnect_by_func (form, G_CALLBACK (connect_cancelled), &info);
+	g_signal_handlers_disconnect_by_func (ac, G_CALLBACK (activate_ac_state_changed), &info);
+	if (device)
+		g_signal_handlers_disconnect_by_func (device, G_CALLBACK (activate_device_state_changed), &info);
 
  done:
 	if (nmt_newt_widget_get_realized (NMT_NEWT_WIDGET (form)))