merge: fix IPv6 support and operation

Notable changes include reporting all available IP configruation
information in the bearer IP details, even if the method is DHCP,
in which case the client should apply the given configuration and
then proceed with DHCP/PPP/SLAAC/etc addressing mechanisms.

QMI, MBIM, and some other plugins have been switched to the "STATIC"
configuration method from DHCP when the firmware supplies all the
necessary details.
This commit is contained in:
Dan Williams
2014-06-13 14:57:08 -05:00
22 changed files with 1473 additions and 297 deletions
+34 -30
View File
@@ -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 (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;
if (mm_bearer_ip_config_get_method (ipv4_config) == MM_BEARER_IP_METHOD_STATIC) {
const gchar **dns;
guint i;
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)));
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 (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 (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;
if (mm_bearer_ip_config_get_method (ipv6_config) == MM_BEARER_IP_METHOD_STATIC) {
const gchar **dns;
guint i;
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)));
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 (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)
+6 -2
View File
@@ -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.
*/
@@ -186,10 +186,15 @@
</varlistentry>
</variablelist>
If the bearer specifies configuration via PPP or DHCP, only the
<literal>"method"</literal> item will be present.
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 only applicable when using the
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>
+1 -4
View File
@@ -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}",
+32 -1
View File
@@ -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 \
+60 -128
View File
@@ -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;
}
+277
View File
@@ -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;
}
+28
View File
@@ -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 */
@@ -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 ();
}
+161
View File
@@ -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);
@@ -465,6 +466,164 @@ dial_3gpp (MMBroadbandBearer *self,
authenticate (ctx);
}
/*****************************************************************************/
/* 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 */
@@ -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;
}
+166
View File
@@ -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;
}
+27
View File
@@ -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 */
+162
View File
@@ -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 ();
}
+1
View File
@@ -187,6 +187,7 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
MMPortSerialAt *secondary,
MMPort *data,
guint cid,
MMBearerIpFamily ip_family,
GAsyncReadyCallback callback,
gpointer user_data)
{
+85 -7
View File
@@ -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));
}
+4 -1
View File
@@ -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);
@@ -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);
+2 -1
View File
@@ -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);
+8
View File
@@ -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 */
+195 -111
View File
@@ -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);
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");
/* 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_mtu (output, &mtu, &error)) {
mm_dbg (" MTU: failed (%s)", error->message);
g_clear_error (&error);
}
/* 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 */
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;
+22 -8
View File
@@ -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
+1
View File
@@ -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,