summaryrefslogtreecommitdiff
path: root/src/devices/wwan
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/wwan')
-rw-r--r--src/devices/wwan/nm-device-modem.c123
-rw-r--r--src/devices/wwan/nm-modem-broadband.c232
-rw-r--r--src/devices/wwan/nm-modem.c386
-rw-r--r--src/devices/wwan/nm-modem.h59
-rw-r--r--src/devices/wwan/wwan-exports.ver1
5 files changed, 711 insertions, 90 deletions
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 98ff610229..fd1b7f2868 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -20,6 +20,7 @@
#include "config.h"
+#include <string.h>
#include <glib.h>
#include "nm-device-modem.h"
@@ -30,6 +31,7 @@
#include "nm-dbus-manager.h"
#include "nm-settings-connection.h"
#include "nm-modem-broadband.h"
+#include "NetworkManagerUtils.h"
G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE)
@@ -153,23 +155,100 @@ modem_ip4_config_result (NMModem *self,
g_return_if_fail (nm_device_activate_ip4_state_in_conf (device) == TRUE);
if (error) {
- nm_log_warn (LOGD_MB | LOGD_IP4, "retrieving IP4 configuration failed: (%d) %s",
+ nm_log_warn (LOGD_MB | LOGD_IP4, "retrieving IPv4 configuration failed: (%d) %s",
error ? error->code : -1,
error && error->message ? error->message : "(unknown)");
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
- } else
- nm_device_activate_schedule_ip4_config_result (device, config);
+ } else {
+ nm_device_set_wwan_ip4_config (device, config);
+ nm_device_activate_schedule_ip4_config_result (device, NULL);
+ }
+}
+
+static void
+modem_ip6_config_result (NMModem *self,
+ NMIP6Config *config,
+ gboolean do_slaac,
+ GError *error,
+ gpointer user_data)
+{
+ NMDevice *device = NM_DEVICE (user_data);
+ NMActStageReturn ret;
+ NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+ NMIP6Config *ignored = NULL;
+ gboolean got_config = !!config;
+
+ g_return_if_fail (nm_device_activate_ip6_state_in_conf (device) == TRUE);
+
+ if (error) {
+ nm_log_warn (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+ return;
+ }
+
+ /* Re-enable IPv6 on the interface */
+ nm_device_ipv6_sysctl_set (device, "disable_ipv6", "0");
+
+ if (config)
+ nm_device_set_wwan_ip6_config (device, config);
+
+ if (do_slaac == FALSE) {
+ if (got_config)
+ nm_device_activate_schedule_ip6_config_result (device);
+ else {
+ nm_log_warn (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: SLAAC not requested and no addresses");
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+ }
+ return;
+ }
+
+ /* Start SLAAC now that we have a link-local address from the modem */
+ ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage3_ip6_config_start (device, &ignored, &reason);
+ g_assert (ignored == NULL);
+ switch (ret) {
+ case NM_ACT_STAGE_RETURN_FAILURE:
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+ break;
+ case NM_ACT_STAGE_RETURN_STOP:
+ /* all done */
+ nm_device_activate_schedule_ip6_config_result (device);
+ break;
+ case NM_ACT_STAGE_RETURN_POSTPONE:
+ /* let SLAAC run */
+ break;
+ default:
+ /* Should never get here since we've assured that the IPv6 method
+ * will either be "auto" or "ignored" when starting IPv6 configuration.
+ */
+ g_assert_not_reached ();
+ }
}
static void
data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
+ const char *old = nm_device_get_ip_iface (self);
+ const char *new = nm_modem_get_data_port (modem);
+ gboolean changed = FALSE;
+
+ if (new && g_strcmp0 (new, old))
+ changed = TRUE;
/* We set the IP iface in the device as soon as we know it, so that we
* properly ifup it if needed */
- nm_device_set_ip_iface (self, nm_modem_get_data_port (modem));
+ nm_device_set_ip_iface (self, new);
+
+ /* Disable IPv6 immediately on the interface since NM handles IPv6
+ * internally, and leaving it enabled could allow the kernel's IPv6
+ * RA handling code to run before NM is ready.
+ */
+ if (changed)
+ nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
}
static void
@@ -394,11 +473,24 @@ act_stage3_ip6_config_start (NMDevice *device,
NMDeviceStateReason *reason)
{
return nm_modem_stage3_ip6_config_start (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem,
- device,
- NM_DEVICE_CLASS (nm_device_modem_parent_class),
+ nm_device_get_act_request (device),
reason);
}
+static gboolean
+get_ip_iface_identifier (NMDevice *device, NMUtilsIPv6IfaceId *out_iid)
+{
+ NMDeviceModem *self = NM_DEVICE_MODEM (device);
+ NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
+ gboolean success;
+
+ g_return_val_if_fail (priv->modem, FALSE);
+ success = nm_modem_get_iid (priv->modem, out_iid);
+ if (!success)
+ success = NM_DEVICE_CLASS (nm_device_modem_parent_class)->get_ip_iface_identifier (device, out_iid);
+ return success;
+}
+
/*****************************************************************************/
static gboolean
@@ -486,8 +578,10 @@ nm_device_modem_new (NMModem *modem)
/* If the data port is known, set it as the IP interface immediately */
data_port = nm_modem_get_data_port (modem);
- if (data_port)
+ if (data_port) {
nm_device_set_ip_iface (device, data_port);
+ nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1");
+ }
return device;
}
@@ -498,6 +592,18 @@ nm_device_modem_init (NMDeviceModem *self)
}
static void
+constructed (GObject *object)
+{
+ G_OBJECT_CLASS (nm_device_modem_parent_class)->constructed (object);
+
+ /* DHCP is always done by the modem firmware, not by the network, and
+ * by the time we get around to DHCP the firmware should already know
+ * the IP addressing details. So the DHCP timeout can be much shorter.
+ */
+ nm_device_set_dhcp_timeout (NM_DEVICE (object), 15);
+}
+
+static void
set_modem (NMDeviceModem *self, NMModem *modem)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
@@ -509,6 +615,7 @@ set_modem (NMDeviceModem *self, NMModem *modem)
g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self);
+ g_signal_connect (modem, NM_MODEM_IP6_CONFIG_RESULT, G_CALLBACK (modem_ip6_config_result), self);
g_signal_connect (modem, NM_MODEM_AUTH_REQUESTED, G_CALLBACK (modem_auth_requested), self);
g_signal_connect (modem, NM_MODEM_AUTH_RESULT, G_CALLBACK (modem_auth_result), self);
g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
@@ -589,6 +696,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
object_class->dispose = dispose;
object_class->get_property = get_property;
object_class->set_property = set_property;
+ object_class->constructed = constructed;
device_class->check_connection_compatible = check_connection_compatible;
device_class->check_connection_available = check_connection_available;
@@ -603,6 +711,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
device_class->set_enabled = set_enabled;
device_class->owns_iface = owns_iface;
device_class->is_available = is_available;
+ device_class->get_ip_iface_identifier = get_ip_iface_identifier;
device_class->state_changed = device_state_changed;
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 6f0f6bd2ba..4ea477d4ac 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -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;
- guint 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 = MM_MODEM_IP_METHOD_PPP;
- break;
- case MM_BEARER_IP_METHOD_STATIC:
- ip_method = MM_MODEM_IP_METHOD_STATIC;
- break;
- case MM_BEARER_IP_METHOD_DHCP:
- ip_method = MM_MODEM_IP_METHOD_DHCP;
- 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_IP_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;
}
@@ -721,11 +866,15 @@ simple_disconnect_ready (MMModemSimple *modem_iface,
}
static void
-disconnect (NMModem *self,
+disconnect (NMModem *modem,
gboolean warn)
{
+ NMModemBroadband *self = NM_MODEM_BROADBAND (modem);
SimpleDisconnectContext *ctx;
+ if (!self->priv->simple_iface)
+ return;
+
ctx = g_slice_new (SimpleDisconnectContext);
ctx->self = g_object_ref (self);
@@ -807,6 +956,21 @@ modem_state_changed (MMModem *modem,
/*****************************************************************************/
+static NMModemIPType
+mm_ip_family_to_nm (MMBearerIpFamily family)
+{
+ NMModemIPType nm_type = NM_MODEM_IP_TYPE_UNKNOWN;
+
+ if (family & MM_BEARER_IP_FAMILY_IPV4)
+ nm_type |= NM_MODEM_IP_TYPE_IPV4;
+ if (family & MM_BEARER_IP_FAMILY_IPV6)
+ nm_type |= NM_MODEM_IP_TYPE_IPV6;
+ if (family & MM_BEARER_IP_FAMILY_IPV4V6)
+ nm_type |= MM_BEARER_IP_FAMILY_IPV4V6;
+
+ return nm_type;
+}
+
NMModem *
nm_modem_broadband_new (GObject *object, GError **error)
{
@@ -831,6 +995,7 @@ nm_modem_broadband_new (GObject *object, GError **error)
NM_MODEM_UID, mm_modem_get_primary_port (modem_iface),
NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface),
NM_MODEM_DATA_PORT, NULL, /* We don't know it until bearer created */
+ NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem_iface)),
NM_MODEM_STATE, mm_state_to_nm (mm_modem_get_state (modem_iface)),
NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface),
NM_MODEM_BROADBAND_MODEM, modem_object,
@@ -968,6 +1133,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;
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 2df9f2c23e..eab98219d5 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -42,11 +42,13 @@ enum {
PROP_PATH,
PROP_UID,
PROP_DRIVER,
- PROP_IP_METHOD,
+ PROP_IP4_METHOD,
+ PROP_IP6_METHOD,
PROP_IP_TIMEOUT,
PROP_STATE,
PROP_DEVICE_ID,
PROP_SIM_ID,
+ PROP_IP_TYPES,
LAST_PROP
};
@@ -58,11 +60,14 @@ typedef struct {
char *control_port;
char *data_port;
char *ppp_iface;
- guint32 ip_method;
+ NMModemIPMethod ip4_method;
+ NMModemIPMethod ip6_method;
+ NMUtilsIPv6IfaceId iid;
NMModemState state;
NMModemState prev_state; /* revert to this state if enable/disable fails */
char *device_id;
char *sim_id;
+ NMModemIPType ip_types;
NMPPPManager *ppp_manager;
@@ -82,6 +87,7 @@ enum {
PPP_FAILED,
PREPARE_RESULT,
IP4_CONFIG_RESULT,
+ IP6_CONFIG_RESULT,
AUTH_REQUESTED,
AUTH_RESULT,
REMOVED,
@@ -218,6 +224,103 @@ nm_modem_emit_removed (NMModem *self)
g_signal_emit (self, signals[REMOVED], 0);
}
+NMModemIPType
+nm_modem_get_supported_ip_types (NMModem *self)
+{
+ return NM_MODEM_GET_PRIVATE (self)->ip_types;
+}
+
+/**
+ * nm_modem_get_connection_ip_type:
+ * @self: the #NMModem
+ * @connection: the #NMConnection to determine IP type to use
+ *
+ * Given a modem and a connection, determine which NMModemIpType to use
+ * when connecting.
+ *
+ * Returns: a single %NMModemIpType value
+ */
+NMModemIPType
+nm_modem_get_connection_ip_type (NMModem *self,
+ NMConnection *connection,
+ GError **error)
+{
+ NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+ NMSettingIP4Config *s_ip4;
+ NMSettingIP6Config *s_ip6;
+ const char *method;
+ gboolean ip4 = TRUE, ip6 = TRUE;
+ gboolean ip4_may_fail = TRUE, ip6_may_fail = TRUE;
+
+ s_ip4 = nm_connection_get_setting_ip4_config (connection);
+ if (s_ip4) {
+ method = nm_setting_ip4_config_get_method (s_ip4);
+ if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
+ ip4 = FALSE;
+ ip4_may_fail = nm_setting_ip4_config_get_may_fail (s_ip4);
+ }
+
+ s_ip6 = nm_connection_get_setting_ip6_config (connection);
+ if (s_ip6) {
+ method = nm_setting_ip6_config_get_method (s_ip6);
+ if (g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
+ ip6 = FALSE;
+ ip6_may_fail = nm_setting_ip6_config_get_may_fail (s_ip6);
+ }
+
+ if (ip4 && !ip6) {
+ if (!(priv->ip_types & NM_MODEM_IP_TYPE_IPV4)) {
+ g_set_error_literal (error,
+ NM_MODEM_ERROR,
+ NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE,
+ "Connection requested IPv4 but IPv4 is "
+ "unsuported by the modem.");
+ return NM_MODEM_IP_TYPE_UNKNOWN;
+ }
+ return NM_MODEM_IP_TYPE_IPV4;
+ }
+
+ if (ip6 && !ip4) {
+ if (!(priv->ip_types & NM_MODEM_IP_TYPE_IPV6)) {
+ g_set_error_literal (error,
+ NM_MODEM_ERROR,
+ NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE,
+ "Connection requested IPv6 but IPv6 is "
+ "unsuported by the modem.");
+ return NM_MODEM_IP_TYPE_UNKNOWN;
+ }
+ return NM_MODEM_IP_TYPE_IPV6;
+ }
+
+ if (ip4 && ip6) {
+ /* Modem supports dual-stack */
+ if (priv->ip_types & NM_MODEM_IP_TYPE_IPV4V6)
+ return NM_MODEM_IP_TYPE_IPV4V6;
+
+ /* Both IPv4 and IPv6 requested, but modem doesn't support dual-stack;
+ * if one method is marked "may-fail" then use the other.
+ */
+ if (ip6_may_fail)
+ return NM_MODEM_IP_TYPE_IPV4;
+ else if (ip4_may_fail)
+ return NM_MODEM_IP_TYPE_IPV6;
+
+ g_set_error_literal (error,
+ NM_MODEM_ERROR,
+ NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE,
+ "Connection requested both IPv4 and IPv6 "
+ "but dual-stack addressing is unsupported "
+ "by the modem.");
+ return NM_MODEM_IP_TYPE_UNKNOWN;
+ }
+
+ g_set_error_literal (error,
+ NM_MODEM_ERROR,
+ NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE,
+ "Connection specified no IP configuration!");
+ return NM_MODEM_IP_TYPE_UNKNOWN;
+}
+
/*****************************************************************************/
/* IP method PPP */
@@ -237,13 +340,24 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_
}
static void
+set_data_port (NMModem *self, const char *new_data_port)
+{
+ NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+
+ if (g_strcmp0 (priv->data_port, new_data_port) != 0) {
+ g_free (priv->data_port);
+ priv->data_port = g_strdup (new_data_port);
+ g_object_notify (G_OBJECT (self), NM_MODEM_DATA_PORT);
+ }
+}
+
+static void
ppp_ip4_config (NMPPPManager *ppp_manager,
const char *iface,
NMIP4Config *config,
gpointer user_data)
{
NMModem *self = NM_MODEM (user_data);
- NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
guint32 i, num;
guint32 bad_dns1 = htonl (0x0A0B0C0D);
guint32 good_dns1 = htonl (0x04020201); /* GTE nameserver */
@@ -252,9 +366,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
gboolean dns_workaround = FALSE;
/* Notify about the new data port to use */
- g_free (priv->ppp_iface);
- priv->ppp_iface = g_strdup (iface);
- g_object_notify (G_OBJECT (self), NM_MODEM_DATA_PORT);
+ set_data_port (self, iface);
/* Work around a PPP bug (#1732) which causes many mobile broadband
* providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers.
@@ -296,6 +408,23 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
}
static void
+ppp_ip6_config (NMPPPManager *ppp_manager,
+ const char *iface,
+ const NMUtilsIPv6IfaceId *iid,
+ NMIP6Config *config,
+ gpointer user_data)
+{
+ NMModem *self = NM_MODEM (user_data);
+
+ /* Notify about the new data port to use */
+ set_data_port (self, iface);
+
+ NM_MODEM_GET_PRIVATE (self)->iid = *iid;
+
+ nm_modem_emit_ip6_config_result (self, config, NULL);
+}
+
+static void
ppp_stats (NMPPPManager *ppp_manager,
guint32 in_bytes,
guint32 out_bytes,
@@ -313,20 +442,27 @@ ppp_stats (NMPPPManager *ppp_manager,
}
static NMActStageReturn
-ppp_stage3_ip4_config_start (NMModem *self,
- NMActRequest *req,
- NMDeviceStateReason *reason)
+ppp_stage3_ip_config_start (NMModem *self,
+ NMActRequest *req,
+ NMDeviceStateReason *reason)
{
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
const char *ppp_name = NULL;
GError *error = NULL;
NMActStageReturn ret;
- guint ip_timeout = 20;
+ guint ip_timeout = 30;
g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+ /* If we're already running PPP don't restart it; for example, if both
+ * IPv4 and IPv6 are requested, IPv4 gets started first, but we use the
+ * same pppd for both v4 and v6.
+ */
+ if (priv->ppp_manager)
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+
if (NM_MODEM_GET_CLASS (self)->get_user_pass) {
NMConnection *connection = nm_act_request_get_connection (req);
@@ -336,7 +472,7 @@ ppp_stage3_ip4_config_start (NMModem *self,
}
/* Check if ModemManager requested a specific IP timeout to be used. If 0 reported,
- * use the default one (20s) */
+ * use the default one (30s) */
if (priv->mm_ip_timeout > 0) {
nm_log_info (LOGD_PPP, "using modem-specified IP timeout: %u seconds",
priv->mm_ip_timeout);
@@ -351,6 +487,9 @@ ppp_stage3_ip4_config_start (NMModem *self,
g_signal_connect (priv->ppp_manager, "ip4-config",
G_CALLBACK (ppp_ip4_config),
self);
+ g_signal_connect (priv->ppp_manager, "ip6-config",
+ G_CALLBACK (ppp_ip6_config),
+ self);
g_signal_connect (priv->ppp_manager, "stats",
G_CALLBACK (ppp_stats),
self);
@@ -382,6 +521,8 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
{
NMModemPrivate *priv;
NMActRequest *req;
+ NMConnection *connection;
+ const char *method;
NMActStageReturn ret;
g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
@@ -391,21 +532,36 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
req = nm_device_get_act_request (device);
g_assert (req);
+ connection = nm_act_request_get_connection (req);
+ g_assert (connection);
+ method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+
+ /* Only Disabled and Auto methods make sense for WWAN */
+ if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
+ return NM_ACT_STAGE_RETURN_STOP;
+
+ if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) != 0) {
+ nm_log_warn (LOGD_MB | LOGD_IP4,
+ "(%s): unhandled WWAN IPv4 method '%s'; will fail",
+ nm_modem_get_uid (self), method);
+ *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
priv = NM_MODEM_GET_PRIVATE (self);
- switch (priv->ip_method) {
- case MM_MODEM_IP_METHOD_PPP:
- ret = ppp_stage3_ip4_config_start (self, req, reason);
+ switch (priv->ip4_method) {
+ case NM_MODEM_IP_METHOD_PPP:
+ ret = ppp_stage3_ip_config_start (self, req, reason);
break;
- case MM_MODEM_IP_METHOD_STATIC:
+ case NM_MODEM_IP_METHOD_STATIC:
ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, reason);
break;
- case MM_MODEM_IP_METHOD_DHCP:
+ case NM_MODEM_IP_METHOD_AUTO:
ret = device_class->act_stage3_ip4_config_start (device, NULL, reason);
break;
default:
- nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
- ret = NM_ACT_STAGE_RETURN_FAILURE;
+ nm_log_info (LOGD_MB, "(%s): IPv4 configuration disabled", nm_modem_get_uid (self));
+ ret = NM_ACT_STAGE_RETURN_STOP;
break;
}
@@ -423,8 +579,8 @@ nm_modem_ip4_pre_commit (NMModem *modem,
* not point-to-point) and IP config has a /32 prefix, then we assume that
* ARP will be pointless and we turn it off.
*/
- if ( priv->ip_method == MM_MODEM_IP_METHOD_STATIC
- || priv->ip_method == MM_MODEM_IP_METHOD_DHCP) {
+ if ( priv->ip4_method == NM_MODEM_IP_METHOD_STATIC
+ || priv->ip4_method == NM_MODEM_IP_METHOD_AUTO) {
const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, 0);
g_assert (address);
@@ -435,15 +591,100 @@ nm_modem_ip4_pre_commit (NMModem *modem,
/*****************************************************************************/
+void
+nm_modem_emit_ip6_config_result (NMModem *self,
+ NMIP6Config *config,
+ GError *error)
+{
+ NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+ guint i, num;
+ gboolean do_slaac = TRUE;
+
+ if (error) {
+ g_signal_emit (self, signals[IP6_CONFIG_RESULT], 0, NULL, FALSE, error);
+ return;
+ }
+
+ if (config) {
+ /* If the IPv6 configuration only included a Link-Local address, then
+ * we have to run SLAAC to get the full IPv6 configuration.
+ */
+ num = nm_ip6_config_get_num_addresses (config);
+ g_assert (num > 0);
+ for (i = 0; i < num; i++) {
+ const NMPlatformIP6Address * addr = nm_ip6_config_get_address (config, i);
+
+ if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
+ if (!priv->iid.id)
+ priv->iid.id = ((guint64 *)(&addr->address.s6_addr))[1];
+ } else
+ do_slaac = FALSE;
+ }
+ }
+ g_assert (config || do_slaac);
+
+ g_signal_emit (self, signals[IP6_CONFIG_RESULT], 0, config, do_slaac, NULL);
+}
+
+static NMActStageReturn
+stage3_ip6_config_request (NMModem *self, NMDeviceStateReason *reason)
+{
+ *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+ return NM_ACT_STAGE_RETURN_FAILURE;
+}
+
NMActStageReturn
nm_modem_stage3_ip6_config_start (NMModem *self,
- NMDevice *device,
- NMDeviceClass *device_class,
+ NMActRequest *req,
NMDeviceStateReason *reason)
{
- /* FIXME: We don't support IPv6 on modems quite yet... */
- nm_device_activate_schedule_ip6_config_timeout (device);
- return NM_ACT_STAGE_RETURN_POSTPONE;
+ NMModemPrivate *priv;
+ NMActStageReturn ret;
+ NMConnection *connection;
+ const char *method;
+
+ g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+ g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
+ g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+ g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
+ g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+ connection = nm_act_request_get_connection (req);
+ g_assert (connection);
+ method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
+
+ /* Only Ignore and Auto methods make sense for WWAN */
+ if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
+ return NM_ACT_STAGE_RETURN_STOP;
+
+ if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) != 0) {
+ nm_log_warn (LOGD_MB | LOGD_IP6,
+ "(%s): unhandled WWAN IPv6 method '%s'; will fail",
+ nm_modem_get_uid (self), method);
+ *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
+
+ priv = NM_MODEM_GET_PRIVATE (self);
+ switch (priv->ip6_method) {
+ case NM_MODEM_IP_METHOD_PPP:
+ ret = ppp_stage3_ip_config_start (self, req, reason);
+ break;
+ case NM_MODEM_IP_METHOD_STATIC:
+ case NM_MODEM_IP_METHOD_AUTO:
+ /* Both static and DHCP/Auto retrieve a base IP config from the modem
+ * which in the static case is the full config, and the DHCP/Auto case
+ * is just the IPv6LL address to use for SLAAC.
+ */
+ ret = NM_MODEM_GET_CLASS (self)->stage3_ip6_config_request (self, reason);
+ break;
+ default:
+ nm_log_info (LOGD_MB, "(%s): IPv6 configuration disabled", nm_modem_get_uid (self));
+ ret = NM_ACT_STAGE_RETURN_STOP;
+ break;
+ }
+
+ return ret;
}
/*****************************************************************************/
@@ -633,22 +874,19 @@ deactivate (NMModem *self, NMDevice *device)
priv->ppp_manager = NULL;
}
- switch (priv->ip_method) {
- case MM_MODEM_IP_METHOD_PPP:
- break;
- case MM_MODEM_IP_METHOD_STATIC:
- case MM_MODEM_IP_METHOD_DHCP:
+ if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC ||
+ priv->ip4_method == NM_MODEM_IP_METHOD_AUTO ||
+ priv->ip6_method == NM_MODEM_IP_METHOD_STATIC ||
+ priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
ifindex = nm_device_get_ip_ifindex (device);
if (ifindex > 0) {
nm_platform_route_flush (ifindex);
nm_platform_address_flush (ifindex);
nm_platform_link_set_down (ifindex);
}
- break;
- default:
- nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
- break;
}
+ priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
+ priv->ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
g_free (priv->ppp_iface);
priv->ppp_iface = NULL;
@@ -772,6 +1010,15 @@ nm_modem_owns_port (NMModem *self, const char *iface)
return FALSE;
}
+gboolean
+nm_modem_get_iid (NMModem *self, NMUtilsIPv6IfaceId *out_iid)
+{
+ g_return_val_if_fail (NM_IS_MODEM (self), FALSE);
+
+ *out_iid = NM_MODEM_GET_PRIVATE (self)->iid;
+ return TRUE;
+}
+
/*****************************************************************************/
void
@@ -846,8 +1093,11 @@ get_property (GObject *object, guint prop_id,
case PROP_UID:
g_value_set_string (value, priv->uid);
break;
- case PROP_IP_METHOD:
- g_value_set_uint (value, priv->ip_method);
+ case PROP_IP4_METHOD:
+ g_value_set_uint (value, priv->ip4_method);
+ break;
+ case PROP_IP6_METHOD:
+ g_value_set_uint (value, priv->ip6_method);
break;
case PROP_IP_TIMEOUT:
g_value_set_uint (value, priv->mm_ip_timeout);
@@ -861,6 +1111,9 @@ get_property (GObject *object, guint prop_id,
case PROP_SIM_ID:
g_value_set_string (value, priv->sim_id);
break;
+ case PROP_IP_TYPES:
+ g_value_set_uint (value, priv->ip_types);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -892,8 +1145,11 @@ set_property (GObject *object, guint prop_id,
/* Construct only */
priv->uid = g_value_dup_string (value);
break;
- case PROP_IP_METHOD:
- priv->ip_method = g_value_get_uint (value);
+ case PROP_IP4_METHOD:
+ priv->ip4_method = g_value_get_uint (value);
+ break;
+ case PROP_IP6_METHOD:
+ priv->ip6_method = g_value_get_uint (value);
break;
case PROP_IP_TIMEOUT:
priv->mm_ip_timeout = g_value_get_uint (value);
@@ -909,6 +1165,9 @@ set_property (GObject *object, guint prop_id,
g_free (priv->sim_id);
priv->sim_id = g_value_dup_string (value);
break;
+ case PROP_IP_TYPES:
+ priv->ip_types = g_value_get_uint (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -959,6 +1218,7 @@ nm_modem_class_init (NMModemClass *klass)
object_class->finalize = finalize;
klass->act_stage1_prepare = act_stage1_prepare;
+ klass->stage3_ip6_config_request = stage3_ip6_config_request;
klass->deactivate = deactivate;
/* Properties */
@@ -999,12 +1259,21 @@ nm_modem_class_init (NMModemClass *klass)
G_PARAM_STATIC_STRINGS));
g_object_class_install_property
- (object_class, PROP_IP_METHOD,
- g_param_spec_uint (NM_MODEM_IP_METHOD, "", "",
- MM_MODEM_IP_METHOD_PPP,
- MM_MODEM_IP_METHOD_DHCP,
- MM_MODEM_IP_METHOD_PPP,
- G_PARAM_READWRITE |
+ (object_class, PROP_IP4_METHOD,
+ g_param_spec_uint (NM_MODEM_IP4_METHOD, "", "",
+ NM_MODEM_IP_METHOD_UNKNOWN,
+ NM_MODEM_IP_METHOD_AUTO,
+ NM_MODEM_IP_METHOD_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property
+ (object_class, PROP_IP6_METHOD,
+ g_param_spec_uint (NM_MODEM_IP6_METHOD, "", "",
+ NM_MODEM_IP_METHOD_UNKNOWN,
+ NM_MODEM_IP_METHOD_AUTO,
+ NM_MODEM_IP_METHOD_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property
@@ -1036,6 +1305,14 @@ nm_modem_class_init (NMModemClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property
+ (object_class, PROP_IP_TYPES,
+ g_param_spec_uint (NM_MODEM_IP_TYPES,
+ "IP Types",
+ "Supported IP types",
+ 0, G_MAXUINT32, NM_MODEM_IP_TYPE_IPV4,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
/* Signals */
signals[PPP_STATS] =
@@ -1063,6 +1340,27 @@ nm_modem_class_init (NMModemClass *klass)
NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_POINTER);
+ /**
+ * NMModem::ip6-config-result:
+ * @modem: the #NMModem on which the signal is emitted
+ * @config: the #NMIP6Config to apply to the modem's data port
+ * @do_slaac: %TRUE if IPv6 SLAAC should be started
+ * @error: a #GError if any error occurred during IP configuration
+ *
+ * This signal is emitted when IPv6 configuration has completed or failed.
+ * If @error is set the configuration failed. If @config is set, then
+ * the details should be applied to the data port before any further
+ * configuration (like SLAAC) is done. @do_slaac indicates whether SLAAC
+ * should be started after applying @config to the data port.
+ */
+ signals[IP6_CONFIG_RESULT] =
+ g_signal_new (NM_MODEM_IP6_CONFIG_RESULT,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMModemClass, ip6_config_result),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_BOOLEAN, G_TYPE_POINTER);
+
signals[PREPARE_RESULT] =
g_signal_new (NM_MODEM_PREPARE_RESULT,
G_OBJECT_CLASS_TYPE (object_class),
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index c992cf7214..776d3d7f39 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -42,25 +42,50 @@ G_BEGIN_DECLS
#define NM_MODEM_DRIVER "driver"
#define NM_MODEM_CONTROL_PORT "control-port"
#define NM_MODEM_DATA_PORT "data-port"
-#define NM_MODEM_IP_METHOD "ip-method"
+#define NM_MODEM_IP4_METHOD "ip4-method"
+#define NM_MODEM_IP6_METHOD "ip6-method"
#define NM_MODEM_IP_TIMEOUT "ip-timeout"
#define NM_MODEM_STATE "state"
#define NM_MODEM_DEVICE_ID "device-id"
#define NM_MODEM_SIM_ID "sim-id"
+#define NM_MODEM_IP_TYPES "ip-types" /* Supported IP types */
/* Signals */
#define NM_MODEM_PPP_STATS "ppp-stats"
#define NM_MODEM_PPP_FAILED "ppp-failed"
#define NM_MODEM_PREPARE_RESULT "prepare-result"
#define NM_MODEM_IP4_CONFIG_RESULT "ip4-config-result"
+#define NM_MODEM_IP6_CONFIG_RESULT "ip6-config-result"
#define NM_MODEM_AUTH_REQUESTED "auth-requested"
#define NM_MODEM_AUTH_RESULT "auth-result"
#define NM_MODEM_REMOVED "removed"
#define NM_MODEM_STATE_CHANGED "state-changed"
-#define MM_MODEM_IP_METHOD_PPP 0
-#define MM_MODEM_IP_METHOD_STATIC 1
-#define MM_MODEM_IP_METHOD_DHCP 2
+typedef enum {
+ NM_MODEM_IP_METHOD_UNKNOWN = 0,
+ NM_MODEM_IP_METHOD_PPP,
+ NM_MODEM_IP_METHOD_STATIC,
+ NM_MODEM_IP_METHOD_AUTO, /* DHCP and/or SLAAC */
+} NMModemIPMethod;
+
+/**
+ * NMModemIPType:
+ * @NM_MODEM_IP_TYPE_UNKNOWN: unknown or no IP support
+ * @NM_MODEM_IP_TYPE_IPV4: IPv4-only bearers are supported
+ * @NM_MODEM_IP_TYPE_IPV6: IPv6-only bearers are supported
+ * @NM_MODEM_IP_TYPE_IPV4V6: dual-stack IPv4 + IPv6 bearers are supported
+ *
+ * Indicates what IP protocols the modem supports for an IP bearer. Any
+ * combination of flags is possible. For example, (%NM_MODEM_IP_TYPE_IPV4 |
+ * %NM_MODEM_IP_TYPE_IPV6) indicates that the modem supports IPv4 and IPv6
+ * but not simultaneously on the same bearer.
+ */
+typedef enum {
+ NM_MODEM_IP_TYPE_UNKNOWN = 0x0,
+ NM_MODEM_IP_TYPE_IPV4 = 0x1,
+ NM_MODEM_IP_TYPE_IPV6 = 0x2,
+ NM_MODEM_IP_TYPE_IPV4V6 = 0x4
+} NMModemIPType;
typedef enum {
NM_MODEM_ERROR_CONNECTION_NOT_GSM, /*< nick=ConnectionNotGsm >*/
@@ -68,6 +93,7 @@ typedef enum {
NM_MODEM_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/
NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
NM_MODEM_ERROR_INITIALIZATION_FAILED, /*< nick=InitializationFailed >*/
+ NM_MODEM_ERROR_IP_CONFIG_INVALID, /*< nick=IpConfigInvalid >*/
} NMModemError;
typedef enum { /*< underscore_name=nm_modem_state >*/
@@ -122,6 +148,12 @@ typedef struct {
NMActRequest *req,
NMDeviceStateReason *reason);
+ /* Request the IP6 config; when the config returns the modem
+ * subclass should emit the ip6_config_result signal.
+ */
+ NMActStageReturn (*stage3_ip6_config_request) (NMModem *self,
+ NMDeviceStateReason *reason);
+
void (*set_mm_enabled) (NMModem *self, gboolean enabled);
void (*disconnect) (NMModem *self, gboolean warn);
@@ -136,6 +168,10 @@ typedef struct {
void (*prepare_result) (NMModem *self, gboolean success, NMDeviceStateReason reason);
void (*ip4_config_result) (NMModem *self, NMIP4Config *config, GError *error);
+ void (*ip6_config_result) (NMModem *self,
+ NMIP6Config *config,
+ gboolean do_slaac,
+ GError *error);
void (*auth_requested) (NMModem *self);
void (*auth_result) (NMModem *self, GError *error);
@@ -154,6 +190,7 @@ const char *nm_modem_get_uid (NMModem *modem);
const char *nm_modem_get_control_port (NMModem *modem);
const char *nm_modem_get_data_port (NMModem *modem);
const char *nm_modem_get_driver (NMModem *modem);
+gboolean nm_modem_get_iid (NMModem *modem, NMUtilsIPv6IfaceId *out_iid);
gboolean nm_modem_owns_port (NMModem *modem, const char *iface);
@@ -182,8 +219,7 @@ NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem,
NMDeviceStateReason *reason);
NMActStageReturn nm_modem_stage3_ip6_config_start (NMModem *modem,
- NMDevice *device,
- NMDeviceClass *device_class,
+ NMActRequest *req,
NMDeviceStateReason *reason);
void nm_modem_ip4_pre_commit (NMModem *modem, NMDevice *device, NMIP4Config *config);
@@ -209,9 +245,20 @@ void nm_modem_set_state (NMModem *self,
void nm_modem_set_prev_state (NMModem *self, const char *reason);
const char * nm_modem_state_to_string (NMModemState state);
+NMModemIPType nm_modem_get_supported_ip_types (NMModem *self);
+
/* For the modem-manager only */
void nm_modem_emit_removed (NMModem *self);
+NMModemIPType nm_modem_get_connection_ip_type (NMModem *self,
+ NMConnection *connection,
+ GError **error);
+
+/* For subclasses */
+void nm_modem_emit_ip6_config_result (NMModem *self,
+ NMIP6Config *config,
+ GError *error);
+
G_END_DECLS
#endif /* NM_MODEM_H */
diff --git a/src/devices/wwan/wwan-exports.ver b/src/devices/wwan/wwan-exports.ver
index dc505d1768..46f2e8dc07 100644
--- a/src/devices/wwan/wwan-exports.ver
+++ b/src/devices/wwan/wwan-exports.ver
@@ -11,6 +11,7 @@ global:
nm_modem_get_control_port;
nm_modem_get_data_port;
nm_modem_get_driver;
+ nm_modem_get_iid;
nm_modem_get_path;
nm_modem_get_secrets;
nm_modem_get_state;