bearer-qmi: handle disconnection indications during connection attempts

If a WDS indication is received telling us that the modem is no longer
connected, abort the ongoing connection attempt right away with an
error and cleanup all allocated clients (implicitly disconnecting them
if required)..

Fixes https://gitlab.freedesktop.org/mobile-broadband/ModemManager/issues/155
This commit is contained in:
Aleksander Morgado
2019-12-03 08:56:47 +00:00
parent ae53b0458b
commit 991e615736
+75 -22
View File
@@ -39,6 +39,10 @@ G_DEFINE_TYPE (MMBearerQmi, mm_bearer_qmi, MM_TYPE_BASE_BEARER)
#define GLOBAL_PACKET_DATA_HANDLE 0xFFFFFFFF
struct _MMBearerQmiPrivate {
/* Cancellables available during a connection attempt */
GCancellable *ongoing_connect_user_cancellable;
GCancellable *ongoing_connect_network_cancellable;
/* State kept while connected */
MMPortQmi *qmi;
gboolean explicit_qmi_open;
@@ -504,9 +508,15 @@ complete_connect (GTask *task,
MMBearerConnectResult *result,
GError *error)
{
ConnectContext *ctx;
g_assert (result || error);
g_assert (!(result && error));
ctx = g_task_get_task_data (task);
g_clear_object (&ctx->self->priv->ongoing_connect_user_cancellable);
g_clear_object (&ctx->self->priv->ongoing_connect_network_cancellable);
if (error)
g_task_return_error (task, error);
else
@@ -1231,12 +1241,13 @@ qmi_port_open_ready (MMPortQmi *qmi,
static void
connect_context_step (GTask *task)
{
MMBearerQmi *self;
ConnectContext *ctx;
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task);
self = g_task_get_source_object (task);
if (g_cancellable_is_cancelled (cancellable)) {
g_assert (self->priv->ongoing_connect_user_cancellable);
if (g_cancellable_is_cancelled (self->priv->ongoing_connect_user_cancellable)) {
complete_connect (task,
NULL,
g_error_new (G_IO_ERROR,
@@ -1245,6 +1256,16 @@ connect_context_step (GTask *task)
return;
}
g_assert (self->priv->ongoing_connect_network_cancellable);
if (g_cancellable_is_cancelled (self->priv->ongoing_connect_network_cancellable)) {
complete_connect (task,
NULL,
g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_ABORTED,
"aborted by the network"));
return;
}
ctx = g_task_get_task_data (task);
switch (ctx->step) {
@@ -1263,7 +1284,7 @@ connect_context_step (GTask *task)
if (!mm_port_qmi_is_open (ctx->qmi)) {
mm_port_qmi_open (ctx->qmi,
TRUE,
cancellable,
g_task_get_cancellable (task),
(GAsyncReadyCallback)qmi_port_open_ready,
task);
return;
@@ -1313,7 +1334,7 @@ connect_context_step (GTask *task)
mm_port_qmi_allocate_client (ctx->qmi,
QMI_SERVICE_WDS,
MM_PORT_QMI_FLAG_WDS_IPV4,
cancellable,
g_task_get_cancellable (task),
(GAsyncReadyCallback)qmi_port_allocate_client_ready,
task);
return;
@@ -1336,7 +1357,7 @@ connect_context_step (GTask *task)
qmi_client_wds_set_ip_family (ctx->client_ipv4,
input,
10,
cancellable,
g_task_get_cancellable (task),
(GAsyncReadyCallback)set_ip_family_ready,
task);
qmi_message_wds_set_ip_family_input_unref (input);
@@ -1355,7 +1376,7 @@ connect_context_step (GTask *task)
&ctx->packet_service_status_ipv4_indication_id);
setup_event_report_unsolicited_events (ctx->self,
ctx->client_ipv4,
cancellable,
g_task_get_cancellable (task),
(GAsyncReadyCallback) connect_enable_indications_ipv4_ready,
task);
return;
@@ -1368,7 +1389,7 @@ connect_context_step (GTask *task)
qmi_client_wds_start_network (ctx->client_ipv4,
input,
45,
cancellable,
g_task_get_cancellable (task),
(GAsyncReadyCallback)start_network_ready,
task);
qmi_message_wds_start_network_input_unref (input);
@@ -1412,7 +1433,7 @@ connect_context_step (GTask *task)
mm_port_qmi_allocate_client (ctx->qmi,
QMI_SERVICE_WDS,
MM_PORT_QMI_FLAG_WDS_IPV6,
cancellable,
g_task_get_cancellable (task),
(GAsyncReadyCallback)qmi_port_allocate_client_ready,
task);
return;
@@ -1437,7 +1458,7 @@ connect_context_step (GTask *task)
qmi_client_wds_set_ip_family (ctx->client_ipv6,
input,
10,
cancellable,
g_task_get_cancellable (task),
(GAsyncReadyCallback)set_ip_family_ready,
task);
qmi_message_wds_set_ip_family_input_unref (input);
@@ -1456,7 +1477,7 @@ connect_context_step (GTask *task)
&ctx->packet_service_status_ipv6_indication_id);
setup_event_report_unsolicited_events (ctx->self,
ctx->client_ipv6,
cancellable,
g_task_get_cancellable (task),
(GAsyncReadyCallback) connect_enable_indications_ipv6_ready,
task);
return;
@@ -1469,7 +1490,7 @@ connect_context_step (GTask *task)
qmi_client_wds_start_network (ctx->client_ipv6,
input,
45,
cancellable,
g_task_get_cancellable (task),
(GAsyncReadyCallback)start_network_ready,
task);
qmi_message_wds_start_network_input_unref (input);
@@ -1552,11 +1573,19 @@ connect_context_step (GTask *task)
}
static void
_connect (MMBaseBearer *self,
cancel_operation_cancellable (GCancellable *cancellable,
GCancellable *operation_cancellable)
{
g_cancellable_cancel (operation_cancellable);
}
static void
_connect (MMBaseBearer *_self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
MMBearerQmi *self = MM_BEARER_QMI (_self);
MMBearerProperties *properties = NULL;
ConnectContext *ctx;
MMBaseModem *modem = NULL;
@@ -1565,6 +1594,7 @@ _connect (MMBaseBearer *self,
GError *error = NULL;
const gchar *apn;
GTask *task;
GCancellable *operation_cancellable = NULL;
g_object_get (self,
MM_BASE_BEARER_MODEM, &modem,
@@ -1598,7 +1628,7 @@ _connect (MMBaseBearer *self,
}
/* Check whether we have an APN */
apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (_self));
/* Is this a 3GPP only modem and no APN was given? If so, error */
if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (modem)) && !apn) {
@@ -1643,7 +1673,9 @@ _connect (MMBaseBearer *self,
MM_BASE_BEARER_CONFIG, &properties,
NULL);
task = g_task_new (self, cancellable, callback, user_data);
operation_cancellable = g_cancellable_new ();
task = g_task_new (self, operation_cancellable, callback, user_data);
g_task_set_check_cancellable (task, FALSE);
g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free);
if (properties) {
@@ -1659,7 +1691,7 @@ _connect (MMBaseBearer *self,
ip_family == MM_BEARER_IP_FAMILY_ANY) {
gchar *ip_family_str;
ip_family = mm_base_bearer_get_default_ip_family (self);
ip_family = mm_base_bearer_get_default_ip_family (_self);
ip_family_str = mm_bearer_ip_family_build_string_from_mask (ip_family);
mm_dbg ("No specific IP family requested, defaulting to %s",
ip_family_str);
@@ -1718,10 +1750,27 @@ _connect (MMBaseBearer *self,
}
}
/* setup network cancellable */
g_assert (!self->priv->ongoing_connect_network_cancellable);
self->priv->ongoing_connect_network_cancellable = g_cancellable_new ();
g_cancellable_connect (self->priv->ongoing_connect_network_cancellable,
G_CALLBACK (cancel_operation_cancellable),
g_object_ref (operation_cancellable),
g_object_unref);
/* setup user cancellable */
g_assert (!self->priv->ongoing_connect_user_cancellable);
self->priv->ongoing_connect_user_cancellable = g_object_ref (cancellable);
g_cancellable_connect (self->priv->ongoing_connect_user_cancellable,
G_CALLBACK (cancel_operation_cancellable),
g_object_ref (operation_cancellable),
g_object_unref);
/* Run! */
connect_context_step (task);
out:
g_clear_object (&operation_cancellable);
g_clear_object (&qmi);
g_clear_object (&data);
g_clear_object (&modem);
@@ -2004,7 +2053,6 @@ disconnect (MMBaseBearer *_self,
ctx->packet_data_handle_ipv6 = self->priv->packet_data_handle_ipv6;
ctx->step = DISCONNECT_STEP_FIRST;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_context_free);
/* Run! */
@@ -2014,15 +2062,20 @@ disconnect (MMBaseBearer *_self,
/*****************************************************************************/
static void
report_connection_status (MMBaseBearer *self,
MMBearerConnectionStatus status)
report_connection_status (MMBaseBearer *_self,
MMBearerConnectionStatus status)
{
if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED)
MMBearerQmi *self = MM_BEARER_QMI (_self);
if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
/* Cancel any ongoing connection attempt */
g_cancellable_cancel (self->priv->ongoing_connect_network_cancellable);
/* Cleanup all connection related data */
reset_bearer_connection (MM_BEARER_QMI (self), TRUE, TRUE);
reset_bearer_connection (self, TRUE, TRUE);
}
/* Chain up parent's report_connection_status() */
MM_BASE_BEARER_CLASS (mm_bearer_qmi_parent_class)->report_connection_status (self, status);
MM_BASE_BEARER_CLASS (mm_bearer_qmi_parent_class)->report_connection_status (_self, status);
}
/*****************************************************************************/