nm-modem-broadband.c 47.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
 */

21
#include "nm-default.h"
22

23 24
#include "nm-modem-broadband.h"

25
#include <string.h>
26
#include <arpa/inet.h>
27
#include <libmm-glib.h>
28 29

#include "nm-core-internal.h"
30
#include "NetworkManagerUtils.h"
31 32
#include "devices/nm-device-private.h"
#include "platform/nm-platform.h"
33 34
#include "nm-ip4-config.h"
#include "nm-ip6-config.h"
35

36 37
#define NM_MODEM_BROADBAND_MODEM "modem"

38 39 40 41 42 43 44 45 46 47
#define MODEM_CAPS_3GPP(caps) (caps & (MM_MODEM_CAPABILITY_GSM_UMTS |    \
                                       MM_MODEM_CAPABILITY_LTE |         \
                                       MM_MODEM_CAPABILITY_LTE_ADVANCED))

#define MODEM_CAPS_3GPP2(caps) (caps & (MM_MODEM_CAPABILITY_CDMA_EVDO))

/* Maximum time to keep the DBus call waiting for a connection result */
#define MODEM_CONNECT_TIMEOUT_SECS 120

/*****************************************************************************/
48

49
typedef enum {
50 51 52 53 54 55
	CONNECT_STEP_FIRST,
	CONNECT_STEP_WAIT_FOR_SIM,
	CONNECT_STEP_UNLOCK,
	CONNECT_STEP_WAIT_FOR_READY,
	CONNECT_STEP_CONNECT,
	CONNECT_STEP_LAST,
56 57 58 59 60 61 62 63 64 65 66 67
} ConnectStep;

typedef struct {
	NMModemBroadband *self;
	ConnectStep step;

	MMModemCapability caps;
	NMConnection *connection;
	GCancellable *cancellable;
	MMSimpleConnectProperties *connect_properties;
	GArray *ip_types;
	guint ip_types_i;
68
	guint ip_type_tries;
69 70 71
	GError *first_error;
} ConnectContext;

72 73 74 75 76 77 78
/*****************************************************************************/

NM_GOBJECT_PROPERTIES_DEFINE_BASE (
	PROP_MODEM,
);

typedef struct {
79 80 81 82 83
	/* The modem object from dbus */
	MMObject *modem_object;
	/* Per-interface objects */
	MMModem *modem_iface;
	MMModemSimple *simple_iface;
84
	MMSim *sim_iface;
85 86

	/* Connection setup */
87
	ConnectContext *ctx;
88

89 90 91 92
	MMBearer *bearer;
	MMBearerIpConfig *ipv4_config;
	MMBearerIpConfig *ipv6_config;

93 94 95
	guint idle_id_ip4;
	guint idle_id_ip6;

96
	guint32 pin_tries;
97
} NMModemBroadbandPrivate;
98

99 100 101
struct _NMModemBroadband {
	NMModem parent;
	NMModemBroadbandPrivate _priv;
102 103
};

104 105 106
struct _NMModemBroadbandClass {
	NMModemClass parent;
};
107

108
G_DEFINE_TYPE (NMModemBroadband, nm_modem_broadband, NM_TYPE_MODEM)
109

110
#define NM_MODEM_BROADBAND_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMModemBroadband, NM_IS_MODEM_BROADBAND)
111

112 113
/*****************************************************************************/

114 115 116 117 118 119 120 121 122 123 124
#define _NMLOG_DOMAIN      LOGD_MB
#define _NMLOG_PREFIX_NAME "modem-broadband"
#define _NMLOG(level, ...) \
    G_STMT_START { \
        const NMLogLevel _level = (level); \
        \
        if (nm_logging_enabled (_level, (_NMLOG_DOMAIN))) { \
            NMModemBroadband *const __self = (self); \
            char __prefix_name[128]; \
            const char *__uid; \
            \
125 126 127 128
            _nm_log (_level, (_NMLOG_DOMAIN), 0, NULL, \
                     ((__self && __self->_priv.ctx) \
                         ? nm_connection_get_uuid (__self->_priv.ctx->connection) \
                         : NULL), \
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
                     "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                     _NMLOG_PREFIX_NAME, \
                     (__self \
                         ? ({ \
                                ((__uid = nm_modem_get_uid ((NMModem *) __self)) \
                                    ? nm_sprintf_buf (__prefix_name, "[%s]", __uid) \
                                    : "(null)"); \
                            }) \
                         : "") \
                     _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
        } \
    } G_STMT_END

/*****************************************************************************/

144
static NMDeviceStateReason
145
translate_mm_error (NMModemBroadband *self, GError *error)
146 147 148
{
	NMDeviceStateReason reason;

149 150
	g_return_val_if_fail (error != NULL, NM_DEVICE_STATE_REASON_UNKNOWN);

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
	if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER))
		reason = NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER;
	else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_DIALTONE))
		reason = NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE;
	else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_BUSY))
		reason = NM_DEVICE_STATE_REASON_MODEM_BUSY;
	else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_ANSWER))
		reason = NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT;
	else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED))
		reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED;
	else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT))
		reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT;
	else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK))
		reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING;
	else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED))
		reason = NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED;
	else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN))
		reason = NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED;
	else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK))
		reason = NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED;
	else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG))
		reason = NM_DEVICE_STATE_REASON_GSM_SIM_WRONG;
173 174
	else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD))
		reason = NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT;
175 176
	else {
		/* unable to map the ModemManager error to a NM_DEVICE_STATE_REASON */
177
		_LOGD ("unmapped error detected: '%s'", error->message);
178 179 180 181 182 183 184 185
		reason = NM_DEVICE_STATE_REASON_UNKNOWN;
	}

	return reason;
}

/*****************************************************************************/

186 187 188 189
static void
get_capabilities (NMModem *_self,
                  NMDeviceModemCapabilities *modem_caps,
                  NMDeviceModemCapabilities *current_caps)
190
{
191
	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
192 193 194
	MMModemCapability all_supported = MM_MODEM_CAPABILITY_NONE;
	MMModemCapability *supported;
	guint n_supported;
195

196 197
	/* For now, we don't care about the capability combinations, just merge all
	 * combinations in a single mask */
198
	if (mm_modem_get_supported_capabilities (self->_priv.modem_iface, &supported, &n_supported)) {
199 200 201 202 203 204 205 206 207
		guint i;

		for (i = 0; i < n_supported; i++)
			all_supported |= supported[i];

		g_free (supported);
	}

	*modem_caps = (NMDeviceModemCapabilities) all_supported;
208
	*current_caps = (NMDeviceModemCapabilities) mm_modem_get_current_capabilities (self->_priv.modem_iface);
209 210
}

211 212 213 214 215 216 217 218
static gboolean
owns_port (NMModem *_self, const char *iface)
{
	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
	const MMModemPortInfo *ports = NULL;
	guint n_ports = 0, i;
	gboolean owns = FALSE;

219
	mm_modem_peek_ports (self->_priv.modem_iface, &ports, &n_ports);
220 221 222 223 224
	for (i = 0; i < n_ports && !owns; i++)
		owns = (g_strcmp0 (iface, ports[i].name) == 0);
	return owns;
}

225 226 227 228 229 230 231
/*****************************************************************************/

static void
ask_for_pin (NMModemBroadband *self)
{
	guint32 tries;

232
	tries = self->_priv.pin_tries++;
233 234 235 236 237 238
	nm_modem_get_secrets (NM_MODEM (self),
	                      NM_SETTING_GSM_SETTING_NAME,
	                      tries ? TRUE : FALSE,
	                      NM_SETTING_GSM_PIN);
}

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
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;
}

254 255 256 257 258
static MMSimpleConnectProperties *
create_cdma_connect_properties (NMConnection *connection)
{
	NMSettingCdma *setting;
	MMSimpleConnectProperties *properties;
259
	const char *str;
260 261 262 263 264 265 266 267 268 269 270 271

	setting = nm_connection_get_setting_cdma (connection);
	properties = mm_simple_connect_properties_new ();

	str = nm_setting_cdma_get_number (setting);
	if (str)
		mm_simple_connect_properties_set_number (properties, str);

	return properties;
}

static MMSimpleConnectProperties *
272
create_gsm_connect_properties (NMConnection *connection)
273 274
{
	NMSettingGsm *setting;
275
	NMSettingPpp *s_ppp;
276
	MMSimpleConnectProperties *properties;
277
	const char *str;
278 279 280 281 282 283 284 285 286

	setting = nm_connection_get_setting_gsm (connection);
	properties = mm_simple_connect_properties_new ();

	/* TODO: not needed */
	str = nm_setting_gsm_get_number (setting);
	if (str)
		mm_simple_connect_properties_set_number (properties, str);

287
	/* Blank APN ("") means the default subscription APN */
288
	str = nm_setting_gsm_get_apn (setting);
289
	mm_simple_connect_properties_set_apn (properties, str ?: "");
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331

	str = nm_setting_gsm_get_network_id (setting);
	if (str)
		mm_simple_connect_properties_set_operator_id (properties, str);

	str = nm_setting_gsm_get_pin (setting);
	if (str)
		mm_simple_connect_properties_set_pin (properties, str);

	str = nm_setting_gsm_get_username (setting);
	if (str)
		mm_simple_connect_properties_set_user (properties, str);

	str = nm_setting_gsm_get_password (setting);
	if (str)
		mm_simple_connect_properties_set_password (properties, str);

	/* Roaming */
	if (nm_setting_gsm_get_home_only (setting))
		mm_simple_connect_properties_set_allow_roaming (properties, FALSE);

	/* For IpMethod == STATIC or DHCP */
	s_ppp = nm_connection_get_setting_ppp (connection);
	if (s_ppp) {
		MMBearerAllowedAuth allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;

		if (nm_setting_ppp_get_noauth (s_ppp))
			allowed_auth = MM_BEARER_ALLOWED_AUTH_NONE;
		if (!nm_setting_ppp_get_refuse_pap (s_ppp))
			allowed_auth |= MM_BEARER_ALLOWED_AUTH_PAP;
		if (!nm_setting_ppp_get_refuse_chap (s_ppp))
			allowed_auth |= MM_BEARER_ALLOWED_AUTH_CHAP;
		if (!nm_setting_ppp_get_refuse_mschap (s_ppp))
			allowed_auth |= MM_BEARER_ALLOWED_AUTH_MSCHAP;
		if (!nm_setting_ppp_get_refuse_mschapv2 (s_ppp))
			allowed_auth |= MM_BEARER_ALLOWED_AUTH_MSCHAPV2;
		if (!nm_setting_ppp_get_refuse_eap (s_ppp))
			allowed_auth |= MM_BEARER_ALLOWED_AUTH_EAP;

		mm_simple_connect_properties_set_allowed_auth (properties, allowed_auth);
	}

332 333 334 335
	return properties;
}

static void
336
connect_context_clear (NMModemBroadband *self)
337
{
338 339
	if (self->_priv.ctx) {
		ConnectContext *ctx = self->_priv.ctx;
340 341

		g_clear_error (&ctx->first_error);
342
		g_clear_pointer (&ctx->ip_types, g_array_unref);
343
		nm_clear_g_cancellable (&ctx->cancellable);
344 345 346 347
		g_clear_object (&ctx->connection);
		g_clear_object (&ctx->connect_properties);
		g_clear_object (&ctx->self);
		g_slice_free (ConnectContext, ctx);
348
		self->_priv.ctx = NULL;
349
	}
350 351
}

352
static void connect_context_step (NMModemBroadband *self);
353 354 355 356

static void
connect_ready (MMModemSimple *simple_iface,
               GAsyncResult *res,
357
               NMModemBroadband *self)
358
{
359
	ConnectContext *ctx = self->_priv.ctx;
360 361 362 363
	GError *error = NULL;
	NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
	NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;

364
	self->_priv.bearer = mm_modem_simple_connect_finish (simple_iface, res, &error);
365 366 367 368

	if (!ctx)
		return;

369
	if (!self->_priv.bearer) {
370 371
		if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
		    (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
372
		     mm_modem_get_unlock_required (self->_priv.modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
373
			g_error_free (error);
374 375 376 377

			/* Request PIN */
			ask_for_pin (self);
			connect_context_clear (self);
378 379 380 381 382 383 384 385 386 387
			return;
		}

		/* Save the error, if it's the first one */
		if (!ctx->first_error) {
			/* Strip remote error info before saving it */
			if (g_dbus_error_is_remote_error (error))
				g_dbus_error_strip_remote_error (error);
			ctx->first_error = error;
		} else
388
			g_clear_error (&error);
389

390 391
		if (   ctx->ip_type_tries == 0
		    && g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) {
392 393 394 395 396 397 398 399 400 401
			/* Try one more time */
			ctx->ip_type_tries++;
		} else {
			/* If the modem/provider lies and the IP type we tried isn't supported,
			 * retry with the next one, if any.
			 */
			ctx->ip_types_i++;
			ctx->ip_type_tries = 0;
		}
		connect_context_step (self);
402 403 404 405
		return;
	}

	/* Grab IP configurations */
406 407 408
	self->_priv.ipv4_config = mm_bearer_get_ipv4_config (self->_priv.bearer);
	if (self->_priv.ipv4_config)
		ip4_method = get_bearer_ip_method (self->_priv.ipv4_config);
409

410 411 412
	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);
413

414 415 416 417 418 419 420 421 422
	if (!nm_modem_set_data_port (NM_MODEM (self),
	                             NM_PLATFORM_GET,
	                             mm_bearer_get_interface (self->_priv.bearer),
	                             ip4_method,
	                             ip6_method,
	                             mm_bearer_get_ip_timeout (self->_priv.bearer),
	                             &error)) {
		_LOGW ("failed to connect modem: %s", error->message);
		g_error_free (error);
423
		nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
424
		connect_context_clear (self);
425
		return;
426 427
	}

428 429
	ctx->step++;
	connect_context_step (self);
430 431 432
}

static void
433
send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self)
434
{
435
	gs_free_error GError *error = NULL;
436

437 438
	mm_sim_send_pin_finish (sim, result, &error);

439 440 441
	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
		return;

442
	if (!self->_priv.ctx || self->_priv.ctx->step != CONNECT_STEP_UNLOCK)
443
		g_return_if_reached ();
444 445

	if (error) {
446 447
		if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
		    (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
448
		     mm_modem_get_unlock_required (self->_priv.modem_iface) == MM_MODEM_LOCK_SIM_PIN))
449
			ask_for_pin (self);
450 451
		else
			nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, translate_mm_error (self, error));
452
		return;
453
	}
454

455
	self->_priv.ctx->step++;
456 457 458 459 460 461
	connect_context_step (self);
}

static void
connect_context_step (NMModemBroadband *self)
{
462
	ConnectContext *ctx = self->_priv.ctx;
463 464 465 466 467 468 469

	switch (ctx->step) {
	case CONNECT_STEP_FIRST:
		ctx->step++;
		/* fall through */

	case CONNECT_STEP_WAIT_FOR_SIM:
470
		if (MODEM_CAPS_3GPP (ctx->caps) && !self->_priv.sim_iface) {
471 472 473 474 475 476 477 478
			/* Have to wait for the SIM to show up */
			break;
		}
		ctx->step++;
		/* fall through */

	case CONNECT_STEP_UNLOCK:
		if (   MODEM_CAPS_3GPP (ctx->caps)
479
		    && mm_modem_get_unlock_required (self->_priv.modem_iface) == MM_MODEM_LOCK_SIM_PIN) {
480 481 482 483 484
			NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (ctx->connection);
			const char *pin = nm_setting_gsm_get_pin (s_gsm);

			/* If we have a PIN already, send it.  If we don't, get it. */
			if (pin) {
485
				mm_sim_send_pin (self->_priv.sim_iface,
486 487 488 489 490 491 492 493 494 495 496 497 498 499
				                 pin,
				                 ctx->cancellable,
				                 (GAsyncReadyCallback) send_pin_ready,
				                 self);
			} else {
				ask_for_pin (self);
			}
			break;
		}
		ctx->step++;
		/* fall through */
	case CONNECT_STEP_WAIT_FOR_READY: {
		GError *error = NULL;

500
		if (mm_modem_get_state (self->_priv.modem_iface) <= MM_MODEM_STATE_LOCKED)
501 502 503 504 505 506 507 508 509 510
			break;

		/* Create core connect properties based on the modem capabilities */
		g_assert (!ctx->connect_properties);

		if (MODEM_CAPS_3GPP (ctx->caps))
			ctx->connect_properties = create_gsm_connect_properties (ctx->connection);
		else if (MODEM_CAPS_3GPP2 (ctx->caps))
			ctx->connect_properties = create_cdma_connect_properties (ctx->connection);
		else {
511 512
			_LOGW ("failed to connect '%s': not a mobile broadband modem",
			       nm_connection_get_id (ctx->connection));
513

514
			nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
515 516 517 518 519 520 521 522
			connect_context_clear (self);
			break;
		}
		g_assert (ctx->connect_properties);

		/* Build up list of IP types that we need to use in the retries */
		ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), ctx->connection, &error);
		if (!ctx->ip_types) {
523 524 525
			_LOGW ("failed to connect '%s': %s",
			       nm_connection_get_id (ctx->connection),
			       error->message);
526 527
			g_clear_error (&error);

528
			nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
529 530 531 532 533
			connect_context_clear (self);
			break;
		}

		ctx->step++;
534
	}
535
		/* fall through */
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
	case CONNECT_STEP_CONNECT:
		if (ctx->ip_types_i < ctx->ip_types->len) {
			NMModemIPType current;

			current = g_array_index (ctx->ip_types, NMModemIPType, ctx->ip_types_i);

			if (current == NM_MODEM_IP_TYPE_IPV4)
				mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4);
			else if (current == NM_MODEM_IP_TYPE_IPV6)
				mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV6);
			else if (current == NM_MODEM_IP_TYPE_IPV4V6)
				mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6);
			else
				g_assert_not_reached ();

551 552 553
			_LOGD ("launching connection with ip type '%s' (try %d)",
			       nm_modem_ip_type_to_string (current),
			       ctx->ip_type_tries + 1);
554

555
			mm_modem_simple_connect (self->_priv.simple_iface,
556 557 558 559 560 561 562 563 564 565 566
			                         ctx->connect_properties,
			                         NULL,
			                         (GAsyncReadyCallback) connect_ready,
			                         self);
			break;
		}

		ctx->step++;
		/* fall through */

	case CONNECT_STEP_LAST:
567 568 569
		if (self->_priv.ipv4_config || self->_priv.ipv6_config)
			nm_modem_emit_prepare_result (NM_MODEM (self), TRUE, NM_DEVICE_STATE_REASON_NONE);
		else {
570 571 572 573 574 575
			/* If we have a saved error from a previous attempt, use it */
			if (!ctx->first_error)
				ctx->first_error = g_error_new_literal (NM_DEVICE_ERROR,
				                                        NM_DEVICE_ERROR_INVALID_CONNECTION,
				                                        "invalid bearer IP configuration");

576 577
			_LOGW ("failed to connect modem: %s",
			       ctx->first_error->message);
578
			nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, translate_mm_error (self, ctx->first_error));
579 580 581 582 583
		}

		connect_context_clear (self);
		break;
	}
584 585 586 587
}

static NMActStageReturn
act_stage1_prepare (NMModem *_self,
588
                    NMConnection *connection,
589
                    NMDeviceStateReason *out_failure_reason)
590 591 592
{
	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);

593
	/* Make sure we can get the Simple interface from the modem */
594 595 596
	if (!self->_priv.simple_iface) {
		self->_priv.simple_iface = mm_object_get_modem_simple (self->_priv.modem_object);
		if (!self->_priv.simple_iface) {
597
			_LOGW ("cannot access the Simple mobile broadband modem interface");
598
			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
599 600 601 602
			return NM_ACT_STAGE_RETURN_FAILURE;
		}
	}

603
	connect_context_clear (self);
604

605
	/* Allocate new context for this connect stage attempt */
606 607 608 609
	self->_priv.ctx = g_slice_new0 (ConnectContext);
	self->_priv.ctx->caps = mm_modem_get_current_capabilities (self->_priv.modem_iface);
	self->_priv.ctx->cancellable = g_cancellable_new ();
	self->_priv.ctx->connection = g_object_ref (connection);
610

611
	g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (self->_priv.simple_iface), MODEM_CONNECT_TIMEOUT_SECS * 1000);
612
	connect_context_step (self);
613 614 615 616 617 618 619

	return NM_ACT_STAGE_RETURN_POSTPONE;
}

/*****************************************************************************/

static gboolean
620
check_connection_compatible_with_modem (NMModem *_self, NMConnection *connection, GError **error)
621 622 623 624
{
	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
	MMModemCapability modem_caps;

625
	modem_caps = mm_modem_get_current_capabilities (self->_priv.modem_iface);
626 627

	if (MODEM_CAPS_3GPP (modem_caps)) {
628
		if (!_nm_connection_check_main_setting (connection, NM_SETTING_GSM_SETTING_NAME, error))
629 630 631 632 633 634
			return FALSE;

		return TRUE;
	}

	if (MODEM_CAPS_3GPP2 (modem_caps)) {
635
		if (!_nm_connection_check_main_setting (connection, NM_SETTING_CDMA_SETTING_NAME, error))
636 637 638 639 640
			return FALSE;

		return TRUE;
	}

641 642 643 644 645 646 647 648 649 650 651 652 653
	if (   !_nm_connection_check_main_setting (connection, NM_SETTING_GSM_SETTING_NAME, NULL)
	    && !_nm_connection_check_main_setting (connection, NM_SETTING_CDMA_SETTING_NAME, NULL)) {
		nm_utils_error_set (error,
		                    NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
		                    "connection type %s is not supported by modem",
		                    nm_connection_get_connection_type (connection));
		return FALSE;
	}

	nm_utils_error_set (error,
	                    NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
	                    "modem lacks capabilities for %s profile",
	                    nm_connection_get_connection_type (connection));
654
	return FALSE;
655 656 657 658 659 660 661
}

/*****************************************************************************/

static gboolean
complete_connection (NMModem *_self,
                     NMConnection *connection,
662
                     NMConnection *const*existing_connections,
663 664 665 666
                     GError **error)
{
	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
	MMModemCapability modem_caps;
667
	NMSettingPpp *s_ppp;
668

669
	modem_caps = mm_modem_get_current_capabilities (self->_priv.modem_iface);
670 671 672 673

	/* PPP settings common to 3GPP and 3GPP2 */
	s_ppp = nm_connection_get_setting_ppp (connection);
	if (!s_ppp) {
674
		s_ppp = (NMSettingPpp *) nm_setting_ppp_new ();
675 676 677 678 679 680 681 682 683 684 685
		g_object_set (G_OBJECT (s_ppp),
		              NM_SETTING_PPP_LCP_ECHO_FAILURE, 5,
		              NM_SETTING_PPP_LCP_ECHO_INTERVAL, 30,
		              NULL);
		nm_connection_add_setting (connection, NM_SETTING (s_ppp));
	}

	if (MODEM_CAPS_3GPP (modem_caps)) {
		NMSettingGsm *s_gsm;

		s_gsm = nm_connection_get_setting_gsm (connection);
686 687
		if (!s_gsm) {
			/* Need a GSM setting at least */
688
			g_set_error_literal (error,
689 690 691 692
			                     NM_CONNECTION_ERROR,
			                     NM_CONNECTION_ERROR_MISSING_SETTING,
			                     _("GSM mobile broadband connection requires a 'gsm' setting"));
			g_prefix_error (error, "%s: ", NM_SETTING_GSM_SETTING_NAME);
693 694 695 696 697 698 699
			return FALSE;
		}

		/* TODO: This is not needed */
		if (!nm_setting_gsm_get_number (s_gsm))
			g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL);

700 701
		nm_utils_complete_generic (NM_PLATFORM_GET,
		                           connection,
702 703 704
		                           NM_SETTING_GSM_SETTING_NAME,
		                           existing_connections,
		                           NULL,
705
		                           _("GSM connection"),
706
		                           NULL,
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
		                           FALSE); /* No IPv6 yet by default */

		return TRUE;
	}

	if (MODEM_CAPS_3GPP2 (modem_caps)) {
		NMSettingCdma *s_cdma;

		s_cdma = nm_connection_get_setting_cdma (connection);
		if (!s_cdma) {
			s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
			nm_connection_add_setting (connection, NM_SETTING (s_cdma));
		}

		if (!nm_setting_cdma_get_number (s_cdma))
			g_object_set (G_OBJECT (s_cdma), NM_SETTING_CDMA_NUMBER, "#777", NULL);

724 725
		nm_utils_complete_generic (NM_PLATFORM_GET,
		                           connection,
726 727 728
		                           NM_SETTING_CDMA_SETTING_NAME,
		                           existing_connections,
		                           NULL,
729
		                           _("CDMA connection"),
730
		                           NULL,
731 732 733 734 735
		                           FALSE); /* No IPv6 yet by default */

		return TRUE;
	}

736
	g_set_error (error,
737 738
	             NM_DEVICE_ERROR,
	             NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
739 740
	             "Device is not a mobile broadband modem");
	return FALSE;
741 742 743 744 745 746 747 748 749 750 751
}

/*****************************************************************************/

static gboolean
get_user_pass (NMModem *modem,
               NMConnection *connection,
               const char **user,
               const char **pass)
{
	NMSettingGsm *s_gsm;
752
	NMSettingCdma *s_cdma;
753 754

	s_gsm = nm_connection_get_setting_gsm (connection);
755 756
	s_cdma = nm_connection_get_setting_cdma (connection);
	if (!s_gsm && !s_cdma)
757 758
		return FALSE;

759 760 761 762 763 764 765 766 767 768 769 770
	if (user) {
		if (s_gsm)
			*user = nm_setting_gsm_get_username (s_gsm);
		else if (s_cdma)
			*user = nm_setting_cdma_get_username (s_cdma);
	}
	if (pass) {
		if (s_gsm)
			*pass = nm_setting_gsm_get_password (s_gsm);
		else if (s_cdma)
			*pass = nm_setting_cdma_get_password (s_cdma);
	}
771 772 773 774 775 776 777

	return TRUE;
}

/*****************************************************************************/
/* Query/Update enabled state */

778 779 780 781 782 783 784 785 786
static void
set_power_state_low_ready (MMModem *modem,
                           GAsyncResult *result,
                           NMModemBroadband *self)
{
	GError *error = NULL;

	if (!mm_modem_set_power_state_finish (modem, result, &error)) {
		/* Log but ignore errors; not all modems support low power state */
787 788
		_LOGD ("failed to set modem low power state: %s",
		       NM_G_ERROR_MSG (error));
789 790 791 792 793 794 795
		g_clear_error (&error);
	}

	/* Balance refcount */
	g_object_unref (self);
}

796 797 798 799 800 801 802
static void
modem_disable_ready (MMModem *modem_iface,
                     GAsyncResult *res,
                     NMModemBroadband *self)
{
	GError *error = NULL;

803 804 805 806 807 808 809 810
	if (mm_modem_disable_finish (modem_iface, res, &error)) {
		/* Once disabled, move to low-power mode */
		mm_modem_set_power_state (modem_iface,
		                          MM_MODEM_POWER_STATE_LOW,
		                          NULL,
		                          (GAsyncReadyCallback) set_power_state_low_ready,
		                          g_object_ref (self));
	} else {
811 812
		_LOGW ("failed to disable modem: %s",
		       NM_G_ERROR_MSG (error));
813
		nm_modem_set_prev_state (NM_MODEM (self), "disable failed");
814
		g_clear_error (&error);
815
	}
816 817 818 819 820 821 822 823 824 825 826 827 828

	/* Balance refcount */
	g_object_unref (self);
}

static void
modem_enable_ready (MMModem *modem_iface,
                    GAsyncResult *res,
                    NMModemBroadband *self)
{
	GError *error = NULL;

	if (!mm_modem_enable_finish (modem_iface, res, &error)) {
829 830
		_LOGW ("failed to enable modem: %s",
		       NM_G_ERROR_MSG (error));
831
		nm_modem_set_prev_state (NM_MODEM (self), "enable failed");
832
		g_clear_error (&error);
833
	}
834 835 836 837 838 839 840 841 842 843 844 845

	/* Balance refcount */
	g_object_unref (self);
}

static void
set_mm_enabled (NMModem *_self,
                gboolean enabled)
{
	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);

	if (enabled) {
846
		mm_modem_enable (self->_priv.modem_iface,
847 848 849 850
		                 NULL, /* cancellable */
		                 (GAsyncReadyCallback)modem_enable_ready,
		                 g_object_ref (self));
	} else {
851
		mm_modem_disable (self->_priv.modem_iface,
852 853 854 855 856 857 858
		                  NULL, /* cancellable */
		                  (GAsyncReadyCallback)modem_disable_ready,
		                  g_object_ref (self));
	}
}

/*****************************************************************************/
859
/* IPv4 method static */
860 861

static gboolean
862
static_stage3_ip4_done (NMModemBroadband *self)
863 864
{
	GError *error = NULL;
Beniamino Galvani's avatar
Beniamino Galvani committed
865
	gs_unref_object NMIP4Config *config = NULL;
866
	const char *data_port;
867 868
	const char *address_string;
	const char *gw_string;
869
	guint32 address_network;
870
	guint32 gw = 0;
871
	NMPlatformIP4Address address;
872
	const char **dns;
873
	guint i;
874 875
	guint32 ip4_route_table, ip4_route_metric;
	NMPlatformIP4Route *r;
876

877 878
	g_assert (self->_priv.ipv4_config);
	g_assert (self->_priv.bearer);
879

880 881
	self->_priv.idle_id_ip4 = 0;

882
	_LOGI ("IPv4 static configuration:");
883 884

	/* Fully fail if invalid IP address retrieved */
885
	address_string = mm_bearer_ip_config_get_address (self->_priv.ipv4_config);
886
	if (   !address_string
887
	    || !nm_utils_parse_inaddr_bin (AF_INET, address_string, NULL, &address_network)) {
888 889
		error = g_error_new (NM_DEVICE_ERROR,
		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
890
		                     "(%s) retrieving IP4 configuration failed: invalid address given %s%s%s",
891
		                     nm_modem_get_uid (NM_MODEM (self)),
892
		                     NM_PRINT_FMT_QUOTE_STRING (address_string));
893 894 895
		goto out;
	}

896
	/* Missing gateway not a hard failure */
897
	gw_string = mm_bearer_ip_config_get_gateway (self->_priv.ipv4_config);
898
	if (   gw_string
899
	    && !nm_utils_parse_inaddr_bin (AF_INET, gw_string, NULL, &gw)) {
900 901
		error = g_error_new (NM_DEVICE_ERROR,
		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
902
		                     "(%s) retrieving IP4 configuration failed: invalid gateway address \"%s\"",
903
		                     nm_modem_get_uid (NM_MODEM (self)),
904
		                     gw_string);
905 906
		goto out;
	}
907

908
	data_port = mm_bearer_get_interface (self->_priv.bearer);
909
	g_assert (data_port);
910 911
	config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
	                            nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port));
912 913 914

	memset (&address, 0, sizeof (address));
	address.address = address_network;
915
	address.peer_address = address_network;
916
	address.plen = mm_bearer_ip_config_get_prefix (self->_priv.ipv4_config);
917
	address.addr_source = NM_IP_CONFIG_SOURCE_WWAN;
918 919
	if (address.plen <= 32)
		nm_ip4_config_add_address (config, &address);
920

921
	_LOGI ("  address %s/%d", address_string, address.plen);
922

923 924 925 926 927 928 929 930 931 932 933 934 935
	nm_modem_get_route_parameters (NM_MODEM (self),
	                               &ip4_route_table,
	                               &ip4_route_metric,
	                               NULL,
	                               NULL);
	r = &(NMPlatformIP4Route) {
		.rt_source = NM_IP_CONFIG_SOURCE_WWAN,
		.gateway = gw,
		.table_coerced = nm_platform_route_table_coerce (ip4_route_table),
		.metric = ip4_route_metric,
	};
	nm_ip4_config_add_route (config, r, NULL);
	_LOGI ("  gateway %s", gw_string);
936 937

	/* DNS servers */
938
	dns = mm_bearer_ip_config_get_dns (self->_priv.ipv4_config);
939
	for (i = 0; dns && dns[i]; i++) {
940
		if (   nm_utils_parse_inaddr_bin (AF_INET, dns[i], NULL, &address_network)
941 942
		    && address_network > 0) {
			nm_ip4_config_add_nameserver (config, address_network);
943
			_LOGI ("  DNS %s", dns[i]);
944 945 946 947
		}
	}

out:
948
	g_signal_emit_by_name (self, NM_MODEM_IP4_CONFIG_RESULT, config, error);
949 950 951 952 953
	g_clear_error (&error);
	return FALSE;
}

static NMActStageReturn
954
static_stage3_ip4_config_start (NMModem *modem,
955
                                NMActRequest *req,
956
                                NMDeviceStateReason *out_failure_reason)
957
{
958 959
	NMModemBroadband *self = NM_MODEM_BROADBAND (modem);
	NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self);
960 961 962

	/* We schedule it in an idle just to follow the same logic as in the
	 * generic modem implementation. */
963 964
	nm_clear_g_source (&priv->idle_id_ip4);
	priv->idle_id_ip4 = g_idle_add ((GSourceFunc) static_stage3_ip4_done, self);
965 966 967 968 969 970 971 972 973 974 975 976

	return NM_ACT_STAGE_RETURN_POSTPONE;
}

/*****************************************************************************/
/* IPv6 method static */

static gboolean
stage3_ip6_done (NMModemBroadband *self)
{
	GError *error = NULL;
	NMIP6Config *config = NULL;
977
	const char *data_port;
978
	const char *address_string;
979 980
	NMPlatformIP6Address address;
	NMModemIPMethod ip_method;
981
	const char **dns;
982 983
	guint i;

984
	g_assert (self->_priv.ipv6_config);
985

986
	self->_priv.idle_id_ip6 = 0;
987 988
	memset (&address, 0, sizeof (address));

989
	ip_method = get_bearer_ip_method (self->_priv.ipv6_config);
990

991
	address_string = mm_bearer_ip_config_get_address (self->_priv.ipv6_config);
992 993 994
	if (!address_string) {
		/* DHCP/SLAAC is allowed to skip addresses; other methods require it */
		if (ip_method != NM_MODEM_IP_METHOD_AUTO) {
995
			error = g_error_new (NM_DEVICE_ERROR,
996 997 998
			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
			                     "(%s) retrieving IPv6 configuration failed: no address given",
			                     nm_modem_get_uid (NM_MODEM (self)));
999 1000 1001 1002 1003 1004
		}
		goto out;
	}

	/* Fail if invalid IP address retrieved */
	if (!inet_pton (AF_INET6, address_string, (void *) &(address.address))) {
1005 1006
		error = g_error_new (NM_DEVICE_ERROR,
		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
1007 1008 1009 1010 1011 1012
		                     "(%s) retrieving IPv6 configuration failed: invalid address given '%s'",
		                     nm_modem_get_uid (NM_MODEM (self)),
		                     address_string);
		goto out;
	}

1013
	_LOGI ("IPv6 base configuration:");
1014

1015
	data_port = mm_bearer_get_interface (self->_priv.bearer);
1016
	g_assert (data_port);
1017 1018
	config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
	                            nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port));