bearer-mbim: implement connection logic using existing profiles

Unlike other implementations, with MBIM we cannot tell the modem to
connect a given profile by its profile number, which is a bit strange,
but it looks like there is no way to do that. So, if the user requests
to connect a given profile, what we do is load the profile settings by
querying the modem, and use those settings in the connect request.
This commit is contained in:
Aleksander Morgado
2021-04-29 10:13:22 +00:00
parent 72879e4095
commit 5d2db5de98
+215 -157
View File
@@ -27,6 +27,7 @@
#include <libmm-glib.h>
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp-profile-manager.h"
#include "mm-modem-helpers-mbim.h"
#include "mm-port-enums-types.h"
#include "mm-bearer-mbim.h"
@@ -196,8 +197,8 @@ reload_stats (MMBaseBearer *self,
typedef enum {
CONNECT_STEP_FIRST,
CONNECT_STEP_LOAD_PROFILE_SETTINGS,
CONNECT_STEP_PACKET_SERVICE,
CONNECT_STEP_PROVISIONED_CONTEXTS,
CONNECT_STEP_SETUP_LINK,
CONNECT_STEP_SETUP_LINK_MASTER_UP,
CONNECT_STEP_CHECK_DISCONNECTED,
@@ -209,12 +210,19 @@ typedef enum {
typedef struct {
MMPortMbim *mbim;
MMBearerProperties *properties;
MMBroadbandModemMbim *modem;
ConnectStep step;
MMPort *data;
MMBearerConnectResult *connect_result;
/* settings to use */
gint profile_id;
gchar *apn;
MbimContextType context_type;
gchar *user;
gchar *password;
MbimAuthProtocol auth;
MbimContextIpType requested_ip_type;
MbimContextIpType activated_ip_type;
MMBearerConnectResult *connect_result;
/* multiplex support */
guint session_id;
gchar *link_prefix_hint;
@@ -232,11 +240,15 @@ connect_context_free (ConnectContext *ctx)
g_clear_object (&ctx->link);
g_free (ctx->link_prefix_hint);
g_free (ctx->apn);
g_free (ctx->user);
g_free (ctx->password);
g_clear_pointer (&ctx->connect_result, (GDestroyNotify)mm_bearer_connect_result_unref);
g_clear_object (&ctx->data);
g_object_unref (ctx->properties);
g_object_unref (ctx->mbim);
g_object_unref (ctx->modem);
g_slice_free (ConnectContext, ctx);
}
@@ -564,6 +576,9 @@ ip_configuration_query_ready (MbimDevice *device,
ipv4_config,
ipv6_config);
mm_bearer_connect_result_set_multiplexed (ctx->connect_result, !!ctx->link);
if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)
mm_bearer_connect_result_set_profile_id (ctx->connect_result, ctx->profile_id);
}
if (error) {
@@ -795,53 +810,6 @@ setup_link_ready (MMPortMbim *mbim,
task);
}
static void
provisioned_contexts_query_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
MMBearerMbim *self;
ConnectContext *ctx;
g_autoptr(GError) error = NULL;
g_autoptr(MbimMessage) response = NULL;
guint32 provisioned_contexts_count;
g_autoptr(MbimProvisionedContextElementArray) provisioned_contexts = NULL;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
if (response &&
mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_provisioned_contexts_response_parse (
response,
&provisioned_contexts_count,
&provisioned_contexts,
&error)) {
guint32 i;
mm_obj_dbg (self, "provisioned contexts found (%u):", provisioned_contexts_count);
for (i = 0; i < provisioned_contexts_count; i++) {
MbimProvisionedContextElement *el = provisioned_contexts[i];
g_autofree gchar *uuid_str = NULL;
uuid_str = mbim_uuid_get_printable (&el->context_type);
mm_obj_dbg (self, "[%u] context type: %s", el->context_id, mbim_context_type_get_string (mbim_uuid_to_context_type (&el->context_type)));
mm_obj_dbg (self, " uuid: %s", uuid_str);
mm_obj_dbg (self, " access string: %s", el->access_string ? el->access_string : "");
mm_obj_dbg (self, " username: %s", el->user_name ? el->user_name : "");
mm_obj_dbg (self, " password: %s", el->password ? el->password : "");
mm_obj_dbg (self, " compression: %s", mbim_compression_get_string (el->compression));
mm_obj_dbg (self, " auth: %s", mbim_auth_protocol_get_string (el->auth_protocol));
}
} else
mm_obj_dbg (self, "error listing provisioned contexts: %s", error->message);
/* Keep on */
ctx->step++;
connect_context_step (task);
}
static void
packet_service_set_ready (MbimDevice *device,
GAsyncResult *res,
@@ -913,6 +881,95 @@ packet_service_set_ready (MbimDevice *device,
connect_context_step (task);
}
static gboolean
load_settings_from_profile (MMBearerMbim *self,
ConnectContext *ctx,
MM3gppProfile *profile,
GError **error)
{
MMBearerAllowedAuth bearer_auth;
MMBearerApnType apn_type;
GError *inner_error = NULL;
/* APN settings */
ctx->apn = g_strdup (mm_3gpp_profile_get_apn (profile));
apn_type = mm_3gpp_profile_get_apn_type (profile);
if (apn_type == MM_BEARER_APN_TYPE_NONE) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"APN type in profile is not initialized");
return FALSE;
}
ctx->context_type = mm_bearer_apn_type_to_mbim_context_type (apn_type, self, &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
/* Auth settings */
ctx->user = g_strdup (mm_3gpp_profile_get_user (profile));
ctx->password = g_strdup (mm_3gpp_profile_get_password (profile));
if (!ctx->user && !ctx->password) {
ctx->auth = MBIM_AUTH_PROTOCOL_NONE;
} else {
bearer_auth = mm_3gpp_profile_get_allowed_auth (profile);
ctx->auth = mm_bearer_allowed_auth_to_mbim_auth_protocol (bearer_auth, self, &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
}
/* This IP type reading is applicable only when the profile comes
* from the input bearer properties, as there is no IP type stored
* in the device profiles. Therefore, only read it if it hasn't been
* read yet */
if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_DEFAULT) {
MMBearerIpFamily ip_type;
ip_type = mm_3gpp_profile_get_ip_type (profile);
mm_3gpp_normalize_ip_family (&ip_type);
ctx->requested_ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_type, &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
}
return TRUE;
}
static void
get_profile_ready (MMIfaceModem3gppProfileManager *modem,
GAsyncResult *res,
GTask *task)
{
MMBearerMbim *self;
ConnectContext *ctx;
GError *error = NULL;
g_autoptr(MM3gppProfile) profile = NULL;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (modem, res, &error);
if (!profile) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (!load_settings_from_profile (self, ctx, profile, &error)) {
g_prefix_error (&error, "Couldn't load settings from profile: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Keep on */
ctx->step++;
connect_context_step (task);
}
static void
connect_context_step (GTask *task)
{
@@ -934,6 +991,19 @@ connect_context_step (GTask *task)
ctx->step++;
/* Fall through */
case CONNECT_STEP_LOAD_PROFILE_SETTINGS:
if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
mm_obj_dbg (self, "loading connection settings from profile '%d'...", ctx->profile_id);
mm_iface_modem_3gpp_profile_manager_get_profile (
MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->modem),
ctx->profile_id,
(GAsyncReadyCallback)get_profile_ready,
task);
return;
}
ctx->step++;
/* Fall through */
case CONNECT_STEP_PACKET_SERVICE:
mm_obj_dbg (self, "activating packet service...");
message = mbim_message_packet_service_set_new (MBIM_PACKET_SERVICE_ACTION_ATTACH, NULL);
@@ -945,17 +1015,6 @@ connect_context_step (GTask *task)
task);
return;
case CONNECT_STEP_PROVISIONED_CONTEXTS:
mm_obj_dbg (self, "listing provisioned contexts...");
message = mbim_message_provisioned_contexts_query_new (NULL);
mbim_device_command (mm_port_mbim_peek_device (ctx->mbim),
message,
10,
NULL,
(GAsyncReadyCallback)provisioned_contexts_query_ready,
task);
return;
case CONNECT_STEP_SETUP_LINK:
/* if a link prefix hint is available, it's because we should be doing
* multiplexing */
@@ -1025,55 +1084,19 @@ connect_context_step (GTask *task)
task);
return;
case CONNECT_STEP_CONNECT: {
const gchar *apn;
const gchar *user;
const gchar *password;
MbimAuthProtocol auth;
MMBearerIpFamily ip_family;
GError *error = NULL;
/* Setup parameters to use */
apn = mm_bearer_properties_get_apn (ctx->properties);
user = mm_bearer_properties_get_user (ctx->properties);
password = mm_bearer_properties_get_password (ctx->properties);
if (!user && !password) {
auth = MBIM_AUTH_PROTOCOL_NONE;
} else {
MMBearerAllowedAuth bearer_auth;
bearer_auth = mm_bearer_properties_get_allowed_auth (ctx->properties);
auth = mm_bearer_allowed_auth_to_mbim_auth_protocol (bearer_auth, self, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
}
ip_family = mm_bearer_properties_get_ip_type (ctx->properties);
mm_3gpp_normalize_ip_family (&ip_family);
ctx->requested_ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_family, &error);
if (error) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
mm_obj_dbg (self, "launching %s connection with APN '%s' in session %u...",
mbim_context_ip_type_get_string (ctx->requested_ip_type), apn, ctx->session_id);
case CONNECT_STEP_CONNECT:
mm_obj_dbg (self, "launching %s connection in session %u...",
mbim_context_ip_type_get_string (ctx->requested_ip_type), ctx->session_id);
message = mbim_message_connect_set_new (
ctx->session_id,
MBIM_ACTIVATION_COMMAND_ACTIVATE,
apn ? apn : "",
user ? user : "",
password ? password : "",
ctx->apn ? ctx->apn : "",
ctx->user ? ctx->user : "",
ctx->password ? ctx->password : "",
MBIM_COMPRESSION_NONE,
auth,
ctx->auth,
ctx->requested_ip_type,
mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
mbim_uuid_from_context_type (ctx->context_type),
NULL);
mbim_device_command (mm_port_mbim_peek_device (ctx->mbim),
message,
@@ -1082,7 +1105,6 @@ connect_context_step (GTask *task)
(GAsyncReadyCallback)connect_set_ready,
task);
return;
}
case CONNECT_STEP_IP_CONFIGURATION:
mm_obj_dbg (self, "querying IP configuration...");
@@ -1143,74 +1165,113 @@ connect_context_step (GTask *task)
g_assert_not_reached ();
}
static gboolean
load_settings_from_bearer (MMBearerMbim *self,
ConnectContext *ctx,
MMBearerProperties *properties,
GError **error)
{
MMBearerMultiplexSupport multiplex;
gboolean multiplex_supported = TRUE;
const gchar *data_port_driver;
data_port_driver = mm_kernel_device_get_driver (mm_port_peek_kernel_device (ctx->data));
if (!g_strcmp0 (data_port_driver, "mhi_net"))
multiplex_supported = FALSE;
/* If no multiplex setting given by the user, assume requested */
multiplex = mm_bearer_properties_get_multiplex (properties);
if (multiplex_supported &&
(multiplex == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN ||
multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED ||
multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED)) {
/* the link prefix hint given must be modem-specific */
ctx->link_prefix_hint = g_strdup_printf ("mbimmux%u.", mm_base_modem_get_dbus_id (MM_BASE_MODEM (ctx->modem)));
}
if (!multiplex_supported && multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Multiplexing required but not supported by %s", data_port_driver);
return FALSE;
}
/* If profile id is given, we'll load all settings from the stored profile,
* so ignore any other setting received in the bearer properties */
ctx->profile_id = mm_bearer_properties_get_profile_id (properties);
if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
MMBearerIpFamily ip_type;
GError *inner_error = NULL;
/* If we're loading settings from a profile, still read the ip-type
* from the user input, as that is not stored in the profile */
ip_type = mm_bearer_properties_get_ip_type (properties);
mm_3gpp_normalize_ip_family (&ip_type);
ctx->requested_ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_type, &inner_error);
if (inner_error) {
g_propagate_error (error, inner_error);
return FALSE;
}
return TRUE;
}
/* If not loading from a stored profile, initialize the
* APN type to 'internet' by default, which is what we've done
* until now. */
if (mm_bearer_properties_get_apn_type (properties) == MM_BEARER_APN_TYPE_NONE)
mm_bearer_properties_set_apn_type (properties, MM_BEARER_APN_TYPE_DEFAULT);
/* Use the implicit profile settings in the bearer properties */
if (!load_settings_from_profile (self, ctx, mm_bearer_properties_peek_3gpp_profile (properties), error))
return FALSE;
/* Is this a 3GPP only modem and no APN or profile id was given? If so, error */
if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (ctx->modem)) && !ctx->apn) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
"3GPP connection logic requires APN or profile id setting");
return FALSE;
}
return TRUE;
}
static void
_connect (MMBaseBearer *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
ConnectContext *ctx;
MMPort *data;
MMPortMbim *mbim;
const gchar *apn;
GTask *task;
MMBearerMultiplexSupport multiplex;
g_autoptr(MMBaseModem) modem = NULL;
const gchar *data_port_driver;
gboolean multiplex_supported = TRUE;
ConnectContext *ctx;
MMPort *data;
MMPortMbim *mbim;
GTask *task;
GError *error = NULL;
g_autoptr(MMBaseModem) modem = NULL;
g_autoptr(MMBearerProperties) properties = NULL;
if (!peek_ports (self, &mbim, &data, callback, user_data))
return;
task = g_task_new (self, cancellable, callback, user_data);
g_object_get (self,
MM_BASE_BEARER_MODEM, &modem,
MM_BASE_BEARER_MODEM, &modem,
MM_BASE_BEARER_CONFIG, &properties,
NULL);
g_assert (modem);
/* Check whether we have an APN */
apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (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) {
g_task_report_new_error (
self,
callback,
user_data,
_connect,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"3GPP connection logic requires APN setting");
return;
}
ctx = g_slice_new0 (ConnectContext);
ctx->modem = g_object_ref (modem);
ctx->mbim = g_object_ref (mbim);
ctx->data = g_object_ref (data);
ctx->step = CONNECT_STEP_FIRST;
ctx->requested_ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
ctx->activated_ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free);
g_object_get (self,
MM_BASE_BEARER_CONFIG, &ctx->properties,
NULL);
data_port_driver = mm_kernel_device_get_driver (mm_port_peek_kernel_device (data));
if (!g_strcmp0 (data_port_driver, "mhi_net"))
multiplex_supported = FALSE;
multiplex = mm_bearer_properties_get_multiplex (ctx->properties);
/* If no multiplex setting given by the user, assume requested */
if (multiplex_supported &&
(multiplex == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN ||
multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED ||
multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED)) {
/* the link prefix hint given must be modem-specific */
ctx->link_prefix_hint = g_strdup_printf ("mbimmux%u.", mm_base_modem_get_dbus_id (MM_BASE_MODEM (modem)));
}
if (!multiplex_supported && multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) {
mm_obj_err (self, "Multiplexing required but not supported by %s", data_port_driver);
if (!load_settings_from_bearer (MM_BEARER_MBIM (self), ctx, properties, &error)) {
g_prefix_error (&error, "Invalid bearer properties: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
@@ -1219,9 +1280,6 @@ _connect (MMBaseBearer *self,
mm_port_subsys_get_string (mm_port_get_subsys (data)),
mm_port_get_device (data));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free);
/* Run! */
connect_context_step (task);
}