aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAleksander Morgado <aleksandermj@chromium.org>2023-11-15 15:26:33 +0000
committerAleksander Morgado <aleksandermj@chromium.org>2024-02-15 21:01:52 +0000
commitf31f42dda1cced436b1927d06fab36507f58210f (patch)
treedf3c3bddca760817423b3c22679e58980c8b73e2 /src
parentb0add8c03b8c2e4588730a494f9abc190c349ba3 (diff)
bearer-mbim: support async IP configuration indications reporting SLAAC result
When IPv6 is requested during a connection attempt, two things may happen: either the modem itself runs SLAAC, or otherwise the host is the one that should run SLAAC. Until now we have assumed that the first IP configuration query that is run upon finishing a connection attempt always gives us a "final result", so if the modem did run SLAAC by itself, we would have expected to see a global IPv6 reported in that first query. And if there was no global IPv6 reported, then we would have assumed that the host was the one tasked with running SLAAC. There is a third option that we now consider, which is the case where the first IP configuration query does not report a global IPv6, but the modem is really running SLAAC, and the global IPv6 address will be reported via a IP configuration indication as soon as SLAAC finishes. This third option cannot be assumed by default, because it introduces the possibility of MM waiting for the indication with the SLAAC results and that indication never arriving in the 10s timeout used as default. If a modem is supposed to use this third option, the feature must be explicitly flagged by the modem plugin.
Diffstat (limited to 'src')
-rw-r--r--src/mm-bearer-mbim.c812
-rw-r--r--src/mm-bearer-mbim.h2
2 files changed, 545 insertions, 269 deletions
diff --git a/src/mm-bearer-mbim.c b/src/mm-bearer-mbim.c
index 625b988e..bb0b80d5 100644
--- a/src/mm-bearer-mbim.c
+++ b/src/mm-bearer-mbim.c
@@ -36,13 +36,28 @@
G_DEFINE_TYPE (MMBearerMbim, mm_bearer_mbim, MM_TYPE_BASE_BEARER)
+enum {
+ PROP_0,
+ PROP_ASYNC_SLAAC,
+ PROP_LAST
+};
+
struct _MMBearerMbimPrivate {
MMPortMbim *mbim;
MMPort *data;
MMPort *link;
guint32 session_id;
+
+ /* Whether IP configuration indications should be expected before finalizing
+ * a connection attempt. */
+ gboolean async_slaac;
+
+ /* Ongoing connection attempt waiting for async SLAAC result */
+ GTask *attempt_ongoing;
};
+static GParamSpec *properties[PROP_LAST];
+
/*****************************************************************************/
static gboolean
@@ -230,6 +245,7 @@ build_disconnect_message (MMBearerMbim *self,
/* Connect */
#define WAIT_LINK_PORT_TIMEOUT_MS 2500
+#define WAIT_IP_CONFIGURATION_ASYNC_TIMEOUT_S 20
typedef enum {
CONNECT_STEP_FIRST,
@@ -240,6 +256,7 @@ typedef enum {
CONNECT_STEP_ENSURE_DISCONNECTED,
CONNECT_STEP_CONNECT,
CONNECT_STEP_IP_CONFIGURATION,
+ CONNECT_STEP_IP_CONFIGURATION_ASYNC,
CONNECT_STEP_LAST
} ConnectStep;
@@ -264,11 +281,19 @@ typedef struct {
gchar *link_prefix_hint;
gchar *link_name;
MMPort *link;
+ /* async slaac support */
+ guint async_slaac_timeout_id;
+ gulong async_slaac_notification_id;
+ gulong async_slaac_cancellation_id;
} ConnectContext;
static void
connect_context_free (ConnectContext *ctx)
{
+ g_assert (!ctx->async_slaac_cancellation_id);
+ g_assert (!ctx->async_slaac_timeout_id);
+ g_assert (!ctx->async_slaac_notification_id);
+
if (ctx->abort_on_failure) {
mbim_device_command (mm_port_mbim_peek_device (ctx->mbim),
ctx->abort_on_failure,
@@ -308,331 +333,523 @@ connect_finish (MMBaseBearer *self,
static void connect_context_step (GTask *task);
static void
-ip_configuration_query_ready (MbimDevice *device,
- GAsyncResult *res,
- GTask *task)
+process_ip_configuration (MMBearerMbim *self,
+ ConnectContext *ctx,
+ MbimIPConfigurationAvailableFlag ipv4configurationavailable,
+ MbimIPConfigurationAvailableFlag ipv6configurationavailable,
+ guint32 ipv4addresscount,
+ const MbimIPv4ElementArray *ipv4address,
+ guint32 ipv6addresscount,
+ const MbimIPv6ElementArray *ipv6address,
+ const MbimIPv4 *ipv4gateway,
+ const MbimIPv6 *ipv6gateway,
+ guint32 ipv4dnsservercount,
+ const MbimIPv4 *ipv4dnsserver,
+ guint32 ipv6dnsservercount,
+ const MbimIPv6 *ipv6dnsserver,
+ guint32 ipv4mtu,
+ guint32 ipv6mtu)
{
- MMBearerMbim *self;
- ConnectContext *ctx;
- GError *error = NULL;
- g_autoptr(MbimMessage) response = NULL;
- MbimIPConfigurationAvailableFlag ipv4configurationavailable;
- MbimIPConfigurationAvailableFlag ipv6configurationavailable;
- guint32 ipv4addresscount;
- g_autoptr(MbimIPv4ElementArray) ipv4address = NULL;
- guint32 ipv6addresscount;
- g_autoptr(MbimIPv6ElementArray) ipv6address = NULL;
- const MbimIPv4 *ipv4gateway;
- const MbimIPv6 *ipv6gateway;
- guint32 ipv4dnsservercount;
- g_autofree MbimIPv4 *ipv4dnsserver = NULL;
- guint32 ipv6dnsservercount;
- g_autofree MbimIPv6 *ipv6dnsserver = NULL;
- guint32 ipv4mtu;
- guint32 ipv6mtu;
+ g_autofree gchar *ipv4configurationavailable_str = NULL;
+ g_autofree gchar *ipv6configurationavailable_str = NULL;
+ g_autoptr(MMBearerIpConfig) ipv4_config = NULL;
+ g_autoptr(MMBearerIpConfig) ipv6_config = NULL;
+ guint64 uplink_speed = 0;
+ guint64 downlink_speed = 0;
+
+ /* Always recreate the connect result completely */
+ g_clear_pointer (&ctx->connect_result, (GDestroyNotify)mm_bearer_connect_result_unref);
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
+ /* IPv4 info */
- response = mbim_device_command_finish (device, res, &error);
- if (response &&
- mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
- mbim_message_ip_configuration_response_parse (
- response,
- NULL, /* sessionid */
- &ipv4configurationavailable,
- &ipv6configurationavailable,
- &ipv4addresscount,
- &ipv4address,
- &ipv6addresscount,
- &ipv6address,
- &ipv4gateway,
- &ipv6gateway,
- &ipv4dnsservercount,
- &ipv4dnsserver,
- &ipv6dnsservercount,
- &ipv6dnsserver,
- &ipv4mtu,
- &ipv6mtu,
- &error)) {
- g_autofree gchar *ipv4configurationavailable_str = NULL;
- g_autofree gchar *ipv6configurationavailable_str = NULL;
- g_autoptr(MMBearerIpConfig) ipv4_config = NULL;
- g_autoptr(MMBearerIpConfig) ipv6_config = NULL;
- guint64 uplink_speed = 0;
- guint64 downlink_speed = 0;
+ ipv4configurationavailable_str = mbim_ip_configuration_available_flag_build_string_from_mask (ipv4configurationavailable);
+ mm_obj_dbg (self, "IPv4 configuration available: '%s'", ipv4configurationavailable_str);
+
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && ipv4addresscount) {
+ guint i;
+
+ mm_obj_dbg (self, " IP addresses (%u)", ipv4addresscount);
+ for (i = 0; i < ipv4addresscount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
+ g_autofree gchar *str = NULL;
- /* IPv4 info */
+ addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4address[i]->ipv4_address, G_SOCKET_FAMILY_IPV4);
+ str = g_inet_address_to_string (addr);
+ mm_obj_dbg (self, " IP [%u]: '%s/%u'", i, str, ipv4address[i]->on_link_prefix_length);
+ }
+ }
- ipv4configurationavailable_str = mbim_ip_configuration_available_flag_build_string_from_mask (ipv4configurationavailable);
- mm_obj_dbg (self, "IPv4 configuration available: '%s'", ipv4configurationavailable_str);
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) && ipv4gateway) {
+ g_autoptr(GInetAddress) addr = NULL;
+ g_autofree gchar *str = NULL;
- if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && ipv4addresscount) {
- guint i;
+ addr = g_inet_address_new_from_bytes ((guint8 *)ipv4gateway, G_SOCKET_FAMILY_IPV4);
+ str = g_inet_address_to_string (addr);
+ mm_obj_dbg (self, " gateway: '%s'", str);
+ }
+
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && ipv4dnsservercount) {
+ guint i;
+
+ mm_obj_dbg (self, " DNS addresses (%u)", ipv4dnsservercount);
+ for (i = 0; i < ipv4dnsservercount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
- mm_obj_dbg (self, " IP addresses (%u)", ipv4addresscount);
- for (i = 0; i < ipv4addresscount; i++) {
- g_autoptr(GInetAddress) addr = NULL;
- g_autofree gchar *str = NULL;
+ addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4dnsserver[i], G_SOCKET_FAMILY_IPV4);
+ if (!g_inet_address_get_is_any (addr)) {
+ g_autofree gchar *str = NULL;
- addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4address[i]->ipv4_address, G_SOCKET_FAMILY_IPV4);
str = g_inet_address_to_string (addr);
- mm_obj_dbg (self, " IP [%u]: '%s/%u'", i, str, ipv4address[i]->on_link_prefix_length);
+ mm_obj_dbg (self, " DNS [%u]: '%s'", i, str);
}
}
+ }
+
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) && ipv4mtu)
+ mm_obj_dbg (self, " MTU: '%u'", ipv4mtu);
+
+ /* IPv6 info */
+
+ ipv6configurationavailable_str = mbim_ip_configuration_available_flag_build_string_from_mask (ipv6configurationavailable);
+ mm_obj_dbg (self, "IPv6 configuration available: '%s'", ipv6configurationavailable_str);
- if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) && ipv4gateway) {
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && ipv6addresscount) {
+ guint i;
+
+ mm_obj_dbg (self, " IP addresses (%u)", ipv6addresscount);
+ for (i = 0; i < ipv6addresscount; i++) {
g_autoptr(GInetAddress) addr = NULL;
g_autofree gchar *str = NULL;
- addr = g_inet_address_new_from_bytes ((guint8 *)ipv4gateway, G_SOCKET_FAMILY_IPV4);
+ addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6address[i]->ipv6_address, G_SOCKET_FAMILY_IPV6);
str = g_inet_address_to_string (addr);
- mm_obj_dbg (self, " gateway: '%s'", str);
+ mm_obj_dbg (self, " IP [%u]: '%s/%u'", i, str, ipv6address[i]->on_link_prefix_length);
}
+ }
+
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) && ipv6gateway) {
+ g_autoptr(GInetAddress) addr = NULL;
+ g_autofree gchar *str = NULL;
- if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && ipv4dnsservercount) {
- guint i;
+ addr = g_inet_address_new_from_bytes ((guint8 *)ipv6gateway, G_SOCKET_FAMILY_IPV6);
+ str = g_inet_address_to_string (addr);
+ mm_obj_dbg (self, " gateway: '%s'", str);
+ }
- mm_obj_dbg (self, " DNS addresses (%u)", ipv4dnsservercount);
- for (i = 0; i < ipv4dnsservercount; i++) {
- g_autoptr(GInetAddress) addr = NULL;
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && ipv6dnsservercount) {
+ guint i;
- addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4dnsserver[i], G_SOCKET_FAMILY_IPV4);
- if (!g_inet_address_get_is_any (addr)) {
- g_autofree gchar *str = NULL;
+ mm_obj_dbg (self, " DNS addresses (%u)", ipv6dnsservercount);
+ for (i = 0; i < ipv6dnsservercount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
+
+ addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6dnsserver[i], G_SOCKET_FAMILY_IPV6);
+ if (!g_inet_address_get_is_any (addr)) {
+ g_autofree gchar *str = NULL;
- str = g_inet_address_to_string (addr);
- mm_obj_dbg (self, " DNS [%u]: '%s'", i, str);
- }
+ str = g_inet_address_to_string (addr);
+ mm_obj_dbg (self, " DNS [%u]: '%s'", i, str);
}
}
+ }
- if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) && ipv4mtu)
- mm_obj_dbg (self, " MTU: '%u'", ipv4mtu);
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) && ipv6mtu)
+ mm_obj_dbg (self, " MTU: '%u'", ipv6mtu);
- /* IPv6 info */
+ /* Build connection results */
- ipv6configurationavailable_str = mbim_ip_configuration_available_flag_build_string_from_mask (ipv6configurationavailable);
- mm_obj_dbg (self, "IPv6 configuration available: '%s'", ipv6configurationavailable_str);
+ /* Build IPv4 config */
+ if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ gboolean address_set = FALSE;
- if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && ipv6addresscount) {
- guint i;
+ ipv4_config = mm_bearer_ip_config_new ();
- mm_obj_dbg (self, " IP addresses (%u)", ipv6addresscount);
- for (i = 0; i < ipv6addresscount; i++) {
- g_autoptr(GInetAddress) addr = NULL;
- g_autofree gchar *str = NULL;
+ /* We assume that if we have an IP we can use static configuration.
+ * Not all modems or providers will return DNS servers or even a
+ * gateway, and not all modems support DHCP either. The IP management
+ * daemon/script just has to deal with this...
+ */
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && (ipv4addresscount > 0)) {
+ g_autoptr(GInetAddress) addr = NULL;
+ g_autofree gchar *str = NULL;
- addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6address[i]->ipv6_address, G_SOCKET_FAMILY_IPV6);
- str = g_inet_address_to_string (addr);
- mm_obj_dbg (self, " IP [%u]: '%s/%u'", i, str, ipv6address[i]->on_link_prefix_length);
+ mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_STATIC);
+
+ /* IP address, pick the first one */
+ addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4address[0]->ipv4_address, G_SOCKET_FAMILY_IPV4);
+ str = g_inet_address_to_string (addr);
+ mm_bearer_ip_config_set_address (ipv4_config, str);
+ address_set = TRUE;
+
+ /* Netmask */
+ mm_bearer_ip_config_set_prefix (ipv4_config, ipv4address[0]->on_link_prefix_length);
+
+ /* Gateway */
+ if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) {
+ g_autoptr(GInetAddress) gw_addr = NULL;
+ g_autofree gchar *gw_str = NULL;
+
+ gw_addr = g_inet_address_new_from_bytes ((guint8 *)ipv4gateway, G_SOCKET_FAMILY_IPV4);
+ gw_str = g_inet_address_to_string (gw_addr);
+ mm_bearer_ip_config_set_gateway (ipv4_config, gw_str);
}
+ } else
+ mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP);
+
+ /* DNS */
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && (ipv4dnsservercount > 0)) {
+ g_auto(GStrv) strarr = NULL;
+ guint i;
+ guint n;
+
+ strarr = g_new0 (gchar *, ipv4dnsservercount + 1);
+ for (i = 0, n = 0; i < ipv4dnsservercount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
+
+ addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4dnsserver[i], G_SOCKET_FAMILY_IPV4);
+ if (!g_inet_address_get_is_any (addr))
+ strarr[n++] = g_inet_address_to_string (addr);
+ }
+ mm_bearer_ip_config_set_dns (ipv4_config, (const gchar **)strarr);
}
- if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) && ipv6gateway) {
+ /* MTU */
+ if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
+ mm_bearer_ip_config_set_mtu (ipv4_config, ipv4mtu);
+
+ /* We requested IPv4, but it wasn't reported as activated. If there is no IP address
+ * provided by the modem, we assume the IPv4 bearer wasn't truly activated */
+ if (!address_set &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4V6 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ mm_obj_dbg (self, "IPv4 requested but no IPv4 activated and no IPv4 address set: ignoring");
+ g_clear_object (&ipv4_config);
+ }
+ }
+
+ /* Build IPv6 config */
+ if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV6 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ gboolean address_set = FALSE;
+
+ ipv6_config = mm_bearer_ip_config_new ();
+
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && (ipv6addresscount > 0)) {
g_autoptr(GInetAddress) addr = NULL;
g_autofree gchar *str = NULL;
- addr = g_inet_address_new_from_bytes ((guint8 *)ipv6gateway, G_SOCKET_FAMILY_IPV6);
+ /* IP address, pick the first one */
+ addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6address[0]->ipv6_address, G_SOCKET_FAMILY_IPV6);
str = g_inet_address_to_string (addr);
- mm_obj_dbg (self, " gateway: '%s'", str);
+ mm_bearer_ip_config_set_address (ipv6_config, str);
+ address_set = TRUE;
+
+ /* If the address is a link-local one, then SLAAC or DHCP must be used
+ * to get the real prefix and address.
+ * If the address is a global one, then the modem did SLAAC already and
+ * there is no need to run host SLAAC.
+ */
+ if (g_inet_address_get_is_link_local (addr))
+ mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
+ else
+ mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_STATIC);
+
+ /* Netmask */
+ mm_bearer_ip_config_set_prefix (ipv6_config, ipv6address[0]->on_link_prefix_length);
+
+ /* If the modem has done SLAAC itself, it is never expected to return a /128 prefix,
+ * warn if it happens and workaround it. Use /64 as default. */
+ if ((mm_bearer_ip_config_get_method (ipv6_config) == MM_BEARER_IP_METHOD_STATIC) &&
+ (mm_bearer_ip_config_get_prefix (ipv6_config) == 128)) {
+ mm_obj_warn (self, "unexpected link prefix returned with global IPv6 address (128): ignoring");
+ mm_bearer_ip_config_set_prefix (ipv6_config, 64);
+ }
+
+ /* Gateway */
+ if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) {
+ g_autoptr(GInetAddress) gw_addr = NULL;
+ g_autofree gchar *gw_str = NULL;
+
+ gw_addr = g_inet_address_new_from_bytes ((guint8 *)ipv6gateway, G_SOCKET_FAMILY_IPV6);
+ gw_str = g_inet_address_to_string (gw_addr);
+ mm_bearer_ip_config_set_gateway (ipv6_config, gw_str);
+ }
+ } else {
+ /* If no address is given, this is likely a bug in the modem firmware, because even in the
+ * case of needing to run host SLAAC, a link-local IPv6 address must be given. Either way,
+ * go on requesting the need of host SLAAC, and let the network decide whether our SLAAC
+ * Router Solicitation messages with an unexpected link-local address are accepted or not. */
+ mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
}
- if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && ipv6dnsservercount) {
- guint i;
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && (ipv6dnsservercount > 0)) {
+ g_auto(GStrv) strarr = NULL;
+ guint i;
+ guint n;
- mm_obj_dbg (self, " DNS addresses (%u)", ipv6dnsservercount);
- for (i = 0; i < ipv6dnsservercount; i++) {
- g_autoptr(GInetAddress) addr = NULL;
+ /* DNS */
+ strarr = g_new0 (gchar *, ipv6dnsservercount + 1);
+ for (i = 0, n = 0; i < ipv6dnsservercount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6dnsserver[i], G_SOCKET_FAMILY_IPV6);
- if (!g_inet_address_get_is_any (addr)) {
- g_autofree gchar *str = NULL;
-
- str = g_inet_address_to_string (addr);
- mm_obj_dbg (self, " DNS [%u]: '%s'", i, str);
- }
+ if (!g_inet_address_get_is_any (addr))
+ strarr[n++] = g_inet_address_to_string (addr);
}
+ mm_bearer_ip_config_set_dns (ipv6_config, (const gchar **)strarr);
}
- if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) && ipv6mtu)
- mm_obj_dbg (self, " MTU: '%u'", ipv6mtu);
+ /* MTU */
+ if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
+ mm_bearer_ip_config_set_mtu (ipv6_config, ipv6mtu);
+
+ /* We requested IPv6, but it wasn't reported as activated. If there is no IPv6 address
+ * provided by the modem, we assume the IPv6 bearer wasn't truly activated */
+ if (!address_set &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV6 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4V6 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ mm_obj_dbg (self, "IPv6 requested but no IPv6 activated and no IPv6 address set: ignoring");
+ g_clear_object (&ipv6_config);
+ }
+ }
+
+ /* Store result */
+ ctx->connect_result = mm_bearer_connect_result_new (ctx->link ? ctx->link : ctx->data,
+ ipv4_config,
+ ipv6_config);
+ mm_bearer_connect_result_set_multiplexed (ctx->connect_result, !!ctx->link);
- /* Build connection results */
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)
+ mm_bearer_connect_result_set_profile_id (ctx->connect_result, ctx->profile_id);
- /* Build IPv4 config */
- if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4 ||
- ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
- ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
- gboolean address_set = FALSE;
+ /* Propagate speeds from modem object */
+ mm_broadband_modem_mbim_get_speeds (ctx->modem, &uplink_speed, &downlink_speed);
+ mm_bearer_connect_result_set_uplink_speed (ctx->connect_result, uplink_speed);
+ mm_bearer_connect_result_set_downlink_speed (ctx->connect_result, downlink_speed);
+}
- ipv4_config = mm_bearer_ip_config_new ();
+static void
+ip_configuration_async_cleanup (GTask *task)
+{
+ ConnectContext *ctx;
- /* We assume that if we have an IP we can use static configuration.
- * Not all modems or providers will return DNS servers or even a
- * gateway, and not all modems support DHCP either. The IP management
- * daemon/script just has to deal with this...
- */
- if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && (ipv4addresscount > 0)) {
- g_autoptr(GInetAddress) addr = NULL;
- g_autofree gchar *str = NULL;
+ ctx = g_task_get_task_data (task);
- mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_STATIC);
+ g_assert (ctx->async_slaac_cancellation_id);
+ g_cancellable_disconnect (g_task_get_cancellable (task), ctx->async_slaac_cancellation_id);
+ ctx->async_slaac_cancellation_id = 0;
- /* IP address, pick the first one */
- addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4address[0]->ipv4_address, G_SOCKET_FAMILY_IPV4);
- str = g_inet_address_to_string (addr);
- mm_bearer_ip_config_set_address (ipv4_config, str);
- address_set = TRUE;
+ g_assert (ctx->async_slaac_notification_id);
+ if (g_signal_handler_is_connected (ctx->mbim, ctx->async_slaac_notification_id))
+ g_signal_handler_disconnect (ctx->mbim, ctx->async_slaac_notification_id);
+ ctx->async_slaac_notification_id = 0;
- /* Netmask */
- mm_bearer_ip_config_set_prefix (ipv4_config, ipv4address[0]->on_link_prefix_length);
+ g_assert (ctx->async_slaac_timeout_id);
+ g_source_remove (ctx->async_slaac_timeout_id);
+ ctx->async_slaac_timeout_id = 0;
+}
- /* Gateway */
- if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) {
- g_autoptr(GInetAddress) gw_addr = NULL;
- g_autofree gchar *gw_str = NULL;
+static gboolean
+ip_configuration_async_timeout (GTask *task)
+{
+ ip_configuration_async_cleanup (task);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_TIMEOUT,
+ "Timed out waiting for SLAAC notification");
+ g_object_unref (task);
- gw_addr = g_inet_address_new_from_bytes ((guint8 *)ipv4gateway, G_SOCKET_FAMILY_IPV4);
- gw_str = g_inet_address_to_string (gw_addr);
- mm_bearer_ip_config_set_gateway (ipv4_config, gw_str);
- }
- } else
- mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP);
+ return G_SOURCE_REMOVE;
+}
- /* DNS */
- if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && (ipv4dnsservercount > 0)) {
- g_auto(GStrv) strarr = NULL;
- guint i;
- guint n;
-
- strarr = g_new0 (gchar *, ipv4dnsservercount + 1);
- for (i = 0, n = 0; i < ipv4dnsservercount; i++) {
- g_autoptr(GInetAddress) addr = NULL;
-
- addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4dnsserver[i], G_SOCKET_FAMILY_IPV4);
- if (!g_inet_address_get_is_any (addr))
- strarr[n++] = g_inet_address_to_string (addr);
- }
- mm_bearer_ip_config_set_dns (ipv4_config, (const gchar **)strarr);
- }
+static void
+ip_configuration_async_cancelled (GTask *task)
+{
+ ip_configuration_async_cleanup (task);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED,
+ "Cancelled waiting for SLAAC notification");
+ g_object_unref (task);
+}
- /* MTU */
- if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
- mm_bearer_ip_config_set_mtu (ipv4_config, ipv4mtu);
-
- /* We requested IPv4, but it wasn't reported as activated. If there is no IP address
- * provided by the modem, we assume the IPv4 bearer wasn't truly activated */
- if (!address_set &&
- ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4 &&
- ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4V6 &&
- ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
- mm_obj_dbg (self, "IPv4 requested but no IPv4 activated and no IPv4 address set: ignoring");
- g_clear_object (&ipv4_config);
- }
- }
+static void
+ip_configuration_async_notification (GTask *task,
+ MbimMessage *notification,
+ MMPortMbim *port)
+{
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ MMBearerIpConfig *ipv6_config;
+ g_autoptr(GError) error = NULL;
+ guint32 session_id;
+ MbimIPConfigurationAvailableFlag ipv4configurationavailable;
+ MbimIPConfigurationAvailableFlag ipv6configurationavailable;
+ guint32 ipv4addresscount;
+ g_autoptr(MbimIPv4ElementArray) ipv4address = NULL;
+ guint32 ipv6addresscount;
+ g_autoptr(MbimIPv6ElementArray) ipv6address = NULL;
+ const MbimIPv4 *ipv4gateway;
+ const MbimIPv6 *ipv6gateway;
+ guint32 ipv4dnsservercount;
+ g_autofree MbimIPv4 *ipv4dnsserver = NULL;
+ guint32 ipv6dnsservercount;
+ g_autofree MbimIPv6 *ipv6dnsserver = NULL;
+ guint32 ipv4mtu;
+ guint32 ipv6mtu;
- /* Build IPv6 config */
- if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV6 ||
- ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
- ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
- gboolean address_set = FALSE;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- ipv6_config = mm_bearer_ip_config_new ();
+ /* Only process notifications if the device still exists */
+ if (!mm_port_mbim_peek_device (port))
+ return;
- if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && (ipv6addresscount > 0)) {
- g_autoptr(GInetAddress) addr = NULL;
- g_autofree gchar *str = NULL;
+ /* We only want the IP configuration updates */
+ if ((mbim_message_indicate_status_get_service (notification) != MBIM_SERVICE_BASIC_CONNECT) ||
+ (mbim_message_indicate_status_get_cid (notification) != MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION))
+ return;
- /* IP address, pick the first one */
- addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6address[0]->ipv6_address, G_SOCKET_FAMILY_IPV6);
- str = g_inet_address_to_string (addr);
- mm_bearer_ip_config_set_address (ipv6_config, str);
- address_set = TRUE;
-
- /* If the address is a link-local one, then SLAAC or DHCP must be used
- * to get the real prefix and address.
- * If the address is a global one, then the modem did SLAAC already and
- * there is no need to run host SLAAC.
- */
- if (g_inet_address_get_is_link_local (addr))
- mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
- else
- mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_STATIC);
-
- /* Netmask */
- mm_bearer_ip_config_set_prefix (ipv6_config, ipv6address[0]->on_link_prefix_length);
-
- /* If the modem has done SLAAC itself, it is never expected to return a /128 prefix,
- * warn if it happens and workaround it. Use /64 as default. */
- if ((mm_bearer_ip_config_get_method (ipv6_config) == MM_BEARER_IP_METHOD_STATIC) &&
- (mm_bearer_ip_config_get_prefix (ipv6_config) == 128)) {
- mm_obj_warn (self, "unexpected link prefix returned with global IPv6 address (128): ignoring");
- mm_bearer_ip_config_set_prefix (ipv6_config, 64);
- }
-
- /* Gateway */
- if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) {
- g_autoptr(GInetAddress) gw_addr = NULL;
- g_autofree gchar *gw_str = NULL;
-
- gw_addr = g_inet_address_new_from_bytes ((guint8 *)ipv6gateway, G_SOCKET_FAMILY_IPV6);
- gw_str = g_inet_address_to_string (gw_addr);
- mm_bearer_ip_config_set_gateway (ipv6_config, gw_str);
- }
- } else {
- /* If no address is given, this is likely a bug in the modem firmware, because even in the
- * case of needing to run host SLAAC, a link-local IPv6 address must be given. Either way,
- * go on requesting the need of host SLAAC, and let the network decide whether our SLAAC
- * Router Solicitation messages with an unexpected link-local address are accepted or not. */
- mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
- }
+ if (!mbim_message_ip_configuration_notification_parse (
+ notification,
+ &session_id,
+ &ipv4configurationavailable,
+ &ipv6configurationavailable,
+ &ipv4addresscount, &ipv4address,
+ &ipv6addresscount, &ipv6address,
+ &ipv4gateway, &ipv6gateway,
+ &ipv4dnsservercount, &ipv4dnsserver,
+ &ipv6dnsservercount, &ipv6dnsserver,
+ &ipv4mtu, &ipv6mtu,
+ &error)) {
+ mm_obj_warn (self, "couldn't parse IP configuration indication: %s", error->message);
+ return;
+ }
- if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && (ipv6dnsservercount > 0)) {
- g_auto(GStrv) strarr = NULL;
- guint i;
- guint n;
-
- /* DNS */
- strarr = g_new0 (gchar *, ipv6dnsservercount + 1);
- for (i = 0, n = 0; i < ipv6dnsservercount; i++) {
- g_autoptr(GInetAddress) addr = NULL;
-
- addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6dnsserver[i], G_SOCKET_FAMILY_IPV6);
- if (!g_inet_address_get_is_any (addr))
- strarr[n++] = g_inet_address_to_string (addr);
- }
- mm_bearer_ip_config_set_dns (ipv6_config, (const gchar **)strarr);
- }
+ /* We only want the updates for the current session ongoing */
+ if (ctx->session_id != session_id)
+ return;
- /* MTU */
- if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
- mm_bearer_ip_config_set_mtu (ipv6_config, ipv6mtu);
-
- /* We requested IPv6, but it wasn't reported as activated. If there is no IPv6 address
- * provided by the modem, we assume the IPv6 bearer wasn't truly activated */
- if (!address_set &&
- ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV6 &&
- ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4V6 &&
- ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
- mm_obj_dbg (self, "IPv6 requested but no IPv6 activated and no IPv6 address set: ignoring");
- g_clear_object (&ipv6_config);
- }
- }
+ /* Process content in a common way */
+ process_ip_configuration (self,
+ ctx,
+ ipv4configurationavailable,
+ ipv6configurationavailable,
+ ipv4addresscount, ipv4address,
+ ipv6addresscount, ipv6address,
+ ipv4gateway, ipv6gateway,
+ ipv4dnsservercount, ipv4dnsserver,
+ ipv6dnsservercount, ipv6dnsserver,
+ ipv4mtu, ipv6mtu);
+
+ /* We will be done once method is STATIC (i.e. when a global IPv6 is detected) */
+ ipv6_config = mm_bearer_connect_result_peek_ipv6_config (ctx->connect_result);
+ if (!ipv6_config || (mm_bearer_ip_config_get_method (ipv6_config) != MM_BEARER_IP_METHOD_STATIC)) {
+ mm_obj_dbg (self, "SLAAC process didn't finish yet");
+ return;
+ }
+
+ mm_obj_dbg (self, "SLAAC process finished");
+ ip_configuration_async_cleanup (task);
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static gboolean
+ip_configuration_async_setup (GTask *task)
+{
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ MMBearerIpConfig *ipv6_config;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* On async SLAAC results if not supported by modem */
+ if (!self->priv->async_slaac)
+ return FALSE;
+
+ /* Only expect SLAAC results if the attempt required IPv6 and if we didn't already
+ * get a global IPv6 address reported. */
+ ipv6_config = mm_bearer_connect_result_peek_ipv6_config (ctx->connect_result);
+ if (!ipv6_config || (mm_bearer_ip_config_get_method (ipv6_config) != MM_BEARER_IP_METHOD_DHCP))
+ return FALSE;
+
+ mm_obj_info (self, "connection attempt requires async updated with SLAAC results");
+
+ /* The task implementing the connection attempt is now shared between the timeout, the notification
+ * signal handler and the cancellation signal handler. Whichever decides to complete the task must
+ * cancel the others right away. */
+ ctx->async_slaac_cancellation_id = g_cancellable_connect (g_task_get_cancellable (task),
+ G_CALLBACK (ip_configuration_async_cancelled),
+ task,
+ NULL);
+ ctx->async_slaac_timeout_id = g_timeout_add_seconds (WAIT_IP_CONFIGURATION_ASYNC_TIMEOUT_S,
+ (GSourceFunc) ip_configuration_async_timeout,
+ task);
+ ctx->async_slaac_notification_id = g_signal_connect_swapped (ctx->mbim,
+ MM_PORT_MBIM_SIGNAL_NOTIFICATION,
+ G_CALLBACK (ip_configuration_async_notification),
+ task);
+ return TRUE;
+}
- /* Store result */
- ctx->connect_result = mm_bearer_connect_result_new (ctx->link ? ctx->link : ctx->data,
- ipv4_config,
- ipv6_config);
- mm_bearer_connect_result_set_multiplexed (ctx->connect_result, !!ctx->link);
+static void
+ip_configuration_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+ MbimIPConfigurationAvailableFlag ipv4configurationavailable;
+ MbimIPConfigurationAvailableFlag ipv6configurationavailable;
+ guint32 ipv4addresscount;
+ g_autoptr(MbimIPv4ElementArray) ipv4address = NULL;
+ guint32 ipv6addresscount;
+ g_autoptr(MbimIPv6ElementArray) ipv6address = NULL;
+ const MbimIPv4 *ipv4gateway;
+ const MbimIPv6 *ipv6gateway;
+ guint32 ipv4dnsservercount;
+ g_autofree MbimIPv4 *ipv4dnsserver = NULL;
+ guint32 ipv6dnsservercount;
+ g_autofree MbimIPv6 *ipv6dnsserver = NULL;
+ guint32 ipv4mtu;
+ guint32 ipv6mtu;
- if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)
- mm_bearer_connect_result_set_profile_id (ctx->connect_result, ctx->profile_id);
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- /* Propagate speeds from modem object */
- mm_broadband_modem_mbim_get_speeds (ctx->modem, &uplink_speed, &downlink_speed);
- mm_bearer_connect_result_set_uplink_speed (ctx->connect_result, uplink_speed);
- mm_bearer_connect_result_set_downlink_speed (ctx->connect_result, downlink_speed);
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
+ mbim_message_ip_configuration_response_parse (
+ response,
+ NULL, /* sessionid */
+ &ipv4configurationavailable,
+ &ipv6configurationavailable,
+ &ipv4addresscount, &ipv4address,
+ &ipv6addresscount, &ipv6address,
+ &ipv4gateway, &ipv6gateway,
+ &ipv4dnsservercount, &ipv4dnsserver,
+ &ipv6dnsservercount, &ipv6dnsserver,
+ &ipv4mtu, &ipv6mtu,
+ &error)) {
+ /* Process content in a common way */
+ process_ip_configuration (self,
+ ctx,
+ ipv4configurationavailable,
+ ipv6configurationavailable,
+ ipv4addresscount, ipv4address,
+ ipv6addresscount, ipv6address,
+ ipv4gateway, ipv6gateway,
+ ipv4dnsservercount, ipv4dnsserver,
+ ipv6dnsservercount, ipv6dnsserver,
+ ipv4mtu, ipv6mtu);
}
if (error) {
@@ -1181,6 +1398,17 @@ connect_context_step (GTask *task)
task);
return;
+ case CONNECT_STEP_IP_CONFIGURATION_ASYNC:
+ /* At this point, we must have IP settings ready. Now, if we know the modem
+ * supports modem-SLAAC and we're returning IPv6 settings, we must make sure
+ * we're returning already a global IPv6 address and method STATIC, or otherwise
+ * we need to wait for indications to happen. */
+ if (ip_configuration_async_setup (task))
+ return;
+
+ ctx->step++;
+ /* fall through */
+
case CONNECT_STEP_LAST:
/* Cleanup the abort message so that we don't
* run it */
@@ -1220,7 +1448,7 @@ connect_context_step (GTask *task)
static gboolean
load_settings_from_bearer (MMBearerMbim *self,
ConnectContext *ctx,
- MMBearerProperties *properties,
+ MMBearerProperties *props,
GError **error)
{
MMBearerMultiplexSupport multiplex;
@@ -1241,7 +1469,7 @@ load_settings_from_bearer (MMBearerMbim *self,
multiplex_supported = FALSE;
/* If no multiplex setting given by the user, assume none */
- multiplex = mm_bearer_properties_get_multiplex (properties);
+ multiplex = mm_bearer_properties_get_multiplex (props);
if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN) {
if (mm_context_get_test_multiplex_requested ())
multiplex = MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED;
@@ -1279,14 +1507,14 @@ load_settings_from_bearer (MMBearerMbim *self,
/* 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);
+ ctx->profile_id = mm_bearer_properties_get_profile_id (props);
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);
+ ip_type = mm_bearer_properties_get_ip_type (props);
mm_3gpp_normalize_ip_family (&ip_type, FALSE);
ctx->requested_ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_type, &inner_error);
if (inner_error) {
@@ -1301,7 +1529,7 @@ load_settings_from_bearer (MMBearerMbim *self,
* (TYPE_DEFAULT) by default, which is what we've done until now. */
if (!load_settings_from_profile (self,
ctx,
- mm_bearer_properties_peek_3gpp_profile (properties),
+ mm_bearer_properties_peek_3gpp_profile (props),
MM_BEARER_APN_TYPE_DEFAULT,
error))
return FALSE;
@@ -1328,7 +1556,7 @@ _connect (MMBaseBearer *self,
GTask *task;
GError *error = NULL;
g_autoptr(MMBaseModem) modem = NULL;
- g_autoptr(MMBearerProperties) properties = NULL;
+ g_autoptr(MMBearerProperties) props = NULL;
if (!peek_ports (self, &mbim, &data, callback, user_data))
return;
@@ -1337,7 +1565,7 @@ _connect (MMBaseBearer *self,
g_object_get (self,
MM_BASE_BEARER_MODEM, &modem,
- MM_BASE_BEARER_CONFIG, &properties,
+ MM_BASE_BEARER_CONFIG, &props,
NULL);
g_assert (modem);
@@ -1350,7 +1578,7 @@ _connect (MMBaseBearer *self,
ctx->activated_ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free);
- if (!load_settings_from_bearer (MM_BEARER_MBIM (self), ctx, properties, &error)) {
+ if (!load_settings_from_bearer (MM_BEARER_MBIM (self), ctx, props, &error)) {
g_prefix_error (&error, "Invalid bearer properties: ");
g_task_return_error (task, error);
g_object_unref (task);
@@ -1797,6 +2025,42 @@ mm_bearer_mbim_init (MMBearerMbim *self)
}
static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMBearerMbim *self = MM_BEARER_MBIM (object);
+
+ switch (prop_id) {
+ case PROP_ASYNC_SLAAC:
+ self->priv->async_slaac = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMBearerMbim *self = MM_BEARER_MBIM (object);
+
+ switch (prop_id) {
+ case PROP_ASYNC_SLAAC:
+ g_value_set_boolean (value, self->priv->async_slaac);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
dispose (GObject *object)
{
MMBearerMbim *self = MM_BEARER_MBIM (object);
@@ -1815,6 +2079,8 @@ mm_bearer_mbim_class_init (MMBearerMbimClass *klass)
g_type_class_add_private (object_class, sizeof (MMBearerMbimPrivate));
/* Virtual methods */
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
object_class->dispose = dispose;
base_bearer_class->connect = _connect;
@@ -1830,4 +2096,12 @@ mm_bearer_mbim_class_init (MMBearerMbimClass *klass)
base_bearer_class->reload_connection_status = reload_connection_status;
base_bearer_class->reload_connection_status_finish = reload_connection_status_finish;
#endif
+
+ properties[PROP_ASYNC_SLAAC] =
+ g_param_spec_boolean (MM_BEARER_MBIM_ASYNC_SLAAC,
+ "Async SLAAC",
+ "Whether async SLAAC updates are expected.",
+ FALSE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_ASYNC_SLAAC, properties[PROP_ASYNC_SLAAC]);
}
diff --git a/src/mm-bearer-mbim.h b/src/mm-bearer-mbim.h
index a1231989..d1867225 100644
--- a/src/mm-bearer-mbim.h
+++ b/src/mm-bearer-mbim.h
@@ -32,6 +32,8 @@
#define MM_IS_BEARER_MBIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_MBIM))
#define MM_BEARER_MBIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_MBIM, MMBearerMbimClass))
+#define MM_BEARER_MBIM_ASYNC_SLAAC "bearer-mbim-async-slaac"
+
typedef struct _MMBearerMbim MMBearerMbim;
typedef struct _MMBearerMbimClass MMBearerMbimClass;
typedef struct _MMBearerMbimPrivate MMBearerMbimPrivate;