summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2015-08-19 12:44:02 -0500
committerAleksander Morgado <aleksander@aleksander.es>2016-03-13 13:25:19 +0100
commita9b69d210d12acc458d2d8ba4e79554f818e8337 (patch)
tree0b838e7604d619c9778efc33afc4fdcb2cd4e67f
parent91d343520fec5c5905a4cc3ce898f75025f3f2a8 (diff)
downloadModemManager-a9b69d210d12acc458d2d8ba4e79554f818e8337.tar.gz
huawei: use AT^DHCP response if available for NDISDUP-capable devices
For non-QMI/non-MBIM Huawei devices that use HiSense chipsets, the recommended way to create the connection is to use NDISDUP and either DHCP on the net interface, or the ^DHCP command. There are some reports of devices that connect successfully, but don't respond to DHCP requests on the interface. Try to get IP addressing info from the device via ^DHCP and fall back to telling clients to use actual DHCP if that fails. https://bugzilla.redhat.com/show_bug.cgi?id=1254886
-rw-r--r--plugins/huawei/mm-broadband-bearer-huawei.c112
-rw-r--r--plugins/huawei/mm-modem-helpers-huawei.c112
-rw-r--r--plugins/huawei/mm-modem-helpers-huawei.h10
-rw-r--r--plugins/huawei/tests/test-modem-helpers-huawei.c51
4 files changed, 276 insertions, 9 deletions
diff --git a/plugins/huawei/mm-broadband-bearer-huawei.c b/plugins/huawei/mm-broadband-bearer-huawei.c
index 6c64d4d0e..60a91e552 100644
--- a/plugins/huawei/mm-broadband-bearer-huawei.c
+++ b/plugins/huawei/mm-broadband-bearer-huawei.c
@@ -67,6 +67,7 @@ typedef enum {
CONNECT_3GPP_CONTEXT_STEP_FIRST = 0,
CONNECT_3GPP_CONTEXT_STEP_NDISDUP,
CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY,
+ CONNECT_3GPP_CONTEXT_STEP_IP_CONFIG,
CONNECT_3GPP_CONTEXT_STEP_LAST
} Connect3gppContextStep;
@@ -80,6 +81,7 @@ typedef struct {
Connect3gppContextStep step;
guint check_count;
guint failed_ndisstatqry_count;
+ MMBearerIpConfig *ipv4_config;
} Connect3gppContext;
static void
@@ -92,6 +94,7 @@ connect_3gpp_context_complete_and_free (Connect3gppContext *ctx)
g_object_unref (ctx->modem);
g_object_unref (ctx->self);
+ g_clear_object (&ctx->ipv4_config);
g_clear_object (&ctx->data);
g_clear_object (&ctx->primary);
@@ -111,6 +114,88 @@ connect_3gpp_finish (MMBroadbandBearer *self,
static void connect_3gpp_context_step (Connect3gppContext *ctx);
+static void
+connect_dhcp_check_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ MMBroadbandBearerHuawei *self)
+{
+ Connect3gppContext *ctx;
+ const gchar *response;
+ GError *error = NULL;
+
+ ctx = self->priv->connect_pending;
+ g_assert (ctx != NULL);
+
+ /* Balance refcount */
+ g_object_unref (self);
+
+ /* Default to automatic/DHCP addressing */
+ ctx->ipv4_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_DHCP);
+
+ /* Cache IPv4 details if available, otherwise clients will have to use DHCP */
+ response = mm_base_modem_at_command_full_finish (modem, res, &error);
+ if (response) {
+ guint address = 0;
+ guint prefix = 0;
+ guint gateway = 0;
+ guint dns1 = 0;
+ guint dns2 = 0;
+
+ if (mm_huawei_parse_dhcp_response (response,
+ &address,
+ &prefix,
+ &gateway,
+ &dns1,
+ &dns2,
+ &error)) {
+ GInetAddress *addr;
+ gchar *strarr[3] = { NULL, NULL, NULL };
+ guint n = 0;
+ gchar *str;
+
+ mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_STATIC);
+
+ addr = g_inet_address_new_from_bytes ((guint8 *)&address, G_SOCKET_FAMILY_IPV4);
+ str = g_inet_address_to_string (addr);
+ mm_bearer_ip_config_set_address (ctx->ipv4_config, str);
+ g_free (str);
+ g_object_unref (addr);
+
+ /* Netmask */
+ mm_bearer_ip_config_set_prefix (ctx->ipv4_config, prefix);
+
+ /* Gateway */
+ addr = g_inet_address_new_from_bytes ((guint8 *)&gateway, G_SOCKET_FAMILY_IPV4);
+ str = g_inet_address_to_string (addr);
+ mm_bearer_ip_config_set_gateway (ctx->ipv4_config, str);
+ g_free (str);
+ g_object_unref (addr);
+
+ /* DNS */
+ if (dns1) {
+ addr = g_inet_address_new_from_bytes ((guint8 *)&dns1, G_SOCKET_FAMILY_IPV4);
+ strarr[n++] = g_inet_address_to_string (addr);
+ g_object_unref (addr);
+ }
+ if (dns2) {
+ addr = g_inet_address_new_from_bytes ((guint8 *)&dns2, G_SOCKET_FAMILY_IPV4);
+ strarr[n++] = g_inet_address_to_string (addr);
+ g_object_unref (addr);
+ }
+ mm_bearer_ip_config_set_dns (ctx->ipv4_config, (const gchar **)strarr);
+ g_free (strarr[0]);
+ g_free (strarr[1]);
+ } else {
+ mm_dbg ("Unexpected response to ^DHCP command: %s", error->message);
+ }
+ }
+
+ g_clear_error (&error);
+ ctx->step++;
+ connect_3gpp_context_step (ctx);
+}
+
static gboolean
connect_retry_ndisstatqry_check_cb (MMBroadbandBearerHuawei *self)
{
@@ -380,21 +465,30 @@ connect_3gpp_context_step (Connect3gppContext *ctx)
g_object_ref (ctx->self));
return;
+ case CONNECT_3GPP_CONTEXT_STEP_IP_CONFIG:
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ "^DHCP?",
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)connect_dhcp_check_ready,
+ g_object_ref (ctx->self));
+ return;
+
case CONNECT_3GPP_CONTEXT_STEP_LAST:
/* Clear context */
ctx->self->priv->connect_pending = NULL;
/* Setup result */
{
- MMBearerIpConfig *ipv4_config;
-
- ipv4_config = mm_bearer_ip_config_new ();
- mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP);
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- mm_bearer_connect_result_new (ctx->data, ipv4_config, NULL),
- (GDestroyNotify)mm_bearer_connect_result_unref);
- g_object_unref (ipv4_config);
+ if (ctx->ipv4_config) {
+ g_simple_async_result_set_op_res_gpointer (
+ ctx->result,
+ mm_bearer_connect_result_new (ctx->data, ctx->ipv4_config, NULL),
+ (GDestroyNotify)mm_bearer_connect_result_unref);
+ }
}
connect_3gpp_context_complete_and_free (ctx);
diff --git a/plugins/huawei/mm-modem-helpers-huawei.c b/plugins/huawei/mm-modem-helpers-huawei.c
index 5362c55a4..87a54e429 100644
--- a/plugins/huawei/mm-modem-helpers-huawei.c
+++ b/plugins/huawei/mm-modem-helpers-huawei.c
@@ -152,6 +152,118 @@ mm_huawei_parse_ndisstatqry_response (const gchar *response,
}
/*****************************************************************************/
+/* ^DHCP response parser */
+
+static gboolean
+match_info_to_ip4_addr (GMatchInfo *match_info,
+ guint match_index,
+ guint *out_addr)
+{
+ gchar *s, *bin = NULL;
+ gchar buf[9];
+ gsize len, bin_len;
+ gboolean success = FALSE;
+
+ s = g_match_info_fetch (match_info, match_index);
+ g_return_val_if_fail (s != NULL, FALSE);
+
+ len = strlen (s);
+ if (len == 1 && s[0] == '0') {
+ *out_addr = 0;
+ success = TRUE;
+ goto done;
+ }
+ if (len < 7 || len > 8)
+ goto done;
+
+ /* Handle possibly missing leading zero */
+ memset (buf, 0, sizeof (buf));
+ if (len == 7) {
+ strcpy (&buf[1], s);
+ buf[0] = '0';
+ } else if (len == 8)
+ strcpy (buf, s);
+ else
+ g_assert_not_reached ();
+
+ bin = mm_utils_hexstr2bin (buf, &bin_len);
+ if (!bin || bin_len != 4)
+ goto done;
+
+ *out_addr = GUINT32_TO_BE (*((guint32 *) bin));
+ success = TRUE;
+
+done:
+ g_free (s);
+ g_free (bin);
+ return success;
+}
+
+gboolean
+mm_huawei_parse_dhcp_response (const char *reply,
+ guint *out_address,
+ guint *out_prefix,
+ guint *out_gateway,
+ guint *out_dns1,
+ guint *out_dns2,
+ GError **error)
+{
+ gboolean matched;
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *match_error = NULL;
+
+ g_assert (reply != NULL);
+ g_assert (out_address != NULL);
+ g_assert (out_prefix != NULL);
+ g_assert (out_gateway != NULL);
+ g_assert (out_dns1 != NULL);
+ g_assert (out_dns2 != NULL);
+
+ /* Format:
+ *
+ * ^DHCP: <address>,<netmask>,<gateway>,<?>,<dns1>,<dns2>,<uplink>,<downlink>
+ *
+ * All numbers are hexadecimal representations of IPv4 addresses, with
+ * least-significant byte first. eg, 192.168.50.32 is expressed as
+ * "2032A8C0". Sometimes leading zeros are stripped, so "1010A0A" is
+ * actually 10.10.1.1.
+ */
+
+ r = g_regex_new ("\\^DHCP:\\s*([0-9a-fA-F]+),([0-9a-fA-F]+),([0-9a-fA-F]+),([0-9a-fA-F]+),([0-9a-fA-F]+),([0-9a-fA-F]+),.*$", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ matched = g_regex_match_full (r, reply, -1, 0, 0, &match_info, &match_error);
+ if (!matched) {
+ if (match_error) {
+ g_propagate_error (error, match_error);
+ g_prefix_error (error, "Could not parse ^DHCP results: ");
+ } else {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't match ^DHCP reply");
+ }
+ } else {
+ guint netmask;
+
+ if (match_info_to_ip4_addr (match_info, 1, out_address) &&
+ match_info_to_ip4_addr (match_info, 2, &netmask) &&
+ match_info_to_ip4_addr (match_info, 3, out_gateway) &&
+ match_info_to_ip4_addr (match_info, 5, out_dns1) &&
+ match_info_to_ip4_addr (match_info, 6, out_dns2)) {
+ *out_prefix = mm_count_bits_set (netmask);
+ matched = TRUE;
+ }
+ }
+
+ if (match_info)
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ return matched;
+}
+
+/*****************************************************************************/
/* ^SYSINFO response parser */
gboolean
diff --git a/plugins/huawei/mm-modem-helpers-huawei.h b/plugins/huawei/mm-modem-helpers-huawei.h
index e225e9087..62019e8cc 100644
--- a/plugins/huawei/mm-modem-helpers-huawei.h
+++ b/plugins/huawei/mm-modem-helpers-huawei.h
@@ -29,6 +29,16 @@ gboolean mm_huawei_parse_ndisstatqry_response (const gchar *response,
GError **error);
/*****************************************************************************/
+/* ^DHCP response parser */
+gboolean mm_huawei_parse_dhcp_response (const char *reply,
+ guint *out_address,
+ guint *out_prefix,
+ guint *out_gateway,
+ guint *out_dns1,
+ guint *out_dns2,
+ GError **error);
+
+/*****************************************************************************/
/* ^SYSINFO response parser */
gboolean mm_huawei_parse_sysinfo_response (const char *reply,
guint *out_srv_status,
diff --git a/plugins/huawei/tests/test-modem-helpers-huawei.c b/plugins/huawei/tests/test-modem-helpers-huawei.c
index 19e6decc4..bd0a8a6dd 100644
--- a/plugins/huawei/tests/test-modem-helpers-huawei.c
+++ b/plugins/huawei/tests/test-modem-helpers-huawei.c
@@ -16,6 +16,7 @@
#include <glib.h>
#include <glib-object.h>
#include <locale.h>
+#include <arpa/inet.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
@@ -139,6 +140,55 @@ test_ndisstatqry (void)
}
/*****************************************************************************/
+/* Test ^DHCP responses */
+
+typedef struct {
+ const gchar *str;
+ const gchar *expected_addr;
+ guint expected_prefix;
+ const gchar *expected_gateway;
+ const gchar *expected_dns1;
+ const gchar *expected_dns2;
+} DhcpTest;
+
+static const DhcpTest dhcp_tests[] = {
+ { "^DHCP:a3ec5c64,f8ffffff,a1ec5c64,a1ec5c64,2200b10a,74bba80a,236800,236800\r\n",
+ "100.92.236.163", 29, "100.92.236.161", "10.177.0.34", "10.168.187.116" },
+ { "^DHCP: 1010A0A,FCFFFFFF,2010A0A,2010A0A,0,0,150000000,150000000\r\n",
+ "10.10.1.1", 30, "10.10.1.2", "0.0.0.0", "0.0.0.0" },
+ { "^DHCP: CCDB080A,F8FFFFFF,C9DB080A,C9DB080A,E67B59C0,E77B59C0,85600,85600\r\n",
+ "10.8.219.204", 29, "10.8.219.201", "192.89.123.230", "192.89.123.231" },
+ { NULL }
+};
+
+static void
+test_dhcp (void)
+{
+ guint i;
+
+ for (i = 0; dhcp_tests[i].str; i++) {
+ GError *error = NULL;
+ guint addr, prefix, gateway, dns1, dns2;
+
+ g_assert (mm_huawei_parse_dhcp_response (
+ dhcp_tests[i].str,
+ &addr,
+ &prefix,
+ &gateway,
+ &dns1,
+ &dns2,
+ &error) == TRUE);
+ g_assert_no_error (error);
+
+ g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &addr)), ==, dhcp_tests[i].expected_addr);
+ g_assert_cmpint (prefix, ==, dhcp_tests[i].expected_prefix);
+ g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &gateway)), ==, dhcp_tests[i].expected_gateway);
+ g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &dns1)), ==, dhcp_tests[i].expected_dns1);
+ g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &dns2)), ==, dhcp_tests[i].expected_dns2);
+ }
+}
+
+/*****************************************************************************/
/* Test ^SYSINFO responses */
typedef struct {
@@ -1171,6 +1221,7 @@ int main (int argc, char **argv)
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/MM/huawei/ndisstatqry", test_ndisstatqry);
+ g_test_add_func ("/MM/huawei/dhcp", test_dhcp);
g_test_add_func ("/MM/huawei/sysinfo", test_sysinfo);
g_test_add_func ("/MM/huawei/sysinfoex", test_sysinfoex);
g_test_add_func ("/MM/huawei/prefmode", test_prefmode);