static void
get_property (GObject *object,
              guint prop_id,
              GValue *value,
              GParamSpec *pspec)
{
	NMDeviceEthernet *device = NM_DEVICE_ETHERNET (object);
	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);

	switch (prop_id) {
	case PROP_HW_ADDRESS:
		g_value_set_string (value, nm_device_ethernet_get_hw_address (device));
		break;
	case PROP_PERM_HW_ADDRESS:
		g_value_set_string (value, nm_device_ethernet_get_permanent_hw_address (device));
		break;
	case PROP_SPEED:
		g_value_set_uint (value, nm_device_ethernet_get_speed (device));
		break;
	case PROP_CARRIER:
		g_value_set_boolean (value, nm_device_ethernet_get_carrier (device));
		break;
	case PROP_S390_SUBCHANNELS:
		g_value_set_boxed (value, priv->s390_subchannels);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}
static void
get_property (GObject *object,
              guint prop_id,
              GValue *value,
              GParamSpec *pspec)
{
	NMDeviceEthernet *device = NM_DEVICE_ETHERNET (object);

	_nm_object_ensure_inited (NM_OBJECT (object));

	switch (prop_id) {
	case PROP_HW_ADDRESS:
		g_value_set_string (value, nm_device_ethernet_get_hw_address (device));
		break;
	case PROP_PERM_HW_ADDRESS:
		g_value_set_string (value, nm_device_ethernet_get_permanent_hw_address (device));
		break;
	case PROP_SPEED:
		g_value_set_uint (value, nm_device_ethernet_get_speed (device));
		break;
	case PROP_CARRIER:
		g_value_set_boolean (value, nm_device_ethernet_get_carrier (device));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}
static gboolean
connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
{
	NMSettingConnection *s_con;
	NMSettingWired *s_wired;
	const char *ctype;
	gboolean is_pppoe = FALSE;

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

	ctype = nm_setting_connection_get_connection_type (s_con);
	if (!strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME))
		is_pppoe = TRUE;
	else if (strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME) != 0) {
		g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_NOT_ETHERNET_CONNECTION,
		             "The connection was not a wired or PPPoE connection.");
		return FALSE;
	}

	s_wired = nm_connection_get_setting_wired (connection);
	/* Wired setting optional for PPPoE */
	if (!is_pppoe && !s_wired) {
		g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_INVALID_ETHERNET_CONNECTION,
		             "The connection was not a valid ethernet connection.");
		return FALSE;
	}

	if (s_wired) {
		const GByteArray *mac;
		const char *perm_str;
		struct ether_addr *perm_mac;

		/* FIXME: filter using s390 subchannels when they are exported over the bus */

		/* Check MAC address */
		perm_str = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device));
		if (perm_str) {
			perm_mac = ether_aton (perm_str);
			if (!perm_mac) {
				g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_INVALID_DEVICE_MAC,
				             "Invalid device MAC address.");
				return FALSE;
			}
			mac = nm_setting_wired_get_mac_address (s_wired);
			if (mac && perm_mac && memcmp (mac->data, perm_mac->ether_addr_octet, ETH_ALEN)) {
				g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_MAC_MISMATCH,
				             "The MACs of the device and the connection didn't match.");
				return FALSE;
			}
		}
	}

	return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->connection_compatible (device, connection, error);
}
static gboolean
connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
{
	NMSettingWired *s_wired;
	gboolean is_pppoe = FALSE;

	if (!NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->connection_compatible (device, connection, error))
		return FALSE;

	if (nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME))
		is_pppoe = TRUE;
	else if (!nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME)) {
		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
		                     _("The connection was not an Ethernet or PPPoE connection."));
		return FALSE;
	}

	s_wired = nm_connection_get_setting_wired (connection);
	/* Wired setting optional for PPPoE */
	if (s_wired) {
		const char *perm_addr, *setting_addr;

		/* FIXME: filter using s390 subchannels when they are exported over the bus */

		/* Check MAC address */
		perm_addr = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device));
		if (perm_addr) {
			if (!nm_utils_hwaddr_valid (perm_addr, ETH_ALEN)) {
				g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
				                     _("Invalid device MAC address."));
				return FALSE;
			}
			setting_addr = nm_setting_wired_get_mac_address (s_wired);
			if (setting_addr && !nm_utils_hwaddr_matches (setting_addr, -1, perm_addr, -1)) {
				g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
				                     _("The MACs of the device and the connection didn't match."));
				return FALSE;
			}
		}
	}

	return TRUE;
}
static void
populate_ui (CEPageVlan *self)
{
    CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
    GSList *devices, *d_iter;
    NMConnection *parent_connection = NULL;
    NMDevice *device, *parent_device = NULL;
    const char *parent, *iface, *current_parent;
    int i, mtu_def, mtu_val;

    devices = get_vlan_devices (self);

    /* Parent */
    build_vlan_parent_list (self, devices);

    parent = nm_setting_vlan_get_parent (priv->setting);
    if (parent) {
        /* UUID? */
        parent_connection = (NMConnection *)nm_remote_settings_get_connection_by_uuid (CE_PAGE (self)->settings, parent);
        if (!parent_connection) {
            /* Interface name? */
            for (d_iter = devices; d_iter; d_iter = d_iter->next) {
                device = d_iter->data;

                if (!g_strcmp0 (parent, nm_device_get_iface (device))) {
                    parent_device = device;
                    break;
                }
            }
        }
    }

    /* If NMSettingVlan:parent didn't indicate a device, but we have a
     * wired setting, figure out the device from that.
     */
    if (priv->s_hw && !parent_device) {
        const GByteArray *mac;
        const char *device_mac_str;
        char *mac_str;

        if (NM_IS_SETTING_WIRED (priv->s_hw))
            mac = nm_setting_wired_get_mac_address (NM_SETTING_WIRED (priv->s_hw));
        else
            mac = NULL;

        if (mac) {
            mac_str = nm_utils_hwaddr_ntoa (mac->data, ARPHRD_ETHER);

            for (d_iter = devices; d_iter; d_iter = d_iter->next) {
                device = d_iter->data;

                if (NM_IS_DEVICE_ETHERNET (device))
                    device_mac_str = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device));
                else
                    device_mac_str = NULL;

                if (!g_strcmp0 (mac_str, device_mac_str)) {
                    parent_device = device;
                    break;
                }
            }
        }
    }

    current_parent = parent;
    if (parent_device || parent_connection) {
        for (i = 0; priv->parents[i]; i++) {
            if (parent_device && parent_device != priv->parents[i]->device)
                continue;
            if (parent_connection != priv->parents[i]->connection)
                continue;

            current_parent = priv->parents[i]->label;
            break;
        }
    }
    ce_page_setup_mac_combo (CE_PAGE (self), priv->parent, current_parent, priv->parent_labels);
    g_signal_connect (priv->parent, "changed", G_CALLBACK (parent_changed), self);

    if (current_parent)
        priv->last_parent = g_strndup (current_parent, strcspn (current_parent, " "));

    /* Name */
    iface = nm_setting_vlan_get_interface_name (priv->setting);
    if (iface)
        gtk_entry_set_text (priv->name_entry, iface);
    g_signal_connect (priv->name_entry, "changed", G_CALLBACK (name_changed), self);

    /* ID */
    priv->last_id = nm_setting_vlan_get_id (priv->setting);
    gtk_spin_button_set_value (priv->id_entry, priv->last_id);
    g_signal_connect (priv->id_entry, "value-changed", G_CALLBACK (id_changed), self);

    /* Cloned MAC address */
    if (NM_IS_SETTING_WIRED (priv->s_hw)) {
        ce_page_mac_to_entry (nm_setting_wired_get_cloned_mac_address (NM_SETTING_WIRED (priv->s_hw)),
                              ARPHRD_ETHER, priv->cloned_mac);
    }
    g_signal_connect (priv->cloned_mac, "changed", G_CALLBACK (stuff_changed), self);

    /* MTU */
    if (NM_IS_SETTING_WIRED (priv->s_hw)) {
        mtu_def = ce_get_property_default (priv->s_hw, NM_SETTING_WIRED_MTU);
        mtu_val = nm_setting_wired_get_mtu (NM_SETTING_WIRED (priv->s_hw));
    } else {
        mtu_def = mtu_val = 1500;
    }
    g_signal_connect (priv->mtu, "output",
                      G_CALLBACK (ce_spin_output_with_default),
                      GINT_TO_POINTER (mtu_def));

    gtk_spin_button_set_value (priv->mtu, (gdouble) mtu_val);
    g_signal_connect (priv->mtu, "value-changed", G_CALLBACK (stuff_changed), self);

    g_slist_free (devices);
}
static void
build_vlan_parent_list (CEPageVlan *self, GSList *devices)
{
    CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
    GSList *connections, *c_iter, *d_iter;
    GPtrArray *parents;
    VlanParent *parent;
    NMDevice *device;
    const char *iface, *mac, *id;
    int i;

    parents = g_ptr_array_new ();

    /* Devices with no L2 configuration can spawn VLANs directly. At the
     * moment, this means just Ethernet.
     */
    for (d_iter = devices; d_iter; d_iter = d_iter->next) {
        device = d_iter->data;

        if (!NM_IS_DEVICE_ETHERNET (device))
            continue;

        parent = g_slice_new (VlanParent);
        parent->device = device;
        parent->connection = NULL;

        iface = nm_device_get_iface (device);
        mac = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device));
        parent->label = g_strdup_printf ("%s (%s)", iface, mac);

        g_ptr_array_add (parents, parent);
    }

    /* Otherwise, VLANs have to be built on top of configured connections */
    connections = nm_remote_settings_list_connections (CE_PAGE (self)->settings);
    for (c_iter = connections; c_iter; c_iter = c_iter->next) {
        NMConnection *candidate = c_iter->data;
        NMSettingConnection *s_con = nm_connection_get_setting_connection (candidate);

        if (nm_setting_connection_get_master (s_con))
            continue;

        for (d_iter = devices; d_iter; d_iter = d_iter->next) {
            device = d_iter->data;

            if (nm_device_connection_valid (device, candidate)) {
                parent = g_slice_new (VlanParent);
                parent->device = device;
                parent->connection = candidate;

                iface = nm_device_get_iface (device);
                id = nm_setting_connection_get_id (s_con);

                parent->label = g_strdup_printf ("%s (%s)", iface, id);
                g_ptr_array_add (parents, parent);
                /* no break here; the connection may apply to multiple devices */
            }
        }
    }

    g_slist_free (connections);

    g_ptr_array_sort (parents, sort_parents);
    g_ptr_array_add (parents, NULL);

    priv->parent_labels = g_new (char *, parents->len);
    priv->parents = (VlanParent **)g_ptr_array_free (parents, FALSE);

    for (i = 0; priv->parents[i]; i++)
        priv->parent_labels[i] = priv->parents[i]->label;
    priv->parent_labels[i] = NULL;
}
static gboolean
connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
{
	NMSettingWired *s_wired;

	if (!NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->connection_compatible (device, connection, error))
		return FALSE;

	if (nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME)) {
		/* NOP */
	} else if (!nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME)) {
		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
		                     _("The connection was not an Ethernet or PPPoE connection."));
		return FALSE;
	}

	s_wired = nm_connection_get_setting_wired (connection);
	/* Wired setting optional for PPPoE */
	if (s_wired) {
		const char *perm_addr, *s_mac;
		gboolean try_mac = TRUE;
		const char * const *mac_blacklist;
		int i;

		/* Check s390 subchannels */
		if (!match_subchans (NM_DEVICE_ETHERNET (device), s_wired, &try_mac)) {
			g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
			                     _("The connection and device differ in S390 subchannels."));
			return FALSE;
		}

		/* Check MAC address */
		perm_addr = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device));
		s_mac = nm_setting_wired_get_mac_address (s_wired);
		if (perm_addr) {
			if (!nm_utils_hwaddr_valid (perm_addr, ETH_ALEN)) {
				g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
				                     _("Invalid device MAC address."));
				return FALSE;
			}
			if (try_mac && s_mac && !nm_utils_hwaddr_matches (s_mac, -1, perm_addr, -1)) {
				g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
				                     _("The MACs of the device and the connection do not match."));
				return FALSE;
			}

			/* Check for MAC address blacklist */
			mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
			for (i = 0; mac_blacklist[i]; i++) {
				if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
					g_warn_if_reached ();
					g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
				                     _("Invalid MAC in the blacklist: %s."), mac_blacklist[i]);
					return FALSE;
				}

				if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_addr, -1)) {
					g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
				                     _("Device MAC (%s) is blacklisted by the connection."), perm_addr);
					return FALSE;
				}
			}
		}
	}

	return TRUE;
}