[PATCH] broadband-modem-icera: add IPv6 support

Dan Williams dcbw at redhat.com
Mon Feb 17 20:19:29 CET 2014


Split out the %IPDPADDR parsing into a helper and add testcases for it,
and add support for IPv6 handling.  It's likely that the returned IPv6
address should only be used for neighbor discovery, and that clients
should listen for IPv6 Router Advertisements to find their IPv6 prefix
and RDNSS/DNSSD information.

---
 plugins/Makefile.am                            |  17 ++-
 plugins/icera/mm-broadband-bearer-icera.c      | 164 ++++++---------------
 plugins/icera/mm-modem-helpers-icera.c         | 196 +++++++++++++++++++++++++
 plugins/icera/mm-modem-helpers-icera.h         |  28 ++++
 plugins/icera/tests/test-modem-helpers-icera.c | 158 ++++++++++++++++++++
 5 files changed, 439 insertions(+), 124 deletions(-)
 create mode 100644 plugins/icera/mm-modem-helpers-icera.c
 create mode 100644 plugins/icera/mm-modem-helpers-icera.h
 create mode 100644 plugins/icera/tests/test-modem-helpers-icera.c

diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 5041ea7..6f40bc2 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -86,21 +86,36 @@ TEST_COMMON_LIBADD_FLAGS = \
 
 # Icera-specific support
 noinst_LTLIBRARIES += libmm-utils-icera.la
 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 = \
 	libmm-plugin-generic.la \
 	libmm-plugin-nokia.la \
 	libmm-plugin-nokia-icera.la \
 	libmm-plugin-cinterion.la \
diff --git a/plugins/icera/mm-broadband-bearer-icera.c b/plugins/icera/mm-broadband-bearer-icera.c
index ad7ca06..c0874d2 100644
--- a/plugins/icera/mm-broadband-bearer-icera.c
+++ b/plugins/icera/mm-broadband-bearer-icera.c
@@ -29,14 +29,15 @@
 
 #include "mm-broadband-bearer-icera.h"
 #include "mm-base-modem-at.h"
 #include "mm-log.h"
 #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);
 
 enum {
     PROP_0,
     PROP_DEFAULT_IP_METHOD,
     PROP_LAST
@@ -98,172 +99,89 @@ get_ip_config_context_complete_and_free (GetIpConfig3gppContext *ctx)
     g_object_unref (ctx->result);
     g_object_unref (ctx->primary);
     g_object_unref (ctx->modem);
     g_object_unref (ctx->self);
     g_free (ctx);
 }
 
+typedef struct {
+    MMBearerIpConfig *ipv4;
+    MMBearerIpConfig *ipv6;
+} IpConfigs;
+
+static void
+ip_configs_free (IpConfigs *configs)
+{
+    g_clear_object (&configs->ipv4);
+    g_clear_object (&configs->ipv6);
+    g_free (configs);
+}
+
 static gboolean
 get_ip_config_3gpp_finish (MMBroadbandBearer *self,
                            GAsyncResult *res,
                            MMBearerIpConfig **ipv4_config,
                            MMBearerIpConfig **ipv6_config,
                            GError **error)
 {
-    MMBearerIpConfig *ip_config;
+    IpConfigs *configs;
 
     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));
+    *ipv4_config = g_object_ref (configs->ipv4);
+    *ipv6_config = g_object_ref (configs->ipv6);
     return TRUE;
 }
 
-#define IPDPADDR_TAG "%IPDPADDR: "
-
 static void
 ip_config_ready (MMBaseModem *modem,
                  GAsyncResult *res,
                  GetIpConfig3gppContext *ctx)
 {
-    MMBearerIpConfig *ip_config = NULL;
+    MMBearerIpConfig *ip4_config = NULL;
+    MMBearerIpConfig *ip6_config = NULL;
     const gchar *response;
     GError *error = NULL;
-    gchar **items;
-    gchar *dns[3] = { 0 };
-    guint i;
-    guint dns_i;
+    IpConfigs *configs;
 
     response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (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,
+                                           &ip4_config,
+                                           &ip6_config,
+                                           &error)) {
+        g_simple_async_result_take_error (ctx->result, error);
+        goto out;
+    }
 
-    /* Check result */
-    if (!g_str_has_prefix (response, IPDPADDR_TAG)) {
+    if (!ip4_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);
-    }
+    configs = g_new0 (IpConfigs, 1);
+    configs->ipv4 = ip4_config;
+    configs->ipv6 = ip6_config;
+    g_simple_async_result_set_op_res_gpointer (ctx->result,
+                                               configs,
+                                               (GDestroyNotify)ip_configs_free);
 
+out:
     get_ip_config_context_complete_and_free (ctx);
-    g_strfreev (items);
 }
 
 static void
 get_ip_config_3gpp (MMBroadbandBearer *self,
                     MMBroadbandModem *modem,
                     MMPortSerialAt *primary,
                     MMPortSerialAt *secondary,
@@ -280,15 +198,15 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
                                           cid,
                                           callback,
                                           user_data);
 
     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,
                                        3,
                                        FALSE,
                                        FALSE, /* raw */
                                        NULL, /* cancellable */
diff --git a/plugins/icera/mm-modem-helpers-icera.c b/plugins/icera/mm-modem-helpers-icera.c
new file mode 100644
index 0000000..ef1511f
--- /dev/null
+++ b/plugins/icera/mm-modem-helpers-icera.c
@@ -0,0 +1,196 @@
+/* -*- 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 at gnu.org>
+ * Copyright (C) 2014 Dan Williams <dcbw at redhat.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.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"
+
+/*****************************************************************************/
+/* %IPDPADDR response parser */
+
+#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;
+    gboolean success;
+    char **items;
+    gchar *dns[3] = { 0 };
+    guint dns_i = 0, num_items;
+    gint num;
+    guint32 tmp;
+
+    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:")) {
+        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, ::, ::, ::, ::, ::
+     */
+    response = mm_strip_tag (response, IPDPADDR_TAG);
+    items = g_strsplit (response, ", ", 0);
+    num_items = g_strv_length (items);
+
+    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 = mm_bearer_ip_config_new ();
+    mm_bearer_ip_config_set_method (ip4_config, MM_BEARER_IP_METHOD_STATIC);
+
+    /* IP address and prefix */
+    tmp = 0;
+    if (!inet_pton (AF_INET, items[1], &tmp)) {
+        mm_warn ("Couldn't parse IPv4 address '%s'", items[1]);
+        goto out;
+    }
+    mm_bearer_ip_config_set_address (ip4_config, items[1]);
+    /* Default prefix */
+    mm_bearer_ip_config_set_prefix (ip4_config, 32);
+
+    /* Gateway */
+    tmp = 0;
+    if (inet_pton (AF_INET, items[2], &tmp) && tmp)
+        mm_bearer_ip_config_set_gateway (ip4_config, items[2]);
+    else {
+        mm_warn ("Couldn't parse gateway address '%s'", items[2]);
+        goto out;
+    }
+
+    /* DNS */
+    tmp = 0;
+    if (inet_pton (AF_INET, items[3], &tmp) && tmp)
+        dns[dns_i++] = items[3];
+    else {
+        mm_warn ("Couldn't parse DNS address '%s'", items[3]);
+        goto out;
+    }
+    tmp = 0;
+    if (inet_pton (AF_INET, items[4], &tmp) && tmp)
+        dns[dns_i++] = items[4];
+    else {
+        mm_warn ("Couldn't parse DNS address '%s'", items[4]);
+        goto out;
+    }
+    if (dns_i > 0)
+        mm_bearer_ip_config_set_dns (ip4_config, (const gchar **) dns);
+
+    /* Secondary gateway */
+    if ((num_items >= 9) && !mm_bearer_ip_config_get_gateway (ip4_config)) {
+        const gchar *gw2 = NULL;
+
+        /* Sometimes at position 9 (index 8).  Other times, some subnet-type
+         * stuff is at position 9 and the gateway is position 10.
+         */
+        if (items[8] && !strstr (items[8], "255."))
+            gw2 = items[8];
+        else if (items[9] && !strstr (items[9], "255."))
+            gw2 = items[9];
+
+        if (gw2 && inet_pton (AF_INET, gw2, &tmp) && tmp)
+            mm_bearer_ip_config_set_gateway (ip4_config, gw2);
+        else {
+            mm_warn ("Couldn't parse secondary gateway address '%s'",
+                     gw2 ? gw2 : "(unknown)");
+            goto out;
+        }
+    }
+
+    if (num_items >= 12) {
+        struct in6_addr tmp6 = IN6ADDR_ANY_INIT;
+
+        /* It appears that for IPv6 %IPDPADDR returns only the expected
+         * link-local address and a DNS address, and that to retrieve the
+         * IPv6 prefix, extra DNS, and search domains, the host must listen
+         * for IPv6 Router Advertisements on the net port.
+         */
+
+        ip6_config = mm_bearer_ip_config_new ();
+        mm_bearer_ip_config_set_method (ip6_config, MM_BEARER_IP_METHOD_STATIC);
+
+        /* IP address and prefix */
+        if (!inet_pton (AF_INET6, items[9], &tmp6) &&
+                !IN6_IS_ADDR_UNSPECIFIED (&tmp6)) {
+            mm_warn ("Couldn't parse IPv6 address '%s'", items[9]);
+            goto out;
+        }
+        mm_bearer_ip_config_set_address (ip6_config, items[9]);
+        mm_bearer_ip_config_set_prefix (ip6_config, 128);
+
+        /* DNS server */
+        memset (&tmp6, 0, sizeof (tmp6));
+        if (inet_pton (AF_INET6, items[11], &tmp6) &&
+                !IN6_IS_ADDR_UNSPECIFIED (&tmp6)) {
+            dns[0] = items[11];
+            dns[1] = NULL;
+            mm_bearer_ip_config_set_dns (ip6_config, (const gchar **) dns);
+        } else {
+            mm_warn ("Couldn't parse DNS address '%s'", items[11]);
+            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 0000000..8a7a87a
--- /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 at 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 0000000..f704c5f
--- /dev/null
+++ b/plugins/icera/tests/test-modem-helpers-icera.c
@@ -0,0 +1,158 @@
+/* -*- 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 at gnu.org>
+ * Copyright (C) 2014 Dan Williams <dcbw at redhat.com>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.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 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", "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", "21.93.217.10", "10.177.0.34", "10.161.171.220",
+        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", "33.196.7.128", "10.177.0.34", "10.161.171.220",
+        "fe80::f:9135:5901", "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", "27.107.96.188", "121.242.190.210", "121.242.190.181",
+        NULL, NULL },
+
+    { 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 */
+        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), ==, 32);
+        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);
+        g_assert_cmpint (dnslen, ==, 2);
+        g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv4_dns1);
+        g_assert_cmpstr (dns[1], ==, ipdpaddr_tests[i].ipv4_dns2);
+
+        /* IPv6 */
+        if (ipdpaddr_tests[i].ipv6_addr) {
+            g_assert (ipv6);
+
+            g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_STATIC);
+            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), ==, 128);
+
+            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 ();
+}
-- 
1.8.5.3




More information about the ModemManager-devel mailing list