broadband-bearer-icera: add IPv6 support
Split out the %IPDPADDR parsing into a helper and add testcases for it, and add support for IPv6 handling. If the returned IPv6 is link-local, the address should be assigned to the interface and SLAAC performed to retrieve the actual IPv6 prefix and RDNSS/DNSSD information.
This commit is contained in:
+16
-1
@@ -90,13 +90,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 = \
|
||||
|
||||
@@ -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
|
||||
@@ -285,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,
|
||||
@@ -301,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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
Reference in New Issue
Block a user