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
This commit is contained in:
committed by
Aleksander Morgado
parent
91d343520f
commit
a9b69d210d
@@ -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);
|
||||
|
||||
@@ -151,6 +151,118 @@ mm_huawei_parse_ndisstatqry_response (const gchar *response,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* ^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 */
|
||||
|
||||
|
||||
@@ -28,6 +28,16 @@ gboolean mm_huawei_parse_ndisstatqry_response (const gchar *response,
|
||||
gboolean *ipv6_connected,
|
||||
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,
|
||||
|
||||
@@ -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
|
||||
@@ -138,6 +139,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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user