summary refs log tree commit diff
path: root/src/devices/wwan
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/wwan')
-rw-r--r--src/devices/wwan/libnm-wwan.ver2
-rw-r--r--src/devices/wwan/meson.build75
-rw-r--r--src/devices/wwan/nm-device-modem.c96
-rw-r--r--src/devices/wwan/nm-modem-broadband.c65
-rw-r--r--src/devices/wwan/nm-modem-ofono.c26
-rw-r--r--src/devices/wwan/nm-modem.c294
-rw-r--r--src/devices/wwan/nm-modem.h19
-rw-r--r--src/devices/wwan/nm-wwan-factory.c8
8 files changed, 207 insertions, 378 deletions
diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver
index 70b954c5..6efcb03f 100644
--- a/src/devices/wwan/libnm-wwan.ver
+++ b/src/devices/wwan/libnm-wwan.ver
@@ -11,10 +11,10 @@ global:
 	nm_modem_get_capabilities;
 	nm_modem_get_configured_mtu;
 	nm_modem_get_control_port;
+	nm_modem_get_data_port;
 	nm_modem_get_driver;
 	nm_modem_get_iid;
 	nm_modem_get_path;
-	nm_modem_get_ip_ifindex;
 	nm_modem_get_secrets;
 	nm_modem_get_state;
 	nm_modem_get_type;
diff --git a/src/devices/wwan/meson.build b/src/devices/wwan/meson.build
deleted file mode 100644
index 032b3585..00000000
--- a/src/devices/wwan/meson.build
+++ /dev/null
@@ -1,75 +0,0 @@
-sources = files(
-  'nm-modem-broadband.c',
-  'nm-modem.c',
-  'nm-modem-manager.c'
-)
-
-deps = [
-  libsystemd_dep,
-  mm_glib_dep,
-  nm_dep
-]
-
-if enable_ofono
-  sources += files('nm-modem-ofono.c')
-endif
-
-linker_script = join_paths(meson.current_source_dir(), 'libnm-wwan.ver')
-
-libnm_wwan = shared_module(
-  'nm-wwan',
-  sources: sources,
-  dependencies: deps,
-  link_args: [
-    '-Wl,--version-script,@0@'.format(linker_script),
-  ],
-  link_depends: linker_script,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-libnm_wwan_dep = declare_dependency(
-  include_directories: include_directories('.'),
-  link_with: libnm_wwan
-)
-
-core_plugins += libnm_wwan
-
-run_target(
-  'check-wwan',
-  command: [check_exports, libnm_wwan.full_path(), linker_script],
-  depends: libnm_wwan
-)
-
-sources = files(
-  'nm-device-modem.c',
-  'nm-wwan-factory.c'
-)
-
-libnm_device_plugin_wwan = shared_module(
-  'nm-device-plugin-wwan',
-  sources: sources,
-  dependencies: deps,
-  link_with: libnm_wwan,
-  link_args: ldflags_linker_script_devices,
-  link_depends: linker_script_devices,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-core_plugins += libnm_device_plugin_wwan
-
-run_target(
-  'check-local-devices-wwan',
-  command: [check_exports, libnm_device_plugin_wwan.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_wwan
-)
-
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-wwan: src/devices/wwan/libnm-device-plugin-wwan.la src/devices/wwan/libnm-wwan.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-device-plugin-wwan.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-device-plugin-wwan.so)
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-wwan.so "$(srcdir)/src/devices/wwan/libnm-wwan.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-wwan.so)
-'''
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 2a3e9ebe..b79d145d 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -32,6 +32,8 @@
 #include "NetworkManagerUtils.h"
 #include "nm-core-internal.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Modem.h"
+
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceModem);
 
@@ -258,26 +260,21 @@ modem_ip6_config_result (NMModem *modem,
 }
 
 static void
-ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
+data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
-
-	if (!nm_device_is_activating (device))
-		return;
+	NMDevice *self = NM_DEVICE (user_data);
+	gboolean changed;
 
-	if (!nm_device_set_ip_ifindex (device,
-	                               nm_modem_get_ip_ifindex (modem))) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-		return;
-	}
+	/* We set the IP iface in the device as soon as we know it, so that we
+	 * properly ifup it if needed */
+	changed = nm_device_set_ip_iface (self, nm_modem_get_data_port (modem));
 
 	/* Disable IPv6 immediately on the interface since NM handles IPv6
 	 * internally, and leaving it enabled could allow the kernel's IPv6
 	 * RA handling code to run before NM is ready.
 	 */
-	nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1");
+	if (changed)
+		nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
 }
 
 static void
@@ -434,7 +431,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device);
@@ -632,7 +629,11 @@ set_modem (NMDeviceModem *self, NMModem *modem)
 	g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
 	g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self);
 
-	g_signal_connect (modem, "notify::" NM_MODEM_IP_IFINDEX, G_CALLBACK (ip_ifindex_changed_cb), self);
+	/* In the old ModemManager the data port is known from the very beginning;
+	 * while in the new ModemManager the data port is set afterwards when the bearer gets
+	 * created */
+	g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self);
+
 	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);
@@ -707,23 +708,34 @@ nm_device_modem_new (NMModem *modem)
 {
 	NMDeviceModemCapabilities caps = NM_DEVICE_MODEM_CAPABILITY_NONE;
 	NMDeviceModemCapabilities current_caps = NM_DEVICE_MODEM_CAPABILITY_NONE;
+	NMDevice *device;
+	const char *data_port;
 
 	g_return_val_if_fail (NM_IS_MODEM (modem), NULL);
 
 	/* Load capabilities */
 	nm_modem_get_capabilities (modem, &caps, &current_caps);
 
-	return g_object_new (NM_TYPE_DEVICE_MODEM,
-	                     NM_DEVICE_UDI, nm_modem_get_path (modem),
-	                     NM_DEVICE_IFACE, nm_modem_get_uid (modem),
-	                     NM_DEVICE_DRIVER, nm_modem_get_driver (modem),
-	                     NM_DEVICE_TYPE_DESC, "Broadband",
-	                     NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MODEM,
-	                     NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WWAN,
-	                     NM_DEVICE_MODEM_MODEM, modem,
-	                     NM_DEVICE_MODEM_CAPABILITIES, caps,
-	                     NM_DEVICE_MODEM_CURRENT_CAPABILITIES, current_caps,
-	                     NULL);
+	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_MODEM,
+	                                    NM_DEVICE_UDI, nm_modem_get_path (modem),
+	                                    NM_DEVICE_IFACE, nm_modem_get_uid (modem),
+	                                    NM_DEVICE_DRIVER, nm_modem_get_driver (modem),
+	                                    NM_DEVICE_TYPE_DESC, "Broadband",
+	                                    NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MODEM,
+	                                    NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WWAN,
+	                                    NM_DEVICE_MODEM_MODEM, modem,
+	                                    NM_DEVICE_MODEM_CAPABILITIES, caps,
+	                                    NM_DEVICE_MODEM_CURRENT_CAPABILITIES, current_caps,
+	                                    NULL);
+
+	/* If the data port is known, set it as the IP interface immediately */
+	data_port = nm_modem_get_data_port (modem);
+	if (data_port) {
+		nm_device_set_ip_iface (device, data_port);
+		nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1");
+	}
+
+	return device;
 }
 
 static void
@@ -731,41 +743,23 @@ dispose (GObject *object)
 {
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) object);
 
-	if (priv->modem) {
+	if (priv->modem)
 		g_signal_handlers_disconnect_by_data (priv->modem, NM_DEVICE_MODEM (object));
-		g_clear_object (&priv->modem);
-	}
+	g_clear_object (&priv->modem);
 
 	G_OBJECT_CLASS (nm_device_modem_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_modem = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_MODEM,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.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),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
-nm_device_modem_class_init (NMDeviceModemClass *klass)
+nm_device_modem_class_init (NMDeviceModemClass *mclass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+	GObjectClass *object_class = G_OBJECT_CLASS (mclass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (mclass);
 
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_modem);
-
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->get_type_description = get_type_description;
 	device_class->check_connection_compatible = check_connection_compatible;
@@ -808,4 +802,8 @@ nm_device_modem_class_init (NMDeviceModemClass *klass)
 	                        G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (mclass),
+	                                        NMDBUS_TYPE_DEVICE_MODEM_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 9a3744db..dc0ce303 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -339,7 +339,7 @@ connect_context_clear (NMModemBroadband *self)
 		ConnectContext *ctx = self->_priv.ctx;
 
 		g_clear_error (&ctx->first_error);
-		g_clear_pointer (&ctx->ip_types, g_array_unref);
+		g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref);
 		nm_clear_g_cancellable (&ctx->cancellable);
 		g_clear_object (&ctx->connection);
 		g_clear_object (&ctx->connect_properties);
@@ -385,10 +385,9 @@ connect_ready (MMModemSimple *simple_iface,
 				g_dbus_error_strip_remote_error (error);
 			ctx->first_error = error;
 		} else
-			g_clear_error (&error);
+			g_error_free (error);
 
-		if (   ctx->ip_type_tries == 0
-		    && g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) {
+		if (ctx->ip_type_tries == 0 && g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) {
 			/* Try one more time */
 			ctx->ip_type_tries++;
 		} else {
@@ -411,20 +410,21 @@ connect_ready (MMModemSimple *simple_iface,
 	if (self->_priv.ipv6_config)
 		ip6_method = get_bearer_ip_method (self->_priv.ipv6_config);
 
-	if (!nm_modem_set_data_port (NM_MODEM (self),
-	                             NM_PLATFORM_GET,
-	                             mm_bearer_get_interface (self->_priv.bearer),
-	                             ip4_method,
-	                             ip6_method,
-	                             mm_bearer_get_ip_timeout (self->_priv.bearer),
-	                             &error)) {
-		_LOGW ("failed to connect modem: %s", error->message);
-		g_error_free (error);
+	if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN &&
+	    ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) {
+		_LOGW ("failed to connect modem: invalid bearer IP configuration");
 		nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 		connect_context_clear (self);
 		return;
 	}
 
+	g_object_set (self,
+	              NM_MODEM_DATA_PORT,  mm_bearer_get_interface (self->_priv.bearer),
+	              NM_MODEM_IP4_METHOD, ip4_method,
+	              NM_MODEM_IP6_METHOD, ip6_method,
+	              NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->_priv.bearer),
+	              NULL);
+
 	ctx->step++;
 	connect_context_step (self);
 }
@@ -663,7 +663,7 @@ check_connection_compatible (NMModem *_self, NMConnection *connection)
 static gboolean
 complete_connection (NMModem *_self,
                      NMConnection *connection,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
@@ -1409,34 +1409,35 @@ nm_modem_broadband_init (NMModemBroadband *self)
 NMModem *
 nm_modem_broadband_new (GObject *object, GError **error)
 {
+	NMModem *modem;
 	MMObject *modem_object;
 	MMModem *modem_iface;
-	const char *const*drivers;
-	gs_free char *driver = NULL;
+	gchar *drivers;
 
 	g_return_val_if_fail (MM_IS_OBJECT (object), NULL);
 	modem_object = MM_OBJECT (object);
 
 	/* Ensure we have the 'Modem' interface and the primary port at least */
 	modem_iface = mm_object_peek_modem (modem_object);
-	g_return_val_if_fail (modem_iface, NULL);
-	g_return_val_if_fail (mm_modem_get_primary_port (modem_iface), NULL);
+	g_return_val_if_fail (!!modem_iface, NULL);
+	g_return_val_if_fail (!!mm_modem_get_primary_port (modem_iface), NULL);
 
 	/* Build a single string with all drivers listed */
-	drivers = mm_modem_get_drivers (modem_iface);
-	if (drivers)
-		driver = g_strjoinv (", ", (char **) drivers);
-
-	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),
-	                     NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface),
-	                     NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem_iface)),
-	                     NM_MODEM_STATE, (int) mm_state_to_nm (mm_modem_get_state (modem_iface)),
-	                     NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface),
-	                     NM_MODEM_BROADBAND_MODEM, modem_object,
-	                     NM_MODEM_DRIVER, driver,
-	                     NULL);
+	drivers = g_strjoinv (", ", (gchar **)mm_modem_get_drivers (modem_iface));
+
+	modem = 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),
+	                      NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface),
+	                      NM_MODEM_DATA_PORT, NULL, /* We don't know it until bearer created */
+	                      NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem_iface)),
+	                      NM_MODEM_STATE, (int) mm_state_to_nm (mm_modem_get_state (modem_iface)),
+	                      NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface),
+	                      NM_MODEM_BROADBAND_MODEM, modem_object,
+	                      NM_MODEM_DRIVER, drivers,
+	                      NULL);
+	g_free (drivers);
+	return modem;
 }
 
 static void
diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c
index a1c6aef2..811c3afb 100644
--- a/src/devices/wwan/nm-modem-ofono.c
+++ b/src/devices/wwan/nm-modem-ofono.c
@@ -836,7 +836,6 @@ context_property_changed (GDBusProxy *proxy,
 	guint32 address_network, gateway_network;
 	guint32 ip4_route_table, ip4_route_metric;
 	int ifindex;
-	GError *error = NULL;
 
 	_LOGD ("PropertyChanged: %s", property);
 
@@ -861,26 +860,27 @@ context_property_changed (GDBusProxy *proxy,
 		_LOGW ("Settings 'Interface' missing");
 		goto out;
 	}
+	if (!interface || !interface[0]) {
+		_LOGW ("Settings 'Interface'; empty");
+		goto out;
+	}
 
-	_LOGD ("Interface: %s", interface);
-	if (!nm_modem_set_data_port (NM_MODEM (self),
-	                             NM_PLATFORM_GET,
-	                             interface,
-	                             NM_MODEM_IP_METHOD_STATIC,
-	                             NM_MODEM_IP_METHOD_UNKNOWN,
-	                             0,
-	                             &error)) {
-		_LOGW ("failed to connect to modem: %s", error->message);
-		g_clear_error (&error);
+	ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, interface);
+	if (ifindex <= 0) {
+		_LOGW ("Interface \"%s\" not found", interface);
 		goto out;
 	}
 
-	ifindex = nm_modem_get_ip_ifindex (NM_MODEM (self));
-	nm_assert (ifindex > 0);
+	_LOGD ("Interface: %s", interface);
+	g_object_set (self,
+	              NM_MODEM_DATA_PORT, interface,
+	              NM_MODEM_IP4_METHOD, NM_MODEM_IP_METHOD_STATIC,
+	              NULL);
 
 	/* TODO: verify handling of ip4_config; check other places it's used... */
 	g_clear_object (&priv->ip4_config);
 
+
 	priv->ip4_config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
 	                                      ifindex);
 
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 61b7247e..010a2b60 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -44,10 +44,13 @@
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMModem,
 	PROP_CONTROL_PORT,
-	PROP_IP_IFINDEX,
+	PROP_DATA_PORT,
 	PROP_PATH,
 	PROP_UID,
 	PROP_DRIVER,
+	PROP_IP4_METHOD,
+	PROP_IP6_METHOD,
+	PROP_IP_TIMEOUT,
 	PROP_STATE,
 	PROP_DEVICE_ID,
 	PROP_SIM_ID,
@@ -76,12 +79,7 @@ typedef struct _NMModemPrivate {
 	char *driver;
 	char *control_port;
 	char *data_port;
-
-	/* TODO: ip_iface is solely used for nm_modem_owns_port().
-	 * We should rework the code that it's not necessary */
-	char *ip_iface;
-
-	int ip_ifindex;
+	char *ppp_iface;
 	NMModemIPMethod ip4_method;
 	NMModemIPMethod ip6_method;
 	NMUtilsIPv6IfaceId iid;
@@ -98,7 +96,7 @@ typedef struct _NMModemPrivate {
 	guint32 secrets_tries;
 	NMActRequestGetSecretsCallId *secrets_id;
 
-	guint mm_ip_timeout;
+	guint32 mm_ip_timeout;
 
 	guint32 ip4_route_table;
 	guint32 ip4_route_metric;
@@ -155,10 +153,6 @@ _nmlog_prefix (char *prefix, NMModem *self)
     } G_STMT_END
 
 /*****************************************************************************/
-
-static void _set_ip_ifindex (NMModem *self, int ifindex, const char *ifname);
-
-/*****************************************************************************/
 /* State/enabled/connected */
 
 static const char *state_table[] = {
@@ -456,28 +450,20 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_
 }
 
 static void
-ppp_ifindex_set (NMPPPManager *ppp_manager,
-                 int ifindex,
-                 const char *iface,
-                 gpointer user_data)
+set_data_port (NMModem *self, const char *new_data_port)
 {
-	NMModem *self = NM_MODEM (user_data);
-
-	nm_assert (ifindex >= 0);
-	nm_assert (NM_MODEM_GET_PRIVATE (self)->ppp_manager == ppp_manager);
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 
-	if (ifindex <= 0 && iface) {
-		/* this might happen, if the ifname was already deleted
-		 * and we failed to resolve ifindex.
-		 *
-		 * Forget about the name. */
-		iface = NULL;
+	if (g_strcmp0 (priv->data_port, new_data_port) != 0) {
+		g_free (priv->data_port);
+		priv->data_port = g_strdup (new_data_port);
+		_notify (self, PROP_DATA_PORT);
 	}
-	_set_ip_ifindex (self, ifindex, iface);
 }
 
 static void
 ppp_ip4_config (NMPPPManager *ppp_manager,
+                const char *iface,
                 NMIP4Config *config,
                 gpointer user_data)
 {
@@ -489,6 +475,9 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 	guint32 good_dns2 = htonl (0x04020202);  /* GTE nameserver */
 	gboolean dns_workaround = FALSE;
 
+	/* Notify about the new data port to use */
+	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.
 	 * Apparently fixed in ppp-2.4.5 but we've had some reports that this is
@@ -530,12 +519,16 @@ 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);
@@ -564,18 +557,6 @@ port_speed_is_zero (const char *port)
 {
 	struct termios options;
 	nm_auto_close int fd = -1;
-	gs_free char *path = NULL;
-
-	nm_assert (port);
-
-	if (port[0] != '/') {
-		if (   !port[0]
-		    || strchr (port, '/')
-		    || NM_IN_STRSET (port, ".", ".."))
-			return FALSE;
-		path = g_build_path ("/sys/class/tty", port, NULL);
-		port = path;
-	}
 
 	fd = open (port, O_RDWR | O_NONBLOCK | O_NOCTTY | O_CLOEXEC);
 	if (fd < 0)
@@ -617,12 +598,6 @@ ppp_stage3_ip_config_start (NMModem *self,
 			return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	if (!priv->data_port) {
-		_LOGE ("error starting PPP (no data port)");
-		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED);
-		return NM_ACT_STAGE_RETURN_FAILURE;
-	}
-
 	/* Check if ModemManager requested a specific IP timeout to be used. If 0 reported,
 	 * use the default one (30s) */
 	if (priv->mm_ip_timeout > 0) {
@@ -654,7 +629,9 @@ ppp_stage3_ip_config_start (NMModem *self,
 	                              ip_timeout, baud_override, &error)) {
 		_LOGE ("error starting PPP: %s", error->message);
 		g_error_free (error);
+
 		g_clear_object (&priv->ppp_manager);
+
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
@@ -662,9 +639,6 @@ ppp_stage3_ip_config_start (NMModem *self,
 	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
 	                  G_CALLBACK (ppp_state_changed),
 	                  self);
-	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IFINDEX_SET,
-	                  G_CALLBACK (ppp_ifindex_set),
-	                  self);
 	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
 	                  G_CALLBACK (ppp_ip4_config),
 	                  self);
@@ -1091,20 +1065,12 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection)
 gboolean
 nm_modem_complete_connection (NMModem *self,
                               NMConnection *connection,
-                              NMConnection *const*existing_connections,
+                              const GSList *existing_connections,
                               GError **error)
 {
-	NMModemClass *klass;
-
-	klass = NM_MODEM_GET_CLASS (self);
-	if (!klass->complete_connection) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
-		             "Modem class %s had no complete_connection method",
-		             G_OBJECT_TYPE_NAME (self));
-		return FALSE;
-	}
-
-	return klass->complete_connection (self, connection, existing_connections, error);
+	if (NM_MODEM_GET_CLASS (self)->complete_connection)
+		return NM_MODEM_GET_CLASS (self)->complete_connection (self, connection, existing_connections, error);
+	return FALSE;
 }
 
 /*****************************************************************************/
@@ -1129,10 +1095,7 @@ deactivate_cleanup (NMModem *self, NMDevice *device)
 
 	priv->in_bytes = priv->out_bytes = 0;
 
-	if (priv->ppp_manager) {
-		g_signal_handlers_disconnect_by_data (priv->ppp_manager, self);
-		g_clear_object (&priv->ppp_manager);
-	}
+	g_clear_object (&priv->ppp_manager);
 
 	if (device) {
 		g_return_if_fail (NM_IS_DEVICE (device));
@@ -1151,12 +1114,11 @@ deactivate_cleanup (NMModem *self, NMDevice *device)
 			}
 		}
 	}
-
-	nm_clear_g_free (&priv->data_port);
-	priv->mm_ip_timeout = 0;
 	priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
 	priv->ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
-	_set_ip_ifindex (self, -1, NULL);
+
+	g_free (priv->ppp_iface);
+	priv->ppp_iface = NULL;
 }
 
 /*****************************************************************************/
@@ -1407,117 +1369,17 @@ nm_modem_get_control_port (NMModem *self)
 	return NM_MODEM_GET_PRIVATE (self)->control_port;
 }
 
-int
-nm_modem_get_ip_ifindex (NMModem *self)
-{
-	NMModemPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_MODEM (self), 0);
-
-	priv = NM_MODEM_GET_PRIVATE (self);
-
-	/* internally we track an unset ip_ifindex as -1.
-	 * For the caller of nm_modem_get_ip_ifindex(), this
-	 * shall be zero too. */
-	return priv->ip_ifindex != -1 ? priv->ip_ifindex : 0;
-}
-
-static void
-_set_ip_ifindex (NMModem *self, int ifindex, const char *ifname)
-{
-	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
-
-	nm_assert (ifindex >= -1);
-	nm_assert ((ifindex > 0) == !!ifname);
-
-	if (!nm_streq0 (priv->ip_iface, ifname)) {
-		g_free (priv->ip_iface);
-		priv->ip_iface = g_strdup (ifname);
-	}
-
-	if (priv->ip_ifindex != ifindex) {
-		priv->ip_ifindex = ifindex;
-		_notify (self, PROP_IP_IFINDEX);
-	}
-}
-
-gboolean
-nm_modem_set_data_port (NMModem *self,
-                        NMPlatform *platform,
-                        const char *data_port,
-                        NMModemIPMethod ip4_method,
-                        NMModemIPMethod ip6_method,
-                        guint timeout,
-                        GError **error)
+const char *
+nm_modem_get_data_port (NMModem *self)
 {
-	NMModemPrivate *priv;
-	gboolean is_ppp;
-	int ifindex = -1;
-
-	g_return_val_if_fail (NM_IS_MODEM (self), FALSE);
-	g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE);
-	g_return_val_if_fail (!error || !*error, FALSE);
-
-	priv = NM_MODEM_GET_PRIVATE (self);
-
-	if (   priv->ppp_manager
-	    || priv->data_port
-	    || priv->ip_ifindex != -1) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     "cannot set data port in activated state");
-		/* this really shouldn't happen. Assert. */
-		g_return_val_if_reached (FALSE);
-	}
-
-	if (!data_port) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     "missing data port");
-		return FALSE;
-	}
-
-	is_ppp =    (ip4_method == NM_MODEM_IP_METHOD_PPP)
-	         || (ip6_method == NM_MODEM_IP_METHOD_PPP);
-	if (is_ppp) {
-		if (   !NM_IN_SET (ip4_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_PPP)
-		    || !NM_IN_SET (ip6_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_PPP)) {
-			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			                     "conflicting ip methods");
-			return FALSE;
-		}
-	} else if (   !NM_IN_SET (ip4_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_STATIC, NM_MODEM_IP_METHOD_AUTO)
-	           || !NM_IN_SET (ip6_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_STATIC, NM_MODEM_IP_METHOD_AUTO)
-	           || (   ip4_method == NM_MODEM_IP_METHOD_UNKNOWN
-	               && ip6_method == NM_MODEM_IP_METHOD_UNKNOWN)) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     "invalid ip methods");
-		return FALSE;
-	}
-
-	if (!is_ppp) {
-		ifindex = nm_platform_if_nametoindex (platform, data_port);
-		if (ifindex <= 0) {
-			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			             "cannot find network interface %s", data_port);
-			return FALSE;
-		}
-		if (!nm_platform_process_events_ensure_link (platform, ifindex, data_port)) {
-			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			             "cannot find network interface %s in platform cache", data_port);
-			return FALSE;
-		}
-	}
+	g_return_val_if_fail (NM_IS_MODEM (self), NULL);
 
-	priv->mm_ip_timeout = timeout;
-	priv->ip4_method = ip4_method;
-	priv->ip6_method = ip6_method;
-	if (is_ppp) {
-		priv->data_port = g_strdup (data_port);
-		_set_ip_ifindex (self, -1, NULL);
-	} else {
-		priv->data_port = NULL;
-		_set_ip_ifindex (self, ifindex, data_port);
-	}
-	return TRUE;
+	/* The ppp_iface takes precedence over the data interface when PPP is used,
+	 * since data_iface is the TTY over which PPP is run, and that TTY can't
+	 * do IP.  The caller really wants the thing that's doing IP.
+	 */
+	return NM_MODEM_GET_PRIVATE (self)->ppp_iface ?
+		NM_MODEM_GET_PRIVATE (self)->ppp_iface : NM_MODEM_GET_PRIVATE (self)->data_port;
 }
 
 gboolean
@@ -1530,10 +1392,15 @@ nm_modem_owns_port (NMModem *self, const char *iface)
 	if (NM_MODEM_GET_CLASS (self)->owns_port)
 		return NM_MODEM_GET_CLASS (self)->owns_port (self, iface);
 
-	return NM_IN_STRSET (iface,
-	                     priv->ip_iface,
-	                     priv->data_port,
-	                     priv->control_port);
+	/* Fall back to data/control ports */
+	if (priv->ppp_iface && (strcmp (priv->ppp_iface, iface) == 0))
+		return TRUE;
+	if (priv->data_port && (strcmp (priv->data_port, iface) == 0))
+		return TRUE;
+	if (priv->control_port && (strcmp (priv->control_port, iface) == 0))
+		return TRUE;
+
+	return FALSE;
 }
 
 gboolean
@@ -1633,8 +1500,7 @@ static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
-	NMModem *self = NM_MODEM (object);
-	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE ((NMModem *) object);
 
 	switch (prop_id) {
 	case PROP_PATH:
@@ -1646,12 +1512,21 @@ get_property (GObject *object, guint prop_id,
 	case PROP_CONTROL_PORT:
 		g_value_set_string (value, priv->control_port);
 		break;
-	case PROP_IP_IFINDEX:
-		g_value_set_int (value, nm_modem_get_ip_ifindex (self));
+	case PROP_DATA_PORT:
+		g_value_set_string (value, nm_modem_get_data_port (NM_MODEM (object)));
 		break;
 	case PROP_UID:
 		g_value_set_string (value, priv->uid);
 		break;
+	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);
+		break;
 	case PROP_STATE:
 		g_value_set_int (value, priv->state);
 		break;
@@ -1694,10 +1569,23 @@ set_property (GObject *object, guint prop_id,
 		/* construct-only */
 		priv->control_port = g_value_dup_string (value);
 		break;
+	case PROP_DATA_PORT:
+		g_free (priv->data_port);
+		priv->data_port = g_value_dup_string (value);
+		break;
 	case PROP_UID:
 		/* construct-only */
 		priv->uid = g_value_dup_string (value);
 		break;
+	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);
+		break;
 	case PROP_STATE:
 		/* construct-only */
 		priv->state = g_value_get_int (value);
@@ -1735,7 +1623,6 @@ nm_modem_init (NMModem *self)
 	self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_MODEM, NMModemPrivate);
 	priv = self->_priv;
 
-	priv->ip_ifindex = -1;
 	priv->ip4_route_table = RT_TABLE_MAIN;
 	priv->ip4_route_metric = 700;
 	priv->ip6_route_table = RT_TABLE_MAIN;
@@ -1751,7 +1638,7 @@ constructed (GObject *object)
 
 	priv = NM_MODEM_GET_PRIVATE (NM_MODEM (object));
 
-	g_return_if_fail (priv->control_port);
+	g_return_if_fail (priv->data_port || priv->control_port);
 }
 
 /*****************************************************************************/
@@ -1776,7 +1663,6 @@ finalize (GObject *object)
 	g_free (priv->driver);
 	g_free (priv->control_port);
 	g_free (priv->data_port);
-	g_free (priv->ip_iface);
 	g_free (priv->device_id);
 	g_free (priv->sim_id);
 	g_free (priv->sim_operator_id);
@@ -1825,11 +1711,33 @@ nm_modem_class_init (NMModemClass *klass)
 	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                          G_PARAM_STATIC_STRINGS);
 
-	obj_properties[PROP_IP_IFINDEX] =
-	     g_param_spec_int (NM_MODEM_IP_IFINDEX, "", "",
-	                       0, G_MAXINT, 0,
-	                       G_PARAM_READABLE |
-	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_DATA_PORT] =
+	     g_param_spec_string (NM_MODEM_DATA_PORT, "", "",
+	                          NULL,
+	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
+
+	obj_properties[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);
+
+	obj_properties[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);
+
+	obj_properties[PROP_IP_TIMEOUT] =
+	     g_param_spec_uint (NM_MODEM_IP_TIMEOUT, "", "",
+	                        0, 360, 20,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_STATE] =
 	     g_param_spec_int (NM_MODEM_STATE, "", "",
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index 3e281c0c..9546e4a1 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -37,7 +37,10 @@
 #define NM_MODEM_PATH            "path"
 #define NM_MODEM_DRIVER          "driver"
 #define NM_MODEM_CONTROL_PORT    "control-port"
-#define NM_MODEM_IP_IFINDEX      "ip-ifindex"
+#define NM_MODEM_DATA_PORT       "data-port"
+#define NM_MODEM_IP4_METHOD      "ip4-method"
+#define NM_MODEM_IP6_METHOD      "ip6-method"
+#define NM_MODEM_IP_TIMEOUT      "ip-timeout"
 #define NM_MODEM_STATE           "state"
 #define NM_MODEM_DEVICE_ID       "device-id"
 #define NM_MODEM_SIM_ID          "sim-id"
@@ -126,7 +129,7 @@ typedef struct {
 
 	gboolean (*complete_connection)            (NMModem *modem,
 	                                            NMConnection *connection,
-	                                            NMConnection *const*existing_connections,
+	                                            const GSList *existing_connections,
 	                                            GError **error);
 
 	NMActStageReturn (*act_stage1_prepare)     (NMModem *modem,
@@ -164,21 +167,13 @@ GType nm_modem_get_type (void);
 const char *nm_modem_get_path            (NMModem *modem);
 const char *nm_modem_get_uid             (NMModem *modem);
 const char *nm_modem_get_control_port    (NMModem *modem);
-int         nm_modem_get_ip_ifindex      (NMModem *modem);
+const char *nm_modem_get_data_port       (NMModem *modem);
 const char *nm_modem_get_driver          (NMModem *modem);
 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);
 
-gboolean    nm_modem_set_data_port (NMModem *self,
-                                    NMPlatform *platform,
-                                    const char *data_port,
-                                    NMModemIPMethod ip4_method,
-                                    NMModemIPMethod ip6_method,
-                                    guint timeout,
-                                    GError **error);
-
 gboolean    nm_modem_owns_port        (NMModem *modem, const char *iface);
 
 void        nm_modem_get_capabilities (NMModem *self,
@@ -189,7 +184,7 @@ gboolean nm_modem_check_connection_compatible (NMModem *self, NMConnection *conn
 
 gboolean nm_modem_complete_connection (NMModem *self,
                                        NMConnection *connection,
-                                       NMConnection *const*existing_connections,
+                                       const GSList *existing_connections,
                                        GError **error);
 
 void nm_modem_get_route_parameters (NMModem *self,
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index f0aae040..663102de 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -80,7 +80,7 @@ modem_added_cb (NMModemManager *manager,
 {
 	NMWwanFactory *self = NM_WWAN_FACTORY (user_data);
 	NMDevice *device;
-	const char *driver;
+	const char *driver, *port;
 
 	/* Do nothing if the modem was consumed by some other plugin */
 	if (nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (self), G_OBJECT (modem)))
@@ -93,8 +93,10 @@ modem_added_cb (NMModemManager *manager,
 	 * by the Bluetooth code during the connection process.
 	 */
 	if (driver && strstr (driver, "bluetooth")) {
-		nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)",
-		             nm_modem_get_control_port (modem));
+		port = nm_modem_get_data_port (modem);
+		if (!port)
+			port = nm_modem_get_control_port (modem);
+		nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)", port);
 		return;
 	}