static NMActStageReturn
act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason)
{
	NMDeviceAdsl *self = NM_DEVICE_ADSL (device);
	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
	NMSettingAdsl *s_adsl;
	const char *protocol;

	g_assert (out_reason);

	s_adsl = nm_connection_get_setting_adsl (nm_device_get_applied_connection (device));
	g_assert (s_adsl);

	protocol = nm_setting_adsl_get_protocol (s_adsl);
	_LOGD (LOGD_ADSL, "using ADSL protocol '%s'", protocol);

	if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE) == 0) {
		/* PPPoE needs RFC2684 bridging before we can do PPP over it */
		ret = br2684_create_iface (self, s_adsl, out_reason);
	} else if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA) == 0) {
		/* PPPoA doesn't need anything special */
		ret = NM_ACT_STAGE_RETURN_SUCCESS;
	} else
		_LOGW (LOGD_ADSL, "unhandled ADSL protocol '%s'", protocol);

	return ret;
}
static void
teamd_dbus_vanished (GDBusConnection *dbus_connection,
                     const gchar *name,
                     gpointer user_data)
{
	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
	NMDevice *device = NM_DEVICE (self);
	NMDeviceState state = nm_device_get_state (device);

	g_return_if_fail (priv->teamd_dbus_watch);

	if (!priv->tdc) {
		/* g_bus_watch_name will always raise an initial signal, to indicate whether the
		 * name exists/not exists initially. Do not take this as a failure if it hadn't
		 * previously appeared.
		 */
		_LOGD (LOGD_TEAM, "teamd not on D-Bus (ignored)");
		return;
	}

	_LOGI (LOGD_TEAM, "teamd vanished from D-Bus");
	teamd_cleanup (device, TRUE);

	/* Attempt to respawn teamd */
	if (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_ACTIVATED) {
		NMConnection *connection = nm_device_get_applied_connection (device);

		g_assert (connection);
		if (!teamd_start (device, nm_connection_get_setting_team (connection)))
			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
	}
}
static gboolean
pppoe_vcc_config (NMDeviceAdsl *self)
{
	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
	NMDevice *device = NM_DEVICE (self);
	NMSettingAdsl *s_adsl;

	s_adsl = nm_connection_get_setting_adsl (nm_device_get_applied_connection (device));
	g_assert (s_adsl);

	/* Set up the VCC */
	if (!br2684_assign_vcc (self, s_adsl))
		return FALSE;

	/* Watch for the 'nas' interface going away */
	g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED,
	                  G_CALLBACK (link_changed_cb),
	                  self);

	_LOGD (LOGD_ADSL, "ATM setup successful");

	/* otherwise we're good for stage3 */
	nm_platform_link_set_up (NM_PLATFORM_GET, priv->nas_ifindex, NULL);

	return TRUE;
}
static NMActStageReturn
act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
{
	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
	NMConnection *connection;
	NMSettingOlpcMesh *s_mesh;
	guint32 channel;
	GBytes *ssid;
	const char *anycast_addr;

	connection = nm_device_get_applied_connection (device);
	g_assert (connection);

	s_mesh = nm_connection_get_setting_olpc_mesh (connection);
	g_assert (s_mesh);

	channel = nm_setting_olpc_mesh_get_channel (s_mesh);
	if (channel != 0)
		_mesh_set_channel (self, channel);

	ssid = nm_setting_olpc_mesh_get_ssid (s_mesh);
	nm_platform_mesh_set_ssid (NM_PLATFORM_GET,
	                           nm_device_get_ifindex (device),
	                           g_bytes_get_data (ssid, NULL),
	                           g_bytes_get_size (ssid));

	anycast_addr = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh);
	nm_device_set_dhcp_anycast_address (device, anycast_addr);

	return NM_ACT_STAGE_RETURN_SUCCESS;
}
static NMActStageReturn
act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason)
{
	NMDeviceAdsl *self = NM_DEVICE_ADSL (device);
	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
	NMSettingAdsl *s_adsl;
	const char *protocol;

	g_assert (out_reason);

	s_adsl = nm_connection_get_setting_adsl (nm_device_get_applied_connection (device));
	g_assert (s_adsl);

	protocol = nm_setting_adsl_get_protocol (s_adsl);
	_LOGD (LOGD_ADSL, "using ADSL protocol '%s'", protocol);

	if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE) == 0) {

		/* PPPoE needs RFC2684 bridging before we can do PPP over it */
		if (!br2684_create_iface (self, s_adsl)) {
			*out_reason = NM_DEVICE_STATE_REASON_BR2684_FAILED;
			goto done;
		}

		/* Set up the VCC */
		if (!br2684_assign_vcc (self, s_adsl)) {
			*out_reason = NM_DEVICE_STATE_REASON_BR2684_FAILED;
			goto done;
		}

		/* Watch for the 'nas' interface going away */
		g_signal_connect (nm_platform_get (), NM_PLATFORM_SIGNAL_LINK_CHANGED,
		                  G_CALLBACK (link_changed_cb),
		                  self);

		_LOGD (LOGD_ADSL, "ATM setup successful");

		/* otherwise we're good for stage3 */
		nm_platform_link_set_up (NM_PLATFORM_GET, priv->nas_ifindex, NULL);
		ret = NM_ACT_STAGE_RETURN_SUCCESS;

	} else if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA) == 0) {
		/* PPPoA doesn't need anything special */
		ret = NM_ACT_STAGE_RETURN_SUCCESS;
	} else
		_LOGW (LOGD_ADSL, "unhandled ADSL protocol '%s'", protocol);

done:
	return ret;
}
static NMActStageReturn
act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
{
	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
	gs_free_error GError *error = NULL;
	NMConnection *connection;
	NMSettingTeam *s_team;
	const char *cfg;

	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);

	ret = NM_DEVICE_CLASS (nm_device_team_parent_class)->act_stage1_prepare (device, reason);
	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
		return ret;

	connection = nm_device_get_applied_connection (device);
	g_assert (connection);
	s_team = nm_connection_get_setting_team (connection);
	g_assert (s_team);

	if (priv->tdc) {
		/* If the existing teamd config is the same as we're about to use,
		 * then we can proceed.  If it's not the same, and we have a PID,
		 * kill it so we can respawn it with the right config.  If we don't
		 * have a PID, then we must fail.
		 */
		cfg = teamdctl_config_get_raw (priv->tdc);
		if (cfg && strcmp (cfg,  nm_setting_team_get_config (s_team)) == 0) {
			_LOGD (LOGD_TEAM, "using existing matching teamd config");
			return NM_ACT_STAGE_RETURN_SUCCESS;
		}

		if (!priv->teamd_pid) {
			_LOGD (LOGD_TEAM, "existing teamd config mismatch; killing existing via teamdctl");
			if (!teamd_kill (self, NULL, &error)) {
				_LOGW (LOGD_TEAM, "existing teamd config mismatch; failed to kill existing teamd: %s", error->message);
				*reason = NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED;
				return NM_ACT_STAGE_RETURN_FAILURE;
			}
		}

		_LOGD (LOGD_TEAM, "existing teamd config mismatch; respawning...");
		teamd_cleanup (device, TRUE);
	}

	return teamd_start (device, s_team) ?
		NM_ACT_STAGE_RETURN_POSTPONE : NM_ACT_STAGE_RETURN_FAILURE;
}
static NMActStageReturn
act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
{
	NMActStageReturn ret;

	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);

	ret = NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->act_stage1_prepare (dev, reason);
	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
		return ret;

	if (!nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE))
		return NM_ACT_STAGE_RETURN_FAILURE;
	return NM_ACT_STAGE_RETURN_SUCCESS;
}
static NMActStageReturn
act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
{
	NMActStageReturn ret;
	NMConnection *connection = nm_device_get_applied_connection (device);

	g_assert (connection);

	ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage1_prepare (device, reason);
	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
		return ret;

	commit_master_options (device, nm_connection_get_setting_bridge (connection));

	return NM_ACT_STAGE_RETURN_SUCCESS;
}
static void
ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
{
	NMConnection *connection;
	NMSettingIPTunnel *s_ip_tunnel;
	guint32 mtu;

	connection = nm_device_get_applied_connection (device);
	g_assert (connection);
	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
	g_assert (s_ip_tunnel);

	/* MTU override */
	mtu = nm_setting_ip_tunnel_get_mtu (s_ip_tunnel);
	if (mtu)
		nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
}
static void
ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
{
	NMConnection *connection;
	NMSettingWired *s_wired;
	guint32 mtu;

	connection = nm_device_get_applied_connection (device);
	g_assert (connection);

	s_wired = nm_connection_get_setting_wired (connection);
	if (s_wired) {
		mtu = nm_setting_wired_get_mtu (s_wired);
		if (mtu)
			nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
	}
}
static NMActStageReturn
apply_bonding_config (NMDevice *device)
{
	NMConnection *connection;
	NMSettingBond *s_bond;
	int ifindex = nm_device_get_ifindex (device);
	const char *mode, *value;
	char *contents;
	gboolean set_arp_interval = TRUE;

	/* Option restrictions:
	 *
	 * arp_interval conflicts miimon > 0
	 * arp_interval conflicts [ alb, tlb ]
	 * arp_validate needs [ active-backup ]
	 * downdelay needs miimon
	 * updelay needs miimon
	 * primary needs [ active-backup, tlb, alb ]
	 *
	 * clearing miimon requires that arp_interval be 0, but clearing
	 *     arp_interval doesn't require miimon to be 0
	 */

	connection = nm_device_get_applied_connection (device);
	g_assert (connection);
	s_bond = nm_connection_get_setting_bond (connection);
	g_assert (s_bond);

	mode = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE);
	if (mode == NULL)
		mode = "balance-rr";

	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MIIMON);
	if (value && atoi (value)) {
		/* clear arp interval */
		set_bond_attr (device, "arp_interval", "0");
		set_arp_interval = FALSE;

		set_bond_attr (device, "miimon", value);
		set_simple_option (device, "updelay", s_bond, NM_SETTING_BOND_OPTION_UPDELAY);
		set_simple_option (device, "downdelay", s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY);
	} else if (!value) {
		/* If not given, and arp_interval is not given, default to 100 */
		long int val_int;
		char *end;

		value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
		errno = 0;
		val_int = strtol (value ? value : "0", &end, 10);
		if (!value || (val_int == 0 && errno == 0 && *end == '\0'))
			set_bond_attr (device, "miimon", "100");
	}

	/* The stuff after 'mode' requires the given mode or doesn't care */
	set_bond_attr (device, "mode", mode);

	/* arp_interval not compatible with ALB, TLB */
	if (g_strcmp0 (mode, "balance-alb") == 0 || g_strcmp0 (mode, "balance-tlb") == 0)
		set_arp_interval = FALSE;

	if (set_arp_interval) {
		set_simple_option (device, "arp_interval", s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL);

		/* Just let miimon get cleared automatically; even setting miimon to
		 * 0 (disabled) clears arp_interval.
		 */
	}

	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_VALIDATE);
	/* arp_validate > 0 only valid in active-backup mode */
	if (   value
	    && g_strcmp0 (value, "0") != 0
	    && g_strcmp0 (value, "none") != 0
	    && g_strcmp0 (mode, "active-backup") == 0)
		set_bond_attr (device, "arp_validate", value);
	else
		set_bond_attr (device, "arp_validate", "0");

	if (   g_strcmp0 (mode, "active-backup") == 0
	    || g_strcmp0 (mode, "balance-alb") == 0
	    || g_strcmp0 (mode, "balance-tlb") == 0) {
		value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
		set_bond_attr (device, "primary", value ? value : "");
	}

	/* Clear ARP targets */
	contents = nm_platform_sysctl_master_get_option (nm_device_get_platform (device), ifindex, "arp_ip_target");
	set_arp_targets (device, contents, " \n", "-");
	g_free (contents);

	/* Add new ARP targets */
	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
	set_arp_targets (device, value, ",", "+");

	set_simple_option (device, "primary_reselect", s_bond, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT);
	set_simple_option (device, "fail_over_mac", s_bond, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC);
	set_simple_option (device, "use_carrier", s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER);
	set_simple_option (device, "ad_select", s_bond, NM_SETTING_BOND_OPTION_AD_SELECT);
	set_simple_option (device, "xmit_hash_policy", s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY);
	set_simple_option (device, "resend_igmp", s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP);

	if (   g_strcmp0 (mode, "4") == 0
	    || g_strcmp0 (mode, "802.3ad") == 0)
		set_simple_option (device, "lacp_rate", s_bond, NM_SETTING_BOND_OPTION_LACP_RATE);

	return NM_ACT_STAGE_RETURN_SUCCESS;
}