summary refs log tree commit diff
path: root/src/devices/wwan
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2019-07-31 10:51:42 +0200
committerMichael Biebl <biebl@debian.org>2019-07-31 10:51:42 +0200
commit2e5fa45ddfbb5cffa1e78221f1cea706e2f298af (patch)
tree86f69d36c56de3074280456eddc854a780b8e04b /src/devices/wwan
parent85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (diff)
New upstream version 1.19.90 upstream/1.19.90
Diffstat (limited to 'src/devices/wwan')
-rw-r--r--src/devices/wwan/libnm-wwan.ver3
-rw-r--r--src/devices/wwan/nm-device-modem.c86
-rw-r--r--src/devices/wwan/nm-device-modem.h4
-rw-r--r--src/devices/wwan/nm-modem-broadband.c71
-rw-r--r--src/devices/wwan/nm-modem-broadband.h1
-rw-r--r--src/devices/wwan/nm-modem-manager.c1
-rw-r--r--src/devices/wwan/nm-modem-manager.h1
-rw-r--r--src/devices/wwan/nm-modem-ofono.c1
-rw-r--r--src/devices/wwan/nm-modem-ofono.h1
-rw-r--r--src/devices/wwan/nm-modem.c80
-rw-r--r--src/devices/wwan/nm-modem.h10
-rw-r--r--src/devices/wwan/nm-wwan-factory.c1
12 files changed, 233 insertions, 27 deletions
diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver
index ea966afe..7ccebcb5 100644
--- a/src/devices/wwan/libnm-wwan.ver
+++ b/src/devices/wwan/libnm-wwan.ver
@@ -7,13 +7,16 @@ global:
 	nm_modem_deactivate;
 	nm_modem_deactivate_async;
 	nm_modem_device_state_changed;
+	nm_modem_get_apn;
 	nm_modem_get_capabilities;
 	nm_modem_get_configured_mtu;
 	nm_modem_get_control_port;
+	nm_modem_get_device_id;
 	nm_modem_get_driver;
 	nm_modem_get_iid;
 	nm_modem_get_path;
 	nm_modem_get_ip_ifindex;
+	nm_modem_get_operator_code;
 	nm_modem_get_secrets;
 	nm_modem_get_state;
 	nm_modem_get_type;
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 1e316280..042a6ca4 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
@@ -15,7 +14,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2009 - 2011 Red Hat, Inc.
+ * Copyright (C) 2009 - 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -36,10 +35,13 @@ _LOG_DECLARE_SELF(NMDeviceModem);
 
 /*****************************************************************************/
 
-NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceModem,
 	PROP_MODEM,
 	PROP_CAPABILITIES,
 	PROP_CURRENT_CAPABILITIES,
+	PROP_DEVICE_ID,
+	PROP_OPERATOR_CODE,
+	PROP_APN,
 );
 
 typedef struct {
@@ -47,6 +49,9 @@ typedef struct {
 	NMDeviceModemCapabilities caps;
 	NMDeviceModemCapabilities current_caps;
 	gboolean rf_enabled;
+	char *device_id;
+	char *operator_code;
+	char *apn;
 } NMDeviceModemPrivate;
 
 struct _NMDeviceModem {
@@ -308,6 +313,34 @@ ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
 }
 
 static void
+operator_code_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
+{
+	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
+	const char *operator_code = nm_modem_get_operator_code (modem);
+
+	if (g_strcmp0 (priv->operator_code, operator_code) != 0) {
+		g_free (priv->operator_code);
+		priv->operator_code = g_strdup (operator_code);
+		_notify (self, PROP_OPERATOR_CODE);
+	}
+}
+
+static void
+apn_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
+{
+	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
+	const char *apn = nm_modem_get_apn (modem);
+
+	if (g_strcmp0 (priv->apn, apn) != 0) {
+		g_free (priv->apn);
+		priv->apn = g_strdup (apn);
+		_notify (self, PROP_APN);
+	}
+}
+
+static void
 ids_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
 {
 	nm_device_recheck_available_connections (NM_DEVICE (user_data));
@@ -499,7 +532,11 @@ complete_connection (NMDevice *device,
 {
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device);
 
-	return nm_modem_complete_connection (priv->modem, connection, existing_connections, error);
+	return nm_modem_complete_connection (priv->modem,
+	                                     nm_device_get_iface (device),
+	                                     connection,
+	                                     existing_connections,
+	                                     error);
 }
 
 static void
@@ -688,6 +725,8 @@ set_modem (NMDeviceModem *self, NMModem *modem)
 	g_signal_connect (modem, "notify::" NM_MODEM_DEVICE_ID, G_CALLBACK (ids_changed_cb), self);
 	g_signal_connect (modem, "notify::" NM_MODEM_SIM_ID, G_CALLBACK (ids_changed_cb), self);
 	g_signal_connect (modem, "notify::" NM_MODEM_SIM_OPERATOR_ID, G_CALLBACK (ids_changed_cb), self);
+	g_signal_connect (modem, "notify::" NM_MODEM_OPERATOR_CODE, G_CALLBACK (operator_code_changed_cb), self);
+	g_signal_connect (modem, "notify::" NM_MODEM_APN, G_CALLBACK (apn_changed_cb), self);
 }
 
 static guint32
@@ -718,6 +757,15 @@ get_property (GObject *object, guint prop_id,
 	case PROP_CURRENT_CAPABILITIES:
 		g_value_set_uint (value, priv->current_caps);
 		break;
+	case PROP_DEVICE_ID:
+		g_value_set_string (value, priv->device_id);
+		break;
+	case PROP_OPERATOR_CODE:
+		g_value_set_string (value, priv->operator_code);
+		break;
+	case PROP_APN:
+		g_value_set_string (value, priv->apn);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -741,6 +789,10 @@ set_property (GObject *object, guint prop_id,
 	case PROP_CURRENT_CAPABILITIES:
 		priv->current_caps = g_value_get_uint (value);
 		break;
+	case PROP_DEVICE_ID:
+		/* construct-only */
+		priv->device_id = g_value_dup_string (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -775,6 +827,7 @@ nm_device_modem_new (NMModem *modem)
 	                     NM_DEVICE_MODEM_MODEM, modem,
 	                     NM_DEVICE_MODEM_CAPABILITIES, caps,
 	                     NM_DEVICE_MODEM_CURRENT_CAPABILITIES, current_caps,
+	                     NM_DEVICE_MODEM_DEVICE_ID, nm_modem_get_device_id (modem),
 	                     NULL);
 }
 
@@ -788,6 +841,10 @@ dispose (GObject *object)
 		g_clear_object (&priv->modem);
 	}
 
+	g_clear_pointer (&priv->device_id, g_free);
+	g_clear_pointer (&priv->operator_code, g_free);
+	g_clear_pointer (&priv->apn, g_free);
+
 	G_OBJECT_CLASS (nm_device_modem_parent_class)->dispose (object);
 }
 
@@ -800,6 +857,9 @@ static const NMDBusInterfaceInfoExtended interface_info_device_modem = {
 		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ModemCapabilities",   "u",  NM_DEVICE_MODEM_CAPABILITIES),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("CurrentCapabilities", "u",  NM_DEVICE_MODEM_CURRENT_CAPABILITIES),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE   ("DeviceId",            "s",  NM_DEVICE_MODEM_DEVICE_ID),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE   ("OperatorCode",        "s",  NM_DEVICE_MODEM_OPERATOR_CODE),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE   ("Apn",                 "s",  NM_DEVICE_MODEM_APN),
 		),
 	),
 	.legacy_property_changed = TRUE,
@@ -857,5 +917,23 @@ nm_device_modem_class_init (NMDeviceModemClass *klass)
 	                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                        G_PARAM_STATIC_STRINGS);
 
+	obj_properties[PROP_DEVICE_ID] =
+	     g_param_spec_string (NM_DEVICE_MODEM_DEVICE_ID, "", "",
+	                          NULL,
+	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_OPERATOR_CODE] =
+	     g_param_spec_string (NM_DEVICE_MODEM_OPERATOR_CODE, "", "",
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_APN] =
+	     g_param_spec_string (NM_DEVICE_MODEM_APN, "", "",
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
+
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 }
diff --git a/src/devices/wwan/nm-device-modem.h b/src/devices/wwan/nm-device-modem.h
index 65bda8b6..0a557f57 100644
--- a/src/devices/wwan/nm-device-modem.h
+++ b/src/devices/wwan/nm-device-modem.h
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
@@ -34,6 +33,9 @@
 #define NM_DEVICE_MODEM_MODEM "modem"
 #define NM_DEVICE_MODEM_CAPABILITIES "modem-capabilities"
 #define NM_DEVICE_MODEM_CURRENT_CAPABILITIES "current-capabilities"
+#define NM_DEVICE_MODEM_DEVICE_ID            "device-id"
+#define NM_DEVICE_MODEM_OPERATOR_CODE        "operator-code"
+#define NM_DEVICE_MODEM_APN                  "apn"
 
 typedef struct _NMDeviceModem NMDeviceModem;
 typedef struct _NMDeviceModemClass NMDeviceModemClass;
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index ffdc61a6..5716500a 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
@@ -79,6 +78,7 @@ typedef struct {
 	MMObject *modem_object;
 	/* Per-interface objects */
 	MMModem *modem_iface;
+	MMModem3gpp *modem_3gpp_iface;
 	MMModemSimple *simple_iface;
 	MMSim *sim_iface;
 
@@ -213,12 +213,13 @@ owns_port (NMModem *_self, const char *iface)
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
 	const MMModemPortInfo *ports = NULL;
 	guint n_ports = 0, i;
-	gboolean owns = FALSE;
 
 	mm_modem_peek_ports (self->_priv.modem_iface, &ports, &n_ports);
-	for (i = 0; i < n_ports && !owns; i++)
-		owns = (g_strcmp0 (iface, ports[i].name) == 0);
-	return owns;
+	for (i = 0; i < n_ports; i++) {
+		if (nm_streq0 (iface, ports[i].name))
+			return TRUE;
+	}
+	return FALSE;
 }
 
 /*****************************************************************************/
@@ -547,6 +548,8 @@ connect_context_step (NMModemBroadband *self)
 			else
 				g_assert_not_reached ();
 
+			_nm_modem_set_apn (NM_MODEM (self), mm_simple_connect_properties_get_apn (ctx->connect_properties));
+
 			_LOGD ("launching connection with ip type '%s' (try %d)",
 			       nm_modem_ip_type_to_string (current),
 			       ctx->ip_type_tries + 1);
@@ -656,12 +659,13 @@ check_connection_compatible_with_modem (NMModem *_self, NMConnection *connection
 /*****************************************************************************/
 
 static gboolean
-complete_connection (NMModem *_self,
+complete_connection (NMModem *modem,
+                     const char *iface,
                      NMConnection *connection,
                      NMConnection *const*existing_connections,
                      GError **error)
 {
-	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
+	NMModemBroadband *self = NM_MODEM_BROADBAND (modem);
 	MMModemCapability modem_caps;
 	NMSettingPpp *s_ppp;
 
@@ -679,6 +683,20 @@ complete_connection (NMModem *_self,
 	}
 
 	if (MODEM_CAPS_3GPP (modem_caps)) {
+		NMSettingGsm *s_gsm;
+
+		s_gsm = nm_connection_get_setting_gsm (connection);
+		if (!s_gsm) {
+			s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
+			nm_connection_add_setting (connection, NM_SETTING (s_gsm));
+		}
+
+		if (!nm_setting_gsm_get_device_id (s_gsm)) {
+			g_object_set (G_OBJECT (s_gsm),
+			              NM_SETTING_GSM_DEVICE_ID, nm_modem_get_device_id (modem),
+			              NULL);
+		}
+
 		nm_utils_complete_generic (NM_PLATFORM_GET,
 		                           connection,
 		                           NM_SETTING_GSM_SETTING_NAME,
@@ -686,6 +704,7 @@ complete_connection (NMModem *_self,
 		                           NULL,
 		                           _("GSM connection"),
 		                           NULL,
+		                           NULL,
 		                           FALSE); /* No IPv6 yet by default */
 
 		return TRUE;
@@ -710,6 +729,7 @@ complete_connection (NMModem *_self,
 		                           NULL,
 		                           _("CDMA connection"),
 		                           NULL,
+		                           iface,
 		                           FALSE); /* No IPv6 yet by default */
 
 		return TRUE;
@@ -1140,6 +1160,7 @@ disconnect (NMModem *modem,
 	DisconnectContext *ctx;
 
 	connect_context_clear (self);
+	_nm_modem_set_apn (NM_MODEM (self), NULL);
 
 	ctx = g_slice_new0 (DisconnectContext);
 	ctx->self = g_object_ref (self);
@@ -1320,6 +1341,15 @@ supported_ip_families_changed (MMModem *modem, GParamSpec *pspec, gpointer user_
 	              NULL);
 }
 
+static void
+operator_code_changed (MMModem3gpp *modem_3gpp, GParamSpec *pspec, gpointer user_data)
+{
+	NMModemBroadband *self = NM_MODEM_BROADBAND (user_data);
+
+	g_return_if_fail (modem_3gpp == self->_priv.modem_3gpp_iface);
+	_nm_modem_set_operator_code (NM_MODEM (self), mm_modem_3gpp_get_operator_code (modem_3gpp));
+}
+
 /*****************************************************************************/
 
 static void
@@ -1353,6 +1383,7 @@ set_property (GObject *object,
 		/* construct-only */
 		self->_priv.modem_object = g_value_dup_object (value);
 		self->_priv.modem_iface = mm_object_get_modem (self->_priv.modem_object);
+		self->_priv.modem_3gpp_iface = mm_object_get_modem_3gpp (self->_priv.modem_object);
 		g_assert (self->_priv.modem_iface != NULL);
 		g_signal_connect (self->_priv.modem_iface,
 		                  "state-changed",
@@ -1368,6 +1399,13 @@ set_property (GObject *object,
 		                  G_CALLBACK (supported_ip_families_changed),
 		                  self);
 
+		if (self->_priv.modem_3gpp_iface) {
+			g_signal_connect (self->_priv.modem_3gpp_iface,
+			                  "notify::operator-code",
+			                  G_CALLBACK (operator_code_changed),
+			                  self);
+		}
+
 		/* Note: don't grab the Simple iface here; the Modem interface is the
 		 * only one assumed to be always valid and available */
 		break;
@@ -1389,7 +1427,9 @@ nm_modem_broadband_new (GObject *object, GError **error)
 {
 	MMObject *modem_object;
 	MMModem *modem_iface;
+	MMModem3gpp *modem_3gpp_iface;
 	const char *const*drivers;
+	const char *operator_code = NULL;
 	gs_free char *driver = NULL;
 
 	g_return_val_if_fail (MM_IS_OBJECT (object), NULL);
@@ -1405,6 +1445,10 @@ nm_modem_broadband_new (GObject *object, GError **error)
 	if (drivers)
 		driver = g_strjoinv (", ", (char **) drivers);
 
+	modem_3gpp_iface = mm_object_peek_modem_3gpp (modem_object);
+	if (modem_3gpp_iface)
+		operator_code = mm_modem_3gpp_get_operator_code (modem_3gpp_iface);
+
 	return g_object_new (NM_TYPE_MODEM_BROADBAND,
 	                     NM_MODEM_PATH, mm_object_get_path (modem_object),
 	                     NM_MODEM_UID, mm_modem_get_primary_port (modem_iface),
@@ -1414,6 +1458,7 @@ nm_modem_broadband_new (GObject *object, GError **error)
 	                     NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface),
 	                     NM_MODEM_BROADBAND_MODEM, modem_object,
 	                     NM_MODEM_DRIVER, driver,
+	                     NM_MODEM_OPERATOR_CODE, operator_code,
 	                     NULL);
 }
 
@@ -1430,7 +1475,17 @@ dispose (GObject *object)
 	g_clear_object (&self->_priv.ipv4_config);
 	g_clear_object (&self->_priv.ipv6_config);
 	g_clear_object (&self->_priv.bearer);
-	g_clear_object (&self->_priv.modem_iface);
+
+	if (self->_priv.modem_iface) {
+		g_signal_handlers_disconnect_by_data (self->_priv.modem_iface, self);
+		g_clear_object (&self->_priv.modem_iface);
+	}
+
+	if (self->_priv.modem_3gpp_iface) {
+		g_signal_handlers_disconnect_by_data (self->_priv.modem_3gpp_iface, self);
+		g_clear_object (&self->_priv.modem_3gpp_iface);
+	}
+
 	g_clear_object (&self->_priv.simple_iface);
 	g_clear_object (&self->_priv.sim_iface);
 	g_clear_object (&self->_priv.modem_object);
diff --git a/src/devices/wwan/nm-modem-broadband.h b/src/devices/wwan/nm-modem-broadband.h
index 4948cb3b..9404f0b9 100644
--- a/src/devices/wwan/nm-modem-broadband.h
+++ b/src/devices/wwan/nm-modem-broadband.h
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c
index 4be089c0..db2c0192 100644
--- a/src/devices/wwan/nm-modem-manager.c
+++ b/src/devices/wwan/nm-modem-manager.c
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/src/devices/wwan/nm-modem-manager.h b/src/devices/wwan/nm-modem-manager.h
index 5f913083..1a26fd9f 100644
--- a/src/devices/wwan/nm-modem-manager.h
+++ b/src/devices/wwan/nm-modem-manager.h
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c
index 78d9a9f0..31111b62 100644
--- a/src/devices/wwan/nm-modem-ofono.c
+++ b/src/devices/wwan/nm-modem-ofono.c
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/src/devices/wwan/nm-modem-ofono.h b/src/devices/wwan/nm-modem-ofono.h
index d9cb68ac..1dcd79b0 100644
--- a/src/devices/wwan/nm-modem-ofono.h
+++ b/src/devices/wwan/nm-modem-ofono.h
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 17794229..617096a7 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
@@ -52,6 +51,8 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMModem,
 	PROP_SIM_ID,
 	PROP_IP_TYPES,
 	PROP_SIM_OPERATOR_ID,
+	PROP_OPERATOR_CODE,
+	PROP_APN,
 );
 
 enum {
@@ -90,6 +91,8 @@ typedef struct _NMModemPrivate {
 	char *sim_id;
 	NMModemIPType ip_types;
 	char *sim_operator_id;
+	char *operator_code;
+	char *apn;
 
 	NMPPPManager *ppp_manager;
 
@@ -344,7 +347,8 @@ nm_modem_get_connection_ip_type (NMModem *self,
 	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)
+		if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+		                          NM_SETTING_IP6_CONFIG_METHOD_DISABLED))
 			ip6 = FALSE;
 		ip6_may_fail = nm_setting_ip_config_get_may_fail (s_ip6);
 	}
@@ -436,6 +440,18 @@ nm_modem_get_sim_operator_id (NMModem *self)
 	return NM_MODEM_GET_PRIVATE (self)->sim_operator_id;
 }
 
+const char *
+nm_modem_get_operator_code (NMModem *self)
+{
+	return NM_MODEM_GET_PRIVATE (self)->operator_code;
+}
+
+const char *
+nm_modem_get_apn (NMModem *self)
+{
+	return NM_MODEM_GET_PRIVATE (self)->apn;
+}
+
 /*****************************************************************************/
 /* IP method PPP */
 
@@ -824,8 +840,9 @@ nm_modem_stage3_ip6_config_start (NMModem *self,
 
 	method = nm_utils_get_ip_config_method (connection, AF_INET6);
 
-	/* Only Ignore and Auto methods make sense for WWAN */
-	if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE))
+	/* Only Ignore, Disabled and Auto methods make sense for WWAN */
+	if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	                          NM_SETTING_IP6_CONFIG_METHOD_DISABLED))
 		return NM_ACT_STAGE_RETURN_IP_DONE;
 
 	if (!nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
@@ -1086,6 +1103,7 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection, G
 
 gboolean
 nm_modem_complete_connection (NMModem *self,
+                              const char *iface,
                               NMConnection *connection,
                               NMConnection *const*existing_connections,
                               GError **error)
@@ -1100,7 +1118,7 @@ nm_modem_complete_connection (NMModem *self,
 		return FALSE;
 	}
 
-	return klass->complete_connection (self, connection, existing_connections, error);
+	return klass->complete_connection (self, iface, connection, existing_connections, error);
 }
 
 /*****************************************************************************/
@@ -1556,9 +1574,9 @@ nm_modem_set_route_parameters_from_device (NMModem *self,
 	g_return_if_fail (NM_IS_DEVICE (device));
 
 	nm_modem_set_route_parameters (self,
-	                               nm_device_get_route_table (device, AF_INET, TRUE),
+	                               nm_device_get_route_table (device, AF_INET),
 	                               nm_device_get_route_metric (device, AF_INET),
-	                               nm_device_get_route_table (device, AF_INET6, TRUE),
+	                               nm_device_get_route_table (device, AF_INET6),
 	                               nm_device_get_route_metric (device, AF_INET6));
 }
 
@@ -1576,6 +1594,30 @@ nm_modem_get_capabilities (NMModem *self,
 
 /*****************************************************************************/
 
+void
+_nm_modem_set_operator_code (NMModem *self, const char *operator_code)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+
+	if (g_strcmp0 (priv->operator_code, operator_code) != 0) {
+		g_free (priv->operator_code);
+		priv->operator_code = g_strdup (operator_code);
+		_notify (self, PROP_OPERATOR_CODE);
+	}
+}
+
+void
+_nm_modem_set_apn (NMModem *self, const char *apn)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+
+	if (g_strcmp0 (priv->apn, apn) != 0) {
+		g_free (priv->apn);
+		priv->apn = g_strdup (apn);
+		_notify (self, PROP_APN);
+	}
+}
+
 static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
@@ -1614,6 +1656,12 @@ get_property (GObject *object, guint prop_id,
 	case PROP_SIM_OPERATOR_ID:
 		g_value_set_string (value, priv->sim_operator_id);
 		break;
+	case PROP_OPERATOR_CODE:
+		g_value_set_string (value, priv->operator_code);
+		break;
+	case PROP_APN:
+		g_value_set_string (value, priv->apn);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1666,6 +1714,10 @@ set_property (GObject *object, guint prop_id,
 		if (s && s[0])
 			priv->sim_operator_id = g_strdup (s);
 		break;
+	case PROP_OPERATOR_CODE:
+		/* construct-only */
+		priv->operator_code = g_value_dup_string (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1727,6 +1779,8 @@ finalize (GObject *object)
 	g_free (priv->device_id);
 	g_free (priv->sim_id);
 	g_free (priv->sim_operator_id);
+	g_free (priv->operator_code);
+	g_free (priv->apn);
 
 	G_OBJECT_CLASS (nm_modem_parent_class)->finalize (object);
 }
@@ -1810,6 +1864,18 @@ nm_modem_class_init (NMModemClass *klass)
 	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
 	                          G_PARAM_STATIC_STRINGS);
 
+	obj_properties[PROP_OPERATOR_CODE] =
+	     g_param_spec_string (NM_MODEM_OPERATOR_CODE, "", "",
+	                          NULL,
+	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_APN] =
+	     g_param_spec_string (NM_MODEM_APN, "", "",
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
+
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	signals[PPP_STATS] =
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index f7b6bfe9..f5b386e8 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
@@ -43,6 +42,8 @@
 #define NM_MODEM_SIM_ID          "sim-id"
 #define NM_MODEM_IP_TYPES        "ip-types"   /* Supported IP types */
 #define NM_MODEM_SIM_OPERATOR_ID "sim-operator-id"
+#define NM_MODEM_OPERATOR_CODE   "operator-code"
+#define NM_MODEM_APN             "apn"
 
 /* Signals */
 #define NM_MODEM_PPP_STATS         "ppp-stats"
@@ -130,6 +131,7 @@ typedef struct {
 	                                                    GError **error);
 
 	gboolean (*complete_connection)            (NMModem *modem,
+	                                            const char *iface,
 	                                            NMConnection *connection,
 	                                            NMConnection *const*existing_connections,
 	                                            GError **error);
@@ -174,6 +176,8 @@ const char *nm_modem_get_device_id       (NMModem *modem);
 const char *nm_modem_get_sim_id          (NMModem *modem);
 const char *nm_modem_get_sim_operator_id (NMModem *modem);
 gboolean    nm_modem_get_iid             (NMModem *modem, NMUtilsIPv6IfaceId *out_iid);
+const char *nm_modem_get_operator_code   (NMModem *modem);
+const char *nm_modem_get_apn             (NMModem *modem);
 
 gboolean    nm_modem_set_data_port (NMModem *self,
                                     NMPlatform *platform,
@@ -194,6 +198,7 @@ gboolean nm_modem_check_connection_compatible (NMModem *self,
                                                GError **error);
 
 gboolean nm_modem_complete_connection (NMModem *self,
+                                       const char *iface,
                                        NMConnection *connection,
                                        NMConnection *const*existing_connections,
                                        GError **error);
@@ -284,4 +289,7 @@ const char *nm_modem_ip_type_to_string (NMModemIPType ip_type);
 
 guint32 nm_modem_get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source);
 
+void _nm_modem_set_operator_code (NMModem *self, const char *operator_code);
+void _nm_modem_set_apn           (NMModem *self, const char *apn);
+
 #endif /* __NETWORKMANAGER_MODEM_H__ */
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index c9ee27ff..a0e5c160 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify