aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/mmcli-bearer.c72
-rw-r--r--include/ModemManager-enums.h8
-rw-r--r--introspection/org.freedesktop.ModemManager1.Bearer.xml13
-rw-r--r--libmm-glib/mm-bearer-ip-config.c5
-rw-r--r--plugins/Makefile.am33
-rw-r--r--plugins/icera/mm-broadband-bearer-icera.c188
-rw-r--r--plugins/icera/mm-modem-helpers-icera.c277
-rw-r--r--plugins/icera/mm-modem-helpers-icera.h28
-rw-r--r--plugins/icera/tests/test-modem-helpers-icera.c191
-rw-r--r--plugins/mbm/mm-broadband-bearer-mbm.c161
-rw-r--r--plugins/mbm/mm-modem-helpers-mbm.c166
-rw-r--r--plugins/mbm/mm-modem-helpers-mbm.h27
-rw-r--r--plugins/mbm/tests/test-modem-helpers-mbm.c162
-rw-r--r--plugins/option/mm-broadband-bearer-hso.c1
-rw-r--r--plugins/sierra/mm-broadband-bearer-sierra.c92
-rw-r--r--plugins/sierra/mm-broadband-bearer-sierra.h5
-rw-r--r--plugins/sierra/mm-broadband-modem-sierra-icera.c3
-rw-r--r--plugins/sierra/mm-broadband-modem-sierra.c3
-rw-r--r--src/mm-bearer-mbim.c8
-rw-r--r--src/mm-bearer-qmi.c306
-rw-r--r--src/mm-broadband-bearer.c30
-rw-r--r--src/mm-broadband-bearer.h1
22 files changed, 1478 insertions, 302 deletions
diff --git a/cli/mmcli-bearer.c b/cli/mmcli-bearer.c
index c54ac54c..5829e60a 100644
--- a/cli/mmcli-bearer.c
+++ b/cli/mmcli-bearer.c
@@ -189,27 +189,29 @@ print_bearer_info (MMBearer *bearer)
(ipv4_config ?
mm_bearer_ip_method_get_string (mm_bearer_ip_config_get_method (ipv4_config)) :
"none"));
- if (ipv4_config) {
- guint mtu;
-
- if (mm_bearer_ip_config_get_method (ipv4_config) == MM_BEARER_IP_METHOD_STATIC) {
- const gchar **dns;
- guint i;
-
- dns = mm_bearer_ip_config_get_dns (ipv4_config);
- g_print (" | address: '%s'\n"
- " | prefix: '%u'\n"
- " | gateway: '%s'\n"
- " | DNS: '%s'",
- VALIDATE_UNKNOWN (mm_bearer_ip_config_get_address (ipv4_config)),
- mm_bearer_ip_config_get_prefix (ipv4_config),
- VALIDATE_UNKNOWN (mm_bearer_ip_config_get_gateway (ipv4_config)),
- VALIDATE_UNKNOWN (dns[0]));
+ if (ipv4_config &&
+ mm_bearer_ip_config_get_method (ipv4_config) != MM_BEARER_IP_METHOD_UNKNOWN) {
+ const gchar **dns = mm_bearer_ip_config_get_dns (ipv4_config);
+ guint i, mtu;
+
+ g_print (" | address: '%s'\n"
+ " | prefix: '%u'\n"
+ " | gateway: '%s'\n",
+ VALIDATE_UNKNOWN (mm_bearer_ip_config_get_address (ipv4_config)),
+ mm_bearer_ip_config_get_prefix (ipv4_config),
+ VALIDATE_UNKNOWN (mm_bearer_ip_config_get_gateway (ipv4_config)));
+
+ if (dns && dns[0]) {
+ g_print (
+ " | DNS: '%s'", dns[0]);
/* Additional DNS addresses */
for (i = 1; dns[i]; i++)
g_print (", '%s'", dns[i]);
- g_print ("\n");
+ } else {
+ g_print (
+ " | DNS: none");
}
+ g_print ("\n");
mtu = mm_bearer_ip_config_get_mtu (ipv4_config);
if (mtu)
@@ -222,27 +224,29 @@ print_bearer_info (MMBearer *bearer)
(ipv6_config ?
mm_bearer_ip_method_get_string (mm_bearer_ip_config_get_method (ipv6_config)) :
"none"));
- if (ipv6_config) {
- guint mtu;
-
- if (mm_bearer_ip_config_get_method (ipv6_config) == MM_BEARER_IP_METHOD_STATIC) {
- const gchar **dns;
- guint i;
-
- dns = mm_bearer_ip_config_get_dns (ipv6_config);
- g_print (" | address: '%s'\n"
- " | prefix: '%u'\n"
- " | gateway: '%s'\n"
- " | DNS: '%s'",
- VALIDATE_UNKNOWN(mm_bearer_ip_config_get_address (ipv6_config)),
- mm_bearer_ip_config_get_prefix (ipv6_config),
- VALIDATE_UNKNOWN(mm_bearer_ip_config_get_gateway (ipv6_config)),
- VALIDATE_UNKNOWN(dns[0]));
+ if (ipv6_config &&
+ mm_bearer_ip_config_get_method (ipv6_config) != MM_BEARER_IP_METHOD_UNKNOWN) {
+ const gchar **dns = mm_bearer_ip_config_get_dns (ipv6_config);
+ guint i, mtu;
+
+ g_print (" | address: '%s'\n"
+ " | prefix: '%u'\n"
+ " | gateway: '%s'\n",
+ VALIDATE_UNKNOWN(mm_bearer_ip_config_get_address (ipv6_config)),
+ mm_bearer_ip_config_get_prefix (ipv6_config),
+ VALIDATE_UNKNOWN(mm_bearer_ip_config_get_gateway (ipv6_config)));
+
+ if (dns && dns[0]) {
+ g_print (
+ " | DNS: '%s'", dns[0]);
/* Additional DNS addresses */
for (i = 1; dns[i]; i++)
g_print (", '%s'", dns[i]);
- g_print ("\n");
+ } else {
+ g_print (
+ " | DNS: none");
}
+ g_print ("\n");
mtu = mm_bearer_ip_config_get_mtu (ipv6_config);
if (mtu)
diff --git a/include/ModemManager-enums.h b/include/ModemManager-enums.h
index ee09ddc0..5ff799f0 100644
--- a/include/ModemManager-enums.h
+++ b/include/ModemManager-enums.h
@@ -849,8 +849,12 @@ typedef enum { /*< underscore_name=mm_modem_contacts_storage >*/
* MMBearerIpMethod:
* @MM_BEARER_IP_METHOD_UNKNOWN: Unknown method.
* @MM_BEARER_IP_METHOD_PPP: Use PPP to get the address.
- * @MM_BEARER_IP_METHOD_STATIC: Use the provided static IP configuration given by the modem to configure the IP data interface.
- * @MM_BEARER_IP_METHOD_DHCP: Begin DHCP on the data interface to obtain necessary IP configuration details.
+ * @MM_BEARER_IP_METHOD_STATIC: Use the provided static IP configuration given
+ * by the modem to configure the IP data interface.
+ * @MM_BEARER_IP_METHOD_DHCP: Begin DHCP or IPv6 SLAAC on the data interface to
+ * obtain necessary IP configuration details. For IPv4 bearers DHCP should
+ * be used. For IPv6 bearers SLAAC should be used to determine the prefix and
+ * any additional details.
*
* Type of IP method configuration to be used in a given Bearer.
*/
diff --git a/introspection/org.freedesktop.ModemManager1.Bearer.xml b/introspection/org.freedesktop.ModemManager1.Bearer.xml
index 1d2f287b..e1463a56 100644
--- a/introspection/org.freedesktop.ModemManager1.Bearer.xml
+++ b/introspection/org.freedesktop.ModemManager1.Bearer.xml
@@ -186,10 +186,15 @@
</varlistentry>
</variablelist>
- If the bearer specifies configuration via PPP or DHCP, only the
- <literal>"method"</literal> item will be present.
-
- Additional items which are only applicable when using the
+ If the bearer specifies configuration via PPP or DHCP, often only the
+ <literal>"method"</literal> item will be present. IPv6 SLAAC should
+ be used to retrieve correct addressing and DNS information via Router
+ Advertisements and DHCPv6. In some cases an IPv6 Link-Local
+ <literal>"address"</literal> item will be present, which should be
+ assigned to the data port before performing SLAAC, as the mobile network
+ may expect SLAAC setup to use this address.
+
+ Additional items which are usually only applicable when using the
<link linkend="MM-BEARER-IP-METHOD-STATIC:CAPS">MM_BEARER_IP_METHOD_STATIC</link>
method are:
<variablelist>
diff --git a/libmm-glib/mm-bearer-ip-config.c b/libmm-glib/mm-bearer-ip-config.c
index 7ffd7bab..8be59ac6 100644
--- a/libmm-glib/mm-bearer-ip-config.c
+++ b/libmm-glib/mm-bearer-ip-config.c
@@ -234,10 +234,7 @@ mm_bearer_ip_config_get_dictionary (MMBearerIpConfig *self)
g_variant_new_uint32 (self ?
self->priv->method :
MM_BEARER_IP_METHOD_UNKNOWN));
-
- /* If static IP method, report remaining configuration */
- if (self &&
- self->priv->method == MM_BEARER_IP_METHOD_STATIC) {
+ if (self) {
if (self->priv->address)
g_variant_builder_add (&builder,
"{sv}",
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 31cc50ed..a45b70d7 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -1,3 +1,4 @@
+
include $(top_srcdir)/gtester.make
# Common CPPFLAGS and LDFLAGS
@@ -90,13 +91,28 @@ libmm_utils_icera_la_SOURCES = \
icera/mm-broadband-modem-icera.h \
icera/mm-broadband-modem-icera.c \
icera/mm-broadband-bearer-icera.h \
- icera/mm-broadband-bearer-icera.c
+ icera/mm-broadband-bearer-icera.c \
+ icera/mm-modem-helpers-icera.c \
+ icera/mm-modem-helpers-icera.h
libmm_utils_icera_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
libmm_utils_icera_la_LIBADD = $(GUDEV_LIBS) $(MM_LIBS)
ICERA_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/icera
ICERA_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-icera.la
+noinst_PROGRAMS += test-modem-helpers-icera
+test_modem_helpers_icera_SOURCES = \
+ icera/mm-modem-helpers-icera.c \
+ icera/mm-modem-helpers-icera.h \
+ icera/tests/test-modem-helpers-icera.c
+test_modem_helpers_icera_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/icera \
+ $(PLUGIN_COMMON_COMPILER_FLAGS)
+test_modem_helpers_icera_LDADD = \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(top_builddir)/src/libmodem-helpers.la
+test_modem_helpers_icera_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
########################################
pkglib_LTLIBRARIES = \
@@ -197,12 +213,27 @@ libmm_plugin_mbm_la_SOURCES = \
mbm/mm-broadband-modem-mbm.h \
mbm/mm-broadband-bearer-mbm.c \
mbm/mm-broadband-bearer-mbm.h \
+ mbm/mm-modem-helpers-mbm.c \
+ mbm/mm-modem-helpers-mbm.h \
mbm/mm-sim-mbm.c \
mbm/mm-sim-mbm.h
libmm_plugin_mbm_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
libmm_plugin_mbm_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
udevrules_DATA += mbm/77-mm-ericsson-mbm.rules
+noinst_PROGRAMS += test-modem-helpers-mbm
+test_modem_helpers_mbm_SOURCES = \
+ mbm/mm-modem-helpers-mbm.c \
+ mbm/mm-modem-helpers-mbm.h \
+ mbm/tests/test-modem-helpers-mbm.c
+test_modem_helpers_mbm_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/mbm \
+ $(PLUGIN_COMMON_COMPILER_FLAGS)
+test_modem_helpers_mbm_LDADD = \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(top_builddir)/src/libmodem-helpers.la
+test_modem_helpers_mbm_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
# Option
libmm_plugin_option_la_SOURCES = \
option/mm-plugin-option.c \
diff --git a/plugins/icera/mm-broadband-bearer-icera.c b/plugins/icera/mm-broadband-bearer-icera.c
index 4507a611..347f373f 100644
--- a/plugins/icera/mm-broadband-bearer-icera.c
+++ b/plugins/icera/mm-broadband-bearer-icera.c
@@ -33,6 +33,7 @@
#include "mm-modem-helpers.h"
#include "mm-error-helpers.h"
#include "mm-daemon-enums-types.h"
+#include "mm-modem-helpers-icera.h"
G_DEFINE_TYPE (MMBroadbandBearerIcera, mm_broadband_bearer_icera, MM_TYPE_BROADBAND_BEARER);
@@ -109,157 +110,72 @@ get_ip_config_3gpp_finish (MMBroadbandBearer *self,
MMBearerIpConfig **ipv6_config,
GError **error)
{
- MMBearerIpConfig *ip_config;
+ MMBearerConnectResult *configs;
+ MMBearerIpConfig *ipv4, *ipv6;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return FALSE;
- /* No IPv6 for now */
- ip_config = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- *ipv4_config = g_object_ref (ip_config);
- *ipv6_config = NULL;
+ configs = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ g_assert (configs);
+
+ ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs);
+ ipv6 = mm_bearer_connect_result_peek_ipv6_config (configs);
+ g_assert (ipv4 || ipv6);
+ if (ipv4_config && ipv4)
+ *ipv4_config = g_object_ref (ipv4);
+ if (ipv6_config && ipv6)
+ *ipv6_config = g_object_ref (ipv6);
+
return TRUE;
}
-#define IPDPADDR_TAG "%IPDPADDR: "
-
static void
ip_config_ready (MMBaseModem *modem,
GAsyncResult *res,
GetIpConfig3gppContext *ctx)
{
- MMBearerIpConfig *ip_config = NULL;
+ MMBearerIpConfig *ipv4_config = NULL;
+ MMBearerIpConfig *ipv6_config = NULL;
const gchar *response;
GError *error = NULL;
- gchar **items;
- gchar *dns[3] = { 0 };
- guint i;
- guint dns_i;
+ MMBearerConnectResult *connect_result;
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
g_simple_async_result_take_error (ctx->result, error);
- get_ip_config_context_complete_and_free (ctx);
- return;
+ goto out;
}
- /* TODO: use a regex to parse this */
+ if (!mm_icera_parse_ipdpaddr_response (response,
+ ctx->cid,
+ &ipv4_config,
+ &ipv6_config,
+ &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ goto out;
+ }
- /* Check result */
- if (!g_str_has_prefix (response, IPDPADDR_TAG)) {
+ if (!ipv4_config && !ipv6_config) {
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
- "Couldn't get IP config: invalid response '%s'",
+ "Couldn't get IP config: couldn't parse response '%s'",
response);
- get_ip_config_context_complete_and_free (ctx);
- return;
+ goto out;
}
- /* %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>[,<nbns1>,<nbns2>[,<??>,<netmask>,<gw>]]
- *
- * Sierra USB305: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0
- * K3805-Z: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,
- */
- response = mm_strip_tag (response, IPDPADDR_TAG);
- items = g_strsplit (response, ", ", 0);
-
- ip_config = mm_bearer_ip_config_new ();
-
- for (i = 0, dns_i = 0; items[i]; i++) {
- if (i == 0) { /* CID */
- gint num;
-
- if (!mm_get_int_from_str (items[i], &num) ||
- num != ctx->cid) {
- error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unknown CID in IPDPADDR response ("
- "got %d, expected %d)",
- (guint) num,
- ctx->cid);
- break;
- }
- } else if (i == 1) { /* IP address */
- guint32 tmp = 0;
-
- if (!inet_pton (AF_INET, items[i], &tmp)) {
- mm_warn ("Couldn't parse IP address '%s'", items[i]);
- g_clear_object (&ip_config);
- break;
- }
-
- mm_bearer_ip_config_set_method (ip_config, MM_BEARER_IP_METHOD_STATIC);
- mm_bearer_ip_config_set_address (ip_config, items[i]);
- } else if (i == 2) { /* Gateway */
- guint32 tmp = 0;
-
- if (!inet_pton (AF_INET, items[i], &tmp)) {
- mm_warn ("Couldn't parse gateway address '%s'", items[i]);
- g_clear_object (&ip_config);
- break;
- }
-
- if (tmp)
- mm_bearer_ip_config_set_gateway (ip_config, items[i]);
- } else if (i == 3 || i == 4) { /* DNS entries */
- guint32 tmp = 0;
-
- if (!inet_pton (AF_INET, items[i], &tmp)) {
- mm_warn ("Couldn't parse DNS address '%s'", items[i]);
- g_clear_object (&ip_config);
- break;
- }
-
- if (tmp)
- dns[dns_i++] = items[i];
- } else if (i == 8) { /* Netmask */
- guint32 tmp = 0;
-
- if (!inet_pton (AF_INET, items[i], &tmp)) {
- mm_warn ("Couldn't parse netmask '%s'", items[i]);
- g_clear_object (&ip_config);
- break;
- }
-
- mm_bearer_ip_config_set_prefix (ip_config, mm_netmask_to_cidr (items[i]));
- } else if (i == 9) { /* Duplicate Gateway */
- if (!mm_bearer_ip_config_get_gateway (ip_config)) {
- guint32 tmp = 0;
-
- if (!inet_pton (AF_INET, items[i], &tmp)) {
- mm_warn ("Couldn't parse (duplicate) gateway address '%s'", items[i]);
- g_clear_object (&ip_config);
- break;
- }
-
- if (tmp)
- mm_bearer_ip_config_set_gateway (ip_config, items[i]);
- }
- }
- }
-
- if (!ip_config) {
- if (error)
- g_simple_async_result_take_error (ctx->result, error);
- else
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get IP config: couldn't parse response '%s'",
- response);
- } else {
- /* If we got DNS entries, set them in the IP config */
- if (dns[0])
- mm_bearer_ip_config_set_dns (ip_config, (const gchar **)dns);
-
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- ip_config,
- (GDestroyNotify)g_object_unref);
- }
+ connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary),
+ ipv4_config,
+ ipv6_config);
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ connect_result,
+ (GDestroyNotify)mm_bearer_connect_result_unref);
+out:
+ g_clear_object (&ipv4_config);
+ g_clear_object (&ipv6_config);
get_ip_config_context_complete_and_free (ctx);
- g_strfreev (items);
}
static void
@@ -269,6 +185,7 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
+ MMBearerIpFamily ip_family,
GAsyncReadyCallback callback,
gpointer user_data)
{
@@ -284,7 +201,7 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
if (ctx->self->priv->default_ip_method == MM_BEARER_IP_METHOD_STATIC) {
gchar *command;
- command = g_strdup_printf ("%%IPDPADDR=%d", cid);
+ command = g_strdup_printf ("%%IPDPADDR=%u", cid);
mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
primary,
command,
@@ -300,13 +217,28 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
/* Otherwise, DHCP */
if (ctx->self->priv->default_ip_method == MM_BEARER_IP_METHOD_DHCP) {
- MMBearerIpConfig *ip_config;
+ MMBearerConnectResult *connect_result;
+ MMBearerIpConfig *ipv4_config, *ipv6_config;
+
+ if (ip_family & MM_BEARER_IP_FAMILY_IPV4 || ip_family & MM_BEARER_IP_FAMILY_IPV4V6) {
+ ipv4_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP);
+ }
+ if (ip_family & MM_BEARER_IP_FAMILY_IPV6 || ip_family & MM_BEARER_IP_FAMILY_IPV4V6) {
+ ipv6_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
+ }
+ g_assert (ipv4_config || ipv6_config);
+
+ connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary),
+ ipv4_config,
+ ipv6_config);
+ g_clear_object (&ipv4_config);
+ g_clear_object (&ipv6_config);
- ip_config = mm_bearer_ip_config_new ();
- mm_bearer_ip_config_set_method (ip_config, MM_BEARER_IP_METHOD_DHCP);
g_simple_async_result_set_op_res_gpointer (ctx->result,
- ip_config,
- (GDestroyNotify)g_object_unref);
+ connect_result,
+ (GDestroyNotify)mm_bearer_connect_result_unref);
get_ip_config_context_complete_and_free (ctx);
return;
}
diff --git a/plugins/icera/mm-modem-helpers-icera.c b/plugins/icera/mm-modem-helpers-icera.c
new file mode 100644
index 00000000..fd3e4fc7
--- /dev/null
+++ b/plugins/icera/mm-modem-helpers-icera.c
@@ -0,0 +1,277 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2012 - 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2014 Dan Williams <dcbw@redhat.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-icera.h"
+
+/*****************************************************************************/
+/* %IPDPADDR response parser */
+
+static MMBearerIpConfig *
+parse_ipdpaddr_v4 (const gchar **items, guint num_items, GError **error)
+{
+ MMBearerIpConfig *config;
+ const gchar *dns[3] = { 0 };
+ guint dns_i = 0, tmp;
+ const gchar *netmask = NULL;
+
+ /* IP address and prefix */
+ tmp = 0;
+ if (!inet_pton (AF_INET, items[1], &tmp)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse IPv4 address '%s'", items[1]);
+ return NULL;
+ }
+
+ if (!tmp) {
+ /* No IPv4 config */
+ return NULL;
+ }
+
+ config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
+ mm_bearer_ip_config_set_address (config, items[1]);
+ mm_bearer_ip_config_set_prefix (config, 32); /* default prefix */
+
+ /* Gateway */
+ tmp = 0;
+ if (inet_pton (AF_INET, items[2], &tmp)) {
+ if (tmp)
+ mm_bearer_ip_config_set_gateway (config, items[2]);
+ } else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse gateway address '%s'", items[2]);
+ goto error;
+ }
+
+ /* DNS */
+ tmp = 0;
+ if (inet_pton (AF_INET, items[3], &tmp) && tmp)
+ dns[dns_i++] = items[3];
+ else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse DNS address '%s'", items[3]);
+ goto error;
+ }
+
+ /* DNS2 - sometimes missing and set to 0.0.0.0 */
+ tmp = 0;
+ if (inet_pton (AF_INET, items[4], &tmp) && tmp)
+ dns[dns_i++] = items[4];
+ if (dns_i > 0)
+ mm_bearer_ip_config_set_dns (config, (const gchar **) dns);
+
+ /* Short form (eg, Sierra USB305) */
+ if (num_items < 9)
+ return config;
+
+ /* Devices return netmask and secondary gateway in one of two
+ * positions. The netmask may be either at index 7 or 8, while
+ * the secondary gateway may be at position 8 or 9.
+ */
+
+ if (items[7] && strstr (items[7], "255.") && !strstr (items[7], "255.0.0.0"))
+ netmask = items[7];
+ if (items[8] && strstr (items[8], "255.") && !strstr (items[8], "255.0.0.0"))
+ netmask = items[8];
+ if (netmask) {
+ if (!inet_pton (AF_INET, netmask, &tmp)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse netmask '%s'",
+ netmask);
+ goto error;
+ }
+ mm_bearer_ip_config_set_prefix (config, mm_netmask_to_cidr (netmask));
+ }
+
+ /* Secondary gateway */
+ if (!mm_bearer_ip_config_get_gateway (config)) {
+ const char *gw2 = NULL;
+
+ if (num_items >= 10 && items[9] && !strstr (items[9], "255.") && !strstr (items[9], "::"))
+ gw2 = items[9];
+ /* Prefer position 8 */
+ if (items[8] && !strstr (items[8], "255."))
+ gw2 = items[8];
+
+ if (gw2 && inet_pton (AF_INET, gw2, &tmp) && tmp)
+ mm_bearer_ip_config_set_gateway (config, gw2);
+ else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse secondary gateway address '%s'",
+ gw2 ? gw2 : "(unknown)");
+ goto error;
+ }
+ }
+
+ return config;
+
+error:
+ g_object_unref (config);
+ return NULL;
+}
+
+static MMBearerIpConfig *
+parse_ipdpaddr_v6 (const gchar **items, guint num_items, GError **error)
+{
+ MMBearerIpConfig *config;
+ const gchar *dns[2] = { 0 };
+ struct in6_addr tmp6 = IN6ADDR_ANY_INIT;
+
+ if (num_items < 12)
+ return NULL;
+
+ /* It appears that for IPv6 %IPDPADDR returns only the expected
+ * link-local address and a DNS address, and that to retrieve the
+ * default router, extra DNS, and search domains, the host must listen
+ * for IPv6 Router Advertisements on the net port.
+ */
+
+ config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
+
+ /* IP address and prefix */
+ if (inet_pton (AF_INET6, items[9], &tmp6) != 1 ||
+ IN6_IS_ADDR_UNSPECIFIED (&tmp6)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse IPv6 address '%s'", items[9]);
+ goto error;
+ }
+ mm_bearer_ip_config_set_address (config, items[9]);
+ mm_bearer_ip_config_set_prefix (config, 64);
+
+ /* If the address is a link-local one, then SLAAC or DHCP must be used
+ * to get the real prefix and address. Change the method to DHCP to
+ * indicate this to clients.
+ */
+ if (IN6_IS_ADDR_LINKLOCAL (&tmp6))
+ mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
+
+ /* DNS server */
+ memset (&tmp6, 0, sizeof (tmp6));
+ if (inet_pton (AF_INET6, items[11], &tmp6) == 1 &&
+ !IN6_IS_ADDR_UNSPECIFIED (&tmp6)) {
+ dns[0] = items[11];
+ dns[1] = NULL;
+ mm_bearer_ip_config_set_dns (config, (const gchar **) dns);
+ } else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse DNS address '%s'", items[11]);
+ goto error;
+ }
+
+ return config;
+
+error:
+ g_object_unref (config);
+ return NULL;
+}
+
+#define IPDPADDR_TAG "%IPDPADDR: "
+
+gboolean
+mm_icera_parse_ipdpaddr_response (const gchar *response,
+ guint expected_cid,
+ MMBearerIpConfig **out_ip4_config,
+ MMBearerIpConfig **out_ip6_config,
+ GError **error)
+{
+ MMBearerIpConfig *ip4_config = NULL;
+ MMBearerIpConfig *ip6_config = NULL;
+ GError *local = NULL;
+ gboolean success;
+ char **items;
+ guint num_items, i, first_free;
+ gint num;
+
+ g_return_val_if_fail (out_ip4_config, FALSE);
+ g_return_val_if_fail (out_ip6_config, FALSE);
+
+ if (!response || !g_str_has_prefix (response, IPDPADDR_TAG)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing %%IPDPADDR prefix");
+ return FALSE;
+ }
+
+ /* %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>[,<nbns1>,<nbns2>[,<??>,<netmask>,<gw>]]
+ * %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>,<nbns1>,<nbns2>,<netmask>,<gw>
+ * %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>,<nbns1>,<nbns2>,<??>,<gw>,<ip6>,::,<ip6_dns1>,::,::,::,::,::
+ *
+ * Sierra USB305: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0
+ * K3805-Z: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,
+ * Nokia 21M: %IPDPADDR: 2, 33.196.7.127, 33.196.7.128, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 33.196.7.128, fe80::f:9135:5901, ::, fd00:976a::9, ::, ::, ::, ::, ::
+ * Nokia 21M: %IPDPADDR: 3, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::2e:437b:7901, ::, fd00:976a::9, ::, ::, ::, ::, ::
+ */
+ response = mm_strip_tag (response, IPDPADDR_TAG);
+ items = g_strsplit_set (response, ",", 0);
+
+ /* Strip any spaces on elements; inet_pton() doesn't like them */
+ num_items = g_strv_length (items);
+ for (i = 0, first_free = 0; i < num_items; i++)
+ items[i] = g_strstrip (items[i]);
+
+ if (num_items < 7) {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Malformed IPDPADDR response (not enough items)");
+ goto out;
+ }
+
+ /* Validate context ID */
+ if (!mm_get_int_from_str (items[0], &num) ||
+ num != expected_cid) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unknown CID in IPDPADDR response (got %d, expected %d)",
+ (guint) num,
+ expected_cid);
+ goto out;
+ }
+
+ ip4_config = parse_ipdpaddr_v4 ((const gchar **) items, num_items, &local);
+ if (local) {
+ g_propagate_error (error, local);
+ goto out;
+ }
+
+ ip6_config = parse_ipdpaddr_v6 ((const gchar **) items, num_items, &local);
+ if (local) {
+ g_propagate_error (error, local);
+ goto out;
+ }
+
+ success = TRUE;
+
+out:
+ g_strfreev (items);
+
+ *out_ip4_config = ip4_config;
+ *out_ip6_config = ip6_config;
+ return success;
+}
+
diff --git a/plugins/icera/mm-modem-helpers-icera.h b/plugins/icera/mm-modem-helpers-icera.h
new file mode 100644
index 00000000..8a7a87a2
--- /dev/null
+++ b/plugins/icera/mm-modem-helpers-icera.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2014 Dan Williams <dcbw@redhat.com>
+ */
+
+#ifndef MM_MODEM_HELPERS_ICERA_H
+#define MM_MODEM_HELPERS_ICERA_H
+
+#include "glib.h"
+
+/* %IPDPADDR response parser */
+gboolean mm_icera_parse_ipdpaddr_response (const gchar *response,
+ guint expected_cid,
+ MMBearerIpConfig **out_ip4_config,
+ MMBearerIpConfig **out_ip6_config,
+ GError **error);
+
+#endif /* MM_MODEM_HELPERS_HUAWEI_H */
diff --git a/plugins/icera/tests/test-modem-helpers-icera.c b/plugins/icera/tests/test-modem-helpers-icera.c
new file mode 100644
index 00000000..8eaff4cb
--- /dev/null
+++ b/plugins/icera/tests/test-modem-helpers-icera.c
@@ -0,0 +1,191 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2014 Dan Williams <dcbw@redhat.com>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-icera.h"
+
+/*****************************************************************************/
+/* Test %IPDPADDR responses */
+
+typedef struct {
+ const gchar *str;
+ const guint expected_cid;
+
+ /* IPv4 */
+ const gchar *ipv4_addr;
+ const guint ipv4_prefix;
+ const gchar *ipv4_gw;
+ const gchar *ipv4_dns1;
+ const gchar *ipv4_dns2;
+
+ /* IPv6 */
+ const gchar *ipv6_addr;
+ const gchar *ipv6_dns1;
+} IpdpaddrTest;
+
+static const IpdpaddrTest ipdpaddr_tests[] = {
+ /* Sierra USB305 */
+ { "%IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0\r\n",
+ 2, "21.93.217.11", 32, "21.93.217.10", "10.177.0.34", "10.161.171.220",
+ NULL, NULL },
+
+ /* ZTE/Vodafone K3805-Z */
+ { "%IPDPADDR: 5, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,\r\n",
+ 5, "21.93.217.11", 24, "21.93.217.10", "10.177.0.34", "10.161.171.220",
+ NULL, NULL },
+
+ /* Secondary gateway check */
+ { "%IPDPADDR: 5, 21.93.217.11, 0.0.0.0, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,\r\n",
+ 5, "21.93.217.11", 24, "21.93.217.10", "10.177.0.34", "10.161.171.220",
+ NULL, NULL },
+
+ /* Secondary gateway check #2 */
+ { "%IPDPADDR: 5, 27.107.96.189, 0.0.0.0, 121.242.190.210, 121.242.190.181, 0.0.0.0, 0.0.0.0, 255.255.255.254, 27.107.96.188\r\n",
+ 5, "27.107.96.189", 31, "27.107.96.188", "121.242.190.210", "121.242.190.181",
+ NULL, NULL },
+
+ /* Nokia 21M */
+ { "%IPDPADDR: 1, 33.196.7.127, 33.196.7.128, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 33.196.7.128, fe80::f:9135:5901, ::, fd00:976a::9, ::, ::, ::, ::, ::\r\n",
+ 1, "33.196.7.127", 32, "33.196.7.128", "10.177.0.34", "10.161.171.220",
+ "fe80::f:9135:5901", "fd00:976a::9" },
+
+ { "%IPDPADDR: 3, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::2e:437b:7901, ::, fd00:976a::9, ::, ::, ::, ::, ::\r\n",
+ 3, NULL, 0, NULL, NULL, NULL,
+ "fe80::2e:437b:7901", "fd00:976a::9" },
+
+ /* Some development chip (cnsbg.p1001.rev2, CL477342) */
+ { "%IPDPADDR: 5, 27.107.96.189, 27.107.96.188, 121.242.190.210, 121.242.190.181, 0.0.0.0, 0.0.0.0, 255.255.255.254, 27.107.96.188\r\n",
+ 5, "27.107.96.189", 31, "27.107.96.188", "121.242.190.210", "121.242.190.181",
+ NULL, NULL },
+
+ /* 21M with newer firmware */
+ { "%IPDPADDR: 2, 188.150.116.13, 188.150.116.14, 188.149.250.16, 0.0.0.0, 0.0.0.0, 0.0.0.0, 255.255.0.0, 188.150.116.14, fe80::1:e414:eb01, ::, 2a00:e18:0:3::6, ::, ::, ::, ::, ::\r\n",
+ 2, "188.150.116.13", 16, "188.150.116.14", "188.149.250.16", NULL,
+ "fe80::1:e414:eb01", "2a00:e18:0:3::6" },
+
+ { NULL }
+};
+
+static void
+test_ipdpaddr (void)
+{
+ guint i;
+
+ for (i = 0; ipdpaddr_tests[i].str; i++) {
+ gboolean success;
+ GError *error = NULL;
+ MMBearerIpConfig *ipv4 = NULL;
+ MMBearerIpConfig *ipv6 = NULL;
+ const gchar **dns;
+ guint dnslen;
+
+ success = mm_icera_parse_ipdpaddr_response (
+ ipdpaddr_tests[i].str,
+ ipdpaddr_tests[i].expected_cid,
+ &ipv4,
+ &ipv6,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ /* IPv4 */
+ if (ipdpaddr_tests[i].ipv4_addr) {
+ g_assert (ipv4);
+ g_assert_cmpint (mm_bearer_ip_config_get_method (ipv4), ==, MM_BEARER_IP_METHOD_STATIC);
+ g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv4), ==, ipdpaddr_tests[i].ipv4_addr);
+ g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv4), ==, ipdpaddr_tests[i].ipv4_prefix);
+ g_assert_cmpstr (mm_bearer_ip_config_get_gateway (ipv4), ==, ipdpaddr_tests[i].ipv4_gw);
+
+ dns = mm_bearer_ip_config_get_dns (ipv4);
+ g_assert (dns);
+ dnslen = g_strv_length ((gchar **) dns);
+ if (ipdpaddr_tests[i].ipv4_dns2 != NULL)
+ g_assert_cmpint (dnslen, ==, 2);
+ else
+ g_assert_cmpint (dnslen, ==, 1);
+ g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv4_dns1);
+ g_assert_cmpstr (dns[1], ==, ipdpaddr_tests[i].ipv4_dns2);
+ } else
+ g_assert (ipv4 == NULL);
+
+ /* IPv6 */
+ if (ipdpaddr_tests[i].ipv6_addr) {
+ struct in6_addr a6;
+ g_assert (ipv6);
+
+ g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv6), ==, ipdpaddr_tests[i].ipv6_addr);
+ g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv6), ==, 64);
+
+ g_assert (inet_pton (AF_INET6, mm_bearer_ip_config_get_address (ipv6), &a6));
+ if (IN6_IS_ADDR_LINKLOCAL (&a6))
+ g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_DHCP);
+ else
+ g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_STATIC);
+
+ dns = mm_bearer_ip_config_get_dns (ipv6);
+ g_assert (dns);
+ dnslen = g_strv_length ((gchar **) dns);
+ g_assert_cmpint (dnslen, ==, 1);
+ g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv6_dns1);
+ } else
+ g_assert (ipv6 == NULL);
+ }
+}
+
+/*****************************************************************************/
+
+void
+_mm_log (const char *loc,
+ const char *func,
+ guint32 level,
+ const char *fmt,
+ ...)
+{
+#if defined ENABLE_TEST_MESSAGE_TRACES
+ /* Dummy log function */
+ va_list args;
+ gchar *msg;
+
+ va_start (args, fmt);
+ msg = g_strdup_vprintf (fmt, args);
+ va_end (args);
+ g_print ("%s\n", msg);
+ g_free (msg);
+#endif
+}
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/icera/ipdpaddr", test_ipdpaddr);
+
+ return g_test_run ();
+}
diff --git a/plugins/mbm/mm-broadband-bearer-mbm.c b/plugins/mbm/mm-broadband-bearer-mbm.c
index ed786439..9e5e838e 100644
--- a/plugins/mbm/mm-broadband-bearer-mbm.c
+++ b/plugins/mbm/mm-broadband-bearer-mbm.c
@@ -39,6 +39,7 @@
#include "mm-broadband-bearer-mbm.h"
#include "mm-log.h"
#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-mbm.h"
#include "mm-daemon-enums-types.h"
G_DEFINE_TYPE (MMBroadbandBearerMbm, mm_broadband_bearer_mbm, MM_TYPE_BROADBAND_BEARER);
@@ -466,6 +467,164 @@ dial_3gpp (MMBroadbandBearer *self,
}
/*****************************************************************************/
+/* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */
+
+typedef struct {
+ MMBroadbandBearerMbm *self;
+ MMBaseModem *modem;
+ MMPortSerialAt *primary;
+ MMBearerIpFamily family;
+ GSimpleAsyncResult *result;
+} GetIpConfig3gppContext;
+
+static GetIpConfig3gppContext *
+get_ip_config_3gpp_context_new (MMBroadbandBearerMbm *self,
+ MMBaseModem *modem,
+ MMPortSerialAt *primary,
+ MMBearerIpFamily family,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GetIpConfig3gppContext *ctx;
+
+ ctx = g_new0 (GetIpConfig3gppContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->modem = g_object_ref (modem);
+ ctx->primary = g_object_ref (primary);
+ ctx->family = family;
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ get_ip_config_3gpp_context_new);
+ return ctx;
+}
+
+static void
+get_ip_config_context_complete_and_free (GetIpConfig3gppContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->primary);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_free (ctx);
+}
+
+static gboolean
+get_ip_config_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ MMBearerIpConfig **ipv4_config,
+ MMBearerIpConfig **ipv6_config,
+ GError **error)
+{
+ MMBearerConnectResult *configs;
+ MMBearerIpConfig *ipv4, *ipv6;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return FALSE;
+
+ configs = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ g_assert (configs);
+
+ ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs);
+ ipv6 = mm_bearer_connect_result_peek_ipv6_config (configs);
+ g_assert (ipv4 || ipv6);
+ if (ipv4_config && ipv4)
+ *ipv4_config = g_object_ref (ipv4);
+ if (ipv6_config && ipv6)
+ *ipv6_config = g_object_ref (ipv6);
+
+ return TRUE;
+}
+
+static void
+ip_config_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GetIpConfig3gppContext *ctx)
+{
+ MMBearerIpConfig *ipv4_config = NULL;
+ MMBearerIpConfig *ipv6_config = NULL;
+ const gchar *response;
+ GError *error = NULL;
+ MMBearerConnectResult *connect_result;
+
+ response = mm_base_modem_at_command_full_finish (modem, res, &error);
+ if (error) {
+ g_error_free (error);
+
+ /* Fall back to DHCP configuration; early devices don't support *E2IPCFG */
+ if (ctx->family == MM_BEARER_IP_FAMILY_IPV4 || ctx->family == MM_BEARER_IP_FAMILY_IPV4V6) {
+ ipv4_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP);
+ }
+ if (ctx->family == MM_BEARER_IP_FAMILY_IPV6 || ctx->family == MM_BEARER_IP_FAMILY_IPV4V6) {
+ ipv6_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
+ }
+ } else {
+ if (!mm_mbm_parse_e2ipcfg_response (response,
+ &ipv4_config,
+ &ipv6_config,
+ &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ goto out;
+ }
+
+ if (!ipv4_config && !ipv6_config) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get IP config: couldn't parse response '%s'",
+ response);
+ goto out;
+ }
+ }
+
+ connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary),
+ ipv4_config,
+ ipv6_config);
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ connect_result,
+ (GDestroyNotify)mm_bearer_connect_result_unref);
+
+out:
+ g_clear_object (&ipv4_config);
+ g_clear_object (&ipv6_config);
+ get_ip_config_context_complete_and_free (ctx);
+}
+
+static void
+get_ip_config_3gpp (MMBroadbandBearer *self,
+ MMBroadbandModem *modem,
+ MMPortSerialAt *primary,
+ MMPortSerialAt *secondary,
+ MMPort *data,
+ guint cid,
+ MMBearerIpFamily ip_family,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GetIpConfig3gppContext *ctx;
+
+ ctx = get_ip_config_3gpp_context_new (MM_BROADBAND_BEARER_MBM (self),
+ MM_BASE_MODEM (modem),
+ primary,
+ ip_family,
+ callback,
+ user_data);
+
+ mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
+ primary,
+ "*E2IPCFG?",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)ip_config_ready,
+ ctx);
+}
+
+/*****************************************************************************/
/* 3GPP disconnect */
typedef struct {
@@ -608,6 +767,8 @@ mm_broadband_bearer_mbm_class_init (MMBroadbandBearerMbmClass *klass)
bearer_class->report_connection_status = report_connection_status;
broadband_bearer_class->dial_3gpp = dial_3gpp;
broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
+ broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp;
+ broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
}
diff --git a/plugins/mbm/mm-modem-helpers-mbm.c b/plugins/mbm/mm-modem-helpers-mbm.c
new file mode 100644
index 00000000..42653d88
--- /dev/null
+++ b/plugins/mbm/mm-modem-helpers-mbm.c
@@ -0,0 +1,166 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2012 - 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2014 Dan Williams <dcbw@redhat.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-mbm.h"
+
+/*****************************************************************************/
+/* *E2IPCFG response parser */
+
+static gboolean
+validate_address (int family, const char *addr)
+{
+ struct in6_addr tmp6 = IN6ADDR_ANY_INIT;
+
+ if (inet_pton (family, addr, (void *) &tmp6) != 1)
+{
+g_message ("%s: famil '%s'", __func__, addr);
+ return FALSE;
+}
+ if ((family == AF_INET6) && IN6_IS_ADDR_UNSPECIFIED (&tmp6))
+ return FALSE;
+ return TRUE;
+}
+
+#define E2IPCFG_TAG "*E2IPCFG"
+
+gboolean
+mm_mbm_parse_e2ipcfg_response (const gchar *response,
+ MMBearerIpConfig **out_ip4_config,
+ MMBearerIpConfig **out_ip6_config,
+ GError **error)
+{
+ MMBearerIpConfig **ip_config = NULL;
+ gboolean got_address = FALSE, got_gw = FALSE, got_dns = FALSE;
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *match_error = NULL;
+ gchar *dns[3] = { 0 };
+ guint dns_idx = 0;
+ int family = AF_INET;
+ MMBearerIpMethod method = MM_BEARER_IP_METHOD_STATIC;
+
+ g_return_val_if_fail (out_ip4_config, FALSE);
+ g_return_val_if_fail (out_ip6_config, FALSE);
+
+ if (!response || !g_str_has_prefix (response, E2IPCFG_TAG)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing " E2IPCFG_TAG " prefix");
+ return FALSE;
+ }
+
+ response = mm_strip_tag (response, "*E2IPCFG: ");
+
+ if (strchr (response, ':')) {
+ family = AF_INET6;
+ ip_config = out_ip6_config;
+ method = MM_BEARER_IP_METHOD_DHCP;
+ } else if (strchr (response, '.')) {
+ family = AF_INET;
+ ip_config = out_ip4_config;
+ method = MM_BEARER_IP_METHOD_STATIC;
+ } else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Failed to detect " E2IPCFG_TAG " address family");
+ return FALSE;
+ }
+
+ /* *E2IPCFG: (1,<IP>)(2,<gateway>)(3,<DNS>)(3,<DNS>)
+ *
+ * *E2IPCFG: (1,"46.157.32.246")(2,"46.157.32.243")(3,"193.213.112.4")(3,"130.67.15.198")
+ * *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0000:e537:1801")(3,"2001:4600:0004:0fff:0000:0000:0000:0054")(3,"2001:4600:0004:1fff:0000:0000:0000:0054")
+ * *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0027:b7fe:9401")(3,"fd00:976a:0000:0000:0000:0000:0000:0009")
+ */
+ r = g_regex_new ("\\((\\d),\"([0-9a-fA-F.:]+)\"\\)", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) {
+ if (match_error) {
+ g_propagate_error (error, match_error);
+ g_prefix_error (error, "Could not parse " E2IPCFG_TAG " results: ");
+ } else {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't match " E2IPCFG_TAG " reply");
+ }
+ goto done;
+ }
+
+ *ip_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (*ip_config, method);
+ while (g_match_info_matches (match_info)) {
+ char *id = g_match_info_fetch (match_info, 1);
+ char *str = g_match_info_fetch (match_info, 2);
+
+ switch (atoi (id)) {
+ case 1:
+ if (validate_address (family, str)) {
+ mm_bearer_ip_config_set_address (*ip_config, str);
+ mm_bearer_ip_config_set_prefix (*ip_config, (family == AF_INET6) ? 64 : 28);
+ got_address = TRUE;
+ }
+ break;
+ case 2:
+ if ((family == AF_INET) && validate_address (family, str)) {
+ mm_bearer_ip_config_set_gateway (*ip_config, str);
+ got_gw = TRUE;
+ }
+ break;
+ case 3:
+ if (validate_address (family, str)) {
+ dns[dns_idx++] = g_strdup (str);
+ got_dns = TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+ g_free (id);
+ g_free (str);
+ g_match_info_next (match_info, NULL);
+ }
+
+ if (got_dns) {
+ mm_bearer_ip_config_set_dns (*ip_config, (const gchar **) dns);
+ g_free (dns[0]);
+ g_free (dns[1]);
+ }
+
+ if (!got_address || (family == AF_INET && !got_gw)) {
+ g_object_unref (*ip_config);
+ *ip_config = NULL;
+ g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Got incomplete IP configuration from " E2IPCFG_TAG);
+ }
+
+done:
+ if (match_info)
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ return !!*ip_config;
+}
+
diff --git a/plugins/mbm/mm-modem-helpers-mbm.h b/plugins/mbm/mm-modem-helpers-mbm.h
new file mode 100644
index 00000000..ef15845d
--- /dev/null
+++ b/plugins/mbm/mm-modem-helpers-mbm.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2014 Dan Williams <dcbw@redhat.com>
+ */
+
+#ifndef MM_MODEM_HELPERS_MBM_H
+#define MM_MODEM_HELPERS_MBM_H
+
+#include "glib.h"
+
+/* *E2IPCFG response parser */
+gboolean mm_mbm_parse_e2ipcfg_response (const gchar *response,
+ MMBearerIpConfig **out_ip4_config,
+ MMBearerIpConfig **out_ip6_config,
+ GError **error);
+
+#endif /* MM_MODEM_HELPERS_MBM_H */
diff --git a/plugins/mbm/tests/test-modem-helpers-mbm.c b/plugins/mbm/tests/test-modem-helpers-mbm.c
new file mode 100644
index 00000000..2e6dd1a3
--- /dev/null
+++ b/plugins/mbm/tests/test-modem-helpers-mbm.c
@@ -0,0 +1,162 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2014 Dan Williams <dcbw@redhat.com>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-mbm.h"
+
+/*****************************************************************************/
+/* Test *E2IPCFG responses */
+
+typedef struct {
+ const gchar *str;
+
+ /* IPv4 */
+ const gchar *ipv4_addr;
+ const gchar *ipv4_gw;
+ const gchar *ipv4_dns1;
+ const gchar *ipv4_dns2;
+
+ /* IPv6 */
+ const gchar *ipv6_addr;
+ const gchar *ipv6_dns1;
+ const gchar *ipv6_dns2;
+} E2ipcfgTest;
+
+static const E2ipcfgTest tests[] = {
+ { "*E2IPCFG: (1,\"46.157.32.246\")(2,\"46.157.32.243\")(3,\"193.213.112.4\")(3,\"130.67.15.198\")\r\n",
+ "46.157.32.246", "46.157.32.243", "193.213.112.4", "130.67.15.198",
+ NULL, NULL },
+
+ { "*E2IPCFG: (1,\"fe80:0000:0000:0000:0000:0000:e537:1801\")(3,\"2001:4600:0004:0fff:0000:0000:0000:0054\")(3,\"2001:4600:0004:1fff:0000:0000:0000:0054\")\r\n",
+ NULL, NULL, NULL, NULL,
+ "fe80:0000:0000:0000:0000:0000:e537:1801", "2001:4600:0004:0fff:0000:0000:0000:0054", "2001:4600:0004:1fff:0000:0000:0000:0054" },
+
+ { "*E2IPCFG: (1,\"fe80:0000:0000:0000:0000:0027:b7fe:9401\")(3,\"fd00:976a:0000:0000:0000:0000:0000:0009\")\r\n",
+ NULL, NULL, NULL, NULL,
+ "fe80:0000:0000:0000:0000:0027:b7fe:9401", "fd00:976a:0000:0000:0000:0000:0000:0009", NULL },
+
+ { NULL }
+};
+
+static void
+test_e2ipcfg (void)
+{
+ guint i;
+
+ for (i = 0; tests[i].str; i++) {
+ gboolean success;
+ GError *error = NULL;
+ MMBearerIpConfig *ipv4 = NULL;
+ MMBearerIpConfig *ipv6 = NULL;
+ const gchar **dns;
+ guint dnslen;
+
+ success = mm_mbm_parse_e2ipcfg_response (tests[i].str, &ipv4, &ipv6, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ /* IPv4 */
+ if (tests[i].ipv4_addr) {
+ g_assert (ipv4);
+ g_assert_cmpint (mm_bearer_ip_config_get_method (ipv4), ==, MM_BEARER_IP_METHOD_STATIC);
+ g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv4), ==, tests[i].ipv4_addr);
+ g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv4), ==, 28);
+ g_assert_cmpstr (mm_bearer_ip_config_get_gateway (ipv4), ==, tests[i].ipv4_gw);
+
+ dns = mm_bearer_ip_config_get_dns (ipv4);
+ g_assert (dns);
+ dnslen = g_strv_length ((gchar **) dns);
+ if (tests[i].ipv4_dns2 != NULL)
+ g_assert_cmpint (dnslen, ==, 2);
+ else
+ g_assert_cmpint (dnslen, ==, 1);
+ g_assert_cmpstr (dns[0], ==, tests[i].ipv4_dns1);
+ g_assert_cmpstr (dns[1], ==, tests[i].ipv4_dns2);
+ } else
+ g_assert (ipv4 == NULL);
+
+ /* IPv6 */
+ if (tests[i].ipv6_addr) {
+ struct in6_addr a6;
+ g_assert (ipv6);
+
+ g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv6), ==, tests[i].ipv6_addr);
+ g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv6), ==, 64);
+
+ g_assert (inet_pton (AF_INET6, mm_bearer_ip_config_get_address (ipv6), &a6));
+ if (IN6_IS_ADDR_LINKLOCAL (&a6))
+ g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_DHCP);
+ else
+ g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_STATIC);
+
+ dns = mm_bearer_ip_config_get_dns (ipv6);
+ g_assert (dns);
+ dnslen = g_strv_length ((gchar **) dns);
+ if (tests[i].ipv6_dns2 != NULL)
+ g_assert_cmpint (dnslen, ==, 2);
+ else
+ g_assert_cmpint (dnslen, ==, 1);
+ g_assert_cmpstr (dns[0], ==, tests[i].ipv6_dns1);
+ g_assert_cmpstr (dns[1], ==, tests[i].ipv6_dns2);
+ } else
+ g_assert (ipv6 == NULL);
+ }
+}
+
+/*****************************************************************************/
+
+void
+_mm_log (const char *loc,
+ const char *func,
+ guint32 level,
+ const char *fmt,
+ ...)
+{
+#if defined ENABLE_TEST_MESSAGE_TRACES
+ /* Dummy log function */
+ va_list args;
+ gchar *msg;
+
+ va_start (args, fmt);
+ msg = g_strdup_vprintf (fmt, args);
+ va_end (args);
+ g_print ("%s\n", msg);
+ g_free (msg);
+#endif
+}
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/mbm/e2ipcfg", test_e2ipcfg);
+
+ return g_test_run ();
+}
diff --git a/plugins/option/mm-broadband-bearer-hso.c b/plugins/option/mm-broadband-bearer-hso.c
index 15d9551b..c7c0e372 100644
--- a/plugins/option/mm-broadband-bearer-hso.c
+++ b/plugins/option/mm-broadband-bearer-hso.c
@@ -187,6 +187,7 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
+ MMBearerIpFamily ip_family,
GAsyncReadyCallback callback,
gpointer user_data)
{
diff --git a/plugins/sierra/mm-broadband-bearer-sierra.c b/plugins/sierra/mm-broadband-bearer-sierra.c
index 67cb3f27..b4bfe109 100644
--- a/plugins/sierra/mm-broadband-bearer-sierra.c
+++ b/plugins/sierra/mm-broadband-bearer-sierra.c
@@ -34,6 +34,16 @@
G_DEFINE_TYPE (MMBroadbandBearerSierra, mm_broadband_bearer_sierra, MM_TYPE_BROADBAND_BEARER);
+struct _MMBroadbandBearerSierraPrivate {
+ gboolean is_icera;
+};
+
+enum {
+ PROP_0,
+ PROP_IS_ICERA,
+ PROP_LAST
+};
+
/*****************************************************************************/
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
@@ -198,7 +208,10 @@ dial_3gpp_context_step (Dial3gppContext *ctx)
if (!user || !password || allowed_auth == MM_BEARER_ALLOWED_AUTH_NONE) {
mm_dbg ("Not using authentication");
- command = g_strdup_printf ("$QCPDPP=%d,0", ctx->cid);
+ if (ctx->self->priv->is_icera)
+ command = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", ctx->cid);
+ else
+ command = g_strdup_printf ("$QCPDPP=%d,0", ctx->cid);
} else {
gchar *quoted_user;
gchar *quoted_password;
@@ -230,11 +243,20 @@ dial_3gpp_context_step (Dial3gppContext *ctx)
quoted_user = mm_port_serial_at_quote_string (user);
quoted_password = mm_port_serial_at_quote_string (password);
- command = g_strdup_printf ("$QCPDPP=%d,%u,%s,%s",
- ctx->cid,
- sierra_auth,
- quoted_password,
- quoted_user);
+ if (ctx->self->priv->is_icera) {
+ command = g_strdup_printf ("%%IPDPCFG=%d,0,%u,%s,%s",
+ ctx->cid,
+ sierra_auth,
+ quoted_user,
+ quoted_password);
+ } else {
+ /* Yes, password comes first... */
+ command = g_strdup_printf ("$QCPDPP=%d,%u,%s,%s",
+ ctx->cid,
+ sierra_auth,
+ quoted_password,
+ quoted_user);
+ }
g_free (quoted_user);
g_free (quoted_password);
}
@@ -421,6 +443,8 @@ disconnect_3gpp (MMBroadbandBearer *self,
/*****************************************************************************/
+#define MM_BROADBAND_BEARER_SIERRA_IS_ICERA "is-icera"
+
MMBearer *
mm_broadband_bearer_sierra_new_finish (GAsyncResult *res,
GError **error)
@@ -442,8 +466,9 @@ mm_broadband_bearer_sierra_new_finish (GAsyncResult *res,
}
void
-mm_broadband_bearer_sierra_new (MMBroadbandModemSierra *modem,
+mm_broadband_bearer_sierra_new (MMBroadbandModem *modem,
MMBearerProperties *config,
+ gboolean is_icera,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -456,21 +481,74 @@ mm_broadband_bearer_sierra_new (MMBroadbandModemSierra *modem,
user_data,
MM_BEARER_MODEM, modem,
MM_BEARER_CONFIG, config,
+ MM_BROADBAND_BEARER_SIERRA_IS_ICERA, is_icera,
NULL);
}
static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMBroadbandBearerSierra *self = MM_BROADBAND_BEARER_SIERRA (object);
+
+ switch (prop_id) {
+ case PROP_IS_ICERA:
+ self->priv->is_icera = 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)
+{
+ MMBroadbandBearerSierra *self = MM_BROADBAND_BEARER_SIERRA (object);
+
+ switch (prop_id) {
+ case PROP_IS_ICERA:
+ g_value_set_boolean (value, self->priv->is_icera);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
mm_broadband_bearer_sierra_init (MMBroadbandBearerSierra *self)
{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+ MM_TYPE_BROADBAND_BEARER_SIERRA,
+ MMBroadbandBearerSierraPrivate);
}
static void
mm_broadband_bearer_sierra_class_init (MMBroadbandBearerSierraClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
+ g_type_class_add_private (object_class, sizeof (MMBroadbandBearerSierraPrivate));
+
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
broadband_bearer_class->dial_3gpp = dial_3gpp;
broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
+
+ g_object_class_install_property (object_class, PROP_IS_ICERA,
+ g_param_spec_boolean (MM_BROADBAND_BEARER_SIERRA_IS_ICERA,
+ "IsIcera",
+ "Whether the modem uses Icera commands or not.",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
diff --git a/plugins/sierra/mm-broadband-bearer-sierra.h b/plugins/sierra/mm-broadband-bearer-sierra.h
index 92a5acc8..fea7b94f 100644
--- a/plugins/sierra/mm-broadband-bearer-sierra.h
+++ b/plugins/sierra/mm-broadband-bearer-sierra.h
@@ -36,9 +36,11 @@
typedef struct _MMBroadbandBearerSierra MMBroadbandBearerSierra;
typedef struct _MMBroadbandBearerSierraClass MMBroadbandBearerSierraClass;
+typedef struct _MMBroadbandBearerSierraPrivate MMBroadbandBearerSierraPrivate;
struct _MMBroadbandBearerSierra {
MMBroadbandBearer parent;
+ MMBroadbandBearerSierraPrivate *priv;
};
struct _MMBroadbandBearerSierraClass {
@@ -48,8 +50,9 @@ struct _MMBroadbandBearerSierraClass {
GType mm_broadband_bearer_sierra_get_type (void);
/* Default 3GPP bearer creation implementation */
-void mm_broadband_bearer_sierra_new (MMBroadbandModemSierra *modem,
+void mm_broadband_bearer_sierra_new (MMBroadbandModem *modem,
MMBearerProperties *config,
+ gboolean is_icera,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
diff --git a/plugins/sierra/mm-broadband-modem-sierra-icera.c b/plugins/sierra/mm-broadband-modem-sierra-icera.c
index d9a4e400..3bfad984 100644
--- a/plugins/sierra/mm-broadband-modem-sierra-icera.c
+++ b/plugins/sierra/mm-broadband-modem-sierra-icera.c
@@ -85,8 +85,9 @@ modem_create_bearer (MMIfaceModem *self,
modem_create_bearer);
mm_dbg ("Creating Sierra bearer...");
- mm_broadband_bearer_sierra_new (MM_BROADBAND_MODEM_SIERRA (self),
+ mm_broadband_bearer_sierra_new (MM_BROADBAND_MODEM (self),
properties,
+ TRUE, /* is_icera */
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_sierra_new_ready,
result);
diff --git a/plugins/sierra/mm-broadband-modem-sierra.c b/plugins/sierra/mm-broadband-modem-sierra.c
index 8d06b017..d6dd965b 100644
--- a/plugins/sierra/mm-broadband-modem-sierra.c
+++ b/plugins/sierra/mm-broadband-modem-sierra.c
@@ -1134,8 +1134,9 @@ modem_create_bearer (MMIfaceModem *self,
modem_create_bearer);
mm_dbg ("Creating Sierra bearer...");
- mm_broadband_bearer_sierra_new (MM_BROADBAND_MODEM_SIERRA (self),
+ mm_broadband_bearer_sierra_new (MM_BROADBAND_MODEM (self),
properties,
+ FALSE, /* is_icera */
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_sierra_new_ready,
result);
diff --git a/src/mm-bearer-mbim.c b/src/mm-bearer-mbim.c
index b1e3176c..c25b2811 100644
--- a/src/mm-bearer-mbim.c
+++ b/src/mm-bearer-mbim.c
@@ -370,6 +370,14 @@ ip_configuration_query_ready (MbimDevice *device,
str = g_inet_address_to_string (addr);
mm_bearer_ip_config_set_address (ipv6_config, str);
g_free (str);
+
+ /* If the address is a link-local one, then SLAAC or DHCP must be used
+ * to get the real prefix and address. Change the method to DHCP to
+ * indicate this to clients.
+ */
+ if (g_inet_address_get_is_link_local (addr))
+ mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
+
g_object_unref (addr);
/* Netmask */
diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c
index 4c693d24..41fdc6eb 100644
--- a/src/mm-bearer-qmi.c
+++ b/src/mm-bearer-qmi.c
@@ -31,6 +31,7 @@
#include "mm-modem-helpers-qmi.h"
#include "mm-port-enums-types.h"
#include "mm-log.h"
+#include "mm-modem-helpers.h"
G_DEFINE_TYPE (MMBearerQmi, mm_bearer_qmi, MM_TYPE_BEARER);
@@ -82,12 +83,14 @@ typedef struct {
gboolean running_ipv4;
QmiClientWds *client_ipv4;
guint32 packet_data_handle_ipv4;
+ MMBearerIpConfig *ipv4_config;
GError *error_ipv4;
gboolean ipv6;
gboolean running_ipv6;
QmiClientWds *client_ipv6;
guint32 packet_data_handle_ipv6;
+ MMBearerIpConfig *ipv6_config;
GError *error_ipv6;
} ConnectContext;
@@ -99,14 +102,12 @@ connect_context_complete_and_free (ConnectContext *ctx)
g_free (ctx->apn);
g_free (ctx->user);
g_free (ctx->password);
- if (ctx->error_ipv4)
- g_error_free (ctx->error_ipv4);
- if (ctx->error_ipv6)
- g_error_free (ctx->error_ipv6);
- if (ctx->client_ipv4)
- g_object_unref (ctx->client_ipv4);
- if (ctx->client_ipv6)
- g_object_unref (ctx->client_ipv6);
+ g_clear_error (&ctx->error_ipv4);
+ g_clear_error (&ctx->error_ipv6);
+ g_clear_object (&ctx->client_ipv4);
+ g_clear_object (&ctx->client_ipv6);
+ g_clear_object (&ctx->ipv4_config);
+ g_clear_object (&ctx->ipv6_config);
g_object_unref (ctx->data);
g_object_unref (ctx->qmi);
g_object_unref (ctx->cancellable);
@@ -247,51 +248,191 @@ build_start_network_input (ConnectContext *ctx)
}
static void
-print_address4 (gboolean success, const char *tag, guint32 address, GError *error)
+qmi_inet4_ntop (guint32 address, char *buf, const gsize buflen)
{
struct in_addr a = { .s_addr = GUINT32_TO_BE (address) };
- char buf[INET_ADDRSTRLEN + 1];
- if (success) {
- memset (buf, 0, sizeof (buf));
- if (inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1))
- mm_dbg (" %s: %s", tag, buf);
- else
- mm_dbg (" %s: failed (address conversion error)", tag);
- return;
+ g_assert (buflen >= INET_ADDRSTRLEN);
+
+ /* We can ignore inet_ntop() return value if 'buf' is
+ * at least INET_ADDRSTRLEN in size. */
+ memset (buf, 0, buflen);
+ g_assert (inet_ntop (AF_INET, &a, buf, buflen));
+}
+
+static MMBearerIpConfig *
+get_ipv4_config (QmiMessageWdsGetCurrentSettingsOutput *output, guint32 mtu)
+{
+ MMBearerIpConfig *config;
+ char buf[INET_ADDRSTRLEN];
+ char buf2[INET_ADDRSTRLEN];
+ const gchar *dns[3] = { 0 };
+ guint dns_idx = 0;
+ guint32 addr = 0;
+ GError *error = NULL;
+ guint32 prefix = 0;
+
+ /* IPv4 subnet mask */
+ if (!qmi_message_wds_get_current_settings_output_get_ipv4_gateway_subnet_mask (output, &addr, &error)) {
+ mm_dbg ("Failed to read IPv4 netmask (%s)", error->message);
+ g_clear_error (&error);
+ return NULL;
}
- mm_dbg (" %s: failed (%s)", tag, error ? error->message : "unknown");
+ qmi_inet4_ntop (addr, buf, sizeof (buf));
+ prefix = mm_netmask_to_cidr (buf);
+
+ /* IPv4 address */
+ if (!qmi_message_wds_get_current_settings_output_get_ipv4_address (output, &addr, &error)) {
+ mm_dbg ("IPv4 family but no IPv4 address (%s)", error->message);
+ g_clear_error (&error);
+ return NULL;
+ }
+
+ mm_dbg ("QMI IPv4 Settings:");
+
+ config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
+
+ /* IPv4 address */
+ qmi_inet4_ntop (addr, buf, sizeof (buf));
+ mm_bearer_ip_config_set_address (config, buf);
+ mm_bearer_ip_config_set_prefix (config, prefix);
+ mm_dbg (" Address: %s/%d", buf, prefix);
+
+ /* IPv4 gateway address */
+ if (qmi_message_wds_get_current_settings_output_get_ipv4_gateway_address (output, &addr, &error)) {
+ qmi_inet4_ntop (addr, buf, sizeof (buf));
+ mm_bearer_ip_config_set_gateway (config, buf);
+ mm_dbg (" Gateway: %s", buf);
+ } else {
+ mm_dbg (" Gateway: failed (%s)", error->message);
+ g_clear_error (&error);
+ }
+
+ /* IPv4 DNS #1 */
+ if (qmi_message_wds_get_current_settings_output_get_primary_ipv4_dns_address (output, &addr, &error)) {
+ qmi_inet4_ntop (addr, buf, sizeof (buf));
+ dns[dns_idx++] = buf;
+ mm_dbg (" DNS #1: %s", buf);
+ } else {
+ mm_dbg (" DNS #1: failed (%s)", error->message);
+ g_clear_error (&error);
+ }
+
+ /* IPv6 DNS #2 */
+ if (qmi_message_wds_get_current_settings_output_get_secondary_ipv4_dns_address (output, &addr, &error)) {
+ qmi_inet4_ntop (addr, buf2, sizeof (buf2));
+ dns[dns_idx++] = buf2;
+ mm_dbg (" DNS #2: %s", buf2);
+ } else {
+ mm_dbg (" DNS #2: failed (%s)", error->message);
+ g_clear_error (&error);
+ }
+
+ if (dns_idx > 0)
+ mm_bearer_ip_config_set_dns (config, (const gchar **) &dns);
+
+ if (mtu) {
+ mm_bearer_ip_config_set_mtu (config, mtu);
+ mm_dbg (" MTU: %d", mtu);
+ }
+
+ return config;
}
static void
-print_address6 (gboolean success,
- const char *tag,
- GArray *array,
- guint32 prefix,
- GError *error)
+qmi_inet6_ntop (GArray *array, char *buf, const gsize buflen)
{
struct in6_addr a;
- char buf[INET6_ADDRSTRLEN + 1];
guint32 i;
- if (success) {
- g_assert (array);
- g_assert (array->len == 8);
+ g_assert (array);
+ g_assert (array->len == 8);
+ g_assert (buflen >= INET6_ADDRSTRLEN);
- memset (buf, 0, sizeof (buf));
- for (i = 0; i < array->len; i++)
- a.s6_addr16[i] = GUINT16_TO_BE (g_array_index (array, guint16, i));
+ for (i = 0; i < array->len; i++)
+ a.s6_addr16[i] = GUINT16_TO_BE (g_array_index (array, guint16, i));
- if (inet_ntop (AF_INET6, &a, buf, sizeof (buf) - 1))
- mm_dbg (" %s: %s/%d", tag, buf, prefix);
- else
- mm_dbg (" %s: failed (address conversion error)", tag);
+ /* We can ignore inet_ntop() return value if 'buf' is
+ * at least INET6_ADDRSTRLEN in size. */
+ memset (buf, 0, buflen);
+ g_assert (inet_ntop (AF_INET6, &a, buf, buflen));
+}
- g_array_free (array, TRUE);
- return;
+static MMBearerIpConfig *
+get_ipv6_config (QmiMessageWdsGetCurrentSettingsOutput *output, guint32 mtu)
+{
+ MMBearerIpConfig *config;
+ char buf[INET6_ADDRSTRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ const gchar *dns[3] = { 0 };
+ guint dns_idx = 0;
+ GArray *array;
+ GError *error = NULL;
+ guint8 prefix = 0;
+
+ /* If the message has an IPv6 address, create an IPv6 bearer config */
+ if (!qmi_message_wds_get_current_settings_output_get_ipv6_address (output, &array, &prefix, &error)) {
+ mm_dbg ("IPv6 family but no IPv6 address (%s)", error->message);
+ g_clear_error (&error);
+ return NULL;
}
- mm_dbg (" %s: failed (%s)", tag, error ? error->message : "unknown");
+ mm_dbg ("QMI IPv6 Settings:");
+
+ config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
+
+ /* IPv6 address */
+ qmi_inet6_ntop (array, buf, sizeof (buf));
+ g_array_unref (array);
+
+ mm_bearer_ip_config_set_address (config, buf);
+ mm_bearer_ip_config_set_prefix (config, prefix);
+ mm_dbg (" Address: %s/%d", buf, prefix);
+
+ /* IPv6 gateway address */
+ if (qmi_message_wds_get_current_settings_output_get_ipv6_gateway_address (output, &array, &prefix, &error)) {
+ qmi_inet6_ntop (array, buf, sizeof (buf));
+ mm_bearer_ip_config_set_gateway (config, buf);
+ mm_dbg (" Gateway: %s", buf);
+ g_array_unref (array);
+ } else {
+ mm_dbg (" Gateway: failed (%s)", error->message);
+ g_clear_error (&error);
+ }
+
+ /* IPv6 DNS #1 */
+ if (qmi_message_wds_get_current_settings_output_get_ipv6_primary_dns_address (output, &array, &error)) {
+ qmi_inet6_ntop (array, buf, sizeof (buf));
+ dns[dns_idx++] = buf;
+ mm_dbg (" DNS #1: %s", buf);
+ g_array_unref (array);
+ } else {
+ mm_dbg (" DNS #1: failed (%s)", error->message);
+ g_clear_error (&error);
+ }
+
+ /* IPv6 DNS #2 */
+ if (qmi_message_wds_get_current_settings_output_get_ipv6_secondary_dns_address (output, &array, &error)) {
+ qmi_inet6_ntop (array, buf2, sizeof (buf2));
+ dns[dns_idx++] = buf2;
+ mm_dbg (" DNS #2: %s", buf2);
+ g_array_unref (array);
+ } else {
+ mm_dbg (" DNS #2: failed (%s)", error->message);
+ g_clear_error (&error);
+ }
+
+ if (dns_idx > 0)
+ mm_bearer_ip_config_set_dns (config, (const gchar **) &dns);
+
+ if (mtu) {
+ mm_bearer_ip_config_set_mtu (config, mtu);
+ mm_dbg (" MTU: %d", mtu);
+ }
+
+ return config;
}
static void
@@ -312,73 +453,33 @@ get_current_settings_ready (QmiClientWds *client,
mm_info ("error: couldn't get current settings: %s", error->message);
g_error_free (error);
} else {
- gboolean success;
- guint32 addr, mtu, i;
+ QmiWdsIpFamily ip_family = QMI_WDS_IP_FAMILY_UNSPECIFIED;
+ guint32 mtu = 0;
GArray *array;
- guint8 prefix;
-
- /* If the message has an IPv4 address, print IPv4 settings */
- if (qmi_message_wds_get_current_settings_output_get_ipv4_address (output, &addr, &error)) {
- mm_dbg ("QMI IPv4 Settings:");
- /* IPv4 address */
- print_address4 (TRUE, "Address", addr, error);
- g_clear_error (&error);
-
- /* IPv4 gateway address */
- success = qmi_message_wds_get_current_settings_output_get_ipv4_gateway_address (output, &addr, &error);
- print_address4 (success, "Gateway", addr, error);
- g_clear_error (&error);
-
- /* IPv4 subnet mask */
- success = qmi_message_wds_get_current_settings_output_get_ipv4_gateway_subnet_mask (output, &addr, &error);
- print_address4 (success, "Netmask", addr, error);
- g_clear_error (&error);
-
- /* IPv4 DNS #1 */
- success = qmi_message_wds_get_current_settings_output_get_primary_ipv4_dns_address (output, &addr, &error);
- print_address4 (success, " DNS #1", addr, error);
- g_clear_error (&error);
-
- /* IPv4 DNS #2 */
- success = qmi_message_wds_get_current_settings_output_get_secondary_ipv4_dns_address (output, &addr, &error);
- print_address4 (success, " DNS #2", addr, error);
- g_clear_error (&error);
- } else {
- /* no IPv4 configuration */
+ if (!qmi_message_wds_get_current_settings_output_get_ip_family (output, &ip_family, &error)) {
+ mm_dbg (" IP Family: failed (%s); assuming IPv4", error->message);
g_clear_error (&error);
+ ip_family = QMI_WDS_IP_FAMILY_IPV4;
}
+ mm_dbg (" IP Family: %s",
+ (ip_family == QMI_WDS_IP_FAMILY_IPV4) ? "IPv4" :
+ (ip_family == QMI_WDS_IP_FAMILY_IPV6) ? "IPv6" : "unknown");
- /* If the message has an IPv6 address, print IPv6 settings */
- if (qmi_message_wds_get_current_settings_output_get_ipv6_address (output, &array, &prefix, &error)) {
- mm_dbg ("QMI IPv6 Settings:");
-
- /* IPv6 address */
- print_address6 (TRUE, "Address", array, prefix, error);
- g_clear_error (&error);
-
- /* IPv6 gateway address */
- success = qmi_message_wds_get_current_settings_output_get_ipv6_gateway_address (output, &array, &prefix, &error);
- print_address6 (success, "Gateway", array, prefix, error);
- g_clear_error (&error);
-
- /* IPv6 DNS #1 */
- success = qmi_message_wds_get_current_settings_output_get_ipv6_primary_dns_address (output, &array, &error);
- print_address6 (success, " DNS #1", array, 0, error);
- g_clear_error (&error);
-
- /* IPv6 DNS #2 */
- success = qmi_message_wds_get_current_settings_output_get_ipv6_secondary_dns_address (output, &array, &error);
- print_address6 (success, " DNS #2", array, 0, error);
- g_clear_error (&error);
- } else {
- /* no IPv6 configuration */
+ if (!qmi_message_wds_get_current_settings_output_get_mtu (output, &mtu, &error)) {
+ mm_dbg (" MTU: failed (%s)", error->message);
g_clear_error (&error);
}
+ if (ip_family == QMI_WDS_IP_FAMILY_IPV4)
+ ctx->ipv4_config = get_ipv4_config (output, mtu);
+ else if (ip_family == QMI_WDS_IP_FAMILY_IPV6)
+ ctx->ipv6_config = get_ipv6_config (output, mtu);
+
/* Domain names */
if (qmi_message_wds_get_current_settings_output_get_domain_name_list (output, &array, &error)) {
GString *s = g_string_sized_new (array ? (array->len * 20) : 1);
+ guint i;
for (i = 0; array && (i < array->len); i++) {
if (s->len)
@@ -391,13 +492,6 @@ get_current_settings_ready (QmiClientWds *client,
mm_dbg (" Domains: failed (%s)", error ? error->message : "unknown");
g_clear_error (&error);
}
-
- if (qmi_message_wds_get_current_settings_output_get_mtu (output, &mtu, &error))
- mm_dbg (" MTU: %d", mtu);
- else {
- mm_dbg (" MTU: failed (%s)", error ? error->message : "unknown");
- g_clear_error (&error);
- }
}
if (output)
@@ -728,8 +822,6 @@ connect_context_step (ConnectContext *ctx)
case CONNECT_STEP_LAST:
/* If one of IPv4 or IPv6 succeeds, we're connected */
if (ctx->packet_data_handle_ipv4 || ctx->packet_data_handle_ipv6) {
- MMBearerIpConfig *config;
-
/* Port is connected; update the state */
mm_port_set_connected (MM_PORT (ctx->data), TRUE);
@@ -751,19 +843,11 @@ connect_context_step (ConnectContext *ctx)
ctx->self->priv->client_ipv6 = g_object_ref (ctx->client_ipv6);
}
- /* Build IP config; always DHCP based */
- config = mm_bearer_ip_config_new ();
- mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
-
/* Set operation result */
g_simple_async_result_set_op_res_gpointer (
ctx->result,
- mm_bearer_connect_result_new (
- ctx->data,
- ctx->packet_data_handle_ipv4 ? config : NULL,
- ctx->packet_data_handle_ipv6 ? config : NULL),
+ mm_bearer_connect_result_new (ctx->data, ctx->ipv4_config, ctx->ipv6_config),
(GDestroyNotify)mm_bearer_connect_result_unref);
- g_object_unref (config);
} else {
GError *error;
diff --git a/src/mm-broadband-bearer.c b/src/mm-broadband-bearer.c
index de762d76..bed9ec2d 100644
--- a/src/mm-broadband-bearer.c
+++ b/src/mm-broadband-bearer.c
@@ -674,7 +674,9 @@ dial_3gpp_ready (MMBroadbandModem *modem,
GAsyncResult *res,
DetailedConnectContext *ctx)
{
- MMBearerIpConfig *config;
+ MMBearerIpMethod ip_method = MM_BEARER_IP_METHOD_UNKNOWN;
+ MMBearerIpConfig *ipv4_config = NULL;
+ MMBearerIpConfig *ipv6_config = NULL;
GError *error = NULL;
ctx->data = MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->dial_3gpp_finish (ctx->self, res, &error);
@@ -701,6 +703,7 @@ dial_3gpp_ready (MMBroadbandModem *modem,
ctx->secondary,
ctx->data,
ctx->cid,
+ ctx->ip_family,
(GAsyncReadyCallback)get_ip_config_3gpp_ready,
ctx);
return;
@@ -714,19 +717,30 @@ dial_3gpp_ready (MMBroadbandModem *modem,
/* If no specific IP retrieval requested, set the default implementation
* (PPP if data port is AT, DHCP otherwise) */
- config = mm_bearer_ip_config_new ();
- mm_bearer_ip_config_set_method (config,
- (MM_IS_PORT_SERIAL_AT (ctx->data) ?
- MM_BEARER_IP_METHOD_PPP :
- MM_BEARER_IP_METHOD_DHCP));
+ ip_method = MM_IS_PORT_SERIAL_AT (ctx->data) ?
+ MM_BEARER_IP_METHOD_PPP :
+ MM_BEARER_IP_METHOD_DHCP;
+
+ if (ctx->ip_family & MM_BEARER_IP_FAMILY_IPV4 ||
+ ctx->ip_family & MM_BEARER_IP_FAMILY_IPV4V6) {
+ ipv4_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (ipv4_config, ip_method);
+ }
+ if (ctx->ip_family & MM_BEARER_IP_FAMILY_IPV6 ||
+ ctx->ip_family & MM_BEARER_IP_FAMILY_IPV4V6) {
+ ipv6_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (ipv6_config, ip_method);
+ }
+ g_assert (ipv4_config || ipv6_config);
g_simple_async_result_set_op_res_gpointer (
ctx->result,
- mm_bearer_connect_result_new (ctx->data, config, NULL),
+ mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config),
(GDestroyNotify)mm_bearer_connect_result_unref);
detailed_connect_context_complete_and_free (ctx);
- g_object_unref (config);
+ g_clear_object (&ipv4_config);
+ g_clear_object (&ipv6_config);
}
static void
diff --git a/src/mm-broadband-bearer.h b/src/mm-broadband-bearer.h
index 4a008cd1..f3869120 100644
--- a/src/mm-broadband-bearer.h
+++ b/src/mm-broadband-bearer.h
@@ -79,6 +79,7 @@ struct _MMBroadbandBearerClass {
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
+ MMBearerIpFamily ip_family,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (* get_ip_config_3gpp_finish) (MMBroadbandBearer *self,