diff options
author | Dan Williams <dcbw@redhat.com> | 2014-02-17 12:32:02 -0600 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2014-06-13 11:30:49 -0500 |
commit | a416d8056c9f9bbc8b926ffbe8f066859054a990 (patch) | |
tree | c48ddfbb58b716a3f07caa0192ca20c6abcf1914 | |
parent | e68d51f439190eb5434581c8db072687028f0b55 (diff) |
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.
-rw-r--r-- | plugins/Makefile.am | 17 | ||||
-rw-r--r-- | plugins/icera/mm-broadband-bearer-icera.c | 187 | ||||
-rw-r--r-- | plugins/icera/mm-modem-helpers-icera.c | 277 | ||||
-rw-r--r-- | plugins/icera/mm-modem-helpers-icera.h | 28 | ||||
-rw-r--r-- | plugins/icera/tests/test-modem-helpers-icera.c | 191 |
5 files changed, 571 insertions, 129 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 31cc50ed..bf44ceb6 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -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 = \ diff --git a/plugins/icera/mm-broadband-bearer-icera.c b/plugins/icera/mm-broadband-bearer-icera.c index 128b20e8..347f373f 100644 --- a/plugins/icera/mm-broadband-bearer-icera.c +++ b/plugins/icera/mm-broadband-bearer-icera.c @@ -33,6 +33,7 @@ #include "mm-modem-helpers.h" #include "mm-error-helpers.h" #include "mm-daemon-enums-types.h" +#include "mm-modem-helpers-icera.h" G_DEFINE_TYPE (MMBroadbandBearerIcera, mm_broadband_bearer_icera, MM_TYPE_BROADBAND_BEARER); @@ -109,157 +110,72 @@ get_ip_config_3gpp_finish (MMBroadbandBearer *self, MMBearerIpConfig **ipv6_config, GError **error) { - MMBearerIpConfig *ip_config; + MMBearerConnectResult *configs; + MMBearerIpConfig *ipv4, *ipv6; if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) return FALSE; - /* No IPv6 for now */ - ip_config = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)); - *ipv4_config = g_object_ref (ip_config); - *ipv6_config = NULL; + configs = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)); + g_assert (configs); + + ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs); + ipv6 = mm_bearer_connect_result_peek_ipv6_config (configs); + g_assert (ipv4 || ipv6); + if (ipv4_config && ipv4) + *ipv4_config = g_object_ref (ipv4); + if (ipv6_config && ipv6) + *ipv6_config = g_object_ref (ipv6); + return TRUE; } -#define IPDPADDR_TAG "%IPDPADDR: " - static void ip_config_ready (MMBaseModem *modem, GAsyncResult *res, GetIpConfig3gppContext *ctx) { - MMBearerIpConfig *ip_config = NULL; + MMBearerIpConfig *ipv4_config = NULL; + MMBearerIpConfig *ipv6_config = NULL; const gchar *response; GError *error = NULL; - gchar **items; - gchar *dns[3] = { 0 }; - guint i; - guint dns_i; + MMBearerConnectResult *connect_result; response = mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { g_simple_async_result_take_error (ctx->result, error); - get_ip_config_context_complete_and_free (ctx); - return; + goto out; } - /* TODO: use a regex to parse this */ + if (!mm_icera_parse_ipdpaddr_response (response, + ctx->cid, + &ipv4_config, + &ipv6_config, + &error)) { + g_simple_async_result_take_error (ctx->result, error); + goto out; + } - /* Check result */ - if (!g_str_has_prefix (response, IPDPADDR_TAG)) { + if (!ipv4_config && !ipv6_config) { g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Couldn't get IP config: invalid response '%s'", + "Couldn't get IP config: couldn't parse response '%s'", response); - get_ip_config_context_complete_and_free (ctx); - return; + goto out; } - /* %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>[,<nbns1>,<nbns2>[,<??>,<netmask>,<gw>]] - * - * Sierra USB305: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0 - * K3805-Z: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10, - */ - response = mm_strip_tag (response, IPDPADDR_TAG); - items = g_strsplit (response, ", ", 0); - - ip_config = mm_bearer_ip_config_new (); - - for (i = 0, dns_i = 0; items[i]; i++) { - if (i == 0) { /* CID */ - gint num; - - if (!mm_get_int_from_str (items[i], &num) || - num != ctx->cid) { - error = g_error_new (MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Unknown CID in IPDPADDR response (" - "got %d, expected %d)", - (guint) num, - ctx->cid); - break; - } - } else if (i == 1) { /* IP address */ - guint32 tmp = 0; - - if (!inet_pton (AF_INET, items[i], &tmp)) { - mm_warn ("Couldn't parse IP address '%s'", items[i]); - g_clear_object (&ip_config); - break; - } - - mm_bearer_ip_config_set_method (ip_config, MM_BEARER_IP_METHOD_STATIC); - mm_bearer_ip_config_set_address (ip_config, items[i]); - } else if (i == 2) { /* Gateway */ - guint32 tmp = 0; - - if (!inet_pton (AF_INET, items[i], &tmp)) { - mm_warn ("Couldn't parse gateway address '%s'", items[i]); - g_clear_object (&ip_config); - break; - } - - if (tmp) - mm_bearer_ip_config_set_gateway (ip_config, items[i]); - } else if (i == 3 || i == 4) { /* DNS entries */ - guint32 tmp = 0; - - if (!inet_pton (AF_INET, items[i], &tmp)) { - mm_warn ("Couldn't parse DNS address '%s'", items[i]); - g_clear_object (&ip_config); - break; - } - - if (tmp) - dns[dns_i++] = items[i]; - } else if (i == 8) { /* Netmask */ - guint32 tmp = 0; - - if (!inet_pton (AF_INET, items[i], &tmp)) { - mm_warn ("Couldn't parse netmask '%s'", items[i]); - g_clear_object (&ip_config); - break; - } - - mm_bearer_ip_config_set_prefix (ip_config, mm_netmask_to_cidr (items[i])); - } else if (i == 9) { /* Duplicate Gateway */ - if (!mm_bearer_ip_config_get_gateway (ip_config)) { - guint32 tmp = 0; - - if (!inet_pton (AF_INET, items[i], &tmp)) { - mm_warn ("Couldn't parse (duplicate) gateway address '%s'", items[i]); - g_clear_object (&ip_config); - break; - } - - if (tmp) - mm_bearer_ip_config_set_gateway (ip_config, items[i]); - } - } - } - - if (!ip_config) { - if (error) - g_simple_async_result_take_error (ctx->result, error); - else - g_simple_async_result_set_error (ctx->result, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't get IP config: couldn't parse response '%s'", - response); - } else { - /* If we got DNS entries, set them in the IP config */ - if (dns[0]) - mm_bearer_ip_config_set_dns (ip_config, (const gchar **)dns); - - g_simple_async_result_set_op_res_gpointer (ctx->result, - ip_config, - (GDestroyNotify)g_object_unref); - } + connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary), + ipv4_config, + ipv6_config); + g_simple_async_result_set_op_res_gpointer (ctx->result, + connect_result, + (GDestroyNotify)mm_bearer_connect_result_unref); +out: + g_clear_object (&ipv4_config); + g_clear_object (&ipv6_config); get_ip_config_context_complete_and_free (ctx); - g_strfreev (items); } static void @@ -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; } diff --git a/plugins/icera/mm-modem-helpers-icera.c b/plugins/icera/mm-modem-helpers-icera.c new file mode 100644 index 00000000..fd3e4fc7 --- /dev/null +++ b/plugins/icera/mm-modem-helpers-icera.c @@ -0,0 +1,277 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2012 - 2013 Aleksander Morgado <aleksander@gnu.org> + * Copyright (C) 2014 Dan Williams <dcbw@redhat.com> + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-modem-helpers.h" +#include "mm-modem-helpers-icera.h" + +/*****************************************************************************/ +/* %IPDPADDR response parser */ + +static MMBearerIpConfig * +parse_ipdpaddr_v4 (const gchar **items, guint num_items, GError **error) +{ + MMBearerIpConfig *config; + const gchar *dns[3] = { 0 }; + guint dns_i = 0, tmp; + const gchar *netmask = NULL; + + /* IP address and prefix */ + tmp = 0; + if (!inet_pton (AF_INET, items[1], &tmp)) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse IPv4 address '%s'", items[1]); + return NULL; + } + + if (!tmp) { + /* No IPv4 config */ + return NULL; + } + + config = mm_bearer_ip_config_new (); + mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC); + mm_bearer_ip_config_set_address (config, items[1]); + mm_bearer_ip_config_set_prefix (config, 32); /* default prefix */ + + /* Gateway */ + tmp = 0; + if (inet_pton (AF_INET, items[2], &tmp)) { + if (tmp) + mm_bearer_ip_config_set_gateway (config, items[2]); + } else { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse gateway address '%s'", items[2]); + goto error; + } + + /* DNS */ + tmp = 0; + if (inet_pton (AF_INET, items[3], &tmp) && tmp) + dns[dns_i++] = items[3]; + else { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse DNS address '%s'", items[3]); + goto error; + } + + /* DNS2 - sometimes missing and set to 0.0.0.0 */ + tmp = 0; + if (inet_pton (AF_INET, items[4], &tmp) && tmp) + dns[dns_i++] = items[4]; + if (dns_i > 0) + mm_bearer_ip_config_set_dns (config, (const gchar **) dns); + + /* Short form (eg, Sierra USB305) */ + if (num_items < 9) + return config; + + /* Devices return netmask and secondary gateway in one of two + * positions. The netmask may be either at index 7 or 8, while + * the secondary gateway may be at position 8 or 9. + */ + + if (items[7] && strstr (items[7], "255.") && !strstr (items[7], "255.0.0.0")) + netmask = items[7]; + if (items[8] && strstr (items[8], "255.") && !strstr (items[8], "255.0.0.0")) + netmask = items[8]; + if (netmask) { + if (!inet_pton (AF_INET, netmask, &tmp)) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse netmask '%s'", + netmask); + goto error; + } + mm_bearer_ip_config_set_prefix (config, mm_netmask_to_cidr (netmask)); + } + + /* Secondary gateway */ + if (!mm_bearer_ip_config_get_gateway (config)) { + const char *gw2 = NULL; + + if (num_items >= 10 && items[9] && !strstr (items[9], "255.") && !strstr (items[9], "::")) + gw2 = items[9]; + /* Prefer position 8 */ + if (items[8] && !strstr (items[8], "255.")) + gw2 = items[8]; + + if (gw2 && inet_pton (AF_INET, gw2, &tmp) && tmp) + mm_bearer_ip_config_set_gateway (config, gw2); + else { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse secondary gateway address '%s'", + gw2 ? gw2 : "(unknown)"); + goto error; + } + } + + return config; + +error: + g_object_unref (config); + return NULL; +} + +static MMBearerIpConfig * +parse_ipdpaddr_v6 (const gchar **items, guint num_items, GError **error) +{ + MMBearerIpConfig *config; + const gchar *dns[2] = { 0 }; + struct in6_addr tmp6 = IN6ADDR_ANY_INIT; + + if (num_items < 12) + return NULL; + + /* It appears that for IPv6 %IPDPADDR returns only the expected + * link-local address and a DNS address, and that to retrieve the + * default router, extra DNS, and search domains, the host must listen + * for IPv6 Router Advertisements on the net port. + */ + + config = mm_bearer_ip_config_new (); + mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC); + + /* IP address and prefix */ + if (inet_pton (AF_INET6, items[9], &tmp6) != 1 || + IN6_IS_ADDR_UNSPECIFIED (&tmp6)) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse IPv6 address '%s'", items[9]); + goto error; + } + mm_bearer_ip_config_set_address (config, items[9]); + mm_bearer_ip_config_set_prefix (config, 64); + + /* If the address is a link-local one, then SLAAC or DHCP must be used + * to get the real prefix and address. Change the method to DHCP to + * indicate this to clients. + */ + if (IN6_IS_ADDR_LINKLOCAL (&tmp6)) + mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP); + + /* DNS server */ + memset (&tmp6, 0, sizeof (tmp6)); + if (inet_pton (AF_INET6, items[11], &tmp6) == 1 && + !IN6_IS_ADDR_UNSPECIFIED (&tmp6)) { + dns[0] = items[11]; + dns[1] = NULL; + mm_bearer_ip_config_set_dns (config, (const gchar **) dns); + } else { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse DNS address '%s'", items[11]); + goto error; + } + + return config; + +error: + g_object_unref (config); + return NULL; +} + +#define IPDPADDR_TAG "%IPDPADDR: " + +gboolean +mm_icera_parse_ipdpaddr_response (const gchar *response, + guint expected_cid, + MMBearerIpConfig **out_ip4_config, + MMBearerIpConfig **out_ip6_config, + GError **error) +{ + MMBearerIpConfig *ip4_config = NULL; + MMBearerIpConfig *ip6_config = NULL; + GError *local = NULL; + gboolean success; + char **items; + guint num_items, i, first_free; + gint num; + + g_return_val_if_fail (out_ip4_config, FALSE); + g_return_val_if_fail (out_ip6_config, FALSE); + + if (!response || !g_str_has_prefix (response, IPDPADDR_TAG)) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing %%IPDPADDR prefix"); + return FALSE; + } + + /* %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>[,<nbns1>,<nbns2>[,<??>,<netmask>,<gw>]] + * %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>,<nbns1>,<nbns2>,<netmask>,<gw> + * %IPDPADDR: <cid>,<ip>,<gw>,<dns1>,<dns2>,<nbns1>,<nbns2>,<??>,<gw>,<ip6>,::,<ip6_dns1>,::,::,::,::,:: + * + * Sierra USB305: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0 + * K3805-Z: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10, + * Nokia 21M: %IPDPADDR: 2, 33.196.7.127, 33.196.7.128, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 33.196.7.128, fe80::f:9135:5901, ::, fd00:976a::9, ::, ::, ::, ::, :: + * Nokia 21M: %IPDPADDR: 3, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::2e:437b:7901, ::, fd00:976a::9, ::, ::, ::, ::, :: + */ + response = mm_strip_tag (response, IPDPADDR_TAG); + items = g_strsplit_set (response, ",", 0); + + /* Strip any spaces on elements; inet_pton() doesn't like them */ + num_items = g_strv_length (items); + for (i = 0, first_free = 0; i < num_items; i++) + items[i] = g_strstrip (items[i]); + + if (num_items < 7) { + g_set_error_literal (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Malformed IPDPADDR response (not enough items)"); + goto out; + } + + /* Validate context ID */ + if (!mm_get_int_from_str (items[0], &num) || + num != expected_cid) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Unknown CID in IPDPADDR response (got %d, expected %d)", + (guint) num, + expected_cid); + goto out; + } + + ip4_config = parse_ipdpaddr_v4 ((const gchar **) items, num_items, &local); + if (local) { + g_propagate_error (error, local); + goto out; + } + + ip6_config = parse_ipdpaddr_v6 ((const gchar **) items, num_items, &local); + if (local) { + g_propagate_error (error, local); + goto out; + } + + success = TRUE; + +out: + g_strfreev (items); + + *out_ip4_config = ip4_config; + *out_ip6_config = ip6_config; + return success; +} + diff --git a/plugins/icera/mm-modem-helpers-icera.h b/plugins/icera/mm-modem-helpers-icera.h new file mode 100644 index 00000000..8a7a87a2 --- /dev/null +++ b/plugins/icera/mm-modem-helpers-icera.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2014 Dan Williams <dcbw@redhat.com> + */ + +#ifndef MM_MODEM_HELPERS_ICERA_H +#define MM_MODEM_HELPERS_ICERA_H + +#include "glib.h" + +/* %IPDPADDR response parser */ +gboolean mm_icera_parse_ipdpaddr_response (const gchar *response, + guint expected_cid, + MMBearerIpConfig **out_ip4_config, + MMBearerIpConfig **out_ip6_config, + GError **error); + +#endif /* MM_MODEM_HELPERS_HUAWEI_H */ diff --git a/plugins/icera/tests/test-modem-helpers-icera.c b/plugins/icera/tests/test-modem-helpers-icera.c new file mode 100644 index 00000000..8eaff4cb --- /dev/null +++ b/plugins/icera/tests/test-modem-helpers-icera.c @@ -0,0 +1,191 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + * Copyright (C) 2014 Dan Williams <dcbw@redhat.com> + */ + +#include <glib.h> +#include <glib-object.h> +#include <locale.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-log.h" +#include "mm-modem-helpers.h" +#include "mm-modem-helpers-icera.h" + +/*****************************************************************************/ +/* Test %IPDPADDR responses */ + +typedef struct { + const gchar *str; + const guint expected_cid; + + /* IPv4 */ + const gchar *ipv4_addr; + const guint ipv4_prefix; + const gchar *ipv4_gw; + const gchar *ipv4_dns1; + const gchar *ipv4_dns2; + + /* IPv6 */ + const gchar *ipv6_addr; + const gchar *ipv6_dns1; +} IpdpaddrTest; + +static const IpdpaddrTest ipdpaddr_tests[] = { + /* Sierra USB305 */ + { "%IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0\r\n", + 2, "21.93.217.11", 32, "21.93.217.10", "10.177.0.34", "10.161.171.220", + NULL, NULL }, + + /* ZTE/Vodafone K3805-Z */ + { "%IPDPADDR: 5, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,\r\n", + 5, "21.93.217.11", 24, "21.93.217.10", "10.177.0.34", "10.161.171.220", + NULL, NULL }, + + /* Secondary gateway check */ + { "%IPDPADDR: 5, 21.93.217.11, 0.0.0.0, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,\r\n", + 5, "21.93.217.11", 24, "21.93.217.10", "10.177.0.34", "10.161.171.220", + NULL, NULL }, + + /* Secondary gateway check #2 */ + { "%IPDPADDR: 5, 27.107.96.189, 0.0.0.0, 121.242.190.210, 121.242.190.181, 0.0.0.0, 0.0.0.0, 255.255.255.254, 27.107.96.188\r\n", + 5, "27.107.96.189", 31, "27.107.96.188", "121.242.190.210", "121.242.190.181", + NULL, NULL }, + + /* Nokia 21M */ + { "%IPDPADDR: 1, 33.196.7.127, 33.196.7.128, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 33.196.7.128, fe80::f:9135:5901, ::, fd00:976a::9, ::, ::, ::, ::, ::\r\n", + 1, "33.196.7.127", 32, "33.196.7.128", "10.177.0.34", "10.161.171.220", + "fe80::f:9135:5901", "fd00:976a::9" }, + + { "%IPDPADDR: 3, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::2e:437b:7901, ::, fd00:976a::9, ::, ::, ::, ::, ::\r\n", + 3, NULL, 0, NULL, NULL, NULL, + "fe80::2e:437b:7901", "fd00:976a::9" }, + + /* Some development chip (cnsbg.p1001.rev2, CL477342) */ + { "%IPDPADDR: 5, 27.107.96.189, 27.107.96.188, 121.242.190.210, 121.242.190.181, 0.0.0.0, 0.0.0.0, 255.255.255.254, 27.107.96.188\r\n", + 5, "27.107.96.189", 31, "27.107.96.188", "121.242.190.210", "121.242.190.181", + NULL, NULL }, + + /* 21M with newer firmware */ + { "%IPDPADDR: 2, 188.150.116.13, 188.150.116.14, 188.149.250.16, 0.0.0.0, 0.0.0.0, 0.0.0.0, 255.255.0.0, 188.150.116.14, fe80::1:e414:eb01, ::, 2a00:e18:0:3::6, ::, ::, ::, ::, ::\r\n", + 2, "188.150.116.13", 16, "188.150.116.14", "188.149.250.16", NULL, + "fe80::1:e414:eb01", "2a00:e18:0:3::6" }, + + { NULL } +}; + +static void +test_ipdpaddr (void) +{ + guint i; + + for (i = 0; ipdpaddr_tests[i].str; i++) { + gboolean success; + GError *error = NULL; + MMBearerIpConfig *ipv4 = NULL; + MMBearerIpConfig *ipv6 = NULL; + const gchar **dns; + guint dnslen; + + success = mm_icera_parse_ipdpaddr_response ( + ipdpaddr_tests[i].str, + ipdpaddr_tests[i].expected_cid, + &ipv4, + &ipv6, + &error); + g_assert_no_error (error); + g_assert (success); + + /* IPv4 */ + if (ipdpaddr_tests[i].ipv4_addr) { + g_assert (ipv4); + g_assert_cmpint (mm_bearer_ip_config_get_method (ipv4), ==, MM_BEARER_IP_METHOD_STATIC); + g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv4), ==, ipdpaddr_tests[i].ipv4_addr); + g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv4), ==, ipdpaddr_tests[i].ipv4_prefix); + g_assert_cmpstr (mm_bearer_ip_config_get_gateway (ipv4), ==, ipdpaddr_tests[i].ipv4_gw); + + dns = mm_bearer_ip_config_get_dns (ipv4); + g_assert (dns); + dnslen = g_strv_length ((gchar **) dns); + if (ipdpaddr_tests[i].ipv4_dns2 != NULL) + g_assert_cmpint (dnslen, ==, 2); + else + g_assert_cmpint (dnslen, ==, 1); + g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv4_dns1); + g_assert_cmpstr (dns[1], ==, ipdpaddr_tests[i].ipv4_dns2); + } else + g_assert (ipv4 == NULL); + + /* IPv6 */ + if (ipdpaddr_tests[i].ipv6_addr) { + struct in6_addr a6; + g_assert (ipv6); + + g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv6), ==, ipdpaddr_tests[i].ipv6_addr); + g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv6), ==, 64); + + g_assert (inet_pton (AF_INET6, mm_bearer_ip_config_get_address (ipv6), &a6)); + if (IN6_IS_ADDR_LINKLOCAL (&a6)) + g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_DHCP); + else + g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_STATIC); + + dns = mm_bearer_ip_config_get_dns (ipv6); + g_assert (dns); + dnslen = g_strv_length ((gchar **) dns); + g_assert_cmpint (dnslen, ==, 1); + g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv6_dns1); + } else + g_assert (ipv6 == NULL); + } +} + +/*****************************************************************************/ + +void +_mm_log (const char *loc, + const char *func, + guint32 level, + const char *fmt, + ...) +{ +#if defined ENABLE_TEST_MESSAGE_TRACES + /* Dummy log function */ + va_list args; + gchar *msg; + + va_start (args, fmt); + msg = g_strdup_vprintf (fmt, args); + va_end (args); + g_print ("%s\n", msg); + g_free (msg); +#endif +} + +int main (int argc, char **argv) +{ + setlocale (LC_ALL, ""); + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/MM/icera/ipdpaddr", test_ipdpaddr); + + return g_test_run (); +} |