summary refs log tree commit diff
path: root/src/devices/wwan/nm-modem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/wwan/nm-modem.c')
-rw-r--r--src/devices/wwan/nm-modem.c482
1 files changed, 378 insertions, 104 deletions
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index fd3b4a36..ba1db8a4 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -19,6 +19,8 @@
  * Copyright (C) 2009 Novell, Inc.
  */
 
+#include "config.h"
+
 #include <string.h>
 #include "nm-modem.h"
 #include "nm-platform.h"
@@ -42,11 +44,13 @@ enum {
 	PROP_PATH,
 	PROP_UID,
 	PROP_DRIVER,
-	PROP_IP_METHOD,
+	PROP_IP4_METHOD,
+	PROP_IP6_METHOD,
 	PROP_IP_TIMEOUT,
 	PROP_STATE,
 	PROP_DEVICE_ID,
 	PROP_SIM_ID,
+	PROP_IP_TYPES,
 
 	LAST_PROP
 };
@@ -58,11 +62,14 @@ typedef struct {
 	char *control_port;
 	char *data_port;
 	char *ppp_iface;
-	guint32 ip_method;
+	NMModemIPMethod ip4_method;
+	NMModemIPMethod ip6_method;
+	NMUtilsIPv6IfaceId iid;
 	NMModemState state;
 	NMModemState prev_state;  /* revert to this state if enable/disable fails */
 	char *device_id;
 	char *sim_id;
+	NMModemIPType ip_types;
 
 	NMPPPManager *ppp_manager;
 
@@ -82,6 +89,7 @@ enum {
 	PPP_FAILED,
 	PREPARE_RESULT,
 	IP4_CONFIG_RESULT,
+	IP6_CONFIG_RESULT,
 	AUTH_REQUESTED,
 	AUTH_RESULT,
 	REMOVED,
@@ -94,17 +102,6 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 
 /*****************************************************************************/
-
-GQuark
-nm_modem_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-modem-error");
-	return quark;
-}
-
-/*****************************************************************************/
 /* State/enabled/connected */
 
 static const char *state_table[] = {
@@ -126,7 +123,7 @@ static const char *state_table[] = {
 const char *
 nm_modem_state_to_string (NMModemState state)
 {
-	if (state >= 0 && state < G_N_ELEMENTS (state_table))
+	if ((gsize) state < G_N_ELEMENTS (state_table))
 		return state_table[state];
 	return NULL;
 }
@@ -218,6 +215,102 @@ nm_modem_emit_removed (NMModem *self)
 	g_signal_emit (self, signals[REMOVED], 0);
 }
 
+NMModemIPType
+nm_modem_get_supported_ip_types (NMModem *self)
+{
+	return NM_MODEM_GET_PRIVATE (self)->ip_types;
+}
+
+/**
+ * nm_modem_get_connection_ip_type:
+ * @self: the #NMModem
+ * @connection: the #NMConnection to determine IP type to use
+ *
+ * Given a modem and a connection, determine which NMModemIpType to use
+ * when connecting.
+ *
+ * Returns: a single %NMModemIpType value
+ */
+NMModemIPType
+nm_modem_get_connection_ip_type (NMModem *self,
+                                 NMConnection *connection,
+                                 GError **error)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	NMSettingIPConfig *s_ip4, *s_ip6;
+	const char *method;
+	gboolean ip4 = TRUE, ip6 = TRUE;
+	gboolean ip4_may_fail = TRUE, ip6_may_fail = TRUE;
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	if (s_ip4) {
+		method = nm_setting_ip_config_get_method (s_ip4);
+		if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
+			ip4 = FALSE;
+		ip4_may_fail = nm_setting_ip_config_get_may_fail (s_ip4);
+	}
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	if (s_ip6) {
+		method = nm_setting_ip_config_get_method (s_ip6);
+		if (g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
+			ip6 = FALSE;
+		ip6_may_fail = nm_setting_ip_config_get_may_fail (s_ip6);
+	}
+
+	if (ip4 && !ip6) {
+		if (!(priv->ip_types & NM_MODEM_IP_TYPE_IPV4)) {
+			g_set_error_literal (error,
+			                     NM_DEVICE_ERROR,
+			                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+			                     "Connection requested IPv4 but IPv4 is "
+			                     "unsuported by the modem.");
+			return NM_MODEM_IP_TYPE_UNKNOWN;
+		}
+		return NM_MODEM_IP_TYPE_IPV4;
+	}
+
+	if (ip6 && !ip4) {
+		if (!(priv->ip_types & NM_MODEM_IP_TYPE_IPV6)) {
+			g_set_error_literal (error,
+			                     NM_DEVICE_ERROR,
+			                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+			                     "Connection requested IPv6 but IPv6 is "
+			                     "unsuported by the modem.");
+			return NM_MODEM_IP_TYPE_UNKNOWN;
+		}
+		return NM_MODEM_IP_TYPE_IPV6;
+	}
+
+	if (ip4 && ip6) {
+		/* Modem supports dual-stack */
+		if (priv->ip_types & NM_MODEM_IP_TYPE_IPV4V6)
+			return NM_MODEM_IP_TYPE_IPV4V6;
+
+		/* Both IPv4 and IPv6 requested, but modem doesn't support dual-stack;
+		 * if one method is marked "may-fail" then use the other.
+		 */
+		if (ip6_may_fail)
+			return NM_MODEM_IP_TYPE_IPV4;
+		else if (ip4_may_fail)
+			return NM_MODEM_IP_TYPE_IPV6;
+
+		g_set_error_literal (error,
+		                     NM_DEVICE_ERROR,
+		                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+		                     "Connection requested both IPv4 and IPv6 "
+		                     "but dual-stack addressing is unsupported "
+		                     "by the modem.");
+		return NM_MODEM_IP_TYPE_UNKNOWN;
+	}
+
+	g_set_error_literal (error,
+	                     NM_DEVICE_ERROR,
+	                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+	                     "Connection specified no IP configuration!");
+	return NM_MODEM_IP_TYPE_UNKNOWN;
+}
+
 /*****************************************************************************/
 /* IP method PPP */
 
@@ -237,13 +330,24 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_
 }
 
 static void
+set_data_port (NMModem *self, const char *new_data_port)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+
+	if (g_strcmp0 (priv->data_port, new_data_port) != 0) {
+		g_free (priv->data_port);
+		priv->data_port = g_strdup (new_data_port);
+		g_object_notify (G_OBJECT (self), NM_MODEM_DATA_PORT);
+	}
+}
+
+static void
 ppp_ip4_config (NMPPPManager *ppp_manager,
 				const char *iface,
 				NMIP4Config *config,
 				gpointer user_data)
 {
 	NMModem *self = NM_MODEM (user_data);
-	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 	guint32 i, num;
 	guint32 bad_dns1 = htonl (0x0A0B0C0D);
 	guint32 good_dns1 = htonl (0x04020201);  /* GTE nameserver */
@@ -252,9 +356,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 	gboolean dns_workaround = FALSE;
 
 	/* Notify about the new data port to use */
-	g_free (priv->ppp_iface);
-	priv->ppp_iface = g_strdup (iface);
-	g_object_notify (G_OBJECT (self), NM_MODEM_DATA_PORT);
+	set_data_port (self, iface);
 
 	/* Work around a PPP bug (#1732) which causes many mobile broadband
 	 * providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers.
@@ -296,6 +398,23 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 }
 
 static void
+ppp_ip6_config (NMPPPManager *ppp_manager,
+                const char *iface,
+                const NMUtilsIPv6IfaceId *iid,
+                NMIP6Config *config,
+                gpointer user_data)
+{
+	NMModem *self = NM_MODEM (user_data);
+
+	/* Notify about the new data port to use */
+	set_data_port (self, iface);
+
+	NM_MODEM_GET_PRIVATE (self)->iid = *iid;
+
+	nm_modem_emit_ip6_config_result (self, config, NULL);
+}
+
+static void
 ppp_stats (NMPPPManager *ppp_manager,
 		   guint32 in_bytes,
 		   guint32 out_bytes,
@@ -313,20 +432,27 @@ ppp_stats (NMPPPManager *ppp_manager,
 }
 
 static NMActStageReturn
-ppp_stage3_ip4_config_start (NMModem *self,
-                             NMActRequest *req,
-                             NMDeviceStateReason *reason)
+ppp_stage3_ip_config_start (NMModem *self,
+                            NMActRequest *req,
+                            NMDeviceStateReason *reason)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 	const char *ppp_name = NULL;
 	GError *error = NULL;
 	NMActStageReturn ret;
-	guint ip_timeout = 20;
+	guint ip_timeout = 30;
 
 	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (reason !=	NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
+	/* If we're already running PPP don't restart it; for example, if both
+	 * IPv4 and IPv6 are requested, IPv4 gets started first, but we use the
+	 * same pppd for both v4 and v6.
+	 */
+	if (priv->ppp_manager)
+		return NM_ACT_STAGE_RETURN_POSTPONE;
+
 	if (NM_MODEM_GET_CLASS (self)->get_user_pass) {
 		NMConnection *connection = nm_act_request_get_connection (req);
 
@@ -336,7 +462,7 @@ ppp_stage3_ip4_config_start (NMModem *self,
 	}
 
 	/* Check if ModemManager requested a specific IP timeout to be used. If 0 reported,
-	 * use the default one (20s) */
+	 * use the default one (30s) */
 	if (priv->mm_ip_timeout > 0) {
 		nm_log_info (LOGD_PPP, "using modem-specified IP timeout: %u seconds",
 		             priv->mm_ip_timeout);
@@ -351,6 +477,9 @@ ppp_stage3_ip4_config_start (NMModem *self,
 		g_signal_connect (priv->ppp_manager, "ip4-config",
 		                  G_CALLBACK (ppp_ip4_config),
 		                  self);
+		g_signal_connect (priv->ppp_manager, "ip6-config",
+		                  G_CALLBACK (ppp_ip6_config),
+		                  self);
 		g_signal_connect (priv->ppp_manager, "stats",
 		                  G_CALLBACK (ppp_stats),
 		                  self);
@@ -382,6 +511,8 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 {
 	NMModemPrivate *priv;
 	NMActRequest *req;
+	NMConnection *connection;
+	const char *method;
 	NMActStageReturn ret;
 
 	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
@@ -391,21 +522,36 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 
 	req = nm_device_get_act_request (device);
 	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+
+	/* Only Disabled and Auto methods make sense for WWAN */
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
+		return NM_ACT_STAGE_RETURN_STOP;
+
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) != 0) {
+		nm_log_warn (LOGD_MB | LOGD_IP4,
+		             "(%s): unhandled WWAN IPv4 method '%s'; will fail",
+		             nm_modem_get_uid (self), method);
+		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
 
 	priv = NM_MODEM_GET_PRIVATE (self);
-	switch (priv->ip_method) {
-	case MM_MODEM_IP_METHOD_PPP:
-		ret = ppp_stage3_ip4_config_start (self, req, reason);
+	switch (priv->ip4_method) {
+	case NM_MODEM_IP_METHOD_PPP:
+		ret = ppp_stage3_ip_config_start (self, req, reason);
 		break;
-	case MM_MODEM_IP_METHOD_STATIC:
+	case NM_MODEM_IP_METHOD_STATIC:
 		ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, reason);
 		break;
-	case MM_MODEM_IP_METHOD_DHCP:
+	case NM_MODEM_IP_METHOD_AUTO:
 		ret = device_class->act_stage3_ip4_config_start (device, NULL, reason);
 		break;
 	default:
-		nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
-		ret = NM_ACT_STAGE_RETURN_FAILURE;
+		nm_log_info (LOGD_MB, "(%s): IPv4 configuration disabled", nm_modem_get_uid (self));
+		ret = NM_ACT_STAGE_RETURN_STOP;
 		break;
 	}
 
@@ -423,8 +569,8 @@ nm_modem_ip4_pre_commit (NMModem *modem,
 	 * not point-to-point) and IP config has a /32 prefix, then we assume that
 	 * ARP will be pointless and we turn it off.
 	 */
-	if (   priv->ip_method == MM_MODEM_IP_METHOD_STATIC
-	    || priv->ip_method == MM_MODEM_IP_METHOD_DHCP) {
+	if (   priv->ip4_method == NM_MODEM_IP_METHOD_STATIC
+	    || priv->ip4_method == NM_MODEM_IP_METHOD_AUTO) {
 		const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, 0);
 
 		g_assert (address);
@@ -435,15 +581,100 @@ nm_modem_ip4_pre_commit (NMModem *modem,
 
 /*****************************************************************************/
 
+void
+nm_modem_emit_ip6_config_result (NMModem *self,
+                                 NMIP6Config *config,
+                                 GError *error)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	guint i, num;
+	gboolean do_slaac = TRUE;
+
+	if (error) {
+		g_signal_emit (self, signals[IP6_CONFIG_RESULT], 0, NULL, FALSE, error);
+		return;
+	}
+
+	if (config) {
+		/* If the IPv6 configuration only included a Link-Local address, then
+		 * we have to run SLAAC to get the full IPv6 configuration.
+		 */
+		num = nm_ip6_config_get_num_addresses (config);
+		g_assert (num > 0);
+		for (i = 0; i < num; i++) {
+			const NMPlatformIP6Address * addr = nm_ip6_config_get_address (config, i);
+
+			if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
+				if (!priv->iid.id)
+					priv->iid.id = ((guint64 *)(&addr->address.s6_addr))[1];
+			} else
+				do_slaac = FALSE;
+		}
+	}
+	g_assert (config || do_slaac);
+
+	g_signal_emit (self, signals[IP6_CONFIG_RESULT], 0, config, do_slaac, NULL);
+}
+
+static NMActStageReturn
+stage3_ip6_config_request (NMModem *self, NMDeviceStateReason *reason)
+{
+	*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+	return NM_ACT_STAGE_RETURN_FAILURE;
+}
+
 NMActStageReturn
 nm_modem_stage3_ip6_config_start (NMModem *self,
-                                  NMDevice *device,
-                                  NMDeviceClass *device_class,
+                                  NMActRequest *req,
                                   NMDeviceStateReason *reason)
 {
-	/* FIXME: We don't support IPv6 on modems quite yet... */
-	nm_device_activate_schedule_ip6_config_timeout (device);
-	return NM_ACT_STAGE_RETURN_POSTPONE;
+	NMModemPrivate *priv;
+	NMActStageReturn ret;
+	NMConnection *connection;
+	const char *method;
+
+	g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
+
+	/* Only Ignore and Auto methods make sense for WWAN */
+	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
+		return NM_ACT_STAGE_RETURN_STOP;
+
+	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) != 0) {
+		nm_log_warn (LOGD_MB | LOGD_IP6,
+		             "(%s): unhandled WWAN IPv6 method '%s'; will fail",
+		             nm_modem_get_uid (self), method);
+		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+	switch (priv->ip6_method) {
+	case NM_MODEM_IP_METHOD_PPP:
+		ret = ppp_stage3_ip_config_start (self, req, reason);
+		break;
+	case NM_MODEM_IP_METHOD_STATIC:
+	case NM_MODEM_IP_METHOD_AUTO:
+		/* Both static and DHCP/Auto retrieve a base IP config from the modem
+		 * which in the static case is the full config, and the DHCP/Auto case
+		 * is just the IPv6LL address to use for SLAAC.
+		 */
+		ret = NM_MODEM_GET_CLASS (self)->stage3_ip6_config_request (self, reason);
+		break;
+	default:
+		nm_log_info (LOGD_MB, "(%s): IPv6 configuration disabled", nm_modem_get_uid (self));
+		ret = NM_ACT_STAGE_RETURN_STOP;
+		break;
+	}
+
+	return ret;
 }
 
 /*****************************************************************************/
@@ -486,12 +717,12 @@ nm_modem_get_secrets (NMModem *self,
                       const char *hint)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
-	NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+	NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 
 	cancel_get_secrets (self);
 
 	if (request_new)
-		flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW;
+		flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
 	priv->secrets_id = nm_act_request_get_secrets (priv->act_request,
 	                                               setting_name,
 	                                               flags,
@@ -524,7 +755,7 @@ nm_modem_act_stage1_prepare (NMModem *self,
 	NMActStageReturn ret;
 	GPtrArray *hints = NULL;
 	const char *setting_name = NULL;
-	NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+	NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 	NMConnection *connection;
 
 	if (priv->act_request)
@@ -543,7 +774,7 @@ nm_modem_act_stage1_prepare (NMModem *self,
 
 	/* Secrets required... */
 	if (priv->secrets_tries++)
-		flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW;
+		flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
 
 	priv->secrets_id = nm_act_request_get_secrets (req,
 	                                               setting_name,
@@ -633,22 +864,19 @@ deactivate (NMModem *self, NMDevice *device)
 		priv->ppp_manager = NULL;
 	}
 
-	switch (priv->ip_method) {
-	case MM_MODEM_IP_METHOD_PPP:
-		break;
-	case MM_MODEM_IP_METHOD_STATIC:
-	case MM_MODEM_IP_METHOD_DHCP:
+	if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC ||
+	    priv->ip4_method == NM_MODEM_IP_METHOD_AUTO ||
+	    priv->ip6_method == NM_MODEM_IP_METHOD_STATIC ||
+	    priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
 		ifindex = nm_device_get_ip_ifindex (device);
 		if (ifindex > 0) {
 			nm_platform_route_flush (ifindex);
 			nm_platform_address_flush (ifindex);
 			nm_platform_link_set_down (ifindex);
 		}
-		break;
-	default:
-		nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
-		break;
 	}
+	priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
+	priv->ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
 
 	g_free (priv->ppp_iface);
 	priv->ppp_iface = NULL;
@@ -772,6 +1000,15 @@ nm_modem_owns_port (NMModem *self, const char *iface)
 	return FALSE;
 }
 
+gboolean
+nm_modem_get_iid (NMModem *self, NMUtilsIPv6IfaceId *out_iid)
+{
+	g_return_val_if_fail (NM_IS_MODEM (self), FALSE);
+
+	*out_iid = NM_MODEM_GET_PRIVATE (self)->iid;
+	return TRUE;
+}
+
 /*****************************************************************************/
 
 void
@@ -846,8 +1083,11 @@ get_property (GObject *object, guint prop_id,
 	case PROP_UID:
 		g_value_set_string (value, priv->uid);
 		break;
-	case PROP_IP_METHOD:
-		g_value_set_uint (value, priv->ip_method);
+	case PROP_IP4_METHOD:
+		g_value_set_uint (value, priv->ip4_method);
+		break;
+	case PROP_IP6_METHOD:
+		g_value_set_uint (value, priv->ip6_method);
 		break;
 	case PROP_IP_TIMEOUT:
 		g_value_set_uint (value, priv->mm_ip_timeout);
@@ -861,6 +1101,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_SIM_ID:
 		g_value_set_string (value, priv->sim_id);
 		break;
+	case PROP_IP_TYPES:
+		g_value_set_uint (value, priv->ip_types);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -892,8 +1135,11 @@ set_property (GObject *object, guint prop_id,
 		/* Construct only */
 		priv->uid = g_value_dup_string (value);
 		break;
-	case PROP_IP_METHOD:
-		priv->ip_method = g_value_get_uint (value);
+	case PROP_IP4_METHOD:
+		priv->ip4_method = g_value_get_uint (value);
+		break;
+	case PROP_IP6_METHOD:
+		priv->ip6_method = g_value_get_uint (value);
 		break;
 	case PROP_IP_TIMEOUT:
 		priv->mm_ip_timeout = g_value_get_uint (value);
@@ -909,6 +1155,9 @@ set_property (GObject *object, guint prop_id,
 		g_free (priv->sim_id);
 		priv->sim_id = g_value_dup_string (value);
 		break;
+	case PROP_IP_TYPES:
+		priv->ip_types = g_value_get_uint (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -959,92 +1208,100 @@ nm_modem_class_init (NMModemClass *klass)
 	object_class->finalize = finalize;
 
 	klass->act_stage1_prepare = act_stage1_prepare;
+	klass->stage3_ip6_config_request = stage3_ip6_config_request;
 	klass->deactivate = deactivate;
 
 	/* Properties */
 
 	g_object_class_install_property
 		(object_class, PROP_UID,
-		 g_param_spec_string (NM_MODEM_UID,
-		                      "UID",
-		                      "Modem unique ID",
+		 g_param_spec_string (NM_MODEM_UID, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_PATH,
-		 g_param_spec_string (NM_MODEM_PATH,
-		                      "DBus path",
-		                      "DBus path",
+		 g_param_spec_string (NM_MODEM_PATH, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DRIVER,
-		 g_param_spec_string (NM_MODEM_DRIVER,
-		                      "Driver",
-		                      "Driver",
+		 g_param_spec_string (NM_MODEM_DRIVER, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_CONTROL_PORT,
-		 g_param_spec_string (NM_MODEM_CONTROL_PORT,
-		                      "Control port",
-		                      "The port controlling the modem",
+		 g_param_spec_string (NM_MODEM_CONTROL_PORT, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DATA_PORT,
-		 g_param_spec_string (NM_MODEM_DATA_PORT,
-		                      "Data port",
-		                      "The port to connect to",
+		 g_param_spec_string (NM_MODEM_DATA_PORT, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
-		(object_class, PROP_IP_METHOD,
-		 g_param_spec_uint (NM_MODEM_IP_METHOD,
-		                    "IP method",
-		                    "IP method",
-		                    MM_MODEM_IP_METHOD_PPP,
-		                    MM_MODEM_IP_METHOD_DHCP,
-		                    MM_MODEM_IP_METHOD_PPP,
-		                    G_PARAM_READWRITE));
+		(object_class, PROP_IP4_METHOD,
+		 g_param_spec_uint (NM_MODEM_IP4_METHOD, "", "",
+		                    NM_MODEM_IP_METHOD_UNKNOWN,
+		                    NM_MODEM_IP_METHOD_AUTO,
+		                    NM_MODEM_IP_METHOD_UNKNOWN,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+		                    G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_IP6_METHOD,
+		 g_param_spec_uint (NM_MODEM_IP6_METHOD, "", "",
+		                    NM_MODEM_IP_METHOD_UNKNOWN,
+		                    NM_MODEM_IP_METHOD_AUTO,
+		                    NM_MODEM_IP_METHOD_UNKNOWN,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_IP_TIMEOUT,
-		 g_param_spec_uint (NM_MODEM_IP_TIMEOUT,
-		                    "IP timeout",
-		                    "IP timeout",
+		 g_param_spec_uint (NM_MODEM_IP_TIMEOUT, "", "",
 		                    0, 360, 20,
-		                    G_PARAM_READWRITE));
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_STATE,
-		 g_param_spec_enum (NM_MODEM_STATE,
-		                   "State",
-		                   "State",
-		                   NM_TYPE_MODEM_STATE,
-		                   NM_MODEM_STATE_UNKNOWN,
-		                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_enum (NM_MODEM_STATE, "", "",
+		                    NM_TYPE_MODEM_STATE,
+		                    NM_MODEM_STATE_UNKNOWN,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DEVICE_ID,
-		 g_param_spec_string (NM_MODEM_DEVICE_ID,
-		                      "DeviceId",
-		                      "Device ID",
+		 g_param_spec_string (NM_MODEM_DEVICE_ID, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_SIM_ID,
-		 g_param_spec_string (NM_MODEM_SIM_ID,
-		                      "SimId",
-		                      "Sim ID",
+		 g_param_spec_string (NM_MODEM_SIM_ID, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_IP_TYPES,
+		 g_param_spec_uint (NM_MODEM_IP_TYPES,
+		                    "IP Types",
+		                    "Supported IP types",
+		                    0, G_MAXUINT32, NM_MODEM_IP_TYPE_IPV4,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
 	/* Signals */
 
@@ -1073,6 +1330,27 @@ nm_modem_class_init (NMModemClass *klass)
 		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_POINTER);
 
+	/**
+	 * NMModem::ip6-config-result:
+	 * @modem: the #NMModem  on which the signal is emitted
+	 * @config: the #NMIP6Config to apply to the modem's data port
+	 * @do_slaac: %TRUE if IPv6 SLAAC should be started
+	 * @error: a #GError if any error occurred during IP configuration
+	 *
+	 * This signal is emitted when IPv6 configuration has completed or failed.
+	 * If @error is set the configuration failed.  If @config is set, then
+	 * the details should be applied to the data port before any further
+	 * configuration (like SLAAC) is done.  @do_slaac indicates whether SLAAC
+	 * should be started after applying @config to the data port.
+	 */
+	signals[IP6_CONFIG_RESULT] =
+		g_signal_new (NM_MODEM_IP6_CONFIG_RESULT,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMModemClass, ip6_config_result),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_BOOLEAN, G_TYPE_POINTER);
+
 	signals[PREPARE_RESULT] =
 		g_signal_new (NM_MODEM_PREPARE_RESULT,
 		              G_OBJECT_CLASS_TYPE (object_class),
@@ -1112,8 +1390,4 @@ nm_modem_class_init (NMModemClass *klass)
 		              G_STRUCT_OFFSET (NMModemClass, state_changed),
 		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 2, NM_TYPE_MODEM_STATE, NM_TYPE_MODEM_STATE);
-
-	dbus_g_error_domain_register (NM_MODEM_ERROR,
-	                              NM_DBUS_INTERFACE_DEVICE_MODEM,
-	                              NM_TYPE_MODEM_ERROR);
 }