wwan: add IPv6 support for ModemManager1 (bgo #682623)

https://bugzilla.gnome.org/show_bug.cgi?id=682623
This commit is contained in:
Dan Williams 2013-10-16 09:30:43 -05:00
parent 8c05e26c42
commit 0caea7db2c

View file

@ -157,13 +157,29 @@ ask_for_pin (NMModemBroadband *self)
NM_SETTING_GSM_PIN);
}
static NMModemIPMethod
get_bearer_ip_method (MMBearerIpConfig *config)
{
MMBearerIpMethod mm_method;
mm_method = mm_bearer_ip_config_get_method (config);
if (mm_method == MM_BEARER_IP_METHOD_PPP)
return NM_MODEM_IP_METHOD_PPP;
else if (mm_method == MM_BEARER_IP_METHOD_STATIC)
return NM_MODEM_IP_METHOD_STATIC;
else if (mm_method == MM_BEARER_IP_METHOD_DHCP)
return NM_MODEM_IP_METHOD_AUTO;
return NM_MODEM_IP_METHOD_UNKNOWN;
}
static void
connect_ready (MMModemSimple *simple_iface,
GAsyncResult *res,
NMModemBroadband *self)
{
GError *error = NULL;
NMModemIPMethod ip_method;
NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
g_clear_object (&self->priv->connect_properties);
@ -196,35 +212,31 @@ connect_ready (MMModemSimple *simple_iface,
/* Grab IP configurations */
self->priv->ipv4_config = mm_bearer_get_ipv4_config (self->priv->bearer);
self->priv->ipv6_config = mm_bearer_get_ipv6_config (self->priv->bearer);
if (self->priv->ipv4_config)
ip4_method = get_bearer_ip_method (self->priv->ipv4_config);
switch (mm_bearer_ip_config_get_method (self->priv->ipv4_config)) {
case MM_BEARER_IP_METHOD_PPP:
ip_method = NM_MODEM_IP_METHOD_PPP;
break;
case MM_BEARER_IP_METHOD_STATIC:
ip_method = NM_MODEM_IP_METHOD_STATIC;
break;
case MM_BEARER_IP_METHOD_DHCP:
ip_method = NM_MODEM_IP_METHOD_AUTO;
break;
default:
error = g_error_new (NM_MODEM_ERROR,
NM_MODEM_ERROR_CONNECTION_INVALID,
"invalid IP config");
nm_log_warn (LOGD_MB, "(%s) failed to connect modem: %s",
nm_modem_get_uid (NM_MODEM (self)),
error->message);
g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error));
self->priv->ipv6_config = mm_bearer_get_ipv6_config (self->priv->bearer);
if (self->priv->ipv6_config)
ip6_method = get_bearer_ip_method (self->priv->ipv6_config);
if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN &&
ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) {
nm_log_warn (LOGD_MB, "(%s) failed to connect modem: invalid bearer IP configuration",
nm_modem_get_uid (NM_MODEM (self)));
error = g_error_new_literal (NM_MODEM_ERROR,
NM_MODEM_ERROR_CONNECTION_INVALID,
"invalid bearer IP configuration");
g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, error);
g_error_free (error);
g_object_unref (self);
return;
}
/* IPv4 for now only */
g_object_set (self,
NM_MODEM_DATA_PORT, mm_bearer_get_interface (self->priv->bearer),
NM_MODEM_IP4_METHOD, ip_method,
NM_MODEM_IP4_METHOD, ip4_method,
NM_MODEM_IP6_METHOD, ip6_method,
NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->priv->bearer),
NULL);
@ -250,12 +262,15 @@ create_cdma_connect_properties (NMConnection *connection)
}
static MMSimpleConnectProperties *
create_gsm_connect_properties (NMConnection *connection)
create_gsm_connect_properties (NMModem *modem,
NMConnection *connection,
GError **error)
{
NMSettingGsm *setting;
NMSettingPPP *s_ppp;
MMSimpleConnectProperties *properties;
const gchar *str;
NMModemIPType ip_type;
setting = nm_connection_get_setting_gsm (connection);
properties = mm_simple_connect_properties_new ();
@ -310,6 +325,22 @@ create_gsm_connect_properties (NMConnection *connection)
mm_simple_connect_properties_set_allowed_auth (properties, allowed_auth);
}
/* Determine IP types to use when connecting */
ip_type = nm_modem_get_connection_ip_type (modem, connection, error);
if (ip_type == NM_MODEM_IP_TYPE_UNKNOWN) {
g_object_unref (properties);
return NULL;
}
if (ip_type == NM_MODEM_IP_TYPE_IPV4)
mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV4);
else if (ip_type == NM_MODEM_IP_TYPE_IPV6)
mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV6);
else if (ip_type == NM_MODEM_IP_TYPE_IPV4V6)
mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV4V6);
else
g_assert_not_reached ();
return properties;
}
@ -320,17 +351,29 @@ act_stage1_prepare (NMModem *_self,
{
NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
MMModemCapability caps;
GError *error = NULL;
g_clear_object (&self->priv->connect_properties);
caps = mm_modem_get_current_capabilities (self->priv->modem_iface);
if (MODEM_CAPS_3GPP (caps))
self->priv->connect_properties = create_gsm_connect_properties (connection);
self->priv->connect_properties = create_gsm_connect_properties (_self, connection, &error);
else if (MODEM_CAPS_3GPP2 (caps))
self->priv->connect_properties = create_cdma_connect_properties (connection);
else {
nm_log_warn (LOGD_MB, "(%s) not a mobile broadband modem",
nm_modem_get_uid (NM_MODEM (self)));
*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
return NM_ACT_STAGE_RETURN_FAILURE;
}
if (error) {
nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s",
nm_modem_get_uid (NM_MODEM (self)),
nm_connection_get_id (connection),
error->message);
g_clear_error (&error);
*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
return NM_ACT_STAGE_RETURN_FAILURE;
}
@ -591,11 +634,10 @@ set_mm_enabled (NMModem *_self,
}
/*****************************************************************************/
/* IP method static */
/* IPv4 method static */
static gboolean
ip_string_to_network_address (const gchar *str,
guint32 *out)
ip4_string_to_num (const gchar *str, guint32 *out)
{
guint32 addr = 0;
gboolean success = FALSE;
@ -610,7 +652,7 @@ ip_string_to_network_address (const gchar *str,
}
static gboolean
static_stage3_done (NMModemBroadband *self)
static_stage3_ip4_done (NMModemBroadband *self)
{
GError *error = NULL;
NMIP4Config *config = NULL;
@ -629,7 +671,7 @@ static_stage3_done (NMModemBroadband *self)
/* Fully fail if invalid IP address retrieved */
address_string = mm_bearer_ip_config_get_address (self->priv->ipv4_config);
if (!ip_string_to_network_address (address_string, &address_network)) {
if (!ip4_string_to_num (address_string, &address_network)) {
error = g_error_new (NM_MODEM_ERROR,
NM_MODEM_ERROR_CONNECTION_INVALID,
"(%s) retrieving IP4 configuration failed: invalid address given '%s'",
@ -640,7 +682,7 @@ static_stage3_done (NMModemBroadband *self)
/* Missing gateway not a hard failure */
gw_string = mm_bearer_ip_config_get_gateway (self->priv->ipv4_config);
ip_string_to_network_address (gw_string, &gw);
ip4_string_to_num (gw_string, &gw);
config = nm_ip4_config_new ();
@ -660,7 +702,7 @@ static_stage3_done (NMModemBroadband *self)
/* DNS servers */
dns = mm_bearer_ip_config_get_dns (self->priv->ipv4_config);
for (i = 0; dns[i]; i++) {
if ( ip_string_to_network_address (dns[i], &address_network)
if ( ip4_string_to_num (dns[i], &address_network)
&& address_network > 0) {
nm_ip4_config_add_nameserver (config, address_network);
nm_log_info (LOGD_MB, " DNS %s", dns[i]);
@ -682,7 +724,110 @@ static_stage3_ip4_config_start (NMModem *_self,
/* We schedule it in an idle just to follow the same logic as in the
* generic modem implementation. */
g_idle_add ((GSourceFunc)static_stage3_done, self);
g_idle_add ((GSourceFunc) static_stage3_ip4_done, self);
return NM_ACT_STAGE_RETURN_POSTPONE;
}
/*****************************************************************************/
/* IPv6 method static */
static gboolean
stage3_ip6_done (NMModemBroadband *self)
{
GError *error = NULL;
NMIP6Config *config = NULL;
const gchar *address_string;
NMPlatformIP6Address address;
NMModemIPMethod ip_method;
const gchar **dns;
guint i;
g_assert (self->priv->ipv6_config);
memset (&address, 0, sizeof (address));
ip_method = get_bearer_ip_method (self->priv->ipv6_config);
address_string = mm_bearer_ip_config_get_address (self->priv->ipv6_config);
if (!address_string) {
/* DHCP/SLAAC is allowed to skip addresses; other methods require it */
if (ip_method != NM_MODEM_IP_METHOD_AUTO) {
error = g_error_new (NM_MODEM_ERROR,
NM_MODEM_ERROR_CONNECTION_INVALID,
"(%s) retrieving IPv6 configuration failed: no address given",
nm_modem_get_uid (NM_MODEM (self)));
}
goto out;
}
/* Fail if invalid IP address retrieved */
if (!inet_pton (AF_INET6, address_string, (void *) &(address.address))) {
error = g_error_new (NM_MODEM_ERROR,
NM_MODEM_ERROR_CONNECTION_INVALID,
"(%s) retrieving IPv6 configuration failed: invalid address given '%s'",
nm_modem_get_uid (NM_MODEM (self)),
address_string);
goto out;
}
nm_log_info (LOGD_MB, "(%s): IPv6 base configuration:",
nm_modem_get_uid (NM_MODEM (self)));
config = nm_ip6_config_new ();
address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv6_config);
nm_ip6_config_add_address (config, &address);
nm_log_info (LOGD_MB, " address %s/%d", address_string, address.plen);
address_string = mm_bearer_ip_config_get_gateway (self->priv->ipv6_config);
if (address_string) {
if (!inet_pton (AF_INET6, address_string, (void *) &(address.address))) {
error = g_error_new (NM_MODEM_ERROR,
NM_MODEM_ERROR_CONNECTION_INVALID,
"(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'",
nm_modem_get_uid (NM_MODEM (self)),
address_string);
goto out;
}
nm_log_info (LOGD_MB, " gateway %s", address_string);
nm_ip6_config_set_gateway (config, &address.address);
} else if (ip_method == NM_MODEM_IP_METHOD_STATIC) {
/* Gateway required for the 'static' method */
error = g_error_new (NM_MODEM_ERROR,
NM_MODEM_ERROR_CONNECTION_INVALID,
"(%s) retrieving IPv6 configuration failed: missing gateway",
nm_modem_get_uid (NM_MODEM (self)));
goto out;
}
/* DNS servers */
dns = mm_bearer_ip_config_get_dns (self->priv->ipv6_config);
for (i = 0; dns[i]; i++) {
struct in6_addr addr;
if (inet_pton (AF_INET6, dns[i], &addr)) {
nm_ip6_config_add_nameserver (config, &addr);
nm_log_info (LOGD_MB, " DNS %s", dns[i]);
}
}
out:
nm_modem_emit_ip6_config_result (NM_MODEM (self), config, error);
g_clear_object (&config);
g_clear_error (&error);
return FALSE;
}
static NMActStageReturn
stage3_ip6_config_request (NMModem *_self, NMDeviceStateReason *reason)
{
NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
/* We schedule it in an idle just to follow the same logic as in the
* generic modem implementation. */
g_idle_add ((GSourceFunc) stage3_ip6_done, self);
return NM_ACT_STAGE_RETURN_POSTPONE;
}
@ -984,6 +1129,7 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass)
modem_class->get_capabilities = get_capabilities;
modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start;
modem_class->stage3_ip6_config_request = stage3_ip6_config_request;
modem_class->disconnect = disconnect;
modem_class->deactivate = deactivate;
modem_class->set_mm_enabled = set_mm_enabled;