nm-device.c 203 KB
Newer Older
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 3 4 5 6 7 8 9 10 11 12 13
/* 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.
 *
14 15 16
 * 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.
17
 *
18
 * Copyright (C) 2005 - 2012 Red Hat, Inc.
19
 * Copyright (C) 2006 - 2008 Novell, Inc.
20 21
 */

22
#include <config.h>
23 24 25 26 27
#include <glib.h>
#include <glib/gi18n.h>
#include <dbus/dbus.h>
#include <netinet/in.h>
#include <string.h>
Dan Williams's avatar
Dan Williams committed
28 29
#include <unistd.h>
#include <errno.h>
30 31
#include <linux/sockios.h>
#include <linux/ethtool.h>
Dan Williams's avatar
Dan Williams committed
32
#include <sys/ioctl.h>
33 34 35 36
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <arpa/inet.h>
37
#include <fcntl.h>
38
#include <linux/if.h>
39

40
#include "libgsystem.h"
41
#include "nm-glib-compat.h"
42
#include "nm-device.h"
43 44
#include "nm-device-private.h"
#include "NetworkManagerUtils.h"
45
#include "nm-platform.h"
46 47
#include "nm-rdisc.h"
#include "nm-lndp-rdisc.h"
48
#include "nm-dhcp-manager.h"
49
#include "nm-dbus-manager.h"
50
#include "nm-utils.h"
51
#include "nm-logging.h"
52
#include "nm-setting-ip4-config.h"
53
#include "nm-setting-ip6-config.h"
54
#include "nm-setting-connection.h"
55
#include "nm-dnsmasq-manager.h"
56
#include "nm-dhcp4-config.h"
57
#include "nm-rfkill-manager.h"
Jiri Popelka's avatar
Jiri Popelka committed
58
#include "nm-firewall-manager.h"
59
#include "nm-properties-changed-signal.h"
60
#include "nm-enum-types.h"
61
#include "nm-settings-connection.h"
62
#include "nm-connection-provider.h"
63
#include "nm-posix-signals.h"
64
#include "nm-manager-auth.h"
65
#include "nm-dbus-glib-types.h"
66
#include "nm-dispatcher.h"
Dan Winship's avatar
Dan Winship committed
67
#include "nm-config-device.h"
68
#include "nm-config.h"
69
#include "nm-platform.h"
70 71 72

static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context);

73
#include "nm-device-glue.h"
74

75 76
#define DBUS_G_TYPE_UINT_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))

77 78 79
/* default to installed helper, but can be modified for testing */
const char *nm_device_autoipd_helper_path = LIBEXECDIR "/nm-avahi-autoipd.action";

80 81 82 83 84 85 86 87 88 89 90 91 92 93
/***********************************************************/
#define NM_DEVICE_ERROR (nm_device_error_quark ())

static GQuark
nm_device_error_quark (void)
{
	static GQuark quark = 0;
	if (!quark)
		quark = g_quark_from_static_string ("nm-device-error");
	return quark;
}

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

94
enum {
95
	STATE_CHANGED,
96
	AUTOCONNECT_ALLOWED,
97
	AUTH_REQUEST,
98 99
	IP4_CONFIG_CHANGED,
	IP6_CONFIG_CHANGED,
100 101 102 103
	LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = { 0 };

104 105
enum {
	PROP_0,
106
	PROP_PLATFORM_DEVICE,
107 108 109 110
	PROP_UDI,
	PROP_IFACE,
	PROP_IP_IFACE,
	PROP_DRIVER,
111 112
	PROP_DRIVER_VERSION,
	PROP_FIRMWARE_VERSION,
113
	PROP_CAPABILITIES,
114
	PROP_CARRIER,
115 116 117 118 119 120
	PROP_IP4_ADDRESS,
	PROP_IP4_CONFIG,
	PROP_DHCP4_CONFIG,
	PROP_IP6_CONFIG,
	PROP_DHCP6_CONFIG,
	PROP_STATE,
121
	PROP_STATE_REASON,
122 123 124
	PROP_ACTIVE_CONNECTION,
	PROP_DEVICE_TYPE,
	PROP_MANAGED,
125
	PROP_AUTOCONNECT,
126 127 128 129
	PROP_FIRMWARE_MISSING,
	PROP_TYPE_DESC,
	PROP_RFKILL_TYPE,
	PROP_IFINDEX,
130
	PROP_AVAILABLE_CONNECTIONS,
131
	PROP_IS_MASTER,
132
	PROP_HW_ADDRESS,
133
	PROP_HAS_PENDING_ACTION,
134 135 136
	LAST_PROP
};

137 138
#define DEFAULT_AUTOCONNECT TRUE

139 140
/***********************************************************/

Dan Winship's avatar
Dan Winship committed
141 142 143 144
static void nm_device_config_device_interface_init (NMConfigDeviceInterface *iface);

G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMDevice, nm_device, G_TYPE_OBJECT,
                                  G_IMPLEMENT_INTERFACE (NM_TYPE_CONFIG_DEVICE, nm_device_config_device_interface_init))
145

146 147
#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))

148 149
typedef enum {
	IP_NONE = 0,
150
	IP_WAIT,
151
	IP_CONF,
152 153
	IP_DONE,
	IP_FAIL
154 155
} IpState;

156 157 158 159 160 161
typedef struct {
	NMDeviceState state;
	NMDeviceStateReason reason;
	guint id;
} QueuedState;

162 163 164 165 166 167
typedef struct {
	NMDevice *slave;
	gboolean enslaved;
	guint watch_id;
} SlaveInfo;

168 169 170 171 172 173 174
typedef struct {
	guint log_domain;
	guint timeout;
	guint watch;
	GPid pid;
} PingInfo;

175 176 177
typedef struct {
	gboolean disposed;
	gboolean initialized;
178
	gboolean in_state_changed;
179

180
	NMDeviceState state;
181
	NMDeviceStateReason state_reason;
182
	QueuedState   queued_state;
183
	guint queued_ip_config_id;
184
	guint pending_actions;
185

186 187 188
	char *        udi;
	char *        path;
	char *        iface;   /* may change, could be renamed by user */
189
	int           ifindex;
190
	gboolean      is_software;
191
	char *        ip_iface;
192
	int           ip_ifindex;
193 194 195 196
	NMDeviceType  type;
	char *        type_desc;
	guint32       capabilities;
	char *        driver;
197 198
	char *        driver_version;
	char *        firmware_version;
199
	RfKillType    rfkill_type;
200
	gboolean      firmware_missing;
201
	GHashTable *  available_connections;
202 203
	guint8        hw_addr[NM_UTILS_HWADDR_LEN_MAX];
	guint         hw_addr_len;
204

205 206
	gboolean      manager_managed; /* whether managed by NMManager or not */
	gboolean      default_unmanaged; /* whether unmanaged by default */
207
	gboolean      is_nm_owned; /* whether the device is a device owned and created by NM */
208
	guint         delete_on_deactivate_id; /* event id for scheduled link_delete action (g_idle_add) */
209

210
	guint32         ip4_address;
211

212
	NMActRequest *  act_request;
213
	guint           act_source_id;
214
	gpointer        act_source_func;
215 216
	guint           act_source6_id;
	gpointer        act_source6_func;
217

218 219 220 221 222 223 224
	/* Link stuff */
	guint           link_connected_id;
	guint           link_disconnected_id;
	guint           carrier_defer_id;
	gboolean        carrier;
	gboolean        ignore_carrier;

225
	/* Generic DHCP stuff */
226
	NMDHCPManager * dhcp_manager;
227 228
	guint32         dhcp_timeout;
	GByteArray *    dhcp_anycast_address;
229

230
	/* IP4 configuration info */
231
	NMIP4Config *   ip4_config;     /* Combined config from VPN, settings, and device */
232
	IpState         ip4_state;
233
	NMIP4Config *   dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */
234
	NMIP4Config *   ext_ip4_config; /* Stuff added outside NM */
235 236

	/* DHCPv4 tracking */
237
	NMDHCPClient *  dhcp4_client;
238 239
	gulong          dhcp4_state_sigid;
	gulong          dhcp4_timeout_sigid;
240
	NMDHCP4Config * dhcp4_config;
241
	NMIP4Config *   vpn4_config;  /* routes added by a VPN which uses this device */
242

243 244
	PingInfo        gw_ping;

245
	/* dnsmasq stuff for shared connections */
246 247
	NMDnsMasqManager *dnsmasq_manager;
	gulong            dnsmasq_state_id;
248

Jiri Popelka's avatar
Jiri Popelka committed
249 250 251 252
	/* Firewall Manager */
	NMFirewallManager *fw_manager;
	DBusGProxyCall    *fw_call;

253
	/* avahi-autoipd stuff */
254 255 256
	GPid    aipd_pid;
	guint   aipd_watch;
	guint   aipd_timeout;
257 258

	/* IP6 configuration info */
259
	NMIP6Config *  ip6_config;
260
	IpState        ip6_state;
261
	NMIP6Config *  vpn6_config;  /* routes added by a VPN which uses this device */
262
	NMIP6Config *  ext_ip6_config; /* Stuff added outside NM */
263

264 265
	NMRDisc *      rdisc;
	gulong         rdisc_config_changed_sigid;
266 267
	/* IP6 config from autoconf */
	NMIP6Config *  ac_ip6_config;
268

269
	char *         ip6_accept_ra_path;
270
	gint32         ip6_accept_ra_save;
271

272 273 274 275
	/* IPv6 privacy extensions (RFC4941) */
	char *         ip6_privacy_tempaddr_path;
	gint32         ip6_privacy_tempaddr_save;

276
	NMDHCPClient *  dhcp6_client;
277
	NMRDiscDHCPLevel dhcp6_mode;
278 279 280
	gulong          dhcp6_state_sigid;
	gulong          dhcp6_timeout_sigid;
	NMDHCP6Config * dhcp6_config;
281 282
	/* IP6 config from DHCP */
	NMIP6Config *   dhcp6_ip6_config;
283

284 285
	/* allow autoconnect feature */
	gboolean        autoconnect;
286

Jiri Pirko's avatar
Jiri Pirko committed
287
	/* master interface for bridge/bond/team slave */
288 289 290
	NMDevice *      master;
	gboolean        enslaved;

291 292 293
	/* slave management */
	gboolean        is_master;
	GSList *        slaves;    /* list of SlaveInfo */
294 295

	NMConnectionProvider *con_provider;
296 297 298 299 300 301

	/* connection provider signals for available connections property */
	guint cp_added_id;
	guint cp_loaded_id;
	guint cp_removed_id;
	guint cp_updated_id;
302

303
} NMDevicePrivate;
304

305 306
static gboolean nm_device_set_ip4_config (NMDevice *dev,
                                          NMIP4Config *config,
307
                                          gboolean commit,
308
                                          NMDeviceStateReason *reason);
309 310
static gboolean ip4_config_merge_and_apply (NMDevice *self,
                                            NMIP4Config *config,
311
                                            gboolean commit,
312 313
                                            NMDeviceStateReason *out_reason);

314 315
static gboolean nm_device_set_ip6_config (NMDevice *dev,
                                          NMIP6Config *config,
316
                                          gboolean commit,
317
                                          NMDeviceStateReason *reason);
318

319 320
static gboolean nm_device_activate_ip6_config_commit (gpointer user_data);

321
static gboolean check_connection_available (NMDevice *device, NMConnection *connection);
322

323 324
static gboolean spec_match_list (NMDevice *device, const GSList *specs);

325 326
static void _clear_available_connections (NMDevice *device, gboolean do_signal);

327
static void dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release);
328

329 330
static const char *reason_to_string (NMDeviceStateReason reason);

331 332
static void ip_check_gw_ping_cleanup (NMDevice *self);

333 334 335 336 337
static void cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
static void cp_connections_loaded (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
static void cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);
static void cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data);

338 339
static const char *state_to_string (NMDeviceState state);

340
static void link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformReason reason, NMDevice *device);
341
static void check_carrier (NMDevice *device);
342

343 344
static void nm_device_queued_ip_config_change_clear (NMDevice *self);
static void update_ip_config (NMDevice *self);
345
static void device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformReason reason, gpointer user_data);
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362

static const char const *platform_ip_signals[] = {
	NM_PLATFORM_IP4_ADDRESS_ADDED,
	NM_PLATFORM_IP4_ADDRESS_CHANGED,
	NM_PLATFORM_IP4_ADDRESS_REMOVED,
	NM_PLATFORM_IP4_ROUTE_ADDED,
	NM_PLATFORM_IP4_ROUTE_CHANGED,
	NM_PLATFORM_IP4_ROUTE_REMOVED,
	NM_PLATFORM_IP6_ADDRESS_ADDED,
	NM_PLATFORM_IP6_ADDRESS_CHANGED,
	NM_PLATFORM_IP6_ADDRESS_REMOVED,
	NM_PLATFORM_IP6_ROUTE_ADDED,
	NM_PLATFORM_IP6_ROUTE_CHANGED,
	NM_PLATFORM_IP6_ROUTE_REMOVED,
};
static const int n_platform_ip_signals = G_N_ELEMENTS (platform_ip_signals);

363
static void
364
nm_device_init (NMDevice *self)
365
{
366 367 368
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);

	priv->type = NM_DEVICE_TYPE_UNKNOWN;
369
	priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED;
370
	priv->state = NM_DEVICE_STATE_UNMANAGED;
371
	priv->state_reason = NM_DEVICE_STATE_REASON_NONE;
372
	priv->dhcp_timeout = 0;
373
	priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
374
	priv->autoconnect = DEFAULT_AUTOCONNECT;
375
	priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
376 377
}

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
static void
update_accept_ra_save (NMDevice *self)
{
	NMDevicePrivate *priv;
	const char *ip_iface;
	char *new_path;

	g_return_if_fail (NM_IS_DEVICE (self));

	priv = NM_DEVICE_GET_PRIVATE (self);
	ip_iface = nm_device_get_ip_iface (self);

	new_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/accept_ra", ip_iface);
	g_assert (new_path);

	if (priv->ip6_accept_ra_path) {
		/* If the IP iface is different from before, use the new value */
		if (!strcmp (new_path, priv->ip6_accept_ra_path)) {
			g_free (new_path);
			return;
		}
		g_free (priv->ip6_accept_ra_path);
	}

	/* Grab the original value of "accept_ra" so we can restore it when NM exits */
	priv->ip6_accept_ra_path = new_path;
404 405 406
	if (!nm_utils_get_proc_sys_net_value_with_bounds (priv->ip6_accept_ra_path,
	                                                  ip_iface,
	                                                  &priv->ip6_accept_ra_save,
407
	                                                  0, 2)) {
408 409 410 411 412
		g_free (priv->ip6_accept_ra_path);
		priv->ip6_accept_ra_path = NULL;
	}
}

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
static void
update_ip6_privacy_save (NMDevice *self)
{
	NMDevicePrivate *priv;
	const char *ip_iface;
	char *new_path;

	g_return_if_fail (NM_IS_DEVICE (self));

	priv = NM_DEVICE_GET_PRIVATE (self);
	ip_iface = nm_device_get_ip_iface (self);

	new_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/use_tempaddr", ip_iface);
	g_assert (new_path);

	if (priv->ip6_privacy_tempaddr_path) {
		/* If the IP iface is different from before, use the new value */
		if (!strcmp (new_path, priv->ip6_privacy_tempaddr_path)) {
			g_free (new_path);
			return;
		}
		g_free (priv->ip6_privacy_tempaddr_path);
	}

	/* Grab the original value of "use_tempaddr" so we can restore it when NM exits */
	priv->ip6_privacy_tempaddr_path = new_path;
	if (!nm_utils_get_proc_sys_net_value (priv->ip6_privacy_tempaddr_path,
	                                      ip_iface,
	                                      &priv->ip6_privacy_tempaddr_save)) {
		g_free (priv->ip6_privacy_tempaddr_path);
		priv->ip6_privacy_tempaddr_path = NULL;
	}
}

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
/*
 * Get driver info from SIOCETHTOOL ioctl() for 'iface'
 * Returns driver and firmware versions to 'driver_version and' 'firmware_version'
 */
static gboolean
device_get_driver_info (const char *iface, char **driver_version, char **firmware_version)
{
	struct ethtool_drvinfo drvinfo;
	struct ifreq req;
	int fd;

	fd = socket (PF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
		nm_log_warn (LOGD_HW, "couldn't open control socket.");
		return FALSE;
	}

	/* Get driver and firmware version info */
465
	memset (&drvinfo, 0, sizeof (drvinfo));
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
	memset (&req, 0, sizeof (struct ifreq));
	strncpy (req.ifr_name, iface, IFNAMSIZ);
	drvinfo.cmd = ETHTOOL_GDRVINFO;
	req.ifr_data = &drvinfo;

	errno = 0;
	if (ioctl (fd, SIOCETHTOOL, &req) < 0) {
		nm_log_dbg (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d",
		            iface, errno);
		close (fd);
		return FALSE;
	}
	if (driver_version)
		*driver_version = g_strdup (drvinfo.version);
	if (firmware_version)
		*firmware_version = g_strdup (drvinfo.fw_version);

	close (fd);
	return TRUE;
}

487 488 489 490 491 492
static gboolean
device_has_capability (NMDevice *device, NMDeviceCapabilities caps)
{
	return !!(NM_DEVICE_GET_PRIVATE (device)->capabilities & caps);
}

493 494
static GObject*
constructor (GType type,
495 496
             guint n_construct_params,
             GObjectConstructParam *construct_params)
497
{
498 499
	GObject *object;
	NMDevice *dev;
500
	NMDevicePrivate *priv;
501 502
	NMPlatform *platform;
	int i;
503
	static guint32 id = 0;
504

505
	object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type,
506 507
	                         n_construct_params,
	                         construct_params);
508 509
	if (!object)
		return NULL;
510

511
	dev = NM_DEVICE (object);
512
	priv = NM_DEVICE_GET_PRIVATE (dev);
513

514
	if (!priv->iface) {
515
		nm_log_err (LOGD_DEVICE, "No device interface provided, ignoring");
516 517 518
		goto error;
	}

519 520 521 522 523
	if (!priv->udi) {
		/* Use a placeholder UDI until we get a real one */
		priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++);
	}

524 525
	if (NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities)
		priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev);
526

527 528
	priv->dhcp_manager = nm_dhcp_manager_get ();

Jiri Popelka's avatar
Jiri Popelka committed
529 530
	priv->fw_manager = nm_firewall_manager_get ();

531 532
	device_get_driver_info (priv->iface, &priv->driver_version, &priv->firmware_version);

533
	update_accept_ra_save (dev);
534
	update_ip6_privacy_save (dev);
535
	update_ip_config (dev);
536

537 538 539 540 541 542 543 544 545 546
	/* Watch for external IP config changes */
	platform = nm_platform_get ();
	for (i = 0; i < n_platform_ip_signals; i++) {
		g_signal_connect (platform, platform_ip_signals[i],
		                  G_CALLBACK (device_ip_changed), dev);
	}

	g_signal_connect (platform, NM_PLATFORM_LINK_CHANGED,
	                  G_CALLBACK (link_changed_cb), dev);

547
	priv->initialized = TRUE;
548
	return object;
549 550 551 552

error:
	g_object_unref (dev);
	return NULL;
553 554
}

555 556 557 558
static void
constructed (GObject *object)
{
	NMDevice *dev = NM_DEVICE (object);
559
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
560

561
	nm_device_update_hw_address (dev);
562 563 564 565 566 567 568

	if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address)
		NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev);

	if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address)
		NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev);

569
	/* Have to call update_initial_hw_address() before calling get_ignore_carrier() */
570 571 572 573 574 575 576 577 578 579 580 581
	if (device_has_capability (dev, NM_DEVICE_CAP_CARRIER_DETECT)) {
		priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), NM_CONFIG_DEVICE (dev));

		check_carrier (dev);
		nm_log_info (LOGD_HW,
		             "(%s): carrier is %s%s",
		             nm_device_get_iface (NM_DEVICE (dev)),
		             priv->carrier ? "ON" : "OFF",
		             priv->ignore_carrier ? " (but ignored)" : "");
	} else {
		/* Fake online link when carrier detection is not available. */
		priv->carrier = TRUE;
582 583
	}

584 585 586
	if (priv->ifindex > 0)
		priv->is_software = nm_platform_link_is_software (priv->ifindex);

587 588 589 590
	if (G_OBJECT_CLASS (nm_device_parent_class)->constructed)
		G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
}

591
static gboolean
592
nm_device_is_up (NMDevice *self)
593 594 595
{
	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);

596 597
	if (NM_DEVICE_GET_CLASS (self)->is_up)
		return NM_DEVICE_GET_CLASS (self)->is_up (self);
598 599 600 601

	return TRUE;
}

602
static gboolean
603
is_up (NMDevice *device)
604 605 606
{
	int ifindex = nm_device_get_ip_ifindex (device);

607
	return ifindex ? nm_platform_link_is_up (ifindex) : TRUE;
608 609
}

610 611 612
void
nm_device_set_path (NMDevice *self, const char *path)
{
613 614
	NMDevicePrivate *priv;

615 616
	g_return_if_fail (self != NULL);

617 618 619 620
	priv = NM_DEVICE_GET_PRIVATE (self);
	g_return_if_fail (priv->path == NULL);

	priv->path = g_strdup (path);
621 622 623 624 625 626 627
}

const char *
nm_device_get_path (NMDevice *self)
{
	g_return_val_if_fail (self != NULL, NULL);

628
	return NM_DEVICE_GET_PRIVATE (self)->path;
629
}
630 631 632 633 634 635

const char *
nm_device_get_udi (NMDevice *self)
{
	g_return_val_if_fail (self != NULL, NULL);

636
	return NM_DEVICE_GET_PRIVATE (self)->udi;
637 638 639 640 641 642 643 644 645 646
}

/*
 * Get/set functions for iface
 */
const char *
nm_device_get_iface (NMDevice *self)
{
	g_return_val_if_fail (self != NULL, NULL);

647
	return NM_DEVICE_GET_PRIVATE (self)->iface;
648 649
}

650 651 652 653 654 655 656
int
nm_device_get_ifindex (NMDevice *self)
{
	g_return_val_if_fail (self != NULL, 0);

	return NM_DEVICE_GET_PRIVATE (self)->ifindex;
}
657

658 659 660 661 662 663 664 665
gboolean
nm_device_is_software (NMDevice *device)
{
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);

	return priv->is_software;
}

666
const char *
667 668
nm_device_get_ip_iface (NMDevice *self)
{
669 670
	NMDevicePrivate *priv;

671 672
	g_return_val_if_fail (self != NULL, NULL);

673
	priv = NM_DEVICE_GET_PRIVATE (self);
674
	/* If it's not set, default to iface */
675
	return priv->ip_iface ? priv->ip_iface : priv->iface;
676 677
}

678 679 680 681 682 683 684 685 686 687 688
int
nm_device_get_ip_ifindex (NMDevice *self)
{
	NMDevicePrivate *priv;

	g_return_val_if_fail (self != NULL, 0);

	priv = NM_DEVICE_GET_PRIVATE (self);
	/* If it's not set, default to iface */
	return priv->ip_iface ? priv->ip_ifindex : priv->ifindex;
}
689 690 691 692

void
nm_device_set_ip_iface (NMDevice *self, const char *iface)
{
693
	NMDevicePrivate *priv;
694
	char *old_ip_iface;
695

696 697
	g_return_if_fail (NM_IS_DEVICE (self));

698
	priv = NM_DEVICE_GET_PRIVATE (self);
699
	old_ip_iface = priv->ip_iface;
700 701 702 703
	priv->ip_ifindex = 0;

	priv->ip_iface = g_strdup (iface);
	if (priv->ip_iface) {
704
		priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface);
Dan Williams's avatar
Dan Williams committed
705 706
		if (priv->ip_ifindex <= 0) {
			/* Device IP interface must always be a kernel network interface */
707 708 709
			nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface);
		}
	}
710 711 712

	/* Emit change notification */
	if (g_strcmp0 (old_ip_iface, priv->ip_iface))
713
		g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
714
	g_free (old_ip_iface);
715 716
}

717
static guint
718
get_hw_address_length (NMDevice *dev, gboolean *out_permanent)
719
{
720 721 722 723
	size_t len;

	if (nm_platform_link_get_address (nm_device_get_ip_ifindex (dev), &len))
		return len;
724
	else
725 726 727 728
		return 0;
}

static guint
729
nm_device_get_hw_address_length (NMDevice *dev, gboolean *out_permanent)
730
{
731
	return NM_DEVICE_GET_CLASS (dev)->get_hw_address_length (dev, out_permanent);
732 733
}

734 735 736
const guint8 *
nm_device_get_hw_address (NMDevice *dev, guint *out_len)
{
737 738
	NMDevicePrivate *priv;

739
	g_return_val_if_fail (NM_IS_DEVICE (dev), NULL);
740
	priv = NM_DEVICE_GET_PRIVATE (dev);
741

742 743 744 745 746 747 748
	if (out_len)
		*out_len = priv->hw_addr_len;

	if (priv->hw_addr_len == 0)
		return NULL;
	else
		return priv->hw_addr;
749
}
750

751 752 753 754 755 756 757 758
/*
 * Get/set functions for driver
 */
const char *
nm_device_get_driver (NMDevice *self)
{
	g_return_val_if_fail (self != NULL, NULL);

759
	return NM_DEVICE_GET_PRIVATE (self)->driver;
760 761
}

762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
const char *
nm_device_get_driver_version (NMDevice *self)
{
	g_return_val_if_fail (self != NULL, NULL);

	return NM_DEVICE_GET_PRIVATE (self)->driver_version;
}

const char *
nm_device_get_firmware_version (NMDevice *self)
{
	g_return_val_if_fail (self != NULL, NULL);

	return NM_DEVICE_GET_PRIVATE (self)->firmware_version;
}

778 779 780 781 782 783 784

/*
 * Get/set functions for type
 */
NMDeviceType
nm_device_get_device_type (NMDevice *self)
{
785
	g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_TYPE_UNKNOWN);
786

787
	return NM_DEVICE_GET_PRIVATE (self)->type;
788 789 790
}


791 792 793 794 795 796 797
/**
 * nm_device_get_priority():
 * @dev: the #NMDevice
 *
 * Returns: the device's routing priority.  Lower numbers means a "better"
 *  device, eg higher priority.
 */
798 799 800
int
nm_device_get_priority (NMDevice *dev)
{
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
	g_return_val_if_fail (NM_IS_DEVICE (dev), 100);

	/* Device 'priority' is used for two things:
	 *
	 * a) two devices on the same IP subnet: the "better" (ie, lower number)
	 *     device is the default outgoing device for that subnet
	 * b) default route: the "better" device gets the default route.  This can
	 *     always be modified by setting a connection to never-default=TRUE, in
	 *     which case that device will never take the default route when
	 *     it's using that connection.
	 */

	switch (nm_device_get_device_type (dev)) {
	case NM_DEVICE_TYPE_ETHERNET:
		return 1;
	case NM_DEVICE_TYPE_INFINIBAND:
		return 2;
	case NM_DEVICE_TYPE_ADSL:
		return 3;
	case NM_DEVICE_TYPE_WIMAX:
		return 4;
	case NM_DEVICE_TYPE_BOND:
		return 5;
Jiri Pirko's avatar
Jiri Pirko committed
824
	case NM_DEVICE_TYPE_TEAM:
825
		return 6;
Jiri Pirko's avatar
Jiri Pirko committed
826
	case NM_DEVICE_TYPE_VLAN:
827
		return 7;
Jiri Pirko's avatar
Jiri Pirko committed
828
	case NM_DEVICE_TYPE_MODEM:
829
		return 8;
Jiri Pirko's avatar
Jiri Pirko committed
830
	case NM_DEVICE_TYPE_BT:
831
		return 9;
Jiri Pirko's avatar
Jiri Pirko committed
832
	case NM_DEVICE_TYPE_WIFI:
833
		return 10;
Jiri Pirko's avatar
Jiri Pirko committed
834 835
	case NM_DEVICE_TYPE_OLPC_MESH:
		return 11;
836 837 838
	default:
		return 20;
	}
839 840
}

841 842 843 844 845
const char *
nm_device_get_type_desc (NMDevice *self)
{
	g_return_val_if_fail (self != NULL, NULL);

846
	return NM_DEVICE_GET_PRIVATE (self)->type_desc;
847 848
}

849 850 851 852 853 854 855 856 857 858 859 860 861
void
nm_device_set_connection_provider (NMDevice *device,
                                   NMConnectionProvider *provider)
{
	NMDevicePrivate *priv;

	g_return_if_fail (device != NULL);
	g_return_if_fail (NM_IS_CONNECTION_PROVIDER (provider));

	priv = NM_DEVICE_GET_PRIVATE (device);
	g_return_if_fail (priv->con_provider == NULL);

	priv->con_provider = provider;
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
	priv->cp_added_id = g_signal_connect (priv->con_provider,
	                                      NM_CP_SIGNAL_CONNECTION_ADDED,
	                                      G_CALLBACK (cp_connection_added),
	                                      device);

	priv->cp_loaded_id = g_signal_connect (priv->con_provider,
	                                       NM_CP_SIGNAL_CONNECTIONS_LOADED,
	                                       G_CALLBACK (cp_connections_loaded),
	                                       device);

	priv->cp_removed_id = g_signal_connect (priv->con_provider,
	                                        NM_CP_SIGNAL_CONNECTION_REMOVED,
	                                        G_CALLBACK (cp_connection_removed),
	                                        device);

	priv->cp_updated_id = g_signal_connect (priv->con_provider,
	                                        NM_CP_SIGNAL_CONNECTION_UPDATED,
	                                        G_CALLBACK (cp_connection_updated),
	                                        device);
881 882 883 884 885 886 887 888 889 890
}

NMConnectionProvider *
nm_device_get_connection_provider (NMDevice *device)
{
	g_return_val_if_fail (device != NULL, NULL);

	return NM_DEVICE_GET_PRIVATE (device)->con_provider;
}

891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
static SlaveInfo *
find_slave_info (NMDevice *self, NMDevice *slave)
{
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
	SlaveInfo *info;
	GSList *iter;

	for (iter = priv->slaves; iter; iter = g_slist_next (iter)) {
		info = iter->data;
		if (info->slave == slave)
			return info;
	}
	return NULL;
}

static void
free_slave_info (SlaveInfo *info)
{
	g_signal_handler_disconnect (info->slave, info->watch_id);
	g_clear_object (&info->slave);
	memset (info, 0, sizeof (*info));
	g_free (info);
}

915 916 917 918
/**
 * nm_device_enslave_slave:
 * @dev: the master device
 * @slave: the slave device to enslave
919
 * @connection: the slave device's connection
920
 *
Jiri Pirko's avatar
Jiri Pirko committed
921 922
 * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team,
 * etc) then this function enslaves @slave.
923 924 925 926
 *
 * Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave
 *  other devices.
 */
927
static gboolean
928
nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connection)
929
{
930 931 932
	SlaveInfo *info;
	gboolean success = FALSE;

933 934 935
	g_return_val_if_fail (dev != NULL, FALSE);
	g_return_val_if_fail (slave != NULL, FALSE);
	g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
936
	g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->enslave_slave != NULL, FALSE);
937

938 939 940 941 942 943 944 945 946 947
	info = find_slave_info (dev, slave);
	if (!info)
		return FALSE;

	g_warn_if_fail (info->enslaved == FALSE);
	success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection);
	if (success) {
		info->enslaved = TRUE;
		nm_device_slave_notify_enslaved (info->slave, TRUE, FALSE);
	}
948 949 950 951

	/* Ensure the device's hardware address is up-to-date; it often changes
	 * when slaves change.
	 */
952
	nm_device_update_hw_address (dev);
953

954 955 956 957 958 959 960 961 962 963 964 965
	/* Restart IP configuration if we're waiting for slaves.  Do this
	 * after updating the hardware address as IP config may need the
	 * new address.
	 */
	if (success) {
		if (NM_DEVICE_GET_PRIVATE (dev)->ip4_state == IP_WAIT)
			nm_device_activate_stage3_ip4_start (dev);

		if (NM_DEVICE_GET_PRIVATE (dev)->ip6_state == IP_WAIT)
			nm_device_activate_stage3_ip6_start (dev);
	}

966
	return success;
967 968 969
}

/**
970
 * nm_device_release_one_slave:
971 972
 * @dev: the master device
 * @slave: the slave device to release
973
 * @failed: %TRUE if the release was unexpected, ie the master failed
974
 *
Jiri Pirko's avatar
Jiri Pirko committed
975 976
 * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team,
 * etc) then this function releases the previously enslaved @slave.
977 978 979 980
 *
 * Returns: %TRUE on success, %FALSE on failure, if this device cannot enslave
 *  other devices, or if @slave was never enslaved.
 */
981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002
static gboolean
nm_device_release_one_slave (NMDevice *dev, NMDevice *slave, gboolean failed)
{
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
	SlaveInfo *info;
	gboolean success = FALSE;

	g_return_val_if_fail (slave != NULL, FALSE);
	g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->release_slave != NULL, FALSE);

	info = find_slave_info (dev, slave);
	if (!info)
		return FALSE;

	if (info->enslaved) {
		success = NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave);
		g_warn_if_fail (success);
	}
	nm_device_slave_notify_enslaved (info->slave, FALSE, failed);

	priv->slaves = g_slist_remove (priv->slaves, info);
	free_slave_info (info);
1003 1004 1005 1006

	/* Ensure the device's hardware address is up-to-date; it often changes
	 * when slaves change.
	 */
1007
	nm_device_update_hw_address (dev);
1008

1009 1010 1011
	return success;
}

1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
static gboolean
connection_is_static (NMConnection *connection)
{
	NMSettingIP4Config *ip4;
	NMSettingIP6Config *ip6;
	const char *method;

	ip4 = nm_connection_get_setting_ip4_config (connection);
	if (ip4) {
		method = nm_setting_ip4_config_get_method (ip4);
		if (   g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) != 0
		    && g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) != 0)
		return FALSE;
	}

	ip6 = nm_connection_get_setting_ip6_config (connection);
	if (ip6) {
		method = nm_setting_ip6_config_get_method (ip6);
		if (   g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) != 0
		    && g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) != 0)
			return FALSE;
	}

	return TRUE;
}

static gboolean
has_static_connection (NMDevice *self)
{
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
	const GSList *connections, *iter;

	connections = nm_connection_provider_get_connections (priv->con_provider);
	for (iter = connections; iter; iter = iter->next) {
		NMConnection *connection = iter->data;

		if (   nm_device_check_connection_compatible (self, connection, NULL)
		    && connection_is_static (connection))
			return TRUE;
	}
	return FALSE;
}

1055 1056 1057 1058 1059
static void
carrier_changed (NMDevice *device, gboolean carrier)
{
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);

1060
	if (!nm_device_get_managed (device))
1061 1062
		return;

1063 1064 1065 1066 1067 1068
	if (priv->ignore_carrier) {
		/* Ignore all carrier-off, and ignore carrier-on on connected devices */
		if (!carrier || priv->state > NM_DEVICE_STATE_DISCONNECTED)
			return;
	}

1069
	if (nm_device_is_master (device)) {
Jiri Pirko's avatar
Jiri Pirko committed
1070 1071 1072
		/* Bridge/bond/team carrier does not affect its own activation,
		 * but when carrier comes on, if there are slaves waiting,
		 * it will restart them.
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083
		 */
		if (!carrier)
			return;

		if (nm_device_activate_ip4_state_in_wait (device))
			nm_device_activate_stage3_ip4_start (device);
		if (nm_device_activate_ip6_state_in_wait (device))
			nm_device_activate_stage3_ip6_start (device);

		return;
	} else if (nm_device_get_enslaved (device) && !carrier) {
Jiri Pirko's avatar
Jiri Pirko committed
1084 1085 1086
		/* Slaves don't deactivate when they lose carrier; for
		 * bonds/teams in particular that would be actively
		 * counterproductive.
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
		 */
		return;
	}

	if (carrier) {
		g_warn_if_fail (priv->state >= NM_DEVICE_STATE_UNAVAILABLE);

		if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) {
			nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED,
			                       NM_DEVICE_STATE_REASON_CARRIER);
		}
	} else {
		g_return_if_fail (priv->state >= NM_DEVICE_STATE_UNAVAILABLE);

		if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) {
			if (nm_device_queued_state_peek (device) >= NM_DEVICE_STATE_DISCONNECTED)
				nm_device_queued_state_clear (device);
		} else {
			nm_device_queue_state (device, NM_DEVICE_STATE_UNAVAILABLE,
			                       NM_DEVICE_STATE_REASON_CARRIER);
		}
	}
}

gboolean
nm_device_has_carrier (NMDevice *device)
{
	return NM_DEVICE_GET_PRIVATE (device)->carrier;
}

/* Returns %TRUE if @device is unavailable for connections because it
 * needs carrier but does not have it.
 */
static gboolean
nm_device_is_unavailable_because_of_carrier (NMDevice *device)
{
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);

	return !priv->carrier && !priv->ignore_carrier;
}

#define LINK_DISCONNECT_DELAY 4

static gboolean
link_disconnect_action_cb (gpointer user_data)
{
	NMDevice *device = NM_DEVICE (user_data);
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);

	priv->carrier_defer_id = 0;

	nm_log_info (LOGD_DEVICE, "(%s): link disconnected (calling deferred action)",
	             nm_device_get_iface (device));

	NM_DEVICE_GET_CLASS (device)->carrier_changed (device, FALSE);

	return FALSE;
}

void
nm_device_set_carrier (NMDevice *device, gboolean carrier)
{
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device);
	NMDeviceState state = nm_device_get_state (device);
	const char *iface = nm_device_get_iface (device);

	if (priv->carrier == carrier)
		return;

	priv->carrier = carrier;
	g_object_notify (G_OBJECT (device), NM_DEVICE_CARRIER);

	if (priv->carrier) {
		nm_log_info (LOGD_DEVICE, "(%s): link connected", iface);
		if (priv->carrier_defer_id) {
			g_source_remove (priv->carrier_defer_id);
			priv->carrier_defer_id = 0;
		}
		klass->carrier_changed (device, TRUE);
	} else if (state <= NM_DEVICE_STATE_DISCONNECTED) {
		nm_log_info (LOGD_DEVICE, "(%s): link disconnected", iface);
		klass->carrier_changed (device, FALSE);
	} else {
		nm_log_info (LOGD_DEVICE, "(%s): link disconnected (deferring action for %d seconds)",
		             iface, LINK_DISCONNECT_DELAY);
		priv->carrier_defer_id = g_timeout_add_seconds (LINK_DISCONNECT_DELAY,
		                                                link_disconnect_action_cb, device);
	}
}

static void
1179
link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformReason reason, NMDevice *device)
1180
{
1181
	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device);
1182
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
1183

1184
	/* Ignore other devices. */
1185 1186 1187
	if (ifindex != nm_device_get_ifindex (device))
		return;

1188 1189 1190 1191 1192 1193
	/* We don't filter by 'reason' because we are interested in *all* link
	 * changes. For example a call to nm_platform_link_set_up() may result
	 * in an internal carrier change (i.e. we ask the kernel to set IFF_UP
	 * and it results in also setting IFF_LOWER_UP.
	 */

1194 1195 1196 1197 1198 1199 1200
	if (info->udi && g_strcmp0 (info->udi, priv->udi)) {
		/* Update UDI to what udev gives us */
		g_free (priv->udi);
		priv->udi = g_strdup (info->udi);
		g_object_notify (G_OBJECT (device), NM_DEVICE_UDI);
	}

1201 1202 1203 1204 1205 1206 1207
	if (klass->link_changed)
		klass->link_changed (device, info);
}

static void
link_changed (NMDevice *device, NMPlatformLink *info)
{
1208
	/* Update carrier from link event if applicable. */
1209 1210
	if (   device_has_capability (device, NM_DEVICE_CAP_CARRIER_DETECT)
	    && !device_has_capability (device, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
1211
		nm_device_set_carrier (device, info->connected);
1212 1213 1214 1215 1216
}

static void
check_carrier (NMDevice *device)
{
1217
	int ifindex = nm_device_get_ip_ifindex (device);
1218

1219 1220
	if (!device_has_capability (device, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
		nm_device_set_carrier (device, nm_platform_link_is_connected (ifindex));
1221 1222
}

1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259
static void
slave_state_changed (NMDevice *slave,
                     NMDeviceState slave_new_state,
                     NMDeviceState slave_old_state,
                     NMDeviceStateReason reason,
                     NMDevice *self)
{
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
	gboolean release = FALSE;

	nm_log_dbg (LOGD_DEVICE, "(%s): slave %s state change %d (%s) -> %d (%s)",
	            nm_device_get_iface (self),
	            nm_device_get_iface (slave),
	            slave_old_state,
	            state_to_string (slave_old_state),
	            slave_new_state,
	            state_to_string (slave_new_state));

	g_assert (priv->state > NM_DEVICE_STATE_DISCONNECTED);
	g_assert (priv->state <= NM_DEVICE_STATE_ACTIVATED);

	/* Don't try to enslave slaves until the master is ready */
	if (priv->state < NM_DEVICE_STATE_CONFIG)
		return;

	if (slave_new_state == NM_DEVICE_STATE_IP_CONFIG)
		nm_device_enslave_slave (self, slave, nm_device_get_connection (slave));
	else if (slave_new_state > NM_DEVICE_STATE_ACTIVATED)
		release = TRUE;
	else if (   slave_new_state <= NM_DEVICE_STATE_DISCONNECTED
	         && slave_old_state > NM_DEVICE_STATE_DISCONNECTED) {
		/* Catch failures due to unavailable or unmanaged */
		release = TRUE;
	}

	if (release) {
		nm_device_release_one_slave (self, slave, FALSE);
Jiri Pirko's avatar
Jiri Pirko committed
1260
		/* Bridge/bond/team interfaces are left up until manually deactivated */
1261 1262 1263
		if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED) {
			nm_log_dbg (LOGD_DEVICE, "(%s): last slave removed; remaining activated",
			            nm_device_get_iface (self));
1264 1265 1266 1267 1268 1269 1270 1271 1272
		}
	}
}

/**
 * nm_device_master_add_slave:
 * @dev: the master device
 * @slave: the slave device to enslave
 *
Jiri Pirko's avatar
Jiri Pirko committed
1273 1274
 * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team,
 * etc) then this function adds @slave to the slave list for later enslavement.
1275 1276 1277
 *
 * Returns: %TRUE on success, %FALSE on failure
 */
1278
gboolean
1279
nm_device_master_add_slave (NMDevice *dev, NMDevice *slave)
1280
{
1281 1282 1283
	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
	SlaveInfo *info;

1284 1285
	g_return_val_if_fail (dev != NULL, FALSE);
	g_return_val_if_fail (slave != NULL, FALSE);
1286 1287 1288 1289 1290 1291 1292 1293 1294