OpenSolaris_b135/lib/cfgadm_plugins/fp/common/cfga_cvt.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include "cfga_fp.h"

/* Function prototypes */

static fpcfga_ret_t get_xport_devlink(const char *hba_phys,
    char **hba_logpp, int *l_errnop);
static char ctoi(char c);
static fpcfga_ret_t is_apid_configured(const char *xport_phys,
	const char *dyncomp, struct luninfo_list **lunlistpp, int *l_errnop);
static fpcfga_ret_t insert_lun_to_lunlist(struct luninfo_list **lunlistpp,
	const char *dyncomp, di_node_t devnode, int *l_errnop);
static fpcfga_ret_t update_lunlist(struct luninfo_list **lunlistpp, int lun,
	uint_t	state, char *pathp, int	*l_errnop);


/* Globals */

/* Various conversions routines */

void
cvt_lawwn_to_dyncomp(const la_wwn_t *pwwn, char **dyncomp, int *l_errnop)
{
	*dyncomp = calloc(1, WWN_SIZE*2 + 1);
	if (*dyncomp == NULL) {
		*l_errnop = errno;
	}

	(void) sprintf(*dyncomp, "%016llx",
	(wwnConversion((uchar_t *)pwwn->raw_wwn)));
}


int
cvt_dyncomp_to_lawwn(const char *dyncomp, la_wwn_t *port_wwn)
{
	int	i;
	char	c, c1;
	uchar_t	*wwnp;

	wwnp = port_wwn->raw_wwn;
	for (i = 0; i < WWN_SIZE; i++, wwnp++) {

		c = ctoi(*dyncomp++);
		c1 = ctoi(*dyncomp++);
		if (c == -1 || c1 == -1)
			return (-1);
		*wwnp = ((c << 4) + c1);
	}

	return (0);
}


static char
ctoi(char c)
{
	if ((c >= '0') && (c <= '9'))
		c -= '0';
	else if ((c >= 'A') && (c <= 'F'))
		c = c - 'A' + 10;
	else if ((c >= 'a') && (c <= 'f'))
		c = c - 'a' + 10;
	else
		c = -1;
	return (c);
}


/*
 * Generates the HBA logical ap_id from physical ap_id.
 */
fpcfga_ret_t
make_xport_logid(const char *xport_phys, char **xport_logpp, int *l_errnop)
{
	if (*xport_logpp != NULL) {
		return (FPCFGA_ERR);
	}

	/*
	 * A devlink for the XPORT should exist.  Without the /dev/cfg link
	 * driver name and instance number based based link needs to be
	 * constructed for the minor node type of DDI_NT_FC_ATTACHMENT_POINT.
	 * sunddi.h defines DDI_NT_FC_ATTACHMENT_POINT for
	 * ddi_ctl:attachment_point:fc
	 */
	if (get_xport_devlink(xport_phys, xport_logpp, l_errnop) == FPCFGA_OK) {
		assert(*xport_logpp != NULL);
		return (FPCFGA_OK);
	} else {
		return (FPCFGA_ERR);
	}
}

static fpcfga_ret_t
get_xport_devlink(const char *xport_phys, char **xport_logpp, int *l_errnop)
{
	int match_minor;
	size_t len;
	fpcfga_ret_t ret;

	match_minor = 1;
	ret = physpath_to_devlink(CFGA_DEV_DIR, (char *)xport_phys,
	    xport_logpp, l_errnop, match_minor);
	if (ret != FPCFGA_OK) {
		return (ret);
	}

	assert(*xport_logpp != NULL);

	/* Remove the "/dev/cfg/"  prefix */
	len = strlen(CFGA_DEV_DIR SLASH);

	(void) memmove(*xport_logpp, *xport_logpp + len,
	    strlen(*xport_logpp + len) + 1);

	return (FPCFGA_OK);
}


/*
 * Given a xport path and dynamic ap_id, returns the physical
 * path in pathpp.  If the dynamic ap is not configured pathpp set to NULL
 * and returns FPCFGA_APID_NOCONFIGURE.
 */
fpcfga_ret_t
dyn_apid_to_path(
	const char *xport_phys,
	const char *dyncomp,
	struct luninfo_list **lunlistpp,
	int *l_errnop)
{
	fpcfga_ret_t 	ret;

	/* A device MUST have a dynamic component */
	if (dyncomp == NULL) {
		return (FPCFGA_LIB_ERR);
	}

	ret = is_apid_configured(xport_phys, dyncomp, lunlistpp, l_errnop);

	assert(ret != FPCFGA_OK);

	return (ret);
}

/*
 * When both the transport and dynamic comp are given this function
 * checks to see if the dynamic ap is configured on the dev tree.
 * If it is configured the devfs path will be stored in pathpp.
 * When the dynamic comp is null this function check to see if the transport
 * node has any child.
 *
 * Retrun value: FPCFGA_OK if the apid is configured.
 *		 FPCFGA_APID_NOCONFIGURE if the apid is not configured.
 *		 FPCFGA_LIB_ERR for other errors.
 */
static fpcfga_ret_t
is_apid_configured(
	const char *xport_phys,
	const char *dyncomp,
	struct luninfo_list **lunlistpp,
	int *l_errnop)
{
	char 		*devfs_phys, *devfs_fp_path, *client_path, *cp,
			*pathp = NULL;
	char 		path_name[MAXPATHLEN];
	di_node_t	tree_root, root, fpnode, dev_node, client_node;
	di_path_t 	path = DI_PATH_NIL;
	di_prop_t	prop = DI_PROP_NIL;
	uchar_t		*port_wwn_data = NULL;
	char		*lun_guid = NULL;
	char		port_wwn[WWN_SIZE*2+1];
	int		count, *lunnump, devlen,
			found_fp = 0;
	uint_t		state;
	uint_t		statep;
	fpcfga_ret_t 	ret;

	if (*lunlistpp != NULL) {
		return (FPCFGA_LIB_ERR);
	}

	ret = FPCFGA_APID_NOCONFIGURE;

	if ((devfs_phys = strdup(xport_phys)) == NULL) {
		*l_errnop = errno;
		return (FPCFGA_LIB_ERR);
	}

	if (strncmp(devfs_phys, DEVICES_DIR SLASH, strlen(DEVICES_DIR) +
			strlen(SLASH)) == 0) {
		cp = devfs_phys + strlen(DEVICES_DIR);
		(void) memmove(devfs_phys, cp, strlen(cp) + 1);
	}

	if ((cp = strstr(devfs_phys, MINOR_SEP)) != NULL) {
		*cp = '\0';  /* Terminate string. */
	}

	if ((tree_root = di_init("/", DINFOCPYALL | DINFOPATH))
			== DI_NODE_NIL) {
		*l_errnop = errno;
		S_FREE(devfs_phys);
		return (FPCFGA_LIB_ERR);
	}

	fpnode = di_drv_first_node("fp", tree_root);

	while (fpnode) {
		devfs_fp_path = di_devfs_path(fpnode);
		if ((devfs_fp_path) && !(strncmp(devfs_fp_path,
				devfs_phys, strlen(devfs_phys)))) {
			found_fp = 1;
			di_devfs_path_free(devfs_fp_path);
			break;
		}
		di_devfs_path_free(devfs_fp_path);
		fpnode = di_drv_next_node(fpnode);
	}
	if (!(found_fp)) {
		ret = FPCFGA_LIB_ERR;
		goto out;
	} else {
		root = fpnode;
	}

	/*
	 * when there is no child and path info node the
	 * FPCFGA_APID_NOCONFIGURE is returned
	 * regardless of the dynamic comp.
	 */
	dev_node = di_child_node(root);
	path = di_path_next_client(root, path);
	if ((dev_node == DI_NODE_NIL) && (path == DI_PATH_NIL)) {
		*l_errnop = errno;
		ret = FPCFGA_APID_NOCONFIGURE;
		goto out;
	}

	/*
	 * when dyn comp is null the function just checks if there is any
	 * child under fp transport attachment point.
	 */
	if (dyncomp == NULL) {
		ret = FPCFGA_OK;
		goto out;
	}

	/*
	 * now checks the children node to find
	 * if dynamic ap is configured. if there are multiple luns
	 * store into lunlist.
	 */
	if (dev_node != DI_NODE_NIL) {
		do {
			while ((prop = di_prop_next(dev_node, prop)) !=
					DI_PROP_NIL) {
				/* is property name port-wwn */
				if ((!(strcmp(PORT_WWN_PROP,
					di_prop_name(prop)))) &&
					(di_prop_type(prop) ==
					DI_PROP_TYPE_BYTE)) {
					break;
				}
			}

			if (prop != DI_PROP_NIL) {
				count = di_prop_bytes(prop, &port_wwn_data);
				if (count != WWN_SIZE) {
					ret = FPCFGA_LIB_ERR;
					goto out;
				} else {
					(void) sprintf(port_wwn,
				"%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
					port_wwn_data[0], port_wwn_data[1],
					port_wwn_data[2], port_wwn_data[3],
					port_wwn_data[4], port_wwn_data[5],
					port_wwn_data[6], port_wwn_data[7]);
					if (!(strncmp(port_wwn, dyncomp,
							WWN_SIZE*2))) {
						ret = insert_lun_to_lunlist(
							lunlistpp, dyncomp,
							dev_node, l_errnop);
						if (ret != FPCFGA_OK) {
							goto out;
						}
					}
				}
			}
			dev_node = di_sibling_node(dev_node);
			prop = DI_PROP_NIL;
		} while (dev_node != DI_NODE_NIL);
	}

	/*
	 * now checks the path info node to find
	 * if dynamic ap is configured. if there are multiple luns
	 * store into lunlist.
	 */
	if (path != DI_PATH_NIL) {
		/*
		 * now parse the path info node.
		 */
		do {
			count = di_path_prop_lookup_bytes(path, PORT_WWN_PROP,
				&port_wwn_data);
			if (count != WWN_SIZE) {
				ret = FPCFGA_LIB_ERR;
				goto out;
			}

			(void) sprintf(port_wwn,
				"%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
				port_wwn_data[0], port_wwn_data[1],
				port_wwn_data[2], port_wwn_data[3],
				port_wwn_data[4], port_wwn_data[5],
				port_wwn_data[6], port_wwn_data[7]);

			/* if matches get the path of scsi_vhci child node. */
			if (!(strncmp(port_wwn, dyncomp, WWN_SIZE*2))) {
				client_node = di_path_client_node(path);
				if (client_node == DI_NODE_NIL) {
					ret = FPCFGA_LIB_ERR;
					*l_errnop = errno;
					goto out;
				}
				count = di_path_prop_lookup_ints(path,
						LUN_PROP, &lunnump);
				client_path = di_devfs_path(client_node);
				strcpy(path_name, client_path);
				di_devfs_path_free(client_path);
				state = di_state(client_node);
				statep = di_path_state(path);

				/*
				 * If the node is
				 * state then check the devfs_path to
				 * see if it has a complete path.
				 * For non scsi_vhci node the path
				 * doesn't contain @w(portwwn) part
				 * consistently.  For scsi_vhci
				 * this behavior may not be there.
				 * To be safe @g(guid) is attempted
				 * to be added here.
				 */
				if ((state & DI_DRIVER_DETACHED) &&
					(strstr(path_name, "@g") == NULL)) {
					prop = DI_PROP_NIL;
					while ((prop = di_prop_next(client_node,
							prop)) != DI_PROP_NIL) {
						/* is property name lun-wwn */
						if ((!(strcmp(LUN_GUID_PROP,
							di_prop_name(prop)))) &&
							(di_prop_type(prop) ==
							DI_PROP_TYPE_STRING)) {
							break;
						}
					}

					if (prop != DI_PROP_NIL) {
						count = di_prop_strings(
							prop, &lun_guid);
						sprintf(&path_name[
							strlen(path_name)],
							"@g%s", lun_guid);
					} else {
						ret = FPCFGA_LIB_ERR;
						goto out;
					}
				}

				devlen = strlen(DEVICES_DIR) +
						strlen(path_name) + 1;
				if ((pathp = calloc(1, devlen))
						== NULL) {
					*l_errnop = errno;
					return (FPCFGA_LIB_ERR);
				} else {
					(void) snprintf(pathp, devlen,
					"%s%s", DEVICES_DIR, path_name);
				}
				if ((ret = (update_lunlist(lunlistpp, *lunnump,
						statep, pathp, l_errnop))) !=
						FPCFGA_OK) {
					S_FREE(pathp);
					goto out;
				}
			}
			path = di_path_next_client(root, path);
		} while (path != DI_PATH_NIL);
	}

out:
	di_fini(tree_root);
	S_FREE(devfs_phys);
	return (ret);
}

static fpcfga_ret_t
insert_lun_to_lunlist(
	struct luninfo_list **lunlistpp,
	const char *dyncomp,
	di_node_t dev_node,
	int *l_errnop)
{
	char		path_name[MAXPATHLEN];
	char 		*pathp, *dev_phys;
	di_prop_t	prop_lun = DI_PROP_NIL;
	uint_t 		state;
	int		count, devlen;
	int		*lunp;

	while ((prop_lun = di_prop_next(dev_node, prop_lun)) != DI_PROP_NIL) {
		if (!(strcmp(LUN_PROP, di_prop_name(prop_lun))) &&
				(di_prop_type(prop_lun) == DI_PROP_TYPE_INT)) {
			count = di_prop_ints(prop_lun, &lunp);
			if (count <= 0) {
				return (FPCFGA_LIB_ERR);
			}
			break;
		}
	}

	if (prop_lun == DI_PROP_NIL) {
		return (FPCFGA_LIB_ERR);
	}

	/*
	 * stores state info in state.
	 * This information is used to get the
	 * validity of path.
	 * if driver_detached don't try to get
	 * the devfs_path since it is not
	 * complete. ex, /pci@1f,2000/pci@1/
	 * SUNW,qlc@5/fp@0,0/ssd
	 * which doesn't contain the port wwn
	 * part.  The attached node looks like
	 * /pci@1f,2000/pci@1/SUNW,qlc@5/fp@0,0/
	 * ssd@w2100002037006b14,0
	 */
	state = di_state(dev_node);

	dev_phys = di_devfs_path(dev_node);
	if (dev_phys == NULL) {
		return (FPCFGA_LIB_ERR);
	}

	strcpy(path_name, dev_phys);

	di_devfs_path_free(dev_phys);

	if ((state & DI_DRIVER_DETACHED) &&
		(strstr(path_name, "@w") == NULL)) {
		sprintf(&path_name[strlen(path_name)], "@w%s,%x", dyncomp,
									*lunp);
	}

	devlen = strlen(DEVICES_DIR) + strlen(path_name) + 1;

	if ((pathp = calloc(1, devlen))
			== NULL) {
		*l_errnop = errno;
		return (FPCFGA_LIB_ERR);
	} else {
		(void) snprintf(pathp, devlen, "%s%s", DEVICES_DIR, path_name);
	}

	return (update_lunlist(lunlistpp, *lunp, state, pathp, l_errnop));
}

static fpcfga_ret_t
update_lunlist(
	struct luninfo_list **lunlistpp,
	int	lun,
	uint_t	state,
	char 	*pathp,
	int 	*l_errnop)
{
	struct luninfo_list *newlun, *curlun, *prevlun;

	newlun = curlun = prevlun = (struct luninfo_list *)NULL;

	newlun = calloc(1, sizeof (struct luninfo_list));
	if (newlun == NULL) {
		*l_errnop = errno;
		return (FPCFGA_LIB_ERR);
	}

	newlun->lunnum = lun;
	newlun->node_state = state;
	newlun->path = pathp;
	newlun->next = (struct luninfo_list *)NULL;

	/* if lunlist is empty add the new lun info and return. */
	if (*lunlistpp == NULL) {
		*lunlistpp = newlun;
		return (FPCFGA_OK);
	}

	/* if the first lun in the list is the same as the new lun return. */
	if ((*lunlistpp)->lunnum == lun) {
		S_FREE(newlun);
		return (FPCFGA_OK);
	}

	/*
	 * if the first lun in the list is less than the new lun add the
	 * new lun as the first lun and return.
	 */
	if ((*lunlistpp)->lunnum < lun) {
		newlun->next = *lunlistpp;
		*lunlistpp = newlun;
		return (FPCFGA_OK);
	}

	/*
	 * if the first lun in the list is greater than the new lun and
	 * there is a single lun add new lun after the first lun and return.
	 */
	if ((*lunlistpp)->next == NULL) {
		(*lunlistpp)->next = newlun;
		return (FPCFGA_OK);
	}

	/*
	 * now there is more than two luns in the list and the first lun
	 * is greter than the input lun.
	 */
	curlun = (*lunlistpp)->next;
	prevlun = *lunlistpp;

	while (curlun != NULL) {
		if (curlun->lunnum == lun) {
			S_FREE(newlun);
			return (FPCFGA_OK);
		} else if (curlun->lunnum < lun) {
			newlun->next = curlun;
			prevlun->next = newlun;
			return (FPCFGA_OK);
		} else {
			prevlun = curlun;
			curlun = curlun->next;
		}
	}

	/* add the new lun at the end of list. */
	prevlun->next = newlun;
	return (FPCFGA_OK);

}


fpcfga_ret_t
make_dyncomp_from_dinode(
	const di_node_t node,
	char **dyncompp,
	int *l_errnop)
{
	di_prop_t	prop = DI_PROP_NIL;
	uchar_t		*port_wwn_data;
	int		count;

	*l_errnop = 0;
	*dyncompp = calloc(1, WWN_SIZE*2 + 1);
	if (*dyncompp == NULL) {
		*l_errnop = errno;
		return (FPCFGA_LIB_ERR);
	}

	/* now get port-wwn for the input node. */
	while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) {
		if (!(strcmp(PORT_WWN_PROP, di_prop_name(prop))) &&
			(di_prop_type(prop) == DI_PROP_TYPE_BYTE)) {
			break;
		}
	}

	if (prop != DI_PROP_NIL) {
		count = di_prop_bytes(prop, &port_wwn_data);
		if (count != WWN_SIZE) {
			S_FREE(*dyncompp);
			return (FPCFGA_LIB_ERR);
		}

		(void) sprintf(*dyncompp, "%016llx",
			(wwnConversion(port_wwn_data)));
	} else {
		*l_errnop = errno;
		S_FREE(*dyncompp);
		return (FPCFGA_LIB_ERR);
	}

	return (FPCFGA_OK);
}

fpcfga_ret_t
make_portwwn_luncomp_from_dinode(
	const di_node_t node,
	char **dyncompp,
	int **luncompp,
	int *l_errnop)
{
	uchar_t		*port_wwn_data;
	int		pwwn_ret, lun_ret;

	*l_errnop = 0;

	if ((dyncompp != NULL) &&
			((pwwn_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY,
			node, PORT_WWN_PROP, &port_wwn_data)) <= 0)) {
		*l_errnop = errno;
	}
	if ((luncompp != NULL) &&
			((lun_ret = di_prop_lookup_ints(DDI_DEV_T_ANY,
			node, LUN_PROP, luncompp)) <= 0)) {
		*l_errnop = errno;
	}

	/*
	 * di_prop* returns the number of entries found or 0 if not found
	 * or -1 for othere failure.
	 */
	if ((pwwn_ret <= 0) || (lun_ret <= 0)) {
		return (FPCFGA_LIB_ERR);
	}

	*dyncompp = calloc(1, WWN_SIZE*2+1);
	if (*dyncompp == NULL) {
		*l_errnop = errno;
		return (FPCFGA_LIB_ERR);
	}

	(void) sprintf(*dyncompp, "%016llx", (wwnConversion(port_wwn_data)));

	return (FPCFGA_OK);
}

fpcfga_ret_t
make_portwwn_luncomp_from_pinode(
	const di_path_t pinode,
	char **dyncompp,
	int **luncompp,
	int *l_errnop)
{
	uchar_t		*port_wwn_data;
	int		pwwn_ret, lun_ret;

	*l_errnop = 0;

	if ((dyncompp != NULL) &&
			((pwwn_ret = di_path_prop_lookup_bytes(pinode,
			PORT_WWN_PROP, &port_wwn_data)) <= 0)) {
		*l_errnop = errno;
	}
	if ((luncompp != NULL) &&
			((lun_ret = di_path_prop_lookup_ints(pinode,
			LUN_PROP, luncompp)) <= 0)) {
		*l_errnop = errno;
	}

	/*
	 * di_prop* returns the number of entries found or 0 if not found
	 * or -1 for othere failure.
	 */
	if ((pwwn_ret <= 0) || (lun_ret <= 0)) {
		return (FPCFGA_LIB_ERR);
	}

	*dyncompp = calloc(1, WWN_SIZE*2+1);
	if (*dyncompp == NULL) {
		*l_errnop = errno;
		return (FPCFGA_LIB_ERR);
	}

	(void) sprintf(*dyncompp, "%016llx", (wwnConversion(port_wwn_data)));

	return (FPCFGA_OK);
}

fpcfga_ret_t
construct_nodepath_from_dinode(
	const di_node_t node,
	char **node_pathp,
	int *l_errnop)
{
	char *devfs_path, path_name[MAXPATHLEN], *lun_guid, *port_wwn;
	uchar_t *port_wwn_data;
	int is_scsi_vhci_dev, di_ret, devlen;
	uint_t	state;

	devfs_path = di_devfs_path(node);
	strcpy(path_name, devfs_path);
	di_devfs_path_free(devfs_path);
	state = di_state(node);

	is_scsi_vhci_dev = (strstr(path_name, SCSI_VHCI_DRVR) != NULL) ? 1 : 0;

	/*
	 * If the node is
	 * state then check the devfs_path to
	 * see if it has a complete path.
	 * For non scsi_vhci node the path
	 * doesn't contain @w(portwwn) part
	 * consistently.  For scsi_vhci
	 * this behavior may not be there.
	 * To be safe @g(guid) is attempted
	 * to be added here.
	 */
	if (state & DI_DRIVER_DETACHED) {
		if (is_scsi_vhci_dev &&
			(strstr(path_name, "@g") == NULL)) {
			di_ret = di_prop_lookup_strings(DDI_DEV_T_ANY, node,
				LUN_GUID_PROP, &lun_guid);
			if (di_ret == -1) {
				*l_errnop = errno;
				return (FPCFGA_LIB_ERR);
			} else {
				sprintf(&path_name[strlen(path_name)],
					"@g%s", lun_guid);
			}
		} else if (!is_scsi_vhci_dev &&
			(strstr(path_name, "@w") == NULL)) {
			di_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
				PORT_WWN_PROP, &port_wwn_data);
			if (di_ret == -1) {
				*l_errnop = errno;
				return (FPCFGA_LIB_ERR);
			} else {
				if ((port_wwn = calloc(1, WWN_SIZE*2 + 1))
						== NULL) {
					*l_errnop = errno;
					return (FPCFGA_LIB_ERR);
				}

				(void) sprintf(port_wwn, "%016llx",
					(wwnConversion(port_wwn_data)));
				(void) sprintf(&path_name[strlen(path_name)],
					"@w%s", port_wwn);
				S_FREE(port_wwn);
			}
		}
	}

	devlen = strlen(DEVICES_DIR) + strlen(path_name) + 1;
	if ((*node_pathp = calloc(1, devlen)) == NULL) {
		*l_errnop = errno;
		return (FPCFGA_LIB_ERR);
	} else {
		(void) snprintf(*node_pathp, devlen,
		"%s%s", DEVICES_DIR, path_name);
	}

	return (FPCFGA_OK);
}

u_longlong_t
wwnConversion(uchar_t *wwn)
{
	u_longlong_t tmp;
	memcpy(&tmp, wwn, sizeof (u_longlong_t));
	return (ntohll(tmp));
}