broadband-bearer-qmi: support IPv6 configuration and use static for IPv4

This commit is contained in:
Dan Williams
2014-06-13 11:30:49 -05:00
parent 8017628353
commit ca0a567f74
+195 -111
View File
@@ -31,6 +31,7 @@
#include "mm-modem-helpers-qmi.h"
#include "mm-port-enums-types.h"
#include "mm-log.h"
#include "mm-modem-helpers.h"
G_DEFINE_TYPE (MMBearerQmi, mm_bearer_qmi, MM_TYPE_BEARER);
@@ -82,12 +83,14 @@ typedef struct {
gboolean running_ipv4;
QmiClientWds *client_ipv4;
guint32 packet_data_handle_ipv4;
MMBearerIpConfig *ipv4_config;
GError *error_ipv4;
gboolean ipv6;
gboolean running_ipv6;
QmiClientWds *client_ipv6;
guint32 packet_data_handle_ipv6;
MMBearerIpConfig *ipv6_config;
GError *error_ipv6;
} ConnectContext;
@@ -99,14 +102,12 @@ connect_context_complete_and_free (ConnectContext *ctx)
g_free (ctx->apn);
g_free (ctx->user);
g_free (ctx->password);
if (ctx->error_ipv4)
g_error_free (ctx->error_ipv4);
if (ctx->error_ipv6)
g_error_free (ctx->error_ipv6);
if (ctx->client_ipv4)
g_object_unref (ctx->client_ipv4);
if (ctx->client_ipv6)
g_object_unref (ctx->client_ipv6);
g_clear_error (&ctx->error_ipv4);
g_clear_error (&ctx->error_ipv6);
g_clear_object (&ctx->client_ipv4);
g_clear_object (&ctx->client_ipv6);
g_clear_object (&ctx->ipv4_config);
g_clear_object (&ctx->ipv6_config);
g_object_unref (ctx->data);
g_object_unref (ctx->qmi);
g_object_unref (ctx->cancellable);
@@ -247,51 +248,191 @@ build_start_network_input (ConnectContext *ctx)
}
static void
print_address4 (gboolean success, const char *tag, guint32 address, GError *error)
qmi_inet4_ntop (guint32 address, char *buf, const gsize buflen)
{
struct in_addr a = { .s_addr = GUINT32_TO_BE (address) };
char buf[INET_ADDRSTRLEN + 1];
if (success) {
memset (buf, 0, sizeof (buf));
if (inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1))
mm_dbg (" %s: %s", tag, buf);
else
mm_dbg (" %s: failed (address conversion error)", tag);
return;
g_assert (buflen >= INET_ADDRSTRLEN);
/* We can ignore inet_ntop() return value if 'buf' is
* at least INET_ADDRSTRLEN in size. */
memset (buf, 0, buflen);
g_assert (inet_ntop (AF_INET, &a, buf, buflen));
}
static MMBearerIpConfig *
get_ipv4_config (QmiMessageWdsGetCurrentSettingsOutput *output, guint32 mtu)
{
MMBearerIpConfig *config;
char buf[INET_ADDRSTRLEN];
char buf2[INET_ADDRSTRLEN];
const gchar *dns[3] = { 0 };
guint dns_idx = 0;
guint32 addr = 0;
GError *error = NULL;
guint32 prefix = 0;
/* IPv4 subnet mask */
if (!qmi_message_wds_get_current_settings_output_get_ipv4_gateway_subnet_mask (output, &addr, &error)) {
mm_dbg ("Failed to read IPv4 netmask (%s)", error->message);
g_clear_error (&error);
return NULL;
}
mm_dbg (" %s: failed (%s)", tag, error ? error->message : "unknown");
qmi_inet4_ntop (addr, buf, sizeof (buf));
prefix = mm_netmask_to_cidr (buf);
/* IPv4 address */
if (!qmi_message_wds_get_current_settings_output_get_ipv4_address (output, &addr, &error)) {
mm_dbg ("IPv4 family but no IPv4 address (%s)", error->message);
g_clear_error (&error);
return NULL;
}
mm_dbg ("QMI IPv4 Settings:");
config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
/* IPv4 address */
qmi_inet4_ntop (addr, buf, sizeof (buf));
mm_bearer_ip_config_set_address (config, buf);
mm_bearer_ip_config_set_prefix (config, prefix);
mm_dbg (" Address: %s/%d", buf, prefix);
/* IPv4 gateway address */
if (qmi_message_wds_get_current_settings_output_get_ipv4_gateway_address (output, &addr, &error)) {
qmi_inet4_ntop (addr, buf, sizeof (buf));
mm_bearer_ip_config_set_gateway (config, buf);
mm_dbg (" Gateway: %s", buf);
} else {
mm_dbg (" Gateway: failed (%s)", error->message);
g_clear_error (&error);
}
/* IPv4 DNS #1 */
if (qmi_message_wds_get_current_settings_output_get_primary_ipv4_dns_address (output, &addr, &error)) {
qmi_inet4_ntop (addr, buf, sizeof (buf));
dns[dns_idx++] = buf;
mm_dbg (" DNS #1: %s", buf);
} else {
mm_dbg (" DNS #1: failed (%s)", error->message);
g_clear_error (&error);
}
/* IPv6 DNS #2 */
if (qmi_message_wds_get_current_settings_output_get_secondary_ipv4_dns_address (output, &addr, &error)) {
qmi_inet4_ntop (addr, buf2, sizeof (buf2));
dns[dns_idx++] = buf2;
mm_dbg (" DNS #2: %s", buf2);
} else {
mm_dbg (" DNS #2: failed (%s)", error->message);
g_clear_error (&error);
}
if (dns_idx > 0)
mm_bearer_ip_config_set_dns (config, (const gchar **) &dns);
if (mtu) {
mm_bearer_ip_config_set_mtu (config, mtu);
mm_dbg (" MTU: %d", mtu);
}
return config;
}
static void
print_address6 (gboolean success,
const char *tag,
GArray *array,
guint32 prefix,
GError *error)
qmi_inet6_ntop (GArray *array, char *buf, const gsize buflen)
{
struct in6_addr a;
char buf[INET6_ADDRSTRLEN + 1];
guint32 i;
if (success) {
g_assert (array);
g_assert (array->len == 8);
g_assert (array);
g_assert (array->len == 8);
g_assert (buflen >= INET6_ADDRSTRLEN);
memset (buf, 0, sizeof (buf));
for (i = 0; i < array->len; i++)
a.s6_addr16[i] = GUINT16_TO_BE (g_array_index (array, guint16, i));
for (i = 0; i < array->len; i++)
a.s6_addr16[i] = GUINT16_TO_BE (g_array_index (array, guint16, i));
if (inet_ntop (AF_INET6, &a, buf, sizeof (buf) - 1))
mm_dbg (" %s: %s/%d", tag, buf, prefix);
else
mm_dbg (" %s: failed (address conversion error)", tag);
/* We can ignore inet_ntop() return value if 'buf' is
* at least INET6_ADDRSTRLEN in size. */
memset (buf, 0, buflen);
g_assert (inet_ntop (AF_INET6, &a, buf, buflen));
}
g_array_free (array, TRUE);
return;
static MMBearerIpConfig *
get_ipv6_config (QmiMessageWdsGetCurrentSettingsOutput *output, guint32 mtu)
{
MMBearerIpConfig *config;
char buf[INET6_ADDRSTRLEN];
char buf2[INET6_ADDRSTRLEN];
const gchar *dns[3] = { 0 };
guint dns_idx = 0;
GArray *array;
GError *error = NULL;
guint8 prefix = 0;
/* If the message has an IPv6 address, create an IPv6 bearer config */
if (!qmi_message_wds_get_current_settings_output_get_ipv6_address (output, &array, &prefix, &error)) {
mm_dbg ("IPv6 family but no IPv6 address (%s)", error->message);
g_clear_error (&error);
return NULL;
}
mm_dbg (" %s: failed (%s)", tag, error ? error->message : "unknown");
mm_dbg ("QMI IPv6 Settings:");
config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
/* IPv6 address */
qmi_inet6_ntop (array, buf, sizeof (buf));
g_array_unref (array);
mm_bearer_ip_config_set_address (config, buf);
mm_bearer_ip_config_set_prefix (config, prefix);
mm_dbg (" Address: %s/%d", buf, prefix);
/* IPv6 gateway address */
if (qmi_message_wds_get_current_settings_output_get_ipv6_gateway_address (output, &array, &prefix, &error)) {
qmi_inet6_ntop (array, buf, sizeof (buf));
mm_bearer_ip_config_set_gateway (config, buf);
mm_dbg (" Gateway: %s", buf);
g_array_unref (array);
} else {
mm_dbg (" Gateway: failed (%s)", error->message);
g_clear_error (&error);
}
/* IPv6 DNS #1 */
if (qmi_message_wds_get_current_settings_output_get_ipv6_primary_dns_address (output, &array, &error)) {
qmi_inet6_ntop (array, buf, sizeof (buf));
dns[dns_idx++] = buf;
mm_dbg (" DNS #1: %s", buf);
g_array_unref (array);
} else {
mm_dbg (" DNS #1: failed (%s)", error->message);
g_clear_error (&error);
}
/* IPv6 DNS #2 */
if (qmi_message_wds_get_current_settings_output_get_ipv6_secondary_dns_address (output, &array, &error)) {
qmi_inet6_ntop (array, buf2, sizeof (buf2));
dns[dns_idx++] = buf2;
mm_dbg (" DNS #2: %s", buf2);
g_array_unref (array);
} else {
mm_dbg (" DNS #2: failed (%s)", error->message);
g_clear_error (&error);
}
if (dns_idx > 0)
mm_bearer_ip_config_set_dns (config, (const gchar **) &dns);
if (mtu) {
mm_bearer_ip_config_set_mtu (config, mtu);
mm_dbg (" MTU: %d", mtu);
}
return config;
}
static void
@@ -312,73 +453,33 @@ get_current_settings_ready (QmiClientWds *client,
mm_info ("error: couldn't get current settings: %s", error->message);
g_error_free (error);
} else {
gboolean success;
guint32 addr, mtu, i;
QmiWdsIpFamily ip_family = QMI_WDS_IP_FAMILY_UNSPECIFIED;
guint32 mtu = 0;
GArray *array;
guint8 prefix;
/* If the message has an IPv4 address, print IPv4 settings */
if (qmi_message_wds_get_current_settings_output_get_ipv4_address (output, &addr, &error)) {
mm_dbg ("QMI IPv4 Settings:");
/* IPv4 address */
print_address4 (TRUE, "Address", addr, error);
if (!qmi_message_wds_get_current_settings_output_get_ip_family (output, &ip_family, &error)) {
mm_dbg (" IP Family: failed (%s); assuming IPv4", error->message);
g_clear_error (&error);
ip_family = QMI_WDS_IP_FAMILY_IPV4;
}
mm_dbg (" IP Family: %s",
(ip_family == QMI_WDS_IP_FAMILY_IPV4) ? "IPv4" :
(ip_family == QMI_WDS_IP_FAMILY_IPV6) ? "IPv6" : "unknown");
/* IPv4 gateway address */
success = qmi_message_wds_get_current_settings_output_get_ipv4_gateway_address (output, &addr, &error);
print_address4 (success, "Gateway", addr, error);
g_clear_error (&error);
/* IPv4 subnet mask */
success = qmi_message_wds_get_current_settings_output_get_ipv4_gateway_subnet_mask (output, &addr, &error);
print_address4 (success, "Netmask", addr, error);
g_clear_error (&error);
/* IPv4 DNS #1 */
success = qmi_message_wds_get_current_settings_output_get_primary_ipv4_dns_address (output, &addr, &error);
print_address4 (success, " DNS #1", addr, error);
g_clear_error (&error);
/* IPv4 DNS #2 */
success = qmi_message_wds_get_current_settings_output_get_secondary_ipv4_dns_address (output, &addr, &error);
print_address4 (success, " DNS #2", addr, error);
g_clear_error (&error);
} else {
/* no IPv4 configuration */
if (!qmi_message_wds_get_current_settings_output_get_mtu (output, &mtu, &error)) {
mm_dbg (" MTU: failed (%s)", error->message);
g_clear_error (&error);
}
/* If the message has an IPv6 address, print IPv6 settings */
if (qmi_message_wds_get_current_settings_output_get_ipv6_address (output, &array, &prefix, &error)) {
mm_dbg ("QMI IPv6 Settings:");
/* IPv6 address */
print_address6 (TRUE, "Address", array, prefix, error);
g_clear_error (&error);
/* IPv6 gateway address */
success = qmi_message_wds_get_current_settings_output_get_ipv6_gateway_address (output, &array, &prefix, &error);
print_address6 (success, "Gateway", array, prefix, error);
g_clear_error (&error);
/* IPv6 DNS #1 */
success = qmi_message_wds_get_current_settings_output_get_ipv6_primary_dns_address (output, &array, &error);
print_address6 (success, " DNS #1", array, 0, error);
g_clear_error (&error);
/* IPv6 DNS #2 */
success = qmi_message_wds_get_current_settings_output_get_ipv6_secondary_dns_address (output, &array, &error);
print_address6 (success, " DNS #2", array, 0, error);
g_clear_error (&error);
} else {
/* no IPv6 configuration */
g_clear_error (&error);
}
if (ip_family == QMI_WDS_IP_FAMILY_IPV4)
ctx->ipv4_config = get_ipv4_config (output, mtu);
else if (ip_family == QMI_WDS_IP_FAMILY_IPV6)
ctx->ipv6_config = get_ipv6_config (output, mtu);
/* Domain names */
if (qmi_message_wds_get_current_settings_output_get_domain_name_list (output, &array, &error)) {
GString *s = g_string_sized_new (array ? (array->len * 20) : 1);
guint i;
for (i = 0; array && (i < array->len); i++) {
if (s->len)
@@ -391,13 +492,6 @@ get_current_settings_ready (QmiClientWds *client,
mm_dbg (" Domains: failed (%s)", error ? error->message : "unknown");
g_clear_error (&error);
}
if (qmi_message_wds_get_current_settings_output_get_mtu (output, &mtu, &error))
mm_dbg (" MTU: %d", mtu);
else {
mm_dbg (" MTU: failed (%s)", error ? error->message : "unknown");
g_clear_error (&error);
}
}
if (output)
@@ -728,8 +822,6 @@ connect_context_step (ConnectContext *ctx)
case CONNECT_STEP_LAST:
/* If one of IPv4 or IPv6 succeeds, we're connected */
if (ctx->packet_data_handle_ipv4 || ctx->packet_data_handle_ipv6) {
MMBearerIpConfig *config;
/* Port is connected; update the state */
mm_port_set_connected (MM_PORT (ctx->data), TRUE);
@@ -751,19 +843,11 @@ connect_context_step (ConnectContext *ctx)
ctx->self->priv->client_ipv6 = g_object_ref (ctx->client_ipv6);
}
/* Build IP config; always DHCP based */
config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
/* Set operation result */
g_simple_async_result_set_op_res_gpointer (
ctx->result,
mm_bearer_connect_result_new (
ctx->data,
ctx->packet_data_handle_ipv4 ? config : NULL,
ctx->packet_data_handle_ipv6 ? config : NULL),
mm_bearer_connect_result_new (ctx->data, ctx->ipv4_config, ctx->ipv6_config),
(GDestroyNotify)mm_bearer_connect_result_unref);
g_object_unref (config);
} else {
GError *error;