static gboolean
complete_connection (NMDevice *device,
                     NMConnection *connection,
                     const char *specific_object,
                     const GSList *existing_connections,
                     GError **error)
{
	NMSettingVxlan *s_vxlan;

	nm_utils_complete_generic (NM_PLATFORM_GET,
	                           connection,
	                           NM_SETTING_VXLAN_SETTING_NAME,
	                           existing_connections,
	                           NULL,
	                           _("VXLAN connection"),
	                           NULL,
	                           TRUE);

	s_vxlan = nm_connection_get_setting_vxlan (connection);
	if (!s_vxlan) {
		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
		                     "A 'vxlan' setting is required.");
		return FALSE;
	}

	return TRUE;
}
static const char *
get_connection_parent (NMDeviceFactory *factory, NMConnection *connection)
{
	NMSettingVxlan *s_vxlan;

	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_VXLAN_SETTING_NAME), NULL);

	s_vxlan = nm_connection_get_setting_vxlan (connection);
	g_assert (s_vxlan);

	return nm_setting_vxlan_get_parent (s_vxlan);
}
static char *
get_connection_iface (NMDeviceFactory *factory,
                      NMConnection *connection,
                      const char *parent_iface)
{
	const char *ifname;
	NMSettingVxlan *s_vxlan;

	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_VXLAN_SETTING_NAME), NULL);

	s_vxlan = nm_connection_get_setting_vxlan (connection);
	g_assert (s_vxlan);

	if (nm_setting_vxlan_get_parent (s_vxlan) && !parent_iface)
		return NULL;

	ifname = nm_connection_get_interface_name (connection);
	return g_strdup (ifname);
}
static gboolean
connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
{
	NMSettingVxlan *s_vxlan;

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

	if (!nm_connection_is_type (connection, NM_SETTING_VXLAN_SETTING_NAME)) {
		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
		                     _("The connection was not a VXLAN connection."));
		return FALSE;
	}

	s_vxlan = nm_connection_get_setting_vxlan (connection);
	if (nm_setting_vxlan_get_id (s_vxlan) != nm_device_vxlan_get_id (NM_DEVICE_VXLAN (device))) {
		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
		                     _("The VXLAN identifiers of the device and the connection didn't match."));
		return FALSE;
	}

	return TRUE;
}
static void
update_connection (NMDevice *device, NMConnection *connection)
{
	NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE (device);
	NMSettingVxlan *s_vxlan = nm_connection_get_setting_vxlan (connection);
	NMDevice *parent = NULL;
	const char *setting_parent, *new_parent;

	if (!s_vxlan) {
		s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new ();
		nm_connection_add_setting (connection, (NMSetting *) s_vxlan);
	}

	if (priv->props.id != nm_setting_vxlan_get_id (s_vxlan))
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_ID, priv->props.id, NULL);

	if (priv->props.parent_ifindex != NM_PLATFORM_LINK_OTHER_NETNS)
		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->props.parent_ifindex);

	/* Update parent in the connection; default to parent's interface name */
	if (parent) {
		new_parent = nm_device_get_iface (parent);
		setting_parent = nm_setting_vxlan_get_parent (s_vxlan);
		if (setting_parent && nm_utils_is_uuid (setting_parent)) {
			NMConnection *parent_connection;

			/* Don't change a parent specified by UUID if it's still valid */
			parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (nm_device_get_settings (device),
			                                                                         setting_parent);
			if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection))
				new_parent = NULL;
		}
		if (new_parent)
			g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, new_parent, NULL);
	} else
		g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, NULL, NULL);

	if (!address_matches (nm_setting_vxlan_get_remote (s_vxlan), priv->props.group, &priv->props.group6)) {
		if (priv->props.group) {
			g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE,
			              nm_utils_inet4_ntop (priv->props.group, NULL),
			              NULL);
		} else {
			g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE,
			              nm_utils_inet6_ntop (&priv->props.group6, NULL),
			              NULL);
		}
	}

	if (!address_matches (nm_setting_vxlan_get_local (s_vxlan), priv->props.local, &priv->props.local6)) {
		if (priv->props.local) {
			g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL,
			              nm_utils_inet4_ntop (priv->props.local, NULL),
			              NULL);
		} else if (memcmp (&priv->props.local6, &in6addr_any, sizeof (in6addr_any))) {
			g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL,
			              nm_utils_inet6_ntop (&priv->props.local6, NULL),
			              NULL);
		}
	}

	if (priv->props.src_port_min != nm_setting_vxlan_get_source_port_min (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_SOURCE_PORT_MIN,
		              priv->props.src_port_min, NULL);
	}

	if (priv->props.src_port_max != nm_setting_vxlan_get_source_port_max (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_SOURCE_PORT_MAX,
		              priv->props.src_port_max, NULL);
	}

	if (priv->props.dst_port != nm_setting_vxlan_get_destination_port (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_DESTINATION_PORT,
		              priv->props.dst_port, NULL);
	}

	if (priv->props.tos != nm_setting_vxlan_get_tos (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_TOS,
		              priv->props.tos, NULL);
	}

	if (priv->props.ttl != nm_setting_vxlan_get_ttl (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_TTL,
		              priv->props.ttl, NULL);
	}

	if (priv->props.learning != nm_setting_vxlan_get_learning (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_LEARNING,
		              priv->props.learning, NULL);
	}

	if (priv->props.ageing != nm_setting_vxlan_get_ageing (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_AGEING,
		              priv->props.ageing, NULL);
	}

	if (priv->props.proxy != nm_setting_vxlan_get_proxy (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_PROXY,
		              priv->props.proxy, NULL);
	}

	if (priv->props.rsc != nm_setting_vxlan_get_rsc (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_RSC,
		              priv->props.rsc, NULL);
	}

	if (priv->props.l2miss != nm_setting_vxlan_get_l2_miss (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_L2_MISS,
		              priv->props.l2miss, NULL);
	}

	if (priv->props.l3miss != nm_setting_vxlan_get_l3_miss (s_vxlan)) {
		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_L3_MISS,
		              priv->props.l3miss, NULL);
	}
}
static gboolean
check_connection_compatible (NMDevice *device, NMConnection *connection)
{
	NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE (device);
	NMSettingVxlan *s_vxlan;
	const char *parent;

	if (!NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->check_connection_compatible (device, connection))
		return FALSE;

	s_vxlan = nm_connection_get_setting_vxlan (connection);
	if (!s_vxlan)
		return FALSE;

	if (nm_device_is_real (device)) {
		parent = nm_setting_vxlan_get_parent (s_vxlan);
		if (   parent
		    && !match_parent (NM_DEVICE_VXLAN (device), parent))
			return FALSE;

		if (priv->props.id != nm_setting_vxlan_get_id (s_vxlan))
			return FALSE;

		if (!address_matches (nm_setting_vxlan_get_local (s_vxlan), priv->props.local, &priv->props.local6))
			return FALSE;

		if (!address_matches (nm_setting_vxlan_get_remote (s_vxlan), priv->props.group, &priv->props.group6))
			return FALSE;

		if (priv->props.src_port_min != nm_setting_vxlan_get_source_port_min (s_vxlan))
			return FALSE;

		if (priv->props.src_port_max != nm_setting_vxlan_get_source_port_max (s_vxlan))
			return FALSE;

		if (priv->props.dst_port != nm_setting_vxlan_get_destination_port (s_vxlan))
			return FALSE;

		if (priv->props.tos != nm_setting_vxlan_get_tos (s_vxlan))
			return FALSE;

		if (priv->props.ttl != nm_setting_vxlan_get_ttl (s_vxlan))
			return FALSE;

		if (priv->props.learning != nm_setting_vxlan_get_learning (s_vxlan))
			return FALSE;

		if (priv->props.ageing != nm_setting_vxlan_get_ageing (s_vxlan))
			return FALSE;

		if (priv->props.proxy != nm_setting_vxlan_get_proxy (s_vxlan))
			return FALSE;

		if (priv->props.rsc != nm_setting_vxlan_get_rsc (s_vxlan))
			return FALSE;

		if (priv->props.l2miss != nm_setting_vxlan_get_l2_miss (s_vxlan))
			return FALSE;

		if (priv->props.l3miss != nm_setting_vxlan_get_l3_miss (s_vxlan))
			return FALSE;
	}

	return TRUE;
}
static gboolean
create_and_realize (NMDevice *device,
                    NMConnection *connection,
                    NMDevice *parent,
                    const NMPlatformLink **out_plink,
                    GError **error)
{
	const char *iface = nm_device_get_iface (device);
	NMPlatformError plerr;
	NMPlatformLnkVxlan props = { };
	NMSettingVxlan *s_vxlan;
	const char *str;
	int ret;

	s_vxlan = nm_connection_get_setting_vxlan (connection);
	g_assert (s_vxlan);

	if (parent)
		props.parent_ifindex = nm_device_get_ifindex (parent);

	props.id = nm_setting_vxlan_get_id (s_vxlan);

	str = nm_setting_vxlan_get_local (s_vxlan);
	if (str) {
		ret = inet_pton (AF_INET, str, &props.local);
		if (ret != 1)
			ret = inet_pton (AF_INET6, str, &props.local6);
		if (ret != 1)
			return FALSE;
	}

	str = nm_setting_vxlan_get_remote (s_vxlan);
	ret = inet_pton (AF_INET, str, &props.group);
	if (ret != 1)
		ret = inet_pton (AF_INET6, str, &props.group6);
	if (ret != 1)
		return FALSE;

	props.tos = nm_setting_vxlan_get_tos (s_vxlan);
	props.ttl = nm_setting_vxlan_get_ttl (s_vxlan);
	props.learning = nm_setting_vxlan_get_learning (s_vxlan);
	props.ageing = nm_setting_vxlan_get_ageing (s_vxlan);
	props.limit = nm_setting_vxlan_get_limit (s_vxlan);
	props.src_port_min = nm_setting_vxlan_get_source_port_min (s_vxlan);
	props.src_port_max = nm_setting_vxlan_get_source_port_max (s_vxlan);
	props.dst_port = nm_setting_vxlan_get_destination_port (s_vxlan);
	props.proxy = nm_setting_vxlan_get_proxy (s_vxlan);
	props.rsc = nm_setting_vxlan_get_rsc (s_vxlan);
	props.l2miss = nm_setting_vxlan_get_l2_miss (s_vxlan);
	props.l3miss = nm_setting_vxlan_get_l3_miss (s_vxlan);

	plerr = nm_platform_link_vxlan_add (NM_PLATFORM_GET, iface, &props, out_plink);
	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
		             "Failed to create VXLAN interface '%s' for '%s': %s",
		             iface,
		             nm_connection_get_id (connection),
		             nm_platform_error_to_string (plerr));
		return FALSE;
	}

	return TRUE;
}