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.ver4
-rw-r--r--src/devices/wwan/nm-device-modem.c50
-rw-r--r--src/devices/wwan/nm-modem-broadband.c103
-rw-r--r--src/devices/wwan/nm-modem-manager.c813
-rw-r--r--src/devices/wwan/nm-modem-manager.h13
-rw-r--r--src/devices/wwan/nm-modem-ofono.c578
-rw-r--r--src/devices/wwan/nm-modem.c300
-rw-r--r--src/devices/wwan/nm-modem.h21
-rw-r--r--src/devices/wwan/nm-wwan-factory.c4
9 files changed, 1240 insertions, 646 deletions
diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver
index eb577aaf..6efcb03f 100644
--- a/src/devices/wwan/libnm-wwan.ver
+++ b/src/devices/wwan/libnm-wwan.ver
@@ -20,7 +20,11 @@ global:
 	nm_modem_get_type;
 	nm_modem_get_uid;
 	nm_modem_ip4_pre_commit;
+	nm_modem_manager_get;
 	nm_modem_manager_get_type;
+	nm_modem_manager_name_owner_get;
+	nm_modem_manager_name_owner_ref;
+	nm_modem_manager_name_owner_unref;
 	nm_modem_owns_port;
 	nm_modem_set_mm_enabled;
 	nm_modem_stage3_ip4_config_start;
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 4a4d2f2c..22fb8c67 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -364,9 +364,8 @@ device_state_changed (NMDevice *device,
 {
 	NMDeviceModem *self = NM_DEVICE_MODEM (device);
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
-	NMSettingsConnection *connection = nm_device_get_settings_connection (device);
 
-	g_assert (priv->modem);
+	g_return_if_fail (priv->modem);
 
 	if (new_state == NM_DEVICE_STATE_UNAVAILABLE &&
 	    old_state < NM_DEVICE_STATE_UNAVAILABLE) {
@@ -374,30 +373,7 @@ device_state_changed (NMDevice *device,
 		_LOGI (LOGD_MB, "modem state '%s'",
 		       nm_modem_state_to_string (nm_modem_get_state (priv->modem)));
 	}
-
 	nm_modem_device_state_changed (priv->modem, new_state, old_state);
-
-	switch (nm_device_state_reason_check (reason)) {
-	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED:
-	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING:
-	case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED:
-	case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED:
-	case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED:
-	case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG:
-	case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT:
-	case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED:
-	case NM_DEVICE_STATE_REASON_GSM_APN_FAILED:
-		/* Block autoconnect of the just-failed connection for situations
-		 * where a retry attempt would just fail again.
-		 */
-		if (connection) {
-			nm_settings_connection_set_autoconnect_blocked_reason (connection,
-			                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED);
-		}
-		break;
-	default:
-		break;
-	}
 }
 
 static NMDeviceCapabilities
@@ -664,6 +640,16 @@ set_modem (NMDeviceModem *self, NMModem *modem)
 	g_signal_connect (modem, "notify::" NM_MODEM_SIM_OPERATOR_ID, G_CALLBACK (ids_changed_cb), self);
 }
 
+static guint32
+get_dhcp_timeout (NMDevice *device, int addr_family)
+{
+	/* DHCP is always done by the modem firmware, not by the network, and
+	 * by the time we get around to DHCP the firmware should already know
+	 * the IP addressing details.  So the DHCP timeout can be much shorter.
+	 */
+	return 15;
+}
+
 /*****************************************************************************/
 
 static void
@@ -718,18 +704,6 @@ nm_device_modem_init (NMDeviceModem *self)
 {
 }
 
-static void
-constructed (GObject *object)
-{
-	G_OBJECT_CLASS (nm_device_modem_parent_class)->constructed (object);
-
-	/* DHCP is always done by the modem firmware, not by the network, and
-	 * by the time we get around to DHCP the firmware should already know
-	 * the IP addressing details.  So the DHCP timeout can be much shorter.
-	 */
-	nm_device_set_dhcp_timeout (NM_DEVICE (object), 15);
-}
-
 NMDevice *
 nm_device_modem_new (NMModem *modem)
 {
@@ -786,7 +760,6 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
-	object_class->constructed = constructed;
 
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->get_type_description = get_type_description;
@@ -807,6 +780,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
 	device_class->is_available = is_available;
 	device_class->get_ip_iface_identifier = get_ip_iface_identifier;
 	device_class->get_configured_mtu = nm_modem_get_configured_mtu;
+	device_class->get_dhcp_timeout = get_dhcp_timeout;
 
 	device_class->state_changed = device_state_changed;
 
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 4b16fb14..6e5f10a0 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -90,6 +90,9 @@ typedef struct {
 	MMBearerIpConfig *ipv4_config;
 	MMBearerIpConfig *ipv6_config;
 
+	guint idle_id_ip4;
+	guint idle_id_ip6;
+
 	guint32 pin_tries;
 } NMModemBroadbandPrivate;
 
@@ -860,21 +863,6 @@ set_mm_enabled (NMModem *_self,
 /* IPv4 method static */
 
 static gboolean
-ip4_string_to_num (const gchar *str, guint32 *out)
-{
-	guint32 addr = 0;
-	gboolean success = FALSE;
-
-	if (!str || inet_pton (AF_INET, str, &addr) != 1)
-		addr = 0;
-	else
-		success = TRUE;
-
-	*out = (guint32)addr;
-	return success;
-}
-
-static gboolean
 static_stage3_ip4_done (NMModemBroadband *self)
 {
 	GError *error = NULL;
@@ -883,7 +871,7 @@ static_stage3_ip4_done (NMModemBroadband *self)
 	const gchar *address_string;
 	const gchar *gw_string;
 	guint32 address_network;
-	guint32 gw;
+	guint32 gw = 0;
 	NMPlatformIP4Address address;
 	const gchar **dns;
 	guint i;
@@ -895,7 +883,7 @@ static_stage3_ip4_done (NMModemBroadband *self)
 
 	/* Fully fail if invalid IP address retrieved */
 	address_string = mm_bearer_ip_config_get_address (self->_priv.ipv4_config);
-	if (!ip4_string_to_num (address_string, &address_network)) {
+	if (!nm_utils_parse_inaddr_bin (AF_INET, address_string, &address_network)) {
 		error = g_error_new (NM_DEVICE_ERROR,
 		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
 		                     "(%s) retrieving IP4 configuration failed: invalid address given '%s'",
@@ -906,11 +894,20 @@ static_stage3_ip4_done (NMModemBroadband *self)
 
 	/* Missing gateway not a hard failure */
 	gw_string = mm_bearer_ip_config_get_gateway (self->_priv.ipv4_config);
-	ip4_string_to_num (gw_string, &gw);
+	if (   !gw_string
+	    || !nm_utils_parse_inaddr_bin (AF_INET, gw_string, &gw)) {
+		error = g_error_new (NM_DEVICE_ERROR,
+		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "(%s) retrieving IP4 configuration failed: invalid gateway address %s%s%s",
+		                     nm_modem_get_uid (NM_MODEM (self)),
+		                     NM_PRINT_FMT_QUOTE_STRING (gw_string));
+		goto out;
+	}
 
 	data_port = mm_bearer_get_interface (self->_priv.bearer);
 	g_assert (data_port);
-	config = nm_ip4_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port));
+	config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
+	                            nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port));
 
 	memset (&address, 0, sizeof (address));
 	address.address = address_network;
@@ -923,14 +920,30 @@ static_stage3_ip4_done (NMModemBroadband *self)
 	_LOGI ("  address %s/%d", address_string, address.plen);
 
 	if (gw) {
-		nm_ip4_config_set_gateway (config, gw);
-		_LOGI ("  gateway %s", gw_string);
+		guint32 ip4_route_table, ip4_route_metric;
+
+		nm_modem_get_route_parameters (NM_MODEM (self),
+		                               &ip4_route_table,
+		                               &ip4_route_metric,
+		                               NULL,
+		                               NULL);
+		{
+			const NMPlatformIP4Route r = {
+				.rt_source = NM_IP_CONFIG_SOURCE_WWAN,
+				.gateway = gw,
+				.table_coerced = nm_platform_route_table_coerce (ip4_route_table),
+				.metric = ip4_route_metric,
+			};
+
+			_LOGI ("  gateway %s", gw_string);
+			nm_ip4_config_add_route (config, &r, NULL);
+		}
 	}
 
 	/* DNS servers */
 	dns = mm_bearer_ip_config_get_dns (self->_priv.ipv4_config);
 	for (i = 0; dns && dns[i]; i++) {
-		if (   ip4_string_to_num (dns[i], &address_network)
+		if (   nm_utils_parse_inaddr_bin (AF_INET, dns[i], &address_network)
 		    && address_network > 0) {
 			nm_ip4_config_add_nameserver (config, address_network);
 			_LOGI ("  DNS %s", dns[i]);
@@ -944,15 +957,17 @@ out:
 }
 
 static NMActStageReturn
-static_stage3_ip4_config_start (NMModem *_self,
+static_stage3_ip4_config_start (NMModem *modem,
                                 NMActRequest *req,
                                 NMDeviceStateReason *out_failure_reason)
 {
-	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
+	NMModemBroadband *self = NM_MODEM_BROADBAND (modem);
+	NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self);
 
 	/* We schedule it in an idle just to follow the same logic as in the
 	 * generic modem implementation. */
-	g_idle_add ((GSourceFunc) static_stage3_ip4_done, self);
+	nm_clear_g_source (&priv->idle_id_ip4);
+	priv->idle_id_ip4 = g_idle_add ((GSourceFunc) static_stage3_ip4_done, self);
 
 	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
@@ -1004,7 +1019,8 @@ stage3_ip6_done (NMModemBroadband *self)
 
 	data_port = mm_bearer_get_interface (self->_priv.bearer);
 	g_assert (data_port);
-	config = nm_ip6_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port));
+	config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
+	                            nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port));
 
 	address.plen = mm_bearer_ip_config_get_prefix (self->_priv.ipv6_config);
 	if (address.plen <= 128)
@@ -1014,7 +1030,9 @@ stage3_ip6_done (NMModemBroadband *self)
 
 	address_string = mm_bearer_ip_config_get_gateway (self->_priv.ipv6_config);
 	if (address_string) {
-		if (!inet_pton (AF_INET6, address_string, (void *) &(address.address))) {
+		guint32 ip6_route_table, ip6_route_metric;
+
+		if (inet_pton (AF_INET6, address_string, &address.address) != 1) {
 			error = g_error_new (NM_DEVICE_ERROR,
 			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
 			                     "(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'",
@@ -1022,8 +1040,23 @@ stage3_ip6_done (NMModemBroadband *self)
 			                     address_string);
 			goto out;
 		}
-		_LOGI ("  gateway %s", address_string);
-		nm_ip6_config_set_gateway (config, &address.address);
+
+		nm_modem_get_route_parameters (NM_MODEM (self),
+		                               NULL,
+		                               NULL,
+		                               &ip6_route_table,
+		                               &ip6_route_metric);
+		{
+			const NMPlatformIP6Route r = {
+				.rt_source = NM_IP_CONFIG_SOURCE_WWAN,
+				.gateway = address.address,
+				.table_coerced = nm_platform_route_table_coerce (ip6_route_table),
+				.metric = ip6_route_metric,
+			};
+
+			_LOGI ("  gateway %s", address_string);
+			nm_ip6_config_add_route (config, &r, NULL);
+		}
 	} else if (ip_method == NM_MODEM_IP_METHOD_STATIC) {
 		/* Gateway required for the 'static' method */
 		error = g_error_new (NM_DEVICE_ERROR,
@@ -1052,13 +1085,15 @@ out:
 }
 
 static NMActStageReturn
-stage3_ip6_config_request (NMModem *_self, NMDeviceStateReason *out_failure_reason)
+stage3_ip6_config_request (NMModem *modem, NMDeviceStateReason *out_failure_reason)
 {
-	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
+	NMModemBroadband *self = NM_MODEM_BROADBAND (modem);
+	NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self);
 
 	/* We schedule it in an idle just to follow the same logic as in the
 	 * generic modem implementation. */
-	g_idle_add ((GSourceFunc) stage3_ip6_done, self);
+	nm_clear_g_source (&priv->idle_id_ip6);
+	priv->idle_id_ip6 = g_idle_add ((GSourceFunc) stage3_ip6_done, self);
 
 	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
@@ -1409,6 +1444,10 @@ static void
 dispose (GObject *object)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (object);
+	NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self);
+
+	nm_clear_g_source (&priv->idle_id_ip4);
+	nm_clear_g_source (&priv->idle_id_ip6);
 
 	connect_context_clear (self);
 	g_clear_object (&self->_priv.ipv4_config);
diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c
index b1f6d92e..59cd2bca 100644
--- a/src/devices/wwan/nm-modem-manager.c
+++ b/src/devices/wwan/nm-modem-manager.c
@@ -45,6 +45,10 @@
 
 /*****************************************************************************/
 
+NM_GOBJECT_PROPERTIES_DEFINE (NMModemManager,
+	PROP_NAME_OWNER,
+);
+
 enum {
 	MODEM_ADDED,
 	LAST_SIGNAL,
@@ -54,14 +58,38 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
 	GDBusConnection *dbus_connection;
-	MMManager *modem_manager;
-	guint mm_launch_id;
-	gulong mm_name_owner_changed_id;
-	gulong mm_object_added_id;
-	gulong mm_object_removed_id;
+
+	/* used during g_bus_get() and later during mm_manager_new(). */
+	GCancellable *main_cancellable;
+
+	struct {
+		MMManager *manager;
+		GCancellable *poke_cancellable;
+		gulong handle_name_owner_changed_id;
+		gulong handle_object_added_id;
+		gulong handle_object_removed_id;
+		guint relaunch_id;
+
+		/* this only has one use: that the <info> logging line about
+		 * ModemManager available distinguishes between first-time
+		 * and later name-owner-changed. */
+		enum {
+			LOG_AVAILABLE_NOT_INITIALIZED = 0,
+			LOG_AVAILABLE_YES,
+			LOG_AVAILABLE_NO,
+		} log_available:3;
+
+		GDBusProxy *proxy;
+		GCancellable *proxy_cancellable;
+		guint proxy_ref_count;
+		char *proxy_name_owner;
+	} modm;
 
 #if WITH_OFONO
-	GDBusProxy *ofono_proxy;
+	struct {
+		GDBusProxy *proxy;
+		GCancellable *cancellable;
+	} ofono;
 #endif
 
 	GHashTable *modems;
@@ -82,19 +110,35 @@ G_DEFINE_TYPE (NMModemManager, nm_modem_manager, G_TYPE_OBJECT)
 
 /*****************************************************************************/
 
+#define _NMLOG_DOMAIN      LOGD_MB
+#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "modem-manager", __VA_ARGS__)
+
+/*****************************************************************************/
+
+NM_DEFINE_SINGLETON_GETTER (NMModemManager, nm_modem_manager_get, NM_TYPE_MODEM_MANAGER);
+
+/*****************************************************************************/
+
+static void modm_schedule_manager_relaunch (NMModemManager *self,
+                                            guint n_seconds);
+static void modm_ensure_manager (NMModemManager *self);
+
+/*****************************************************************************/
+
 static void
 handle_new_modem (NMModemManager *self, NMModem *modem)
 {
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 	const char *path;
 
 	path = nm_modem_get_path (modem);
-	if (g_hash_table_lookup (self->_priv.modems, path)) {
+	if (g_hash_table_lookup (priv->modems, path)) {
 		g_warn_if_reached ();
 		return;
 	}
 
 	/* Track the new modem */
-	g_hash_table_insert (self->_priv.modems, g_strdup (path), modem);
+	g_hash_table_insert (priv->modems, g_strdup (path), modem);
 	g_signal_emit (self, signals[MODEM_ADDED], 0, modem);
 }
 
@@ -105,22 +149,27 @@ remove_one_modem (gpointer key, gpointer value, gpointer user_data)
 	return TRUE;
 }
 
+/*****************************************************************************/
+
 static void
-clear_modem_manager (NMModemManager *self)
+modm_clear_manager (NMModemManager *self)
 {
-	if (!self->_priv.modem_manager)
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	if (!priv->modm.manager)
 		return;
-	nm_clear_g_signal_handler (self->_priv.modem_manager, &self->_priv.mm_name_owner_changed_id);
-	nm_clear_g_signal_handler (self->_priv.modem_manager, &self->_priv.mm_object_added_id);
-	nm_clear_g_signal_handler (self->_priv.modem_manager, &self->_priv.mm_object_removed_id);
-	g_clear_object (&self->_priv.modem_manager);
+	nm_clear_g_signal_handler (priv->modm.manager, &priv->modm.handle_name_owner_changed_id);
+	nm_clear_g_signal_handler (priv->modm.manager, &priv->modm.handle_object_added_id);
+	nm_clear_g_signal_handler (priv->modm.manager, &priv->modm.handle_object_removed_id);
+	g_clear_object (&priv->modm.manager);
 }
 
 static void
-modem_object_added (MMManager *modem_manager,
-                    MMObject  *modem_object,
-                    NMModemManager *self)
+modm_handle_object_added (MMManager *modem_manager,
+                          MMObject  *modem_object,
+                          NMModemManager *self)
 {
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 	const gchar *path;
 	MMModem *modem_iface;
 	NMModem *modem;
@@ -128,21 +177,21 @@ modem_object_added (MMManager *modem_manager,
 
 	/* Ensure we don't have the same modem already */
 	path = mm_object_get_path (modem_object);
-	if (g_hash_table_lookup (self->_priv.modems, path)) {
-		nm_log_warn (LOGD_MB, "modem with path %s already exists, ignoring", path);
+	if (g_hash_table_lookup (priv->modems, path)) {
+		_LOGW ("modem with path %s already exists, ignoring", path);
 		return;
 	}
 
 	/* Ensure we have the 'Modem' interface at least */
 	modem_iface = mm_object_peek_modem (modem_object);
 	if (!modem_iface) {
-		nm_log_warn (LOGD_MB, "modem with path %s doesn't have the Modem interface, ignoring", path);
+		_LOGW ("modem with path %s doesn't have the Modem interface, ignoring", path);
 		return;
 	}
 
 	/* Ensure we have a primary port reported */
 	if (!mm_modem_get_primary_port (modem_iface)) {
-		nm_log_warn (LOGD_MB, "modem with path %s has unknown primary port, ignoring", path);
+		_LOGW ("modem with path %s has unknown primary port, ignoring", path);
 		return;
 	}
 
@@ -150,65 +199,68 @@ modem_object_added (MMManager *modem_manager,
 	modem = nm_modem_broadband_new (G_OBJECT (modem_object), &error);
 	if (modem)
 		handle_new_modem (self, modem);
-	else {
-		nm_log_warn (LOGD_MB, "failed to create modem: %s",
-		             error->message);
-	}
+	else
+		_LOGW ("failed to create modem: %s", error->message);
 	g_clear_error (&error);
 }
 
 static void
-modem_object_removed (MMManager *manager,
-                      MMObject  *modem_object,
-                      NMModemManager *self)
+modm_handle_object_removed (MMManager *manager,
+                            MMObject  *modem_object,
+                            NMModemManager *self)
 {
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 	NMModem *modem;
 	const gchar *path;
 
 	path = mm_object_get_path (modem_object);
-	modem = (NMModem *) g_hash_table_lookup (self->_priv.modems, path);
+	modem = (NMModem *) g_hash_table_lookup (priv->modems, path);
 	if (!modem)
 		return;
 
 	nm_modem_emit_removed (modem);
-	g_hash_table_remove (self->_priv.modems, path);
+	g_hash_table_remove (priv->modems, path);
 }
 
 static void
-modem_manager_available (NMModemManager *self)
+modm_manager_available (NMModemManager *self)
 {
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 	GList *modems, *l;
 
-	nm_log_info (LOGD_MB, "ModemManager available in the bus");
+	if (priv->modm.log_available != LOG_AVAILABLE_YES) {
+		_LOGI ("ModemManager %savailable", priv->modm.log_available ? "now " : "");
+		priv->modm.log_available = LOG_AVAILABLE_YES;
+	}
 
 	/* Update initial modems list */
-	modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->_priv.modem_manager));
+	modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (priv->modm.manager));
 	for (l = modems; l; l = g_list_next (l))
-		modem_object_added (self->_priv.modem_manager, MM_OBJECT (l->data), self);
+		modm_handle_object_added (priv->modm.manager, MM_OBJECT (l->data), self);
 	g_list_free_full (modems, (GDestroyNotify) g_object_unref);
 }
 
-static void schedule_modem_manager_relaunch (NMModemManager *self,
-                                             guint n_seconds);
-static void ensure_modem_manager (NMModemManager *self);
-
 static void
-modem_manager_name_owner_changed (MMManager *modem_manager,
-                                  GParamSpec *pspec,
-                                  NMModemManager *self)
+modm_handle_name_owner_changed (MMManager *modem_manager,
+                                GParamSpec *pspec,
+                                NMModemManager *self)
 {
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 	gchar *name_owner;
 
 	/* Quit poking, if any */
-	nm_clear_g_source (&self->_priv.mm_launch_id);
+	nm_clear_g_source (&priv->modm.relaunch_id);
 
 	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (modem_manager));
 	if (!name_owner) {
-		nm_log_info (LOGD_MB, "ModemManager disappeared from bus");
+		if (priv->modm.log_available != LOG_AVAILABLE_NO) {
+			_LOGI ("ModemManager %savailable", priv->modm.log_available ? "no longer " : "not ");
+			priv->modm.log_available = LOG_AVAILABLE_NO;
+		}
 
 		/* If not managed by systemd, schedule relaunch */
 		if (!sd_booted ())
-			schedule_modem_manager_relaunch (self, 0);
+			modm_schedule_manager_relaunch (self, 0);
 
 		return;
 	}
@@ -220,18 +272,322 @@ modem_manager_name_owner_changed (MMManager *modem_manager,
 	 * nor 'object-removed' if it was created while there was no ModemManager in
 	 * the bus. This hack avoids this issue until we get a GIO with the fix
 	 * included... */
-	clear_modem_manager (self);
-	ensure_modem_manager (self);
+	modm_clear_manager (self);
+	modm_ensure_manager (self);
 
 	/* Whenever GDBusObjectManagerClient is fixed, we can just do the following:
-	 * modem_manager_available (self);
+	 * modm_manager_available (self);
 	 */
 }
 
+static void
+modm_manager_poke_cb (GObject *connection,
+                      GAsyncResult *res,
+                      gpointer user_data)
+{
+	NMModemManager *self;
+	NMModemManagerPrivate *priv;
+	gs_free_error GError *error = NULL;
+	gs_unref_variant GVariant *result = NULL;
+
+	result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (connection), res, &error);
+
+	if (   !result
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = user_data;
+	priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	g_clear_object (&priv->modm.poke_cancellable);
+
+	if (error) {
+		_LOGW ("error poking ModemManager: %s", error->message);
+
+		/* Don't reschedule poke is MM service doesn't exist. */
+		if (   !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)
+			&& !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) {
+
+			/* Setup timeout to relaunch */
+			modm_schedule_manager_relaunch (self, MODEM_POKE_INTERVAL);
+		}
+	}
+}
+
+static void
+modm_manager_poke (NMModemManager *self)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	nm_clear_g_cancellable (&priv->modm.poke_cancellable);
+	priv->modm.poke_cancellable = g_cancellable_new ();
+
+	/* If there is no current owner right away, ensure we poke to get one */
+	g_dbus_connection_call (priv->dbus_connection,
+	                        NM_MODEM_MANAGER_MM_DBUS_SERVICE,
+	                        NM_MODEM_MANAGER_MM_DBUS_PATH,
+	                        DBUS_INTERFACE_PEER,
+	                        "Ping",
+	                        NULL,
+	                        NULL,
+	                        G_DBUS_CALL_FLAGS_NONE,
+	                        -1,
+	                        priv->modm.poke_cancellable,
+	                        modm_manager_poke_cb,
+	                        self);
+}
+
+static void
+modm_manager_check_name_owner (NMModemManager *self)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+	gs_free gchar *name_owner = NULL;
+
+	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (priv->modm.manager));
+	if (name_owner) {
+		modm_manager_available (self);
+		return;
+	}
+
+	/* If the lifecycle is not managed by systemd, poke */
+	if (!sd_booted ())
+		modm_manager_poke (self);
+}
+
+static void
+modm_manager_new_cb (GObject *source,
+                     GAsyncResult *res,
+                     gpointer user_data)
+{
+	NMModemManager *self;
+	NMModemManagerPrivate *priv;
+	gs_free_error GError *error = NULL;
+	MMManager *modem_manager;
+
+	modem_manager = mm_manager_new_finish (res, &error);
+	if (   !modem_manager
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = user_data;
+	priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	nm_assert (!priv->modm.manager);
+
+	g_clear_object (&priv->main_cancellable);
+
+	if (!modem_manager) {
+		/* We're not really supposed to get any error here. If we do get one,
+		 * though, just re-schedule the MMManager creation after some time.
+		 * During this period, name-owner changes won't be followed. */
+		_LOGW ("error creating ModemManager client: %s", error->message);
+		/* Setup timeout to relaunch */
+		modm_schedule_manager_relaunch (self, MODEM_POKE_INTERVAL);
+		return;
+	}
+
+	priv->modm.manager = modem_manager;
+
+	/* Setup signals in the GDBusObjectManagerClient */
+	priv->modm.handle_name_owner_changed_id =
+	    g_signal_connect (priv->modm.manager,
+	                      "notify::name-owner",
+	                      G_CALLBACK (modm_handle_name_owner_changed),
+	                      self);
+	priv->modm.handle_object_added_id =
+	    g_signal_connect (priv->modm.manager,
+	                      "object-added",
+	                      G_CALLBACK (modm_handle_object_added),
+	                      self);
+	priv->modm.handle_object_removed_id =
+	    g_signal_connect (priv->modm.manager,
+	                      "object-removed",
+	                      G_CALLBACK (modm_handle_object_removed),
+	                      self);
+
+	modm_manager_check_name_owner (self);
+}
+
+static void
+modm_ensure_manager (NMModemManager *self)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	g_assert (priv->dbus_connection);
+
+	/* Create the GDBusObjectManagerClient. We do not request to autostart, as
+	 * we don't really want the MMManager creation to fail. We can always poke
+	 * later on if we want to request the autostart */
+	if (!priv->modm.manager) {
+		if (!priv->main_cancellable)
+			priv->main_cancellable = g_cancellable_new ();
+		mm_manager_new (priv->dbus_connection,
+		                G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
+		                priv->main_cancellable,
+		                modm_manager_new_cb,
+		                self);
+		return;
+	}
+
+	/* If already available, recheck name owner! */
+	modm_manager_check_name_owner (self);
+}
+
+static gboolean
+modm_schedule_manager_relaunch_cb (NMModemManager *self)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	priv->modm.relaunch_id = 0;
+	modm_ensure_manager (self);
+	return G_SOURCE_REMOVE;
+}
+
+static void
+modm_schedule_manager_relaunch (NMModemManager *self,
+                                guint n_seconds)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	/* No need to pass an extra reference to self; timeout/idle will be
+	 * cancelled if the object gets disposed. */
+	if (n_seconds)
+		priv->modm.relaunch_id = g_timeout_add_seconds (n_seconds, (GSourceFunc)modm_schedule_manager_relaunch_cb, self);
+	else
+		priv->modm.relaunch_id = g_idle_add ((GSourceFunc)modm_schedule_manager_relaunch_cb, self);
+}
+
+/*****************************************************************************/
+
+static void
+modm_proxy_name_owner_reset (NMModemManager *self)
+{
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+	char *name = NULL;
+
+	if (priv->modm.proxy)
+		name = g_dbus_proxy_get_name_owner (priv->modm.proxy);
+
+	if (nm_streq0 (priv->modm.proxy_name_owner, name)) {
+		g_free (name);
+		return;
+	}
+	g_free (priv->modm.proxy_name_owner);
+	priv->modm.proxy_name_owner = name;
+
+	_notify (self, PROP_NAME_OWNER);
+}
+
+static void
+modm_proxy_name_owner_changed_cb (GObject    *object,
+                                  GParamSpec *pspec,
+                                  gpointer    user_data)
+{
+	modm_proxy_name_owner_reset (user_data);
+}
+
+static void
+modm_proxy_new_cb (GObject *source_object,
+                   GAsyncResult *result,
+                   gpointer user_data)
+{
+	NMModemManager *self;
+	NMModemManagerPrivate *priv;
+	GDBusProxy *proxy;
+	gs_free_error GError *error = NULL;
+
+	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (   !proxy
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = user_data;
+	priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	g_clear_object (&priv->modm.proxy_cancellable);
+
+	if (!proxy) {
+		_LOGW ("could not obtain D-Bus proxy for ModemManager: %s", error->message);
+		return;
+	}
+
+	priv->modm.proxy = proxy;
+	g_signal_connect (priv->modm.proxy, "notify::g-name-owner",
+	                  G_CALLBACK (modm_proxy_name_owner_changed_cb), self);
+
+	modm_proxy_name_owner_reset (self);
+}
+
+void
+nm_modem_manager_name_owner_ref (NMModemManager *self)
+{
+	NMModemManagerPrivate *priv;
+
+	g_return_if_fail (NM_IS_MODEM_MANAGER (self));
+
+	priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	if (priv->modm.proxy_ref_count++ > 0) {
+		/* only try once to create the proxy. If proxy creation
+		 * for the first "ref" failed, it's unclear what to do.
+		 * The proxy is hosed. */
+		return;
+	}
+
+	nm_assert (!priv->modm.proxy && !priv->modm.proxy_cancellable);
+
+	priv->modm.proxy_cancellable = g_cancellable_new ();
+
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+	                          | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS
+	                          | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+	                          NULL,
+	                          NM_MODEM_MANAGER_MM_DBUS_SERVICE,
+	                          NM_MODEM_MANAGER_MM_DBUS_PATH,
+	                          NM_MODEM_MANAGER_MM_DBUS_INTERFACE,
+	                          priv->modm.proxy_cancellable,
+	                          modm_proxy_new_cb,
+	                          self);
+}
+
+void
+nm_modem_manager_name_owner_unref (NMModemManager *self)
+{
+	NMModemManagerPrivate *priv;
+
+	g_return_if_fail (NM_IS_MODEM_MANAGER (self));
+
+	priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->modm.proxy_ref_count > 0);
+
+	if (--priv->modm.proxy_ref_count > 0)
+		return;
+
+	nm_clear_g_cancellable (&priv->modm.proxy_cancellable);
+	g_clear_object (&priv->modm.proxy);
+
+	modm_proxy_name_owner_reset (self);
+}
+
+const char *
+nm_modem_manager_name_owner_get (NMModemManager *self)
+{
+	g_return_val_if_fail (NM_IS_MODEM_MANAGER (self), NULL);
+	nm_assert (NM_MODEM_MANAGER_GET_PRIVATE (self)->modm.proxy_ref_count > 0);
+
+	return NM_MODEM_MANAGER_GET_PRIVATE (self)->modm.proxy_name_owner;
+}
+
+/*****************************************************************************/
+
 #if WITH_OFONO
+
 static void
 ofono_create_modem (NMModemManager *self, const char *path)
 {
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 	NMModem *modem = NULL;
 
 	/* Ensure duplicate modems aren't created.  Because we're not using the
@@ -239,12 +595,12 @@ ofono_create_modem (NMModemManager *self, const char *path)
 	 * receive ModemAdded signals before GetModems() returns, so some of the
 	 * modems returned from GetModems() may already have been created.
 	 */
-	if (!g_hash_table_lookup (self->_priv.modems, path)) {
+	if (!g_hash_table_lookup (priv->modems, path)) {
 		modem = nm_modem_ofono_new (path);
 		if (modem)
 			handle_new_modem (self, modem);
 		else
-			nm_log_warn (LOGD_MB, "Failed to create oFono modem for %s", path);
+			_LOGW ("Failed to create oFono modem for %s", path);
 	}
 }
 
@@ -256,80 +612,96 @@ ofono_signal_cb (GDBusProxy *proxy,
                  gpointer user_data)
 {
 	NMModemManager *self = NM_MODEM_MANAGER (user_data);
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 	gchar *object_path;
 	NMModem *modem;
 
 	if (g_strcmp0 (signal_name, "ModemAdded") == 0) {
 		g_variant_get (parameters, "(oa{sv})", &object_path, NULL);
-		nm_log_info (LOGD_MB, "oFono modem appeared: %s", object_path);
+		_LOGI ("oFono modem appeared: %s", object_path);
 
 		ofono_create_modem (NM_MODEM_MANAGER (user_data), object_path);
 		g_free (object_path);
 	} else if (g_strcmp0 (signal_name, "ModemRemoved") == 0) {
 		g_variant_get (parameters, "(o)", &object_path);
-		nm_log_info (LOGD_MB, "oFono modem removed: %s", object_path);
+		_LOGI ("oFono modem removed: %s", object_path);
 
-		modem = (NMModem *) g_hash_table_lookup (self->_priv.modems, object_path);
+		modem = (NMModem *) g_hash_table_lookup (priv->modems, object_path);
 		if (modem) {
 			nm_modem_emit_removed (modem);
-			g_hash_table_remove (self->_priv.modems, object_path);
+			g_hash_table_remove (priv->modems, object_path);
 		} else {
-			nm_log_warn (LOGD_MB, "could not remove modem %s, not found in table",
-			             object_path);
+			_LOGW ("could not remove modem %s, not found in table",
+			       object_path);
 		}
 		g_free (object_path);
 	}
 }
 
 static void
-ofono_enumerate_devices_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
+ofono_enumerate_devices_done (GObject *proxy,
+                              GAsyncResult *res,
+                              gpointer user_data)
 {
-	NMModemManager *manager = NM_MODEM_MANAGER (user_data);
+	NMModemManager *self;
+	NMModemManagerPrivate *priv;
 	gs_free_error GError *error = NULL;
 	GVariant *results;
 	GVariantIter *iter;
 	const char *path;
 
-	results = g_dbus_proxy_call_finish (proxy, res, &error);
-	if (results) {
-		g_variant_get (results, "(a(oa{sv}))", &iter);
-		while (g_variant_iter_loop (iter, "(&oa{sv})", &path, NULL))
-			ofono_create_modem (manager, path);
-		g_variant_iter_free (iter);
-		g_variant_unref (results);
-	}
+	results = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, &error);
+	if (   !results
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
 
-	if (error) {
-		nm_log_warn (LOGD_MB, "failed to enumerate oFono devices: %s",
-		             error->message);
+	self = NM_MODEM_MANAGER (user_data);
+	priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	g_clear_object (&priv->ofono.cancellable);
+
+	if (!results) {
+		_LOGW ("failed to enumerate oFono devices: %s",
+		       error->message);
+		return;
 	}
+
+	g_variant_get (results, "(a(oa{sv}))", &iter);
+	while (g_variant_iter_loop (iter, "(&oa{sv})", &path, NULL))
+		ofono_create_modem (self, path);
+	g_variant_iter_free (iter);
+	g_variant_unref (results);
 }
 
 static void
-ofono_check_name_owner (NMModemManager *self)
+ofono_check_name_owner (NMModemManager *self, gboolean first_invocation)
 {
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 	gs_free char *name_owner = NULL;
 
-	name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (self->_priv.ofono_proxy));
+	name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (priv->ofono.proxy));
 	if (name_owner) {
-		nm_log_info (LOGD_MB, "oFono is now available");
+		_LOGI ("oFono is %savailable", first_invocation ? "" : "now ");
+
+		nm_clear_g_cancellable (&priv->ofono.cancellable);
+		priv->ofono.cancellable = g_cancellable_new ();
 
-		g_dbus_proxy_call (self->_priv.ofono_proxy,
+		g_dbus_proxy_call (priv->ofono.proxy,
 		                   "GetModems",
 		                   NULL,
 		                   G_DBUS_CALL_FLAGS_NONE,
 		                   -1,
-		                   NULL,
-		                   (GAsyncReadyCallback) ofono_enumerate_devices_done,
-		                   g_object_ref (self));
+		                   priv->ofono.cancellable,
+		                   ofono_enumerate_devices_done,
+		                   self);
 	} else {
 		GHashTableIter iter;
 		NMModem *modem;
 
-		nm_log_info (LOGD_MB, "oFono disappeared from bus");
+		_LOGI ("oFono is %savailable", first_invocation ? "not " : "no longer ");
 
 		/* Remove any oFono modems that might be left around */
-		g_hash_table_iter_init (&iter, self->_priv.modems);
+		g_hash_table_iter_init (&iter, priv->modems);
 		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &modem)) {
 			if (NM_IS_MODEM_OFONO (modem)) {
 				nm_modem_emit_removed (modem);
@@ -344,219 +716,121 @@ ofono_name_owner_changed (GDBusProxy *ofono_proxy,
                           GParamSpec *pspec,
                           NMModemManager *self)
 {
-	ofono_check_name_owner (self);
+	ofono_check_name_owner (self, FALSE);
 }
 
 static void
-ofono_proxy_new_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+ofono_proxy_new_cb (GObject *source_object,
+                    GAsyncResult *res,
+                    gpointer user_data)
 {
-	gs_unref_object NMModemManager *self = NM_MODEM_MANAGER (user_data);
+	NMModemManager *self;
+	NMModemManagerPrivate *priv;
 	gs_free_error GError *error = NULL;
+	GDBusProxy *proxy;
 
-	self->_priv.ofono_proxy = g_dbus_proxy_new_finish (res, &error);
-	if (error) {
-		nm_log_warn (LOGD_MB, "error getting oFono bus proxy: %s", error->message);
+	proxy = g_dbus_proxy_new_finish (res, &error);
+	if (   !proxy
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_MODEM_MANAGER (user_data);
+	priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	g_clear_object (&priv->ofono.cancellable);
+
+	if (!proxy) {
+		_LOGW ("error getting oFono bus proxy: %s", error->message);
 		return;
 	}
 
-	g_signal_connect (self->_priv.ofono_proxy,
+	priv->ofono.proxy = proxy;
+
+	g_signal_connect (priv->ofono.proxy,
 	                  "notify::g-name-owner",
 	                  G_CALLBACK (ofono_name_owner_changed),
 	                  self);
 
-	g_signal_connect (self->_priv.ofono_proxy,
+	g_signal_connect (priv->ofono.proxy,
 	                  "g-signal",
 	                  G_CALLBACK (ofono_signal_cb),
 	                  self);
 
-	ofono_check_name_owner (self);
+	ofono_check_name_owner (self, TRUE);
 }
 
 static void
-ensure_ofono_client (NMModemManager *self)
+ofono_init_proxy (NMModemManager *self)
 {
-	g_assert (self->_priv.dbus_connection);
-	g_dbus_proxy_new (self->_priv.dbus_connection,
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	nm_assert (priv->dbus_connection);
+	nm_assert (!priv->ofono.cancellable);
+
+	priv->ofono.cancellable = g_cancellable_new ();
+
+	g_dbus_proxy_new (priv->dbus_connection,
 	                  G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
 	                  NULL,
 	                  OFONO_DBUS_SERVICE,
 	                  OFONO_DBUS_PATH,
 	                  OFONO_DBUS_INTERFACE,
-	                  NULL,
-	                  (GAsyncReadyCallback) ofono_proxy_new_cb,
-	                  g_object_ref (self));
+	                  priv->ofono.cancellable,
+	                  ofono_proxy_new_cb,
+	                  self);
 }
 #endif
 
-static void
-modem_manager_poke_cb (GDBusConnection *connection,
-                       GAsyncResult *res,
-                       NMModemManager *self)
-{
-	GError *error = NULL;
-	GVariant *result;
-
-	result = g_dbus_connection_call_finish (connection, res, &error);
-	if (error) {
-		nm_log_warn (LOGD_MB, "error poking ModemManager: %s",
-					error ? error->message : "");
-
-		/* Don't reschedule poke is MM service doesn't exist. */
-		if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)
-			&& !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) {
-
-			/* Setup timeout to relaunch */
-			schedule_modem_manager_relaunch (self, MODEM_POKE_INTERVAL);
-		}
-
-		g_error_free (error);
-	} else
-		g_variant_unref (result);
-
-	/* Balance refcount */
-	g_object_unref (self);
-}
-
-static void
-modem_manager_poke (NMModemManager *self)
-{
-	/* If there is no current owner right away, ensure we poke to get one */
-	g_dbus_connection_call (self->_priv.dbus_connection,
-	                        "org.freedesktop.ModemManager1",
-	                        "/org/freedesktop/ModemManager1",
-	                        DBUS_INTERFACE_PEER,
-	                        "Ping",
-	                        NULL, /* inputs */
-	                        NULL, /* outputs */
-	                        G_DBUS_CALL_FLAGS_NONE,
-	                        -1,
-	                        NULL, /* cancellable */
-	                        (GAsyncReadyCallback)modem_manager_poke_cb, /* callback */
-	                        g_object_ref (self)); /* user_data */
-}
+/*****************************************************************************/
 
 static void
-modem_manager_check_name_owner (NMModemManager *self)
+bus_get_ready (GObject *source,
+               GAsyncResult *res,
+               gpointer user_data)
 {
-	gs_free gchar *name_owner = NULL;
+	NMModemManager *self;
+	NMModemManagerPrivate *priv;
+	gs_free_error GError *error = NULL;
+	GDBusConnection *connection;
 
-	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (self->_priv.modem_manager));
-	if (name_owner) {
-		/* Available! */
-		modem_manager_available (self);
+	connection = g_bus_get_finish (res, &error);
+	if (   !connection
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
-	}
-
-	/* If the lifecycle is not managed by systemd, poke */
-	if (!sd_booted ())
-		modem_manager_poke (self);
-}
-
-static void
-manager_new_ready (GObject *source,
-                   GAsyncResult *res,
-                   NMModemManager *self)
-{
-	/* Note we always get an extra reference to self here */
 
-	GError *error = NULL;
+	self = NM_MODEM_MANAGER (user_data);
+	priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 
-	g_return_if_fail (!self->_priv.modem_manager);
-
-	self->_priv.modem_manager = mm_manager_new_finish (res, &error);
-	if (!self->_priv.modem_manager) {
-		/* We're not really supposed to get any error here. If we do get one,
-		 * though, just re-schedule the MMManager creation after some time.
-		 * During this period, name-owner changes won't be followed. */
-		nm_log_warn (LOGD_MB, "error creating ModemManager client: %s", error->message);
-		g_error_free (error);
-		/* Setup timeout to relaunch */
-		schedule_modem_manager_relaunch (self, MODEM_POKE_INTERVAL);
-	} else {
-		/* Setup signals in the GDBusObjectManagerClient */
-		self->_priv.mm_name_owner_changed_id =
-		    g_signal_connect (self->_priv.modem_manager,
-		                      "notify::name-owner",
-		                      G_CALLBACK (modem_manager_name_owner_changed),
-		                      self);
-		self->_priv.mm_object_added_id =
-		    g_signal_connect (self->_priv.modem_manager,
-		                      "object-added",
-		                      G_CALLBACK (modem_object_added),
-		                      self);
-		self->_priv.mm_object_removed_id =
-		    g_signal_connect (self->_priv.modem_manager,
-		                      "object-removed",
-		                      G_CALLBACK (modem_object_removed),
-		                      self);
-
-		modem_manager_check_name_owner (self);
-	}
-
-	/* Balance refcount */
-	g_object_unref (self);
-}
-
-static void
-ensure_modem_manager (NMModemManager *self)
-{
-	g_assert (self->_priv.dbus_connection);
-
-	/* Create the GDBusObjectManagerClient. We do not request to autostart, as
-	 * we don't really want the MMManager creation to fail. We can always poke
-	 * later on if we want to request the autostart */
-	if (!self->_priv.modem_manager) {
-		mm_manager_new (self->_priv.dbus_connection,
-		                G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
-		                NULL,
-		                (GAsyncReadyCallback)manager_new_ready,
-		                g_object_ref (self));
+	if (!connection) {
+		_LOGW ("error getting bus connection: %s", error->message);
 		return;
 	}
 
-	/* If already available, recheck name owner! */
-	modem_manager_check_name_owner (self);
-}
+	priv->dbus_connection = connection;
 
-static gboolean
-mm_launch_cb (NMModemManager *self)
-{
-	self->_priv.mm_launch_id = 0;
-	ensure_modem_manager (self);
-	return G_SOURCE_REMOVE;
+	modm_ensure_manager (self);
+#if WITH_OFONO
+	ofono_init_proxy (self);
+#endif
 }
 
-static void
-schedule_modem_manager_relaunch (NMModemManager *self,
-                                 guint n_seconds)
-{
-	/* No need to pass an extra reference to self; timeout/idle will be
-	 * cancelled if the object gets disposed. */
-	if (n_seconds)
-		self->_priv.mm_launch_id = g_timeout_add_seconds (n_seconds, (GSourceFunc)mm_launch_cb, self);
-	else
-		self->_priv.mm_launch_id = g_idle_add ((GSourceFunc)mm_launch_cb, self);
-}
+/*****************************************************************************/
 
 static void
-bus_get_ready (GObject *source,
-               GAsyncResult *res,
-               gpointer user_data)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	gs_unref_object NMModemManager *self = NM_MODEM_MANAGER (user_data);
-	gs_free_error GError *error = NULL;
-
-	self->_priv.dbus_connection = g_bus_get_finish (res, &error);
-	if (!self->_priv.dbus_connection) {
-		nm_log_warn (LOGD_MB, "error getting bus connection: %s", error->message);
-		return;
+	NMModemManager *self = NM_MODEM_MANAGER (object);
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_NAME_OWNER:
+		g_value_set_string (value, priv->modm.proxy_name_owner);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
 	}
-
-	/* Got the bus, ensure clients */
-	ensure_modem_manager (self);
-#if WITH_OFONO
-	ensure_ofono_client (self);
-#endif
 }
 
 /*****************************************************************************/
@@ -564,36 +838,50 @@ bus_get_ready (GObject *source,
 static void
 nm_modem_manager_init (NMModemManager *self)
 {
-	self->_priv.modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
+
+	priv->modems = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+
+	priv->main_cancellable = g_cancellable_new ();
 
 	g_bus_get (G_BUS_TYPE_SYSTEM,
-	           NULL,
-	           (GAsyncReadyCallback)bus_get_ready,
-	           g_object_ref (self));
+	           priv->main_cancellable,
+	           bus_get_ready,
+	           self);
 }
 
 static void
 dispose (GObject *object)
 {
 	NMModemManager *self = NM_MODEM_MANAGER (object);
+	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 
-	nm_clear_g_source (&self->_priv.mm_launch_id);
+	nm_clear_g_cancellable (&priv->main_cancellable);
+	nm_clear_g_cancellable (&priv->modm.poke_cancellable);
 
-	clear_modem_manager (self);
+	nm_clear_g_source (&priv->modm.relaunch_id);
+
+	nm_clear_g_cancellable (&priv->modm.proxy_cancellable);
+	g_clear_object (&priv->modm.proxy);
+	nm_clear_g_free (&priv->modm.proxy_name_owner);
+
+	modm_clear_manager (self);
 
 #if WITH_OFONO
-	if (self->_priv.ofono_proxy) {
-		g_signal_handlers_disconnect_by_func (self->_priv.ofono_proxy, ofono_name_owner_changed, self);
-		g_signal_handlers_disconnect_by_func (self->_priv.ofono_proxy, ofono_signal_cb, self);
-		g_clear_object (&self->_priv.ofono_proxy);
+	if (priv->ofono.proxy) {
+		g_signal_handlers_disconnect_by_func (priv->ofono.proxy, ofono_name_owner_changed, self);
+		g_signal_handlers_disconnect_by_func (priv->ofono.proxy, ofono_signal_cb, self);
+		g_clear_object (&priv->ofono.proxy);
 	}
+	nm_clear_g_cancellable (&priv->ofono.cancellable);
 #endif
 
-	g_clear_object (&self->_priv.dbus_connection);
+	g_clear_object (&priv->dbus_connection);
 
-	if (self->_priv.modems) {
-		g_hash_table_foreach_remove (self->_priv.modems, remove_one_modem, object);
-		g_hash_table_destroy (self->_priv.modems);
+	if (priv->modems) {
+		g_hash_table_foreach_remove (priv->modems, remove_one_modem, object);
+		g_hash_table_destroy (priv->modems);
+		priv->modems = NULL;
 	}
 
 	G_OBJECT_CLASS (nm_modem_manager_parent_class)->dispose (object);
@@ -605,6 +893,15 @@ nm_modem_manager_class_init (NMModemManagerClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
 	object_class->dispose = dispose;
+	object_class->get_property = get_property;
+
+	obj_properties[PROP_NAME_OWNER] =
+	     g_param_spec_string (NM_MODEM_MANAGER_NAME_OWNER, "", "",
+	                          NULL,
+	                          G_PARAM_READABLE
+	                          | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	signals[MODEM_ADDED] =
 	    g_signal_new (NM_MODEM_MANAGER_MODEM_ADDED,
diff --git a/src/devices/wwan/nm-modem-manager.h b/src/devices/wwan/nm-modem-manager.h
index 65594dfa..5f913083 100644
--- a/src/devices/wwan/nm-modem-manager.h
+++ b/src/devices/wwan/nm-modem-manager.h
@@ -34,9 +34,22 @@
 
 #define NM_MODEM_MANAGER_MODEM_ADDED "modem-added"
 
+#define NM_MODEM_MANAGER_NAME_OWNER "name-owner"
+
+#define NM_MODEM_MANAGER_MM_DBUS_SERVICE   "org.freedesktop.ModemManager1"
+#define NM_MODEM_MANAGER_MM_DBUS_PATH      "/org/freedesktop/ModemManager1"
+#define NM_MODEM_MANAGER_MM_DBUS_INTERFACE "org.freedesktop.ModemManager1"
+
 typedef struct _NMModemManager NMModemManager;
 typedef struct _NMModemManagerClass NMModemManagerClass;
 
 GType nm_modem_manager_get_type (void);
 
+NMModemManager *nm_modem_manager_get (void);
+
+void nm_modem_manager_name_owner_ref (NMModemManager *self);
+void nm_modem_manager_name_owner_unref (NMModemManager *self);
+
+const char *nm_modem_manager_name_owner_get (NMModemManager *self);
+
 #endif /* __NETWORKMANAGER_MODEM_MANAGER_H__ */
diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c
index 52b335c7..8b3fc2e8 100644
--- a/src/devices/wwan/nm-modem-ofono.c
+++ b/src/devices/wwan/nm-modem-ofono.c
@@ -46,6 +46,11 @@ typedef struct {
 	GDBusProxy *context_proxy;
 	GDBusProxy *sim_proxy;
 
+	GCancellable *modem_proxy_cancellable;
+	GCancellable *connman_proxy_cancellable;
+	GCancellable *context_proxy_cancellable;
+	GCancellable *sim_proxy_cancellable;
+
 	GError *property_error;
 
 	char *context_path;
@@ -99,22 +104,6 @@ G_DEFINE_TYPE (NMModemOfono, nm_modem_ofono, NM_TYPE_MODEM)
 
 /*****************************************************************************/
 
-static gboolean
-ip_string_to_network_address (const gchar *str,
-                              guint32 *out)
-{
-	guint32 addr = 0;
-	gboolean success = FALSE;
-
-	if (!str || inet_pton (AF_INET, str, &addr) != 1)
-		addr = 0;
-	else
-		success = TRUE;
-
-	*out = (guint32)addr;
-	return success;
-}
-
 static void
 get_capabilities (NMModem *_self,
                   NMDeviceModemCapabilities *modem_caps,
@@ -165,29 +154,17 @@ typedef struct {
 static void
 disconnect_context_complete (DisconnectContext *ctx)
 {
-	g_simple_async_result_complete_in_idle (ctx->result);
 	if (ctx->cancellable)
 		g_object_unref (ctx->cancellable);
-	g_object_unref (ctx->result);
+	if (ctx->result) {
+		g_simple_async_result_complete_in_idle (ctx->result);
+		g_object_unref (ctx->result);
+	}
 	g_object_unref (ctx->self);
 	g_slice_free (DisconnectContext, ctx);
 }
 
 static gboolean
-disconnect_context_complete_if_cancelled (DisconnectContext *ctx)
-{
-	GError *error = NULL;
-
-	if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) {
-		g_simple_async_result_take_error (ctx->result, error);
-		disconnect_context_complete (ctx);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-static gboolean
 disconnect_finish (NMModem *self,
                    GAsyncResult *result,
                    GError **error)
@@ -196,25 +173,25 @@ disconnect_finish (NMModem *self,
 }
 
 static void
-disconnect_done (GDBusProxy *proxy,
-				 GAsyncResult *result,
-				 gpointer user_data)
+disconnect_done (GObject *source,
+                 GAsyncResult *result,
+                 gpointer user_data)
 {
 	DisconnectContext *ctx = (DisconnectContext*) user_data;
 	NMModemOfono *self = ctx->self;
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
+	gs_unref_variant GVariant *v = NULL;
 
-	g_dbus_proxy_call_finish (proxy, result, &error);
+	v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error);
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-		_LOGD ("disconnect cancelled");
+		if (ctx->result)
+			g_simple_async_result_take_error (ctx->result, g_steal_pointer (&error));
+		disconnect_context_complete (ctx);
 		return;
 	}
 
-	if (error) {
-		if (ctx->warn)
-			_LOGW ("failed to disconnect modem: %s", error->message);
-		g_clear_error (&error);
-	}
+	if (error && ctx->warn)
+		_LOGW ("failed to disconnect modem: %s", error->message);
 
 	_LOGD ("modem disconnected");
 
@@ -233,18 +210,15 @@ disconnect (NMModem *modem,
 	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
 	DisconnectContext *ctx;
 	NMModemState state = nm_modem_get_state (NM_MODEM (self));
+	GError *error = NULL;
 
 	_LOGD ("warn: %s modem_state: %s",
 	       warn ? "TRUE" : "FALSE",
 	       nm_modem_state_to_string (state));
 
-	if (state != NM_MODEM_STATE_CONNECTED)
-		return;
-
-	ctx = g_slice_new (DisconnectContext);
+	ctx = g_slice_new0 (DisconnectContext);
 	ctx->self = g_object_ref (self);
 	ctx->warn = warn;
-
 	if (callback) {
 		ctx->result = g_simple_async_result_new (G_OBJECT (self),
 		                                         callback,
@@ -252,9 +226,28 @@ disconnect (NMModem *modem,
 		                                         disconnect);
 	}
 
-	ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-	if (disconnect_context_complete_if_cancelled (ctx))
+	if (state != NM_MODEM_STATE_CONNECTED) {
+		if (ctx->result) {
+			g_set_error_literal (&error,
+			                     NM_UTILS_ERROR,
+			                     NM_UTILS_ERROR_UNKNOWN,
+			                     ("modem is currently not connected"));
+			g_simple_async_result_take_error (ctx->result, error);
+		}
+		disconnect_context_complete (ctx);
 		return;
+	}
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+		if (ctx->result)
+			g_simple_async_result_take_error (ctx->result, error);
+		else
+			g_clear_error (&error);
+		disconnect_context_complete (ctx);
+		return;
+	}
+
+	ctx->cancellable = nm_g_object_ref (cancellable);
 
 	nm_modem_set_state (NM_MODEM (self),
 	                    NM_MODEM_STATE_DISCONNECTING,
@@ -267,8 +260,8 @@ disconnect (NMModem *modem,
 	                                  g_variant_new ("b", warn)),
 	                   G_DBUS_CALL_FLAGS_NONE,
 	                   20000,
-	                   NULL,
-	                   (GAsyncReadyCallback) disconnect_done,
+	                   ctx->cancellable,
+	                   disconnect_done,
 	                   ctx);
 }
 
@@ -375,22 +368,35 @@ sim_property_changed (GDBusProxy *proxy,
 }
 
 static void
-sim_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+sim_get_properties_done (GObject *source,
+                         GAsyncResult *result,
+                         gpointer user_data)
 {
-	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
-	GError *error = NULL;
-	GVariant *v_properties, *v_dict, *v;
+	NMModemOfono *self;
+	NMModemOfonoPrivate *priv;
+	gs_free_error GError *error = NULL;
+	gs_unref_variant GVariant *v_properties = NULL;
+	gs_unref_variant GVariant *v_dict = NULL;
+	GVariant *v;
 	GVariantIter i;
 	const char *property;
 
-	v_properties = _nm_dbus_proxy_call_finish (proxy,
+	v_properties = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source),
 	                                           result,
 	                                           G_VARIANT_TYPE ("(a{sv})"),
 	                                           &error);
+	if (   !v_properties
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_MODEM_OFONO (user_data);
+	priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	g_clear_object (&priv->sim_proxy_cancellable);
+
 	if (!v_properties) {
 		g_dbus_error_strip_remote_error (error);
 		_LOGW ("error getting sim properties: %s", error->message);
-		g_error_free (error);
 		return;
 	}
 
@@ -418,9 +424,49 @@ sim_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_
 		handle_sim_property (NULL, property, v, self);
 		g_variant_unref (v);
 	}
+}
 
-	g_variant_unref (v_dict);
-	g_variant_unref (v_properties);
+static void
+_sim_proxy_new_cb (GObject *source,
+                   GAsyncResult *result,
+                   gpointer user_data)
+{
+	NMModemOfono *self;
+	NMModemOfonoPrivate *priv;
+	gs_free_error GError *error = NULL;
+	GDBusProxy *proxy;
+
+	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (   !proxy
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = user_data;
+	priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	if (!proxy) {
+		_LOGW ("failed to create SimManager proxy: %s", error->message);
+		g_clear_object (&priv->sim_proxy_cancellable);
+		return;
+	}
+
+	priv->sim_proxy = proxy;
+
+	/* Watch for custom ofono PropertyChanged signals */
+	_nm_dbus_signal_connect (priv->sim_proxy,
+	                         "PropertyChanged",
+	                         G_VARIANT_TYPE ("(sv)"),
+	                         G_CALLBACK (sim_property_changed),
+	                         self);
+
+	g_dbus_proxy_call (priv->sim_proxy,
+	                   "GetProperties",
+	                   NULL,
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   20000,
+	                   priv->sim_proxy_cancellable,
+	                   sim_get_properties_done,
+	                   self);
 }
 
 static void
@@ -430,47 +476,30 @@ handle_sim_iface (NMModemOfono *self, gboolean found)
 
 	_LOGD ("SimManager interface %sfound", found ? "" : "not ");
 
-	if (!found && priv->sim_proxy) {
+	if (!found && (priv->sim_proxy || priv->sim_proxy_cancellable)) {
 		_LOGI ("SimManager interface disappeared");
-		g_signal_handlers_disconnect_by_data (priv->sim_proxy, NM_MODEM_OFONO (self));
-		g_clear_object (&priv->sim_proxy);
+		nm_clear_g_cancellable (&priv->sim_proxy_cancellable);
+		if (priv->sim_proxy) {
+			g_signal_handlers_disconnect_by_data (priv->sim_proxy, self);
+			g_clear_object (&priv->sim_proxy);
+		}
 		g_clear_pointer (&priv->imsi, g_free);
 		update_modem_state (self);
-	} else if (found && !priv->sim_proxy) {
-		GError *error = NULL;
-
+	} else if (found && (!priv->sim_proxy && !priv->sim_proxy_cancellable)) {
 		_LOGI ("found new SimManager interface");
 
-		priv->sim_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
-		                                                 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
-		                                                 | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
-		                                                 NULL, /* GDBusInterfaceInfo */
-		                                                 OFONO_DBUS_SERVICE,
-		                                                 nm_modem_get_path (NM_MODEM (self)),
-		                                                 OFONO_DBUS_INTERFACE_SIM_MANAGER,
-		                                                 NULL, /* GCancellable */
-		                                                 &error);
-		if (priv->sim_proxy == NULL) {
-			_LOGW ("failed to create SimManager proxy: %s", error->message);
-			g_error_free (error);
-			return;
-		}
-
-		/* Watch for custom ofono PropertyChanged signals */
-		_nm_dbus_signal_connect (priv->sim_proxy,
-		                         "PropertyChanged",
-		                         G_VARIANT_TYPE ("(sv)"),
-		                         G_CALLBACK (sim_property_changed),
-		                         self);
-
-		g_dbus_proxy_call (priv->sim_proxy,
-		                   "GetProperties",
-		                   NULL,
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   20000,
-		                   NULL,
-		                   (GAsyncReadyCallback) sim_get_properties_done,
-		                   g_object_ref (self));
+		priv->sim_proxy_cancellable = g_cancellable_new ();
+
+		g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+		                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+		                          | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+		                          NULL, /* GDBusInterfaceInfo */
+		                          OFONO_DBUS_SERVICE,
+		                          nm_modem_get_path (NM_MODEM (self)),
+		                          OFONO_DBUS_INTERFACE_SIM_MANAGER,
+		                          priv->sim_proxy_cancellable, /* GCancellable */
+		                          _sim_proxy_new_cb,
+		                          self);
 	}
 }
 
@@ -514,22 +543,35 @@ connman_property_changed (GDBusProxy *proxy,
 }
 
 static void
-connman_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+connman_get_properties_done (GObject *source,
+                             GAsyncResult *result,
+                             gpointer user_data)
 {
-	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
-	GError *error = NULL;
-	GVariant *v_properties, *v_dict, *v;
+	NMModemOfono *self;
+	NMModemOfonoPrivate *priv;
+	gs_free_error GError *error = NULL;
+	gs_unref_variant GVariant *v_properties = NULL;
+	gs_unref_variant GVariant *v_dict = NULL;
+	GVariant *v;
 	GVariantIter i;
 	const char *property;
 
-	v_properties = _nm_dbus_proxy_call_finish (proxy,
-		                                       result,
-		                                       G_VARIANT_TYPE ("(a{sv})"),
-		                                       &error);
+	v_properties = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source),
+	                                           result,
+	                                           G_VARIANT_TYPE ("(a{sv})"),
+	                                           &error);
+	if (   !v_properties
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_MODEM_OFONO (user_data);
+	priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	g_clear_object (&priv->connman_proxy_cancellable);
+
 	if (!v_properties) {
 		g_dbus_error_strip_remote_error (error);
 		_LOGW ("error getting connman properties: %s", error->message);
-		g_error_free (error);
 		return;
 	}
 
@@ -549,9 +591,48 @@ connman_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer u
 		handle_connman_property (NULL, property, v, self);
 		g_variant_unref (v);
 	}
+}
+
+static void
+_connman_proxy_new_cb (GObject *source,
+                       GAsyncResult *result,
+                       gpointer user_data)
+{
+	NMModemOfono *self;
+	NMModemOfonoPrivate *priv;
+	gs_free_error GError *error = NULL;
+	GDBusProxy *proxy;
 
-	g_variant_unref (v_dict);
-	g_variant_unref (v_properties);
+	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (   !proxy
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = user_data;
+	priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	if (!proxy) {
+		_LOGW ("failed to create ConnectionManager proxy: %s", error->message);
+		g_clear_object (&priv->connman_proxy_cancellable);
+		return;
+	}
+
+	priv->connman_proxy = proxy;
+
+	_nm_dbus_signal_connect (priv->connman_proxy,
+	                         "PropertyChanged",
+	                         G_VARIANT_TYPE ("(sv)"),
+	                         G_CALLBACK (connman_property_changed),
+	                         self);
+
+	g_dbus_proxy_call (priv->connman_proxy,
+	                   "GetProperties",
+	                   NULL,
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   20000,
+	                   priv->connman_proxy_cancellable,
+	                   connman_get_properties_done,
+	                   self);
 }
 
 static void
@@ -561,11 +642,13 @@ handle_connman_iface (NMModemOfono *self, gboolean found)
 
 	_LOGD ("ConnectionManager interface %sfound", found ? "" : "not ");
 
-	if (!found && priv->connman_proxy) {
+	if (!found && (priv->connman_proxy || priv->connman_proxy_cancellable)) {
 		_LOGI ("ConnectionManager interface disappeared");
-
-		g_signal_handlers_disconnect_by_data (priv->connman_proxy, NM_MODEM_OFONO (self));
-		g_clear_object (&priv->connman_proxy);
+		nm_clear_g_cancellable (&priv->connman_proxy_cancellable);
+		if (priv->connman_proxy) {
+			g_signal_handlers_disconnect_by_data (priv->connman_proxy, self);
+			g_clear_object (&priv->connman_proxy);
+		}
 
 		/* The connection manager proxy disappeared, we should
 		 * consider the modem disabled.
@@ -573,41 +656,21 @@ handle_connman_iface (NMModemOfono *self, gboolean found)
 		priv->gprs_attached = FALSE;
 
 		update_modem_state (self);
-	} else if (found && !priv->connman_proxy) {
-		GError *error = NULL;
-
+	} else if (found && (!priv->connman_proxy && !priv->connman_proxy_cancellable)) {
 		_LOGI ("found new ConnectionManager interface");
 
-		priv->connman_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
-		                                                     G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
-		                                                     | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
-		                                                     NULL, /* GDBusInterfaceInfo */
-		                                                     OFONO_DBUS_SERVICE,
-		                                                     nm_modem_get_path (NM_MODEM (self)),
-		                                                     OFONO_DBUS_INTERFACE_CONNECTION_MANAGER,
-		                                                     NULL, /* GCancellable */
-		                                                     &error);
-		if (priv->connman_proxy == NULL) {
-			_LOGW ("failed to create ConnectionManager proxy: %s", error->message);
-			g_error_free (error);
-			return;
-		}
-
-		/* Watch for custom ofono PropertyChanged signals */
-		_nm_dbus_signal_connect (priv->connman_proxy,
-		                         "PropertyChanged",
-		                         G_VARIANT_TYPE ("(sv)"),
-		                         G_CALLBACK (connman_property_changed),
-		                         self);
-
-		g_dbus_proxy_call (priv->connman_proxy,
-		                   "GetProperties",
-		                   NULL,
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   20000,
-		                   NULL,
-		                   (GAsyncReadyCallback) connman_get_properties_done,
-		                   g_object_ref (self));
+		priv->connman_proxy_cancellable = g_cancellable_new ();
+
+		g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+		                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+		                          | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+		                          NULL, /* GDBusInterfaceInfo */
+		                          OFONO_DBUS_SERVICE,
+		                          nm_modem_get_path (NM_MODEM (self)),
+		                          OFONO_DBUS_INTERFACE_CONNECTION_MANAGER,
+		                          priv->connman_proxy_cancellable,
+		                          _connman_proxy_new_cb,
+		                          NULL);
 	}
 }
 
@@ -667,22 +730,35 @@ modem_property_changed (GDBusProxy *proxy,
 }
 
 static void
-modem_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+modem_get_properties_done (GObject *source,
+                           GAsyncResult *result,
+                           gpointer user_data)
 {
-	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
-	GError *error = NULL;
-	GVariant *v_properties, *v_dict, *v;
+	NMModemOfono *self;
+	NMModemOfonoPrivate *priv;
+	gs_free_error GError *error = NULL;
+	gs_unref_variant GVariant *v_properties = NULL;
+	gs_unref_variant GVariant *v_dict = NULL;
+	GVariant *v;
 	GVariantIter i;
 	const char *property;
 
-	v_properties = _nm_dbus_proxy_call_finish (proxy,
+	v_properties = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source),
 	                                           result,
 	                                           G_VARIANT_TYPE ("(a{sv})"),
 	                                           &error);
+	if (   !v_properties
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_MODEM_OFONO (user_data);
+	priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	g_clear_object (&priv->modem_proxy_cancellable);
+
 	if (!v_properties) {
 		g_dbus_error_strip_remote_error (error);
 		_LOGW ("error getting modem properties: %s", error->message);
-		g_error_free (error);
 		return;
 	}
 
@@ -706,21 +782,29 @@ modem_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer use
 		handle_modem_property (NULL, property, v, self);
 		g_variant_unref (v);
 	}
-
-	g_variant_unref (v_dict);
-	g_variant_unref (v_properties);
 }
 
 static void
-stage1_prepare_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+stage1_prepare_done (GObject *source,
+                     GAsyncResult *result,
+                     gpointer user_data)
 {
-	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
-	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
-	GError *error = NULL;
+	NMModemOfono *self;
+	NMModemOfonoPrivate *priv;
+	gs_free_error GError *error = NULL;
+	gs_unref_variant GVariant *v = NULL;
+
+	v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_MODEM_OFONO (user_data);
+	priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	g_clear_object (&priv->context_proxy_cancellable);
 
 	g_clear_pointer (&priv->connect_properties, g_hash_table_destroy);
 
-	g_dbus_proxy_call_finish (proxy, result, &error);
 	if (error) {
 		_LOGW ("connection failed: %s", error->message);
 
@@ -732,8 +816,6 @@ stage1_prepare_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data
 		 * leading to the connection being disabled, and a 5m
 		 * timeout...
 		 */
-
-		g_clear_error (&error);
 	}
 }
 
@@ -751,6 +833,7 @@ context_property_changed (GDBusProxy *proxy,
 	const gchar *s, *addr_s;
 	const gchar **array, **iter;
 	guint32 address_network, gateway_network;
+	guint32 ip4_route_table, ip4_route_metric;
 	guint prefix = 0;
 
 	_LOGD ("PropertyChanged: %s", property);
@@ -809,15 +892,20 @@ context_property_changed (GDBusProxy *proxy,
 	 * 'Interface'.
 	 *
 	 * This needs discussion with upstream.
+	 *
+	 * FIXME: it is no longer allowed to omit the ifindex for NMIP4Config instances.
+	 * This is broken.
 	 */
-	priv->ip4_config = nm_ip4_config_new (0);
+	priv->ip4_config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
+	                                      0);
 
 	/* TODO: simply if/else error logic! */
 
 	if (g_variant_lookup (v_dict, "Address", "&s", &addr_s)) {
 		_LOGD ("Address: %s", addr_s);
 
-		if (ip_string_to_network_address (addr_s, &address_network)) {
+		if (   addr_s
+		    && nm_utils_parse_inaddr_bin (AF_INET, addr_s, &address_network)) {
 			addr.address = address_network;
 			addr.addr_source = NM_IP_CONFIG_SOURCE_WWAN;
 		} else {
@@ -833,7 +921,8 @@ context_property_changed (GDBusProxy *proxy,
 	if (g_variant_lookup (v_dict, "Netmask", "&s", &s)) {
 		_LOGD ("Netmask: %s", s);
 
-		if (s && ip_string_to_network_address (s, &address_network)) {
+		if (   s
+		    && nm_utils_parse_inaddr_bin (AF_INET, s, &address_network)) {
 			prefix = nm_utils_ip4_netmask_to_prefix (address_network);
 			if (prefix > 0)
 				addr.plen = prefix;
@@ -850,15 +939,30 @@ context_property_changed (GDBusProxy *proxy,
 
 	nm_ip4_config_add_address (priv->ip4_config, &addr);
 
-	if (g_variant_lookup (v_dict, "Gateway", "&s", &s)) {
-		if (s && ip_string_to_network_address (s, &gateway_network)) {
-			_LOGI ("Gateway: %s", s);
-			nm_ip4_config_set_gateway (priv->ip4_config, gateway_network);
-		} else {
+	if (   g_variant_lookup (v_dict, "Gateway", "&s", &s)
+	    && s) {
+
+		if (!nm_utils_parse_inaddr_bin (AF_INET, s, &gateway_network)) {
 			_LOGW ("invalid 'Gateway': %s", s);
 			goto out;
 		}
-		nm_ip4_config_set_gateway (priv->ip4_config, gateway_network);
+
+		nm_modem_get_route_parameters (NM_MODEM (self),
+		                               &ip4_route_table,
+		                               &ip4_route_metric,
+		                               NULL,
+		                               NULL);
+		{
+			const NMPlatformIP4Route r = {
+				.rt_source = NM_IP_CONFIG_SOURCE_WWAN,
+				.gateway = gateway_network,
+				.table_coerced = nm_platform_route_table_coerce (ip4_route_table),
+				.metric = ip4_route_metric,
+			};
+
+			_LOGI ("Gateway: %s", s);
+			nm_ip4_config_add_route (priv->ip4_config, &r, NULL);
+		}
 	} else {
 		_LOGW ("Settings 'Gateway' missing");
 		goto out;
@@ -867,7 +971,8 @@ context_property_changed (GDBusProxy *proxy,
 	if (g_variant_lookup (v_dict, "DomainNameServers", "^a&s", &array)) {
 		if (array) {
 			for (iter = array; *iter; iter++) {
-				if (ip_string_to_network_address (*iter, &address_network) && address_network > 0) {
+				if (   nm_utils_parse_inaddr_bin (AF_INET, *iter, &address_network)
+				    && address_network) {
 					_LOGI ("DNS: %s", *iter);
 					nm_ip4_config_add_nameserver (priv->ip4_config, address_network);
 				} else {
@@ -889,16 +994,25 @@ context_property_changed (GDBusProxy *proxy,
 
 	if (g_variant_lookup (v_dict, "MessageProxy", "&s", &s)) {
 		_LOGI ("MessageProxy: %s", s);
-		if (s && ip_string_to_network_address (s, &address_network)) {
-			NMPlatformIP4Route mms_route;
-
-			mms_route.network = address_network;
-			mms_route.plen = 32;
-			mms_route.gateway = gateway_network;
-
-			mms_route.metric = 1;
-
-			nm_ip4_config_add_route (priv->ip4_config, &mms_route);
+		if (   s
+		    && nm_utils_parse_inaddr_bin (AF_INET, s, &address_network)) {
+			nm_modem_get_route_parameters (NM_MODEM (self),
+			                               &ip4_route_table,
+			                               &ip4_route_metric,
+			                               NULL,
+			                               NULL);
+
+			{
+				const NMPlatformIP4Route mms_route = {
+					.network = address_network,
+					.plen = 32,
+					.gateway = gateway_network,
+					.table_coerced = nm_platform_route_table_coerce (ip4_route_table),
+					.metric = ip4_route_metric,
+				};
+
+				nm_ip4_config_add_route (priv->ip4_config, &mms_route, NULL);
+			}
 		} else {
 			_LOGW ("invalid MessageProxy: %s", s);
 		}
@@ -946,21 +1060,33 @@ static_stage3_ip4_config_start (NMModem *modem,
 }
 
 static void
-context_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+context_proxy_new_cb (GObject *source, GAsyncResult *result, gpointer user_data)
 {
-	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
-	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
-	GError *error = NULL;
+	NMModemOfono *self;
+	NMModemOfonoPrivate *priv;
+	gs_free_error GError *error = NULL;
+	GDBusProxy *proxy;
 
-	priv->context_proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
-	if (error) {
+	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (   !proxy
+	    || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_MODEM_OFONO (user_data);
+	priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	if (!proxy) {
 		_LOGE ("failed to create ofono ConnectionContext DBus proxy: %s", error->message);
+		g_clear_object (&priv->context_proxy_cancellable);
 		nm_modem_emit_prepare_result (NM_MODEM (self), FALSE,
 		                              NM_DEVICE_STATE_REASON_MODEM_BUSY);
 		return;
 	}
 
+	priv->context_proxy = proxy;
+
 	if (!priv->gprs_attached) {
+		g_clear_object (&priv->context_proxy_cancellable);
 		nm_modem_emit_prepare_result (NM_MODEM (self), FALSE,
 		                              NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
 		return;
@@ -972,7 +1098,6 @@ context_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat
 	 */
 	g_clear_object (&priv->ip4_config);
 
-	/* Watch for custom ofono PropertyChanged signals */
 	_nm_dbus_signal_connect (priv->context_proxy,
 	                         "PropertyChanged",
 	                         G_VARIANT_TYPE ("(sv)"),
@@ -986,9 +1111,9 @@ context_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat
 	                                   g_variant_new ("b", TRUE)),
 	                   G_DBUS_CALL_FLAGS_NONE,
 	                   20000,
-	                   NULL,
-	                   (GAsyncReadyCallback) stage1_prepare_done,
-	                   g_object_ref (self));
+	                   priv->context_proxy_cancellable,
+	                   stage1_prepare_done,
+	                   self);
 }
 
 static void
@@ -998,16 +1123,20 @@ do_context_activate (NMModemOfono *self)
 
 	g_return_if_fail (NM_IS_MODEM_OFONO (self));
 
+	nm_clear_g_cancellable (&priv->context_proxy_cancellable);
 	g_clear_object (&priv->context_proxy);
+
+	priv->context_proxy_cancellable = g_cancellable_new ();
+
 	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
 	                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
 	                          NULL,
 	                          OFONO_DBUS_SERVICE,
 	                          priv->context_path,
 	                          OFONO_DBUS_INTERFACE_CONNECTION_CONTEXT,
-	                          NULL,
-	                          (GAsyncReadyCallback) context_proxy_new_cb,
-	                          g_object_ref (self));
+	                          priv->context_proxy_cancellable,
+	                          context_proxy_new_cb,
+	                          self);
 }
 
 static GHashTable *
@@ -1018,7 +1147,7 @@ create_connect_properties (NMConnection *connection)
 	const char *str;
 
 	setting = nm_connection_get_setting_gsm (connection);
-	properties = g_hash_table_new (g_str_hash, g_str_equal);
+	properties = g_hash_table_new (nm_str_hash, g_str_equal);
 
 	str = nm_setting_gsm_get_apn (setting);
 	if (str)
@@ -1081,19 +1210,29 @@ act_stage1_prepare (NMModem *modem,
 }
 
 static void
-modem_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+modem_proxy_new_cb (GObject *source, GAsyncResult *result, gpointer user_data)
 {
-	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
-	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
-	GError *error = NULL;
+	NMModemOfono *self;
+	NMModemOfonoPrivate *priv;
+	gs_free_error GError *error = NULL;
+	GDBusProxy *proxy;
 
-	priv->modem_proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
-	if (error) {
+	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (   !proxy
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_MODEM_OFONO (user_data);
+	priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	if (!proxy) {
 		_LOGE ("failed to create ofono modem DBus proxy: %s", error->message);
+		g_clear_object (&priv->modem_proxy_cancellable);
 		return;
 	}
 
-	/* Watch for custom ofono PropertyChanged signals */
+	priv->modem_proxy = proxy;
+
 	_nm_dbus_signal_connect (priv->modem_proxy,
 	                         "PropertyChanged",
 	                         G_VARIANT_TYPE ("(sv)"),
@@ -1105,9 +1244,9 @@ modem_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	                   NULL,
 	                   G_DBUS_CALL_FLAGS_NONE,
 	                   20000,
-	                   NULL,
-	                   (GAsyncReadyCallback) modem_get_properties_done,
-	                   g_object_ref (self));
+	                   priv->modem_proxy_cancellable,
+	                   modem_get_properties_done,
+	                   self);
 }
 
 /*****************************************************************************/
@@ -1121,6 +1260,9 @@ static void
 constructed (GObject *object)
 {
 	NMModemOfono *self = NM_MODEM_OFONO (object);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	priv->modem_proxy_cancellable = g_cancellable_new ();
 
 	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
 	                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
@@ -1128,9 +1270,11 @@ constructed (GObject *object)
 	                          OFONO_DBUS_SERVICE,
 	                          nm_modem_get_path (NM_MODEM (self)),
 	                          OFONO_DBUS_INTERFACE_MODEM,
-	                          NULL,
-	                          (GAsyncReadyCallback) modem_proxy_new_cb,
-	                          g_object_ref (self));
+	                          priv->modem_proxy_cancellable,
+	                          modem_proxy_new_cb,
+	                          self);
+
+	G_OBJECT_CLASS (nm_modem_ofono_parent_class)->constructed (object);
 }
 
 NMModem *
@@ -1163,6 +1307,11 @@ dispose (GObject *object)
 	NMModemOfono *self = NM_MODEM_OFONO (object);
 	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
 
+	nm_clear_g_cancellable (&priv->modem_proxy_cancellable);
+	nm_clear_g_cancellable (&priv->connman_proxy_cancellable);
+	nm_clear_g_cancellable (&priv->context_proxy_cancellable);
+	nm_clear_g_cancellable (&priv->sim_proxy_cancellable);
+
 	if (priv->connect_properties) {
 		g_hash_table_destroy (priv->connect_properties);
 		priv->connect_properties = NULL;
@@ -1171,15 +1320,22 @@ dispose (GObject *object)
 	g_clear_object (&priv->ip4_config);
 
 	if (priv->modem_proxy) {
-		g_signal_handlers_disconnect_by_data (priv->modem_proxy, NM_MODEM_OFONO (self));
+		g_signal_handlers_disconnect_by_data (priv->modem_proxy, self);
 		g_clear_object (&priv->modem_proxy);
 	}
 
-	g_clear_object (&priv->connman_proxy);
-	g_clear_object (&priv->context_proxy);
+	if (priv->connman_proxy) {
+		g_signal_handlers_disconnect_by_data (priv->connman_proxy, self);
+		g_clear_object (&priv->connman_proxy);
+	}
+
+	if (priv->context_proxy) {
+		g_signal_handlers_disconnect_by_data (priv->context_proxy, self);
+		g_clear_object (&priv->context_proxy);
+	}
 
 	if (priv->sim_proxy) {
-		g_signal_handlers_disconnect_by_data (priv->sim_proxy, NM_MODEM_OFONO (self));
+		g_signal_handlers_disconnect_by_data (priv->sim_proxy, self);
 		g_clear_object (&priv->sim_proxy);
 	}
 
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 6494b849..77495b62 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -26,13 +26,13 @@
 #include <fcntl.h>
 #include <string.h>
 #include <termios.h>
+#include <linux/rtnetlink.h>
 
 #include "nm-core-internal.h"
 #include "platform/nm-platform.h"
 #include "nm-setting-connection.h"
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
-#include "nm-route-manager.h"
 #include "nm-netns.h"
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
@@ -98,6 +98,11 @@ typedef struct _NMModemPrivate {
 
 	guint32 mm_ip_timeout;
 
+	guint32 ip4_route_table;
+	guint32 ip4_route_metric;
+	guint32 ip6_route_table;
+	guint32 ip6_route_metric;
+
 	/* PPP stats */
 	guint32 in_bytes;
 	guint32 out_bytes;
@@ -108,6 +113,46 @@ G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT)
 #define NM_MODEM_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMModem, NM_IS_MODEM)
 
 /*****************************************************************************/
+
+#define _NMLOG_PREFIX_BUFLEN              64
+#define _NMLOG_PREFIX_NAME                "modem"
+#define _NMLOG_DOMAIN                     LOGD_MB
+
+static const char *
+_nmlog_prefix (char *prefix, NMModem *self)
+{
+	const char *uuid;
+	int c;
+
+	if (!self)
+		return "";
+
+	uuid = nm_modem_get_uid (self);
+
+	if (uuid) {
+		char pp[_NMLOG_PREFIX_BUFLEN - 5];
+
+		c = g_snprintf (prefix, _NMLOG_PREFIX_BUFLEN, "[%s]",
+		                nm_strquote (pp, sizeof (pp), uuid));
+	} else
+		c = g_snprintf (prefix, _NMLOG_PREFIX_BUFLEN, "[%p]", self);
+	nm_assert (c < _NMLOG_PREFIX_BUFLEN);
+
+	return prefix;
+}
+
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        char _prefix[_NMLOG_PREFIX_BUFLEN]; \
+        \
+        nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
+                "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME, \
+                _nmlog_prefix (_prefix, (self)) \
+                _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+    } G_STMT_END
+
+/*****************************************************************************/
 /* State/enabled/connected */
 
 static const char *state_table[] = {
@@ -151,11 +196,10 @@ nm_modem_set_state (NMModem *self,
 	priv->prev_state = NM_MODEM_STATE_UNKNOWN;
 
 	if (new_state != old_state) {
-		nm_log_info (LOGD_MB, "(%s): modem state changed, '%s' --> '%s' (reason: %s)\n",
-		             nm_modem_get_uid (self),
-		             nm_modem_state_to_string (old_state),
-		             nm_modem_state_to_string (new_state),
-		             reason ? reason : "none");
+		_LOGI ("modem state changed, '%s' --> '%s' (reason: %s)",
+		       nm_modem_state_to_string (old_state),
+		       nm_modem_state_to_string (new_state),
+		       reason ? reason : "none");
 
 		priv->state = new_state;
 		_notify (self, PROP_STATE);
@@ -181,24 +225,20 @@ nm_modem_set_mm_enabled (NMModem *self,
 	NMModemState prev_state = priv->state;
 
 	if (enabled && priv->state >= NM_MODEM_STATE_ENABLING) {
-		nm_log_dbg (LOGD_MB, "(%s): cannot enable modem: already enabled",
-		            nm_modem_get_uid (self));
+		_LOGD ("cannot enable modem: already enabled");
 		return;
 	}
 	if (!enabled && priv->state <= NM_MODEM_STATE_DISABLING) {
-		nm_log_dbg (LOGD_MB, "(%s): cannot disable modem: already disabled",
-		            nm_modem_get_uid (self));
+		_LOGD ("cannot disable modem: already disabled");
 		return;
 	}
 
 	if (priv->state <= NM_MODEM_STATE_INITIALIZING) {
-		nm_log_dbg (LOGD_MB, "(%s): cannot enable/disable modem: initializing or failed",
-		            nm_modem_get_uid (self));
+		_LOGD ("cannot enable/disable modem: initializing or failed");
 		return;
 	} else if (priv->state == NM_MODEM_STATE_LOCKED) {
 		/* Don't try to enable if the modem is locked since that will fail */
-		nm_log_warn (LOGD_MB, "(%s): cannot enable/disable modem: locked",
-		             nm_modem_get_uid (self));
+		_LOGW ("cannot enable/disable modem: locked");
 
 		/* Try to unlock the modem if it's being enabled */
 		if (enabled)
@@ -468,7 +508,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 	}
 
 	if (!num || dns_workaround) {
-		nm_log_warn (LOGD_PPP, "compensating for invalid PPP-provided nameservers");
+		_LOGW ("compensating for invalid PPP-provided nameservers");
 		nm_ip4_config_reset_nameservers (config);
 		nm_ip4_config_add_nameserver (config, good_dns1);
 		nm_ip4_config_add_nameserver (config, good_dns2);
@@ -561,9 +601,8 @@ ppp_stage3_ip_config_start (NMModem *self,
 	/* 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) {
-		nm_log_info (LOGD_PPP, "(%s): using modem-specified IP timeout: %u seconds",
-		             nm_modem_get_uid (self),
-		             priv->mm_ip_timeout);
+		_LOGI ("using modem-specified IP timeout: %u seconds",
+		       priv->mm_ip_timeout);
 		ip_timeout = priv->mm_ip_timeout;
 	}
 
@@ -577,12 +616,18 @@ ppp_stage3_ip_config_start (NMModem *self,
 
 	priv->ppp_manager = nm_ppp_manager_create (priv->data_port, &error);
 
+	if (priv->ppp_manager) {
+		nm_ppp_manager_set_route_parameters (priv->ppp_manager,
+		                                     priv->ip4_route_table,
+		                                     priv->ip4_route_metric,
+		                                     priv->ip6_route_table,
+		                                     priv->ip6_route_metric);
+	}
+
 	if (   !priv->ppp_manager
 	    || !nm_ppp_manager_start (priv->ppp_manager, req, ppp_name,
 	                              ip_timeout, baud_override, &error)) {
-		nm_log_err (LOGD_PPP, "(%s): error starting PPP: %s",
-		            nm_modem_get_uid (self),
-		            error->message);
+		_LOGE ("error starting PPP: %s", error->message);
 		g_error_free (error);
 
 		g_clear_object (&priv->ppp_manager);
@@ -621,7 +666,7 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 	const char *method;
 	NMActStageReturn ret;
 
-	nm_log_dbg (LOGD_MB, "ip4_config_start");
+	_LOGD ("ip4_config_start");
 
 	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE);
@@ -640,9 +685,8 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 		return NM_ACT_STAGE_RETURN_SUCCESS;
 
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) != 0) {
-		nm_log_warn (LOGD_MB | LOGD_IP4,
-		             "(%s): unhandled WWAN IPv4 method '%s'; will fail",
-		             nm_modem_get_uid (self), method);
+		_LOGW ("unhandled WWAN IPv4 method '%s'; will fail",
+		       method);
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
@@ -653,15 +697,15 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 		ret = ppp_stage3_ip_config_start (self, req, out_failure_reason);
 		break;
 	case NM_MODEM_IP_METHOD_STATIC:
-		nm_log_dbg (LOGD_MB, "MODEM_IP_METHOD_STATIC");
+		_LOGD ("MODEM_IP_METHOD_STATIC");
 		ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, out_failure_reason);
 		break;
 	case NM_MODEM_IP_METHOD_AUTO:
-		nm_log_dbg (LOGD_MB, "MODEM_IP_METHOD_AUTO");
+		_LOGD ("MODEM_IP_METHOD_AUTO");
 		ret = device_class->act_stage3_ip4_config_start (device, NULL, out_failure_reason);
 		break;
 	default:
-		nm_log_info (LOGD_MB, "(%s): IPv4 configuration disabled", nm_modem_get_uid (self));
+		_LOGI ("IPv4 configuration disabled");
 		ret = NM_ACT_STAGE_RETURN_IP_FAIL;
 		break;
 	}
@@ -676,13 +720,15 @@ nm_modem_ip4_pre_commit (NMModem *modem,
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (modem);
 
+	nm_modem_set_route_parameters_from_device (modem, device);
+
 	/* If the modem has an ethernet-type data interface (ie, not PPP and thus
 	 * not point-to-point) and IP config has a /32 prefix, then we assume that
 	 * ARP will be pointless and we turn it off.
 	 */
 	if (   priv->ip4_method == NM_MODEM_IP_METHOD_STATIC
 	    || priv->ip4_method == NM_MODEM_IP_METHOD_AUTO) {
-		const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, 0);
+		const NMPlatformIP4Address *address = nm_ip4_config_get_first_address (config);
 
 		g_assert (address);
 		if (address->plen == 32)
@@ -698,7 +744,8 @@ nm_modem_emit_ip6_config_result (NMModem *self,
                                  GError *error)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
-	guint i, num;
+	NMDedupMultiIter ipconf_iter;
+	const NMPlatformIP6Address *addr;
 	gboolean do_slaac = TRUE;
 
 	if (error) {
@@ -710,11 +757,7 @@ nm_modem_emit_ip6_config_result (NMModem *self,
 		/* If the IPv6 configuration only included a Link-Local address, then
 		 * we have to run SLAAC to get the full IPv6 configuration.
 		 */
-		num = nm_ip6_config_get_num_addresses (config);
-		g_assert (num > 0);
-		for (i = 0; i < num; i++) {
-			const NMPlatformIP6Address * addr = nm_ip6_config_get_address (config, i);
-
+		nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, config, &addr) {
 			if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
 				if (!priv->iid.id)
 					priv->iid.id = ((guint64 *)(&addr->address.s6_addr))[1];
@@ -757,9 +800,8 @@ nm_modem_stage3_ip6_config_start (NMModem *self,
 		return NM_ACT_STAGE_RETURN_IP_DONE;
 
 	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) != 0) {
-		nm_log_warn (LOGD_MB | LOGD_IP6,
-		             "(%s): unhandled WWAN IPv6 method '%s'; will fail",
-		             nm_modem_get_uid (self), method);
+		_LOGW ("unhandled WWAN IPv6 method '%s'; will fail",
+		       method);
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
@@ -778,7 +820,7 @@ nm_modem_stage3_ip6_config_start (NMModem *self,
 		ret = NM_MODEM_GET_CLASS (self)->stage3_ip6_config_request (self, out_failure_reason);
 		break;
 	default:
-		nm_log_info (LOGD_MB, "(%s): IPv6 configuration disabled", nm_modem_get_uid (self));
+		_LOGI ("IPv6 configuration disabled");
 		ret = NM_ACT_STAGE_RETURN_IP_FAIL;
 		break;
 	}
@@ -854,7 +896,7 @@ modem_secrets_cb (NMActRequest *req,
 		return;
 
 	if (error)
-		nm_log_warn (LOGD_MB, "(%s): %s", nm_modem_get_uid (self), error->message);
+		_LOGW ("modem-secrets: %s", error->message);
 
 	g_signal_emit (self, signals[AUTH_RESULT], 0, error);
 }
@@ -975,17 +1017,15 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection)
 		str = nm_setting_gsm_get_device_id (s_gsm);
 		if (str) {
 			if (!priv->device_id) {
-				nm_log_dbg (LOGD_MB, "(%s): %s/%s has device-id, device does not",
-				            priv->uid,
-				            nm_connection_get_uuid (connection),
-				            nm_connection_get_id (connection));
+				_LOGD ("%s/%s has device-id, device does not",
+				       nm_connection_get_uuid (connection),
+				       nm_connection_get_id (connection));
 				return FALSE;
 			}
 			if (strcmp (str, priv->device_id)) {
-				nm_log_dbg (LOGD_MB, "(%s): %s/%s device-id mismatch",
-				            priv->uid,
-				            nm_connection_get_uuid (connection),
-				            nm_connection_get_id (connection));
+				_LOGD ("%s/%s device-id mismatch",
+				       nm_connection_get_uuid (connection),
+				       nm_connection_get_id (connection));
 				return FALSE;
 			}
 		}
@@ -998,10 +1038,9 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection)
 		str = nm_setting_gsm_get_sim_id (s_gsm);
 		if (str && priv->sim_id) {
 			if (strcmp (str, priv->sim_id)) {
-				nm_log_dbg (LOGD_MB, "(%s): %s/%s sim-id mismatch",
-				            priv->uid,
-				            nm_connection_get_uuid (connection),
-				            nm_connection_get_id (connection));
+				_LOGD ("%s/%s sim-id mismatch",
+				       nm_connection_get_uuid (connection),
+				       nm_connection_get_id (connection));
 				return FALSE;
 			}
 		}
@@ -1009,10 +1048,9 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection)
 		str = nm_setting_gsm_get_sim_operator_id (s_gsm);
 		if (str && priv->sim_operator_id) {
 			if (strcmp (str, priv->sim_operator_id)) {
-				nm_log_dbg (LOGD_MB, "(%s): %s/%s sim-operator-id mismatch",
-				            priv->uid,
-				            nm_connection_get_uuid (connection),
-				            nm_connection_get_id (connection));
+				_LOGD ("%s/%s sim-operator-id mismatch",
+				       nm_connection_get_uuid (connection),
+				       nm_connection_get_id (connection));
 				return FALSE;
 			}
 		}
@@ -1069,10 +1107,11 @@ deactivate_cleanup (NMModem *self, NMDevice *device)
 		    priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
 			ifindex = nm_device_get_ip_ifindex (device);
 			if (ifindex > 0) {
-				nm_route_manager_route_flush (nm_netns_get_route_manager (nm_device_get_netns (device)),
-				                              ifindex);
-				nm_platform_address_flush (nm_device_get_platform (device), ifindex);
-				nm_platform_link_set_down (nm_device_get_platform (device), ifindex);
+				NMPlatform *platform = nm_device_get_platform (device);
+
+				nm_platform_ip_route_flush (platform, AF_UNSPEC, ifindex);
+				nm_platform_ip_address_flush (platform, AF_UNSPEC, ifindex);
+				nm_platform_link_set_down (platform, ifindex);
 			}
 		}
 	}
@@ -1149,12 +1188,12 @@ ppp_manager_stop_ready (NMPPPManager *ppp_manager,
                         GAsyncResult *res,
                         DeactivateContext *ctx)
 {
+	NMModem *self = ctx->self;
 	GError *error = NULL;
 
 	if (!nm_ppp_manager_stop_finish (ppp_manager, res, &error)) {
-		nm_log_warn (LOGD_MB, "(%s): cannot stop PPP manager: %s",
-		             nm_modem_get_uid (ctx->self),
-		             error->message);
+		_LOGW ("cannot stop PPP manager: %s",
+		       error->message);
 		g_simple_async_result_take_error (ctx->result, error);
 		deactivate_context_complete (ctx);
 		return;
@@ -1168,7 +1207,8 @@ ppp_manager_stop_ready (NMPPPManager *ppp_manager,
 static void
 deactivate_step (DeactivateContext *ctx)
 {
-	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (ctx->self);
+	NMModem *self = ctx->self;
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 	GError *error = NULL;
 
 	/* Check cancellable in each step */
@@ -1187,7 +1227,7 @@ deactivate_step (DeactivateContext *ctx)
 		if (priv->ppp_manager)
 			ctx->ppp_manager = g_object_ref (priv->ppp_manager);
 		/* Run cleanup */
-		NM_MODEM_GET_CLASS (ctx->self)->deactivate_cleanup (ctx->self, ctx->device);
+		NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, ctx->device);
 		ctx->step++;
 		/* fall through */
 	case DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP:
@@ -1203,16 +1243,15 @@ deactivate_step (DeactivateContext *ctx)
 		/* fall through */
 	case DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT:
 		/* Disconnect asynchronously */
-		NM_MODEM_GET_CLASS (ctx->self)->disconnect (ctx->self,
-		                                            FALSE,
-		                                            ctx->cancellable,
-		                                            (GAsyncReadyCallback) disconnect_ready,
-		                                            ctx);
+		NM_MODEM_GET_CLASS (self)->disconnect (self,
+		                                       FALSE,
+		                                       ctx->cancellable,
+		                                       (GAsyncReadyCallback) disconnect_ready,
+		                                       ctx);
 		return;
 
 	case DEACTIVATE_CONTEXT_STEP_LAST:
-		nm_log_dbg (LOGD_MB, "(%s): modem deactivation finished",
-		            nm_modem_get_uid (ctx->self));
+		_LOGD ("modem deactivation finished");
 		deactivate_context_complete (ctx);
 		return;
 	}
@@ -1377,6 +1416,76 @@ nm_modem_get_iid (NMModem *self, NMUtilsIPv6IfaceId *out_iid)
 /*****************************************************************************/
 
 void
+nm_modem_get_route_parameters (NMModem *self,
+                               guint32 *out_ip4_route_table,
+                               guint32 *out_ip4_route_metric,
+                               guint32 *out_ip6_route_table,
+                               guint32 *out_ip6_route_metric)
+{
+	NMModemPrivate *priv;
+
+	g_return_if_fail (NM_IS_MODEM (self));
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+	NM_SET_OUT (out_ip4_route_table, priv->ip4_route_table);
+	NM_SET_OUT (out_ip4_route_metric, priv->ip4_route_metric);
+	NM_SET_OUT (out_ip6_route_table, priv->ip6_route_table);
+	NM_SET_OUT (out_ip6_route_metric, priv->ip6_route_metric);
+}
+
+void
+nm_modem_set_route_parameters (NMModem *self,
+                               guint32 ip4_route_table,
+                               guint32 ip4_route_metric,
+                               guint32 ip6_route_table,
+                               guint32 ip6_route_metric)
+{
+	NMModemPrivate *priv;
+
+	g_return_if_fail (NM_IS_MODEM (self));
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+	if (   priv->ip4_route_table  != ip4_route_table
+	    || priv->ip4_route_metric != ip4_route_metric
+	    || priv->ip6_route_table  != ip6_route_table
+	    || priv->ip6_route_metric != ip6_route_metric) {
+		priv->ip4_route_table = ip4_route_table;
+		priv->ip4_route_metric = ip4_route_metric;
+		priv->ip6_route_table = ip6_route_table;
+		priv->ip6_route_metric = ip6_route_metric;
+
+		_LOGT ("route-parameters: table-v4: %u, metric-v4: %u, table-v6: %u, metric-v6: %u",
+		       priv->ip4_route_table,
+		       priv->ip4_route_metric,
+		       priv->ip6_route_table,
+		       priv->ip6_route_metric);
+	}
+
+	if (priv->ppp_manager) {
+		nm_ppp_manager_set_route_parameters (priv->ppp_manager,
+		                                     priv->ip4_route_table,
+		                                     priv->ip4_route_metric,
+		                                     priv->ip6_route_table,
+		                                     priv->ip6_route_metric);
+	}
+}
+
+void
+nm_modem_set_route_parameters_from_device (NMModem *self,
+                                           NMDevice *device)
+{
+	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_metric (device, AF_INET),
+	                               nm_device_get_route_table (device, AF_INET6, TRUE),
+	                               nm_device_get_route_metric (device, AF_INET6));
+}
+
+/*****************************************************************************/
+
+void
 nm_modem_get_capabilities (NMModem *self,
                            NMDeviceModemCapabilities *modem_caps,
                            NMDeviceModemCapabilities *current_caps)
@@ -1451,6 +1560,7 @@ set_property (GObject *object, guint prop_id,
 	case PROP_PATH:
 		/* construct-only */
 		priv->path = g_value_dup_string (value);
+		g_return_if_fail (priv->path);
 		break;
 	case PROP_DRIVER:
 		/* construct-only */
@@ -1509,40 +1619,27 @@ set_property (GObject *object, guint prop_id,
 static void
 nm_modem_init (NMModem *self)
 {
+	NMModemPrivate *priv;
+
 	self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_MODEM, NMModemPrivate);
+	priv = self->_priv;
+
+	priv->ip4_route_table = RT_TABLE_MAIN;
+	priv->ip4_route_metric = 700;
+	priv->ip6_route_table = RT_TABLE_MAIN;
+	priv->ip6_route_metric = 700;
 }
 
-static GObject*
-constructor (GType type,
-             guint n_construct_params,
-             GObjectConstructParam *construct_params)
+static void
+constructed (GObject *object)
 {
-	GObject *object;
 	NMModemPrivate *priv;
 
-	object = G_OBJECT_CLASS (nm_modem_parent_class)->constructor (type,
-	                                                              n_construct_params,
-	                                                              construct_params);
-	if (!object)
-		return NULL;
-
-	priv = NM_MODEM_GET_PRIVATE ((NMModem *) object);
+	G_OBJECT_CLASS (nm_modem_parent_class)->constructed (object);
 
-	if (!priv->data_port && !priv->control_port) {
-		nm_log_err (LOGD_PLATFORM, "neither modem command nor data interface provided");
-		goto err;
-	}
+	priv = NM_MODEM_GET_PRIVATE (NM_MODEM (object));
 
-	if (!priv->path) {
-		nm_log_err (LOGD_PLATFORM, "D-Bus path not provided");
-		goto err;
-	}
-
-	return object;
-
-err:
-	g_object_unref (object);
-	return NULL;
+	g_return_if_fail (priv->data_port || priv->control_port);
 }
 
 /*****************************************************************************/
@@ -1552,10 +1649,7 @@ dispose (GObject *object)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE ((NMModem *) object);
 
-	if (priv->act_request) {
-		g_object_unref (priv->act_request);
-		priv->act_request = NULL;
-	}
+	g_clear_object (&priv->act_request);
 
 	G_OBJECT_CLASS (nm_modem_parent_class)->dispose (object);
 }
@@ -1584,7 +1678,7 @@ nm_modem_class_init (NMModemClass *klass)
 
 	g_type_class_add_private (object_class, sizeof (NMModemPrivate));
 
-	object_class->constructor = constructor;
+	object_class->constructed = constructed;
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index a50727a9..9546e4a1 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -105,10 +105,12 @@ typedef enum {  /*< underscore_name=nm_modem_state >*/
 
 struct _NMModemPrivate;
 
-typedef struct {
+struct _NMModem {
 	GObject parent;
 	struct _NMModemPrivate *_priv;
-} NMModem;
+};
+
+typedef struct _NMModem NMModem;
 
 typedef struct {
 	GObjectClass parent;
@@ -185,6 +187,21 @@ gboolean nm_modem_complete_connection (NMModem *self,
                                        const GSList *existing_connections,
                                        GError **error);
 
+void nm_modem_get_route_parameters (NMModem *self,
+                                    guint32 *out_ip4_route_table,
+                                    guint32 *out_ip4_route_metric,
+                                    guint32 *out_ip6_route_table,
+                                    guint32 *out_ip6_route_metric);
+
+void nm_modem_set_route_parameters (NMModem *self,
+                                    guint32 ip4_route_table,
+                                    guint32 ip4_route_metric,
+                                    guint32 ip6_route_table,
+                                    guint32 ip6_route_metric);
+
+void nm_modem_set_route_parameters_from_device (NMModem *modem,
+                                                NMDevice *device);
+
 NMActStageReturn nm_modem_act_stage1_prepare (NMModem *modem,
                                               NMActRequest *req,
                                               NMDeviceStateReason *out_failure_reason);
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index fa4c8dbb..663102de 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -127,8 +127,8 @@ start (NMDeviceFactory *factory)
 	NMWwanFactory *self = NM_WWAN_FACTORY (factory);
 	NMWwanFactoryPrivate *priv = NM_WWAN_FACTORY_GET_PRIVATE (self);
 
-	priv->mm = g_object_new (NM_TYPE_MODEM_MANAGER, NULL);
-	g_assert (priv->mm);
+	priv->mm = g_object_ref (nm_modem_manager_get ());
+
 	g_signal_connect (priv->mm,
 	                  NM_MODEM_MANAGER_MODEM_ADDED,
 	                  G_CALLBACK (modem_added_cb),