diff options
author | Dan Williams <dcbw@redhat.com> | 2014-06-13 14:57:08 -0500 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2014-06-13 14:57:08 -0500 |
commit | 6e490944d07061ca15837963552d023807958feb (patch) | |
tree | 70661775ab5381c240fb016eedb91fd602374881 /src | |
parent | ff4e45791ddac7086c6b45b9b732c67d54dc6c1a (diff) | |
parent | 02f9b926cd5776835bcd7c2b77c452e2ff8169d9 (diff) |
merge: fix IPv6 support and operation
Notable changes include reporting all available IP configruation
information in the bearer IP details, even if the method is DHCP,
in which case the client should apply the given configuration and
then proceed with DHCP/PPP/SLAAC/etc addressing mechanisms.
QMI, MBIM, and some other plugins have been switched to the "STATIC"
configuration method from DHCP when the firmware supplies all the
necessary details.
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-bearer-mbim.c | 8 | ||||
-rw-r--r-- | src/mm-bearer-qmi.c | 306 | ||||
-rw-r--r-- | src/mm-broadband-bearer.c | 30 | ||||
-rw-r--r-- | src/mm-broadband-bearer.h | 1 |
4 files changed, 226 insertions, 119 deletions
diff --git a/src/mm-bearer-mbim.c b/src/mm-bearer-mbim.c index b1e3176c..c25b2811 100644 --- a/src/mm-bearer-mbim.c +++ b/src/mm-bearer-mbim.c @@ -370,6 +370,14 @@ ip_configuration_query_ready (MbimDevice *device, str = g_inet_address_to_string (addr); mm_bearer_ip_config_set_address (ipv6_config, str); g_free (str); + + /* 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 (g_inet_address_get_is_link_local (addr)) + mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP); + g_object_unref (addr); /* Netmask */ diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c index 4c693d24..41fdc6eb 100644 --- a/src/mm-bearer-qmi.c +++ b/src/mm-bearer-qmi.c @@ -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); - g_clear_error (&error); - - /* 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_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"); - /* 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 */ + 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 (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; diff --git a/src/mm-broadband-bearer.c b/src/mm-broadband-bearer.c index de762d76..bed9ec2d 100644 --- a/src/mm-broadband-bearer.c +++ b/src/mm-broadband-bearer.c @@ -674,7 +674,9 @@ dial_3gpp_ready (MMBroadbandModem *modem, GAsyncResult *res, DetailedConnectContext *ctx) { - MMBearerIpConfig *config; + MMBearerIpMethod ip_method = MM_BEARER_IP_METHOD_UNKNOWN; + MMBearerIpConfig *ipv4_config = NULL; + MMBearerIpConfig *ipv6_config = NULL; GError *error = NULL; ctx->data = MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->dial_3gpp_finish (ctx->self, res, &error); @@ -701,6 +703,7 @@ dial_3gpp_ready (MMBroadbandModem *modem, ctx->secondary, ctx->data, ctx->cid, + ctx->ip_family, (GAsyncReadyCallback)get_ip_config_3gpp_ready, ctx); return; @@ -714,19 +717,30 @@ dial_3gpp_ready (MMBroadbandModem *modem, /* If no specific IP retrieval requested, set the default implementation * (PPP if data port is AT, DHCP otherwise) */ - config = mm_bearer_ip_config_new (); - mm_bearer_ip_config_set_method (config, - (MM_IS_PORT_SERIAL_AT (ctx->data) ? - MM_BEARER_IP_METHOD_PPP : - MM_BEARER_IP_METHOD_DHCP)); + ip_method = MM_IS_PORT_SERIAL_AT (ctx->data) ? + MM_BEARER_IP_METHOD_PPP : + MM_BEARER_IP_METHOD_DHCP; + + if (ctx->ip_family & MM_BEARER_IP_FAMILY_IPV4 || + ctx->ip_family & MM_BEARER_IP_FAMILY_IPV4V6) { + ipv4_config = mm_bearer_ip_config_new (); + mm_bearer_ip_config_set_method (ipv4_config, ip_method); + } + if (ctx->ip_family & MM_BEARER_IP_FAMILY_IPV6 || + ctx->ip_family & MM_BEARER_IP_FAMILY_IPV4V6) { + ipv6_config = mm_bearer_ip_config_new (); + mm_bearer_ip_config_set_method (ipv6_config, ip_method); + } + g_assert (ipv4_config || ipv6_config); g_simple_async_result_set_op_res_gpointer ( ctx->result, - mm_bearer_connect_result_new (ctx->data, config, NULL), + mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config), (GDestroyNotify)mm_bearer_connect_result_unref); detailed_connect_context_complete_and_free (ctx); - g_object_unref (config); + g_clear_object (&ipv4_config); + g_clear_object (&ipv6_config); } static void diff --git a/src/mm-broadband-bearer.h b/src/mm-broadband-bearer.h index 4a008cd1..f3869120 100644 --- a/src/mm-broadband-bearer.h +++ b/src/mm-broadband-bearer.h @@ -79,6 +79,7 @@ struct _MMBroadbandBearerClass { MMPortSerialAt *secondary, MMPort *data, guint cid, + MMBearerIpFamily ip_family, GAsyncReadyCallback callback, gpointer user_data); gboolean (* get_ip_config_3gpp_finish) (MMBroadbandBearer *self, |