static gboolean
hwaddr_matches (NMDevice *device,
                NMConnection *connection,
                const guint8 *other_hwaddr,
                guint other_hwaddr_len,
                gboolean fail_if_no_hwaddr)
{
	NMSettingInfiniband *s_ib;
	const guint8 *devaddr;
	const GByteArray *mac = NULL;
	int devtype;

	devtype = nm_device_wired_get_hwaddr_type (NM_DEVICE_WIRED (device));
	devaddr = nm_device_wired_get_hwaddr (NM_DEVICE_WIRED (device));
	g_return_val_if_fail (devaddr != NULL, FALSE);

	s_ib = nm_connection_get_setting_infiniband (connection);
	if (s_ib)
		mac = nm_setting_infiniband_get_mac_address (s_ib);

	if (mac) {
		g_return_val_if_fail (mac->len == INFINIBAND_ALEN, FALSE);
		if (other_hwaddr) {
			g_return_val_if_fail (other_hwaddr_len == INFINIBAND_ALEN, FALSE);
			if (memcmp (mac->data, other_hwaddr, mac->len) == 0)
				return TRUE;
		} else if (memcmp (mac->data, devaddr, mac->len) == 0)
			return TRUE;
	} else if (fail_if_no_hwaddr == FALSE)
		return TRUE;

	return FALSE;
}
static void
real_update_hw_address (NMDevice *dev)
{
	const guint8 *hw_addr;
	guint8 old_addr[INFINIBAND_ALEN];

	hw_addr = nm_device_wired_get_hwaddr (NM_DEVICE_WIRED (dev));
	memcpy (old_addr, hw_addr, INFINIBAND_ALEN);

	NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->update_hw_address (dev);

	hw_addr = nm_device_wired_get_hwaddr (NM_DEVICE_WIRED (dev));
	if (memcmp (old_addr, hw_addr, INFINIBAND_ALEN))
		g_object_notify (G_OBJECT (dev), NM_DEVICE_INFINIBAND_HW_ADDRESS);
}
static gboolean
carrier_action_defer_cb (gpointer user_data)
{
	NMDeviceWired *self = NM_DEVICE_WIRED (user_data);
	NMDeviceWiredPrivate *priv = NM_DEVICE_WIRED_GET_PRIVATE (self);
	NMDeviceState state;

	priv->carrier_action_defer_id = 0;

	state = nm_device_get_state (NM_DEVICE (self));
	if (state == NM_DEVICE_STATE_UNAVAILABLE) {
		if (priv->carrier)
			nm_device_queue_state (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER);
		else {
			/* clear any queued state changes if they wouldn't be valid when the
			 * carrier is off.
			 */
			if (nm_device_queued_state_peek (NM_DEVICE (self)) >= NM_DEVICE_STATE_DISCONNECTED)
				nm_device_queued_state_clear (NM_DEVICE (self));
		}
	} else if (state >= NM_DEVICE_STATE_DISCONNECTED) {
		if (!priv->carrier)
			nm_device_queue_state (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER);
	}

	return FALSE;
}
static void
get_property (GObject *object, guint prop_id,
              GValue *value, GParamSpec *pspec)
{
	NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (object);
	GPtrArray *slaves;
	GSList *list, *iter;
	char *hwaddr;

	switch (prop_id) {
	case PROP_HW_ADDRESS:
		hwaddr = nm_utils_hwaddr_ntoa (priv->hw_addr, nm_utils_hwaddr_type (priv->hw_addr_len));
		g_value_take_string (value, hwaddr);
		break;
	case PROP_CARRIER:
		g_value_set_boolean (value, nm_device_wired_get_carrier (NM_DEVICE_WIRED (object)));
		break;
	case PROP_SLAVES:
		slaves = g_ptr_array_new ();
		list = nm_device_master_get_slaves (NM_DEVICE (object));
		for (iter = list; iter; iter = iter->next)
			g_ptr_array_add (slaves, g_strdup (nm_device_get_path (NM_DEVICE (iter->data))));
		g_slist_free (list);
		g_value_take_boxed (value, slaves);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}
static gboolean
hw_bring_up (NMDevice *dev, gboolean *no_firmware)
{
	gboolean success, carrier;
	guint32 caps;

	success = nm_system_iface_set_up (nm_device_get_ip_ifindex (dev), TRUE, no_firmware);
	if (success) {
		caps = nm_device_get_capabilities (dev);
		if (caps & NM_DEVICE_CAP_CARRIER_DETECT) {
			carrier = get_carrier_sync (NM_DEVICE_WIRED (dev));
			set_carrier (NM_DEVICE_WIRED (dev), carrier, carrier ? FALSE : TRUE);
		}
	}
	return success;
}
static void
carrier_off (NMNetlinkMonitor *monitor,
             int idx,
             gpointer user_data)
{
	NMDevice *device = NM_DEVICE (user_data);
	NMDeviceWired *self = NM_DEVICE_WIRED (device);
	guint32 caps;

	/* Make sure signal is for us */
	if (idx == nm_device_get_ifindex (device)) {
		NMDeviceState state;
		gboolean defer = FALSE;

		caps = nm_device_get_capabilities (device);
		g_return_if_fail (caps & NM_DEVICE_CAP_CARRIER_DETECT);

		/* Defer carrier-off event actions while connected by a few seconds
		 * so that tripping over a cable, power-cycling a switch, or breaking
		 * off the RJ45 locking tab isn't so catastrophic.
		 */
		state = nm_device_get_state (device);
		if (state > NM_DEVICE_STATE_DISCONNECTED)
			defer = TRUE;

		set_carrier (self, FALSE, defer);
	}
}
static void
get_property (GObject *object, guint prop_id,
              GValue *value, GParamSpec *pspec)
{
	const guint8 *current_addr;

	switch (prop_id) {
	case PROP_HW_ADDRESS:
		current_addr = nm_device_wired_get_hwaddr (NM_DEVICE_WIRED (object));
		g_value_take_string (value, nm_utils_hwaddr_ntoa (current_addr, ARPHRD_INFINIBAND));
		break;
	case PROP_CARRIER:
		g_value_set_boolean (value, nm_device_wired_get_carrier (NM_DEVICE_WIRED (object)));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}
static gboolean
is_available (NMDevice *dev)
{
	NMDeviceWired *self = NM_DEVICE_WIRED (dev);

	/* Can't do anything if there isn't a carrier */
	if (!NM_DEVICE_WIRED_GET_PRIVATE (self)->carrier)
		return FALSE;

	return TRUE;
}
static gboolean
spec_match_list (NMDevice *device, const GSList *specs)
{
	char *hwaddr;
	gboolean matched;

	hwaddr = nm_utils_hwaddr_ntoa (nm_device_wired_get_hwaddr (NM_DEVICE_WIRED (device)), ARPHRD_INFINIBAND);
	matched = nm_match_spec_hwaddr (specs, hwaddr);
	g_free (hwaddr);

	return matched;
}
static gboolean
real_complete_connection (NMDevice *device,
                          NMConnection *connection,
                          const char *specific_object,
                          const GSList *existing_connections,
                          GError **error)
{
	NMSettingInfiniband *s_infiniband;
	const GByteArray *setting_mac;
	const guint8 *hwaddr;

	nm_utils_complete_generic (connection,
	                           NM_SETTING_INFINIBAND_SETTING_NAME,
	                           existing_connections,
	                           _("InfiniBand connection %d"),
	                           NULL,
	                           TRUE);

	s_infiniband = nm_connection_get_setting_infiniband (connection);
	if (!s_infiniband) {
		s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
		nm_connection_add_setting (connection, NM_SETTING (s_infiniband));
	}

	hwaddr = nm_device_wired_get_hwaddr (NM_DEVICE_WIRED (device));
	setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband);
	if (setting_mac) {
		/* Make sure the setting MAC (if any) matches the device's MAC */
		if (memcmp (setting_mac->data, hwaddr, INFINIBAND_ALEN)) {
			g_set_error_literal (error,
			                     NM_SETTING_INFINIBAND_ERROR,
			                     NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY,
			                     NM_SETTING_INFINIBAND_MAC_ADDRESS);
			return FALSE;
		}
	} else {
		GByteArray *mac;

		/* Lock the connection to this device by default */
		mac = g_byte_array_sized_new (INFINIBAND_ALEN);
		g_byte_array_append (mac, hwaddr, INFINIBAND_ALEN);
		g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
		g_byte_array_free (mac, TRUE);
	}

	if (!nm_setting_infiniband_get_transport_mode (s_infiniband))
		g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL);

	return TRUE;
}
static gboolean
can_interrupt_activation (NMDevice *dev)
{
	NMDeviceWired *self = NM_DEVICE_WIRED (dev);
	gboolean interrupt = FALSE;

	/* Devices that support carrier detect can interrupt activation
	 * if the link becomes inactive.
	 */
	if (nm_device_get_capabilities (dev) & NM_DEVICE_CAP_CARRIER_DETECT) {
		if (NM_DEVICE_WIRED_GET_PRIVATE (self)->carrier == FALSE)
			interrupt = TRUE;
	}
	return interrupt;
}
static gboolean
infiniband_match_config (NMDevice *self, NMConnection *connection)
{
	NMSettingInfiniband *s_infiniband;
	const GByteArray *s_mac;

	s_infiniband = nm_connection_get_setting_infiniband (connection);
	if (!s_infiniband)
		return FALSE;

	/* MAC address check */
	s_mac = nm_setting_infiniband_get_mac_address (s_infiniband);
	if (s_mac && memcmp (s_mac->data, nm_device_wired_get_hwaddr (NM_DEVICE_WIRED (self)), INFINIBAND_ALEN))
		return FALSE;

	return TRUE;
}
static void
carrier_on (NMNetlinkMonitor *monitor,
            int idx,
            gpointer user_data)
{
	NMDevice *device = NM_DEVICE (user_data);
	NMDeviceWired *self = NM_DEVICE_WIRED (device);
	guint32 caps;

	/* Make sure signal is for us */
	if (idx == nm_device_get_ifindex (device)) {
		caps = nm_device_get_capabilities (device);
		g_return_if_fail (caps & NM_DEVICE_CAP_CARRIER_DETECT);

		set_carrier (self, TRUE, FALSE);
		set_speed (self, ethtool_get_speed (self));
	}
}
static gboolean
real_check_connection_compatible (NMDevice *device,
                                  NMConnection *connection,
                                  GError **error)
{
	NMSettingInfiniband *s_infiniband;
	const GByteArray *mac;

	if (!nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) {
		g_set_error (error,
		             NM_INFINIBAND_ERROR,
					 NM_INFINIBAND_ERROR_CONNECTION_NOT_INFINIBAND,
		             "The connection was not an InfiniBand connection.");
		return FALSE;
	}

	s_infiniband = nm_connection_get_setting_infiniband (connection);
	if (!s_infiniband) {
		g_set_error (error,
		             NM_INFINIBAND_ERROR, NM_INFINIBAND_ERROR_CONNECTION_INVALID,
		             "The connection was not a valid infiniband connection.");
		return FALSE;
	}

	if (s_infiniband) {
		const guint8 *hwaddr = nm_device_wired_get_hwaddr (NM_DEVICE_WIRED (device));

		mac = nm_setting_infiniband_get_mac_address (s_infiniband);
		if (mac && memcmp (mac->data, hwaddr, INFINIBAND_ALEN)) {
			g_set_error (error,
			             NM_INFINIBAND_ERROR,
			             NM_INFINIBAND_ERROR_CONNECTION_INCOMPATIBLE,
			             "The connection's MAC address did not match this device.");
			return FALSE;
		}
	}

	return TRUE;
}
static void
update_hw_address (NMDevice *dev)
{
	NMDeviceWired *self = NM_DEVICE_WIRED (dev);
	NMDeviceWiredPrivate *priv = NM_DEVICE_WIRED_GET_PRIVATE (self);
	struct rtnl_link *rtnl;
	struct nl_addr *addr;

	rtnl = nm_netlink_index_to_rtnl_link (nm_device_get_ip_ifindex (dev));
	if (!rtnl) {
		nm_log_err (LOGD_HW | NM_DEVICE_WIRED_LOG_LEVEL (dev),
		            "(%s) failed to read hardware address (error %d)",
		            nm_device_get_iface (dev), errno);
		return;
	}

	addr = rtnl_link_get_addr (rtnl);
	if (!addr) {
		nm_log_err (LOGD_HW | NM_DEVICE_WIRED_LOG_LEVEL (dev),
		            "(%s) no hardware address?",
		            nm_device_get_iface (dev));
		rtnl_link_put (rtnl);
		return;
	}

	if (nl_addr_get_len (addr) != priv->hw_addr_len) {
		nm_log_err (LOGD_HW | NM_DEVICE_WIRED_LOG_LEVEL (dev),
		            "(%s) hardware address is wrong length (expected %d got %d)",
		            nm_device_get_iface (dev),
		            priv->hw_addr_len, nl_addr_get_len (addr));
	} else {
		memcpy (&priv->hw_addr, nl_addr_get_binary_addr (addr),
				priv->hw_addr_len);
	}

	rtnl_link_put (rtnl);
}
static NMConnection *
real_get_best_auto_connection (NMDevice *dev,
                               GSList *connections,
                               char **specific_object)
{
	GSList *iter;

	for (iter = connections; iter; iter = g_slist_next (iter)) {
		NMConnection *connection = NM_CONNECTION (iter->data);
		NMSettingConnection *s_con;
		NMSettingInfiniband *s_infiniband;

		s_con = nm_connection_get_setting_connection (connection);
		g_assert (s_con);

		if (!nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME))
			continue;
		if (!nm_setting_connection_get_autoconnect (s_con))
			continue;

		s_infiniband = nm_connection_get_setting_infiniband (connection);
		if (!s_infiniband)
			continue;

		if (s_infiniband) {
			const guint8 *hwaddr = nm_device_wired_get_hwaddr (NM_DEVICE_WIRED (dev));
			const GByteArray *mac;

			mac = nm_setting_infiniband_get_mac_address (s_infiniband);
			if (mac && memcmp (mac->data, hwaddr, INFINIBAND_ALEN))
				continue;
		}

		return connection;
	}
	return NULL;
}
static void
dispose (GObject *object)
{
	NMDeviceWired *self = NM_DEVICE_WIRED (object);
	NMDeviceWiredPrivate *priv = NM_DEVICE_WIRED_GET_PRIVATE (self);

	if (priv->link_connected_id) {
		g_signal_handler_disconnect (priv->monitor, priv->link_connected_id);
		priv->link_connected_id = 0;
	}
	if (priv->link_disconnected_id) {
		g_signal_handler_disconnect (priv->monitor, priv->link_disconnected_id);
		priv->link_disconnected_id = 0;
	}

	carrier_action_defer_clear (self);

	if (priv->monitor) {
		g_object_unref (priv->monitor);
		priv->monitor = NULL;
	}

	G_OBJECT_CLASS (nm_device_wired_parent_class)->dispose (object);
}
static GObject*
constructor (GType type,
			 guint n_construct_params,
			 GObjectConstructParam *construct_params)
{
	GObject *object;
	NMDeviceWiredPrivate *priv;
	NMDevice *self;
	guint32 caps;

	object = G_OBJECT_CLASS (nm_device_wired_parent_class)->constructor (type,
	                                                                     n_construct_params,
	                                                                     construct_params);
	if (!object)
		return NULL;

	self = NM_DEVICE (object);
	priv = NM_DEVICE_WIRED_GET_PRIVATE (self);

	nm_log_dbg (LOGD_HW | NM_DEVICE_WIRED_LOG_LEVEL (NM_DEVICE (self)),
	            "(%s): kernel ifindex %d",
	            nm_device_get_iface (NM_DEVICE (self)),
	            nm_device_get_ifindex (NM_DEVICE (self)));

	if (nm_device_get_device_type (self) == NM_DEVICE_TYPE_ETHERNET) {
		priv->hw_addr_type = ARPHRD_ETHER;
		priv->hw_addr_len = ETH_ALEN;
	} else if (nm_device_get_device_type (self) == NM_DEVICE_TYPE_INFINIBAND) {
		priv->hw_addr_type = ARPHRD_INFINIBAND;
		priv->hw_addr_len = INFINIBAND_ALEN;
	} else if (nm_device_get_device_type (self) == NM_DEVICE_TYPE_BOND) {
		/* We may not know the hardware address type until a slave is added */
		priv->hw_addr_type = ARPHRD_ETHER;
		priv->hw_addr_len = ETH_ALEN;
	} else
		g_assert_not_reached ();

	caps = nm_device_get_capabilities (self);
	if (caps & NM_DEVICE_CAP_CARRIER_DETECT) {
		/* Only listen to netlink for cards that support carrier detect */
		priv->monitor = nm_netlink_monitor_get ();

		priv->link_connected_id = g_signal_connect (priv->monitor, "carrier-on",
		                                            G_CALLBACK (carrier_on),
		                                            self);
		priv->link_disconnected_id = g_signal_connect (priv->monitor, "carrier-off",
		                                               G_CALLBACK (carrier_off),
		                                               self);

		priv->carrier = get_carrier_sync (NM_DEVICE_WIRED (self));

		nm_log_info (LOGD_HW | NM_DEVICE_WIRED_LOG_LEVEL (NM_DEVICE (self)),
		             "(%s): carrier is %s",
		             nm_device_get_iface (NM_DEVICE (self)),
		             priv->carrier ? "ON" : "OFF");

		/* Request link state again just in case an error occurred getting the
		 * initial link state.
		 */
		nm_netlink_monitor_request_status (priv->monitor);
	} else {
		nm_log_info (LOGD_HW | NM_DEVICE_WIRED_LOG_LEVEL (NM_DEVICE (self)),
		             "(%s): driver '%s' does not support carrier detection.",
		             nm_device_get_iface (self),
		             nm_device_get_driver (self));
		priv->carrier = TRUE;
	}

	return object;
}