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, 378 insertions, 207 deletions
diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver
index 6efcb03f..70b954c5 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
new file mode 100644
index 00000000..032b3585
--- /dev/null
+++ b/src/devices/wwan/meson.build
@@ -0,0 +1,75 @@
+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 b79d145d..2a3e9ebe 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -32,8 +32,6 @@
 #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);
 
@@ -260,21 +258,26 @@ modem_ip6_config_result (NMModem *modem,
 }
 
 static void
-data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
+ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
 {
-	NMDevice *self = NM_DEVICE (user_data);
-	gboolean changed;
+	NMDevice *device = NM_DEVICE (user_data);
+
+	if (!nm_device_is_activating (device))
+		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));
+	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;
+	}
 
 	/* 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.
 	 */
-	if (changed)
-		nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
+	nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1");
 }
 
 static void
@@ -431,7 +434,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     const GSList *existing_connections,
+                     NMConnection *const*existing_connections,
                      GError **error)
 {
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device);
@@ -629,11 +632,7 @@ 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);
 
-	/* 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_IP_IFINDEX, G_CALLBACK (ip_ifindex_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);
@@ -708,34 +707,23 @@ 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);
 
-	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;
+	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);
 }
 
 static void
@@ -743,23 +731,41 @@ 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 *mclass)
+nm_device_modem_class_init (NMDeviceModemClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (mclass);
-	NMDeviceClass *device_class = NM_DEVICE_CLASS (mclass);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	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;
@@ -802,8 +808,4 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
 	                        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 dc0ce303..9a3744db 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, (GDestroyNotify) g_array_unref);
+		g_clear_pointer (&ctx->ip_types, g_array_unref);
 		nm_clear_g_cancellable (&ctx->cancellable);
 		g_clear_object (&ctx->connection);
 		g_clear_object (&ctx->connect_properties);
@@ -385,9 +385,10 @@ connect_ready (MMModemSimple *simple_iface,
 				g_dbus_error_strip_remote_error (error);
 			ctx->first_error = error;
 		} else
-			g_error_free (error);
+			g_clear_error (&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 {
@@ -410,21 +411,20 @@ connect_ready (MMModemSimple *simple_iface,
 	if (self->_priv.ipv6_config)
 		ip6_method = get_bearer_ip_method (self->_priv.ipv6_config);
 
-	if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN &&
-	    ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) {
-		_LOGW ("failed to connect modem: invalid bearer IP configuration");
+	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);
 		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,
-                     const GSList *existing_connections,
+                     NMConnection *const*existing_connections,
                      GError **error)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
@@ -1409,35 +1409,34 @@ nm_modem_broadband_init (NMModemBroadband *self)
 NMModem *
 nm_modem_broadband_new (GObject *object, GError **error)
 {
-	NMModem *modem;
 	MMObject *modem_object;
 	MMModem *modem_iface;
-	gchar *drivers;
+	const char *const*drivers;
+	gs_free char *driver = NULL;
 
 	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 = 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;
+	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);
 }
 
 static void
diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c
index 811c3afb..a1c6aef2 100644
--- a/src/devices/wwan/nm-modem-ofono.c
+++ b/src/devices/wwan/nm-modem-ofono.c
@@ -836,6 +836,7 @@ 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);
 
@@ -860,27 +861,26 @@ context_property_changed (GDBusProxy *proxy,
 		_LOGW ("Settings 'Interface' missing");
 		goto out;
 	}
-	if (!interface || !interface[0]) {
-		_LOGW ("Settings 'Interface'; empty");
-		goto out;
-	}
 
-	ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, interface);
-	if (ifindex <= 0) {
-		_LOGW ("Interface \"%s\" not found", interface);
+	_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);
 		goto out;
 	}
 
-	_LOGD ("Interface: %s", interface);
-	g_object_set (self,
-	              NM_MODEM_DATA_PORT, interface,
-	              NM_MODEM_IP4_METHOD, NM_MODEM_IP_METHOD_STATIC,
-	              NULL);
+	ifindex = nm_modem_get_ip_ifindex (NM_MODEM (self));
+	nm_assert (ifindex > 0);
 
 	/* 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 010a2b60..61b7247e 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -44,13 +44,10 @@
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMModem,
 	PROP_CONTROL_PORT,
-	PROP_DATA_PORT,
+	PROP_IP_IFINDEX,
 	PROP_PATH,
 	PROP_UID,
 	PROP_DRIVER,
-	PROP_IP4_METHOD,
-	PROP_IP6_METHOD,
-	PROP_IP_TIMEOUT,
 	PROP_STATE,
 	PROP_DEVICE_ID,
 	PROP_SIM_ID,
@@ -79,7 +76,12 @@ typedef struct _NMModemPrivate {
 	char *driver;
 	char *control_port;
 	char *data_port;
-	char *ppp_iface;
+
+	/* 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;
 	NMModemIPMethod ip4_method;
 	NMModemIPMethod ip6_method;
 	NMUtilsIPv6IfaceId iid;
@@ -96,7 +98,7 @@ typedef struct _NMModemPrivate {
 	guint32 secrets_tries;
 	NMActRequestGetSecretsCallId *secrets_id;
 
-	guint32 mm_ip_timeout;
+	guint mm_ip_timeout;
 
 	guint32 ip4_route_table;
 	guint32 ip4_route_metric;
@@ -153,6 +155,10 @@ _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[] = {
@@ -450,20 +456,28 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_
 }
 
 static void
-set_data_port (NMModem *self, const char *new_data_port)
+ppp_ifindex_set (NMPPPManager *ppp_manager,
+                 int ifindex,
+                 const char *iface,
+                 gpointer user_data)
 {
-	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	NMModem *self = NM_MODEM (user_data);
 
-	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);
+	nm_assert (ifindex >= 0);
+	nm_assert (NM_MODEM_GET_PRIVATE (self)->ppp_manager == ppp_manager);
+
+	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;
 	}
+	_set_ip_ifindex (self, ifindex, iface);
 }
 
 static void
 ppp_ip4_config (NMPPPManager *ppp_manager,
-                const char *iface,
                 NMIP4Config *config,
                 gpointer user_data)
 {
@@ -475,9 +489,6 @@ 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
@@ -519,16 +530,12 @@ 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);
@@ -557,6 +564,18 @@ 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)
@@ -598,6 +617,12 @@ 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) {
@@ -629,9 +654,7 @@ 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;
 	}
@@ -639,6 +662,9 @@ 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);
@@ -1065,12 +1091,20 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection)
 gboolean
 nm_modem_complete_connection (NMModem *self,
                               NMConnection *connection,
-                              const GSList *existing_connections,
+                              NMConnection *const*existing_connections,
                               GError **error)
 {
-	if (NM_MODEM_GET_CLASS (self)->complete_connection)
-		return NM_MODEM_GET_CLASS (self)->complete_connection (self, connection, existing_connections, error);
-	return FALSE;
+	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);
 }
 
 /*****************************************************************************/
@@ -1095,7 +1129,10 @@ deactivate_cleanup (NMModem *self, NMDevice *device)
 
 	priv->in_bytes = priv->out_bytes = 0;
 
-	g_clear_object (&priv->ppp_manager);
+	if (priv->ppp_manager) {
+		g_signal_handlers_disconnect_by_data (priv->ppp_manager, self);
+		g_clear_object (&priv->ppp_manager);
+	}
 
 	if (device) {
 		g_return_if_fail (NM_IS_DEVICE (device));
@@ -1114,11 +1151,12 @@ 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;
-
-	g_free (priv->ppp_iface);
-	priv->ppp_iface = NULL;
+	_set_ip_ifindex (self, -1, NULL);
 }
 
 /*****************************************************************************/
@@ -1369,17 +1407,117 @@ nm_modem_get_control_port (NMModem *self)
 	return NM_MODEM_GET_PRIVATE (self)->control_port;
 }
 
-const char *
-nm_modem_get_data_port (NMModem *self)
+int
+nm_modem_get_ip_ifindex (NMModem *self)
 {
-	g_return_val_if_fail (NM_IS_MODEM (self), NULL);
+	NMModemPrivate *priv;
 
-	/* 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;
+	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)
+{
+	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;
+		}
+	}
+
+	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;
 }
 
 gboolean
@@ -1392,15 +1530,10 @@ 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);
 
-	/* 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;
+	return NM_IN_STRSET (iface,
+	                     priv->ip_iface,
+	                     priv->data_port,
+	                     priv->control_port);
 }
 
 gboolean
@@ -1500,7 +1633,8 @@ static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
-	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE ((NMModem *) object);
+	NMModem *self = NM_MODEM (object);
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 
 	switch (prop_id) {
 	case PROP_PATH:
@@ -1512,21 +1646,12 @@ get_property (GObject *object, guint prop_id,
 	case PROP_CONTROL_PORT:
 		g_value_set_string (value, priv->control_port);
 		break;
-	case PROP_DATA_PORT:
-		g_value_set_string (value, nm_modem_get_data_port (NM_MODEM (object)));
+	case PROP_IP_IFINDEX:
+		g_value_set_int (value, nm_modem_get_ip_ifindex (self));
 		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;
@@ -1569,23 +1694,10 @@ 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);
@@ -1623,6 +1735,7 @@ 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;
@@ -1638,7 +1751,7 @@ constructed (GObject *object)
 
 	priv = NM_MODEM_GET_PRIVATE (NM_MODEM (object));
 
-	g_return_if_fail (priv->data_port || priv->control_port);
+	g_return_if_fail (priv->control_port);
 }
 
 /*****************************************************************************/
@@ -1663,6 +1776,7 @@ 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);
@@ -1711,33 +1825,11 @@ nm_modem_class_init (NMModemClass *klass)
 	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                          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_IP_IFINDEX] =
+	     g_param_spec_int (NM_MODEM_IP_IFINDEX, "", "",
+	                       0, G_MAXINT, 0,
+	                       G_PARAM_READABLE |
+	                       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 9546e4a1..3e281c0c 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -37,10 +37,7 @@
 #define NM_MODEM_PATH            "path"
 #define NM_MODEM_DRIVER          "driver"
 #define NM_MODEM_CONTROL_PORT    "control-port"
-#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_IP_IFINDEX      "ip-ifindex"
 #define NM_MODEM_STATE           "state"
 #define NM_MODEM_DEVICE_ID       "device-id"
 #define NM_MODEM_SIM_ID          "sim-id"
@@ -129,7 +126,7 @@ typedef struct {
 
 	gboolean (*complete_connection)            (NMModem *modem,
 	                                            NMConnection *connection,
-	                                            const GSList *existing_connections,
+	                                            NMConnection *const*existing_connections,
 	                                            GError **error);
 
 	NMActStageReturn (*act_stage1_prepare)     (NMModem *modem,
@@ -167,13 +164,21 @@ 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);
-const char *nm_modem_get_data_port       (NMModem *modem);
+int         nm_modem_get_ip_ifindex      (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,
@@ -184,7 +189,7 @@ gboolean nm_modem_check_connection_compatible (NMModem *self, NMConnection *conn
 
 gboolean nm_modem_complete_connection (NMModem *self,
                                        NMConnection *connection,
-                                       const GSList *existing_connections,
+                                       NMConnection *const*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 663102de..f0aae040 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, *port;
+	const char *driver;
 
 	/* 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,10 +93,8 @@ modem_added_cb (NMModemManager *manager,
 	 * by the Bluetooth code during the connection process.
 	 */
 	if (driver && strstr (driver, "bluetooth")) {
-		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);
+		nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)",
+		             nm_modem_get_control_port (modem));
 		return;
 	}