OpenSolaris_b135/lib/libdlpi/common/libdlpi.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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Data-Link Provider Interface (Version 2)
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <stropts.h>
#include <sys/dlpi.h>
#include <errno.h>
#include <alloca.h>
#include <sys/sysmacros.h>
#include <ctype.h>
#include <net/if_types.h>
#include <netinet/arp.h>
#include <libdladm.h>
#include <libdllink.h>
#include <libdlpi.h>
#include <libintl.h>
#include <libinetutil.h>
#include <dirent.h>

#include "libdlpi_impl.h"

static int i_dlpi_open(const char *, int *, uint_t, boolean_t);
static int i_dlpi_style1_open(dlpi_impl_t *);
static int i_dlpi_style2_open(dlpi_impl_t *);
static int i_dlpi_checkstyle(dlpi_impl_t *, t_uscalar_t);
static int i_dlpi_attach(dlpi_impl_t *);
static void i_dlpi_passive(dlpi_impl_t *);

static int i_dlpi_strputmsg(dlpi_impl_t *, const dlpi_msg_t *, const void *,
    size_t, int);
static int i_dlpi_strgetmsg(dlpi_impl_t *, int, dlpi_msg_t *, t_uscalar_t,
    t_uscalar_t, size_t, void *, size_t *, size_t *);
static int i_dlpi_msg_common(dlpi_impl_t *, const dlpi_msg_t *, dlpi_msg_t *,
    size_t, int);

static size_t i_dlpi_getprimsize(t_uscalar_t);
static int i_dlpi_multi(dlpi_handle_t, t_uscalar_t, const uint8_t *, size_t);
static int i_dlpi_promisc(dlpi_handle_t, t_uscalar_t, uint_t);
static uint_t i_dlpi_buildsap(uint8_t *, uint_t);
static void i_dlpi_writesap(void *, uint_t, uint_t);
static int i_dlpi_notifyind_process(dlpi_impl_t *, dl_notify_ind_t *);
static boolean_t i_dlpi_notifyidexists(dlpi_impl_t *, dlpi_notifyent_t *);
static void i_dlpi_deletenotifyid(dlpi_impl_t *);

struct i_dlpi_walklink_arg {
	dlpi_walkfunc_t *fn;
	void *arg;
};

static int
i_dlpi_walk_link(const char *name, void *arg)
{
	struct i_dlpi_walklink_arg *warg = arg;

	return ((warg->fn(name, warg->arg)) ? DLADM_WALK_TERMINATE :
	    DLADM_WALK_CONTINUE);
}

/*ARGSUSED*/
void
dlpi_walk(dlpi_walkfunc_t *fn, void *arg, uint_t flags)
{
	struct i_dlpi_walklink_arg warg;
	struct dirent *d;
	DIR *dp;
	dladm_handle_t handle;

	warg.fn = fn;
	warg.arg = arg;

	if (flags & DLPI_DEVIPNET) {
		if ((dp = opendir("/dev/ipnet")) == NULL)
			return;

		while ((d = readdir(dp)) != NULL) {
			if (d->d_name[0] == '.')
				continue;

			if (warg.fn(d->d_name, warg.arg))
				break;
		}

		(void) closedir(dp);
	} else {
		/*
		 * Rather than have libdlpi take the libdladm handle,
		 * open the handle here.
		 */
		if (dladm_open(&handle) != DLADM_STATUS_OK)
			return;

		(void) dladm_walk(i_dlpi_walk_link, handle, &warg,
		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
		    DLADM_OPT_ACTIVE);

		dladm_close(handle);
	}
}

int
dlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags)
{
	int		retval, on = 1;
	ifspec_t	ifsp;
	dlpi_impl_t  	*dip;

	/*
	 * Validate linkname, fail if logical unit number (lun) is specified,
	 * otherwise decompose the contents into ifsp.
	 */
	if (linkname == NULL || (strchr(linkname, ':') != NULL) ||
	    !ifparse_ifspec(linkname, &ifsp))
		return (DLPI_ELINKNAMEINVAL);

	/*
	 * Ensure flags values are sane.
	 */
	if ((flags & (DLPI_DEVIPNET|DLPI_DEVONLY)) ==
	    (DLPI_DEVIPNET|DLPI_DEVONLY))
		return (DLPI_EINVAL);

	/* Allocate a new dlpi_impl_t. */
	if ((dip = calloc(1, sizeof (dlpi_impl_t))) == NULL)
		return (DL_SYSERR);

	/* Fill in known/default libdlpi handle values. */
	dip->dli_timeout = DLPI_DEF_TIMEOUT;
	dip->dli_ppa = ifsp.ifsp_ppa;
	dip->dli_oflags = flags;
	dip->dli_notifylistp = NULL;
	dip->dli_note_processing = B_FALSE;
	if (getenv("DLPI_DEVONLY") != NULL)
		dip->dli_oflags |= DLPI_DEVONLY;

	/* Copy linkname provided to the function. */
	if (strlcpy(dip->dli_linkname, linkname, sizeof (dip->dli_linkname)) >=
	    sizeof (dip->dli_linkname)) {
		free(dip);
		return (DLPI_ELINKNAMEINVAL);
	}

	/* Copy provider name. */
	(void) strlcpy(dip->dli_provider, ifsp.ifsp_devnm,
	    sizeof (dip->dli_provider));

	/*
	 * Special case: DLPI_SERIAL flag is set to indicate a synchronous
	 * serial line interface (see syncinit(1M), syncstat(1M),
	 * syncloop(1M)), which is not a DLPI link.
	 */
	if (dip->dli_oflags & DLPI_SERIAL) {
		if ((retval = i_dlpi_style2_open(dip)) != DLPI_SUCCESS) {
			free(dip);
			return (retval);
		}

		*dhp = (dlpi_handle_t)dip;
		return (retval);
	}

	if ((retval = i_dlpi_style1_open(dip)) != DLPI_SUCCESS) {
		if (retval == DLPI_ENOTSTYLE2) {
			/*
			 * The error code indicates not to continue the
			 * style-2 open. Change the error code back to
			 * DL_SYSERR, so that one would know the cause
			 * of failure from errno.
			 */
			retval = DL_SYSERR;
		} else if (!(dip->dli_oflags & DLPI_DEVIPNET)) {
			retval = i_dlpi_style2_open(dip);
		}
		if (retval != DLPI_SUCCESS) {
			free(dip);
			return (retval);
		}
	}

	if (dip->dli_oflags & DLPI_PASSIVE)
		i_dlpi_passive(dip);

	if ((dip->dli_oflags & DLPI_RAW) &&
	    ioctl(dip->dli_fd, DLIOCRAW, 0) < 0) {
		dlpi_close((dlpi_handle_t)dip);
		return (DLPI_ERAWNOTSUP);
	}

	if ((dip->dli_oflags & DLPI_IPNETINFO) &&
	    ioctl(dip->dli_fd, DLIOCIPNETINFO, &on) < 0) {
		dlpi_close((dlpi_handle_t)dip);
		return (DLPI_EIPNETINFONOTSUP);
	}

	/*
	 * We intentionally do not care if this request fails, as this
	 * indicates the underlying DLPI device does not support Native mode
	 * (pre-GLDV3 device drivers).
	 */
	if (dip->dli_oflags & DLPI_NATIVE) {
		if ((retval = ioctl(dip->dli_fd, DLIOCNATIVE, 0)) > 0)
			dip->dli_mactype = retval;
	}

	*dhp = (dlpi_handle_t)dip;
	return (DLPI_SUCCESS);
}

void
dlpi_close(dlpi_handle_t dh)
{
	dlpi_impl_t	*dip = (dlpi_impl_t *)dh;
	dlpi_notifyent_t *next, *dnp;

	if (dip != NULL) {
		for (dnp = dip->dli_notifylistp; dnp != NULL; dnp = next) {
			next = dnp->dln_next;
			free(dnp);
		}

		(void) close(dip->dli_fd);
		free(dip);
	}
}

/*
 * NOTE: The opt argument must be zero and is reserved for future use to extend
 * fields to the dlpi_info_t structure (see dlpi_info(3DLPI)).
 */
int
dlpi_info(dlpi_handle_t dh, dlpi_info_t *infop, uint_t opt)
{
	int 		retval;
	dlpi_msg_t	req, ack;
	dl_info_ack_t	*infoackp;
	uint8_t		*sapp, *addrp;
	caddr_t		ackendp, datap;
	t_uscalar_t	dataoff, datalen;
	dlpi_impl_t	*dip = (dlpi_impl_t *)dh;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	if (infop == NULL || opt != 0)
		return (DLPI_EINVAL);

	(void) memset(infop, 0, sizeof (dlpi_info_t));

	/* Set QoS range parameters to default unsupported value. */
	infop->di_qos_range.dl_qos_type = (t_uscalar_t)DL_UNKNOWN;
	infop->di_qos_range.dl_trans_delay.dl_target_value = DL_UNKNOWN;
	infop->di_qos_range.dl_trans_delay.dl_accept_value = DL_UNKNOWN;
	infop->di_qos_range.dl_priority.dl_min = DL_UNKNOWN;
	infop->di_qos_range.dl_priority.dl_max = DL_UNKNOWN;
	infop->di_qos_range.dl_protection.dl_min = DL_UNKNOWN;
	infop->di_qos_range.dl_protection.dl_max = DL_UNKNOWN;
	infop->di_qos_range.dl_residual_error = DL_UNKNOWN;

	/* Set QoS parameters to default unsupported value. */
	infop->di_qos_sel.dl_qos_type = (t_uscalar_t)DL_UNKNOWN;
	infop->di_qos_sel.dl_trans_delay = DL_UNKNOWN;
	infop->di_qos_sel.dl_priority = DL_UNKNOWN;
	infop->di_qos_sel.dl_protection = DL_UNKNOWN;
	infop->di_qos_sel.dl_residual_error = DL_UNKNOWN;

	DLPI_MSG_CREATE(req, DL_INFO_REQ);
	DLPI_MSG_CREATE(ack, DL_INFO_ACK);

	retval = i_dlpi_msg_common(dip, &req, &ack, DL_INFO_ACK_SIZE, RS_HIPRI);
	if (retval != DLPI_SUCCESS)
		return (retval);

	infoackp = &(ack.dlm_msg->info_ack);
	if (infoackp->dl_version != DL_VERSION_2)
		return (DLPI_EVERNOTSUP);

	if (infoackp->dl_service_mode != DL_CLDLS)
		return (DLPI_EMODENOTSUP);

	dip->dli_style = infoackp->dl_provider_style;
	dip->dli_mactype = infoackp->dl_mac_type;

	ackendp = (caddr_t)ack.dlm_msg + ack.dlm_msgsz;

	/* Check and save QoS selection information, if any. */
	datalen = infoackp->dl_qos_length;
	dataoff = infoackp->dl_qos_offset;
	if (dataoff != 0 && datalen != 0) {
		datap = (caddr_t)infoackp + dataoff;
		if (datalen > sizeof (dl_qos_cl_sel1_t) ||
		    dataoff < DL_INFO_ACK_SIZE || datap + datalen > ackendp)
			return (DLPI_EBADMSG);

		(void) memcpy(&infop->di_qos_sel, datap, datalen);
		if (infop->di_qos_sel.dl_qos_type != DL_QOS_CL_SEL1)
			return (DLPI_EMODENOTSUP);
	}

	/* Check and save QoS range information, if any. */
	datalen = infoackp->dl_qos_range_length;
	dataoff = infoackp->dl_qos_range_offset;
	if (dataoff != 0 && datalen != 0) {
		datap = (caddr_t)infoackp + dataoff;
		if (datalen > sizeof (dl_qos_cl_range1_t) ||
		    dataoff < DL_INFO_ACK_SIZE || datap + datalen > ackendp)
			return (DLPI_EBADMSG);

		(void) memcpy(&infop->di_qos_range, datap, datalen);
		if (infop->di_qos_range.dl_qos_type != DL_QOS_CL_RANGE1)
			return (DLPI_EMODENOTSUP);
	}

	/* Check and save physical address and SAP information. */
	dip->dli_saplen = abs(infoackp->dl_sap_length);
	dip->dli_sapbefore = (infoackp->dl_sap_length > 0);
	infop->di_physaddrlen = infoackp->dl_addr_length - dip->dli_saplen;

	if (infop->di_physaddrlen > DLPI_PHYSADDR_MAX ||
	    dip->dli_saplen > DLPI_SAPLEN_MAX)
		return (DL_BADADDR);

	dataoff = infoackp->dl_addr_offset;
	datalen = infoackp->dl_addr_length;
	if (dataoff != 0 && datalen != 0) {
		datap = (caddr_t)infoackp + dataoff;
		if (dataoff < DL_INFO_ACK_SIZE || datap + datalen > ackendp)
			return (DLPI_EBADMSG);

		sapp = addrp = (uint8_t *)datap;
		if (dip->dli_sapbefore)
			addrp += dip->dli_saplen;
		else
			sapp += infop->di_physaddrlen;

		(void) memcpy(infop->di_physaddr, addrp, infop->di_physaddrlen);
		infop->di_sap = i_dlpi_buildsap(sapp, dip->dli_saplen);
	}

	/* Check and save broadcast address information, if any. */
	datalen = infoackp->dl_brdcst_addr_length;
	dataoff = infoackp->dl_brdcst_addr_offset;
	if (dataoff != 0 && datalen != 0) {
		datap = (caddr_t)infoackp + dataoff;
		if (dataoff < DL_INFO_ACK_SIZE || datap + datalen > ackendp)
			return (DLPI_EBADMSG);
		if (datalen != infop->di_physaddrlen)
			return (DL_BADADDR);

		infop->di_bcastaddrlen = datalen;
		(void) memcpy(infop->di_bcastaddr, datap, datalen);
	}

	infop->di_max_sdu = infoackp->dl_max_sdu;
	infop->di_min_sdu = infoackp->dl_min_sdu;
	infop->di_state = infoackp->dl_current_state;
	infop->di_mactype = infoackp->dl_mac_type;

	/* Information retrieved from the handle. */
	(void) strlcpy(infop->di_linkname, dip->dli_linkname,
	    sizeof (infop->di_linkname));
	infop->di_timeout = dip->dli_timeout;

	return (DLPI_SUCCESS);
}

/*
 * This function parses 'linkname' and stores the 'provider' name and 'PPA'.
 */
int
dlpi_parselink(const char *linkname, char *provider, uint_t *ppa)
{
	dladm_status_t status;

	status = dladm_parselink(linkname, provider, ppa);

	if (status != DLADM_STATUS_OK)
		return (DLPI_ELINKNAMEINVAL);

	return (DLPI_SUCCESS);
}

/*
 * This function takes a provider name and a PPA and stores a full linkname
 * as 'linkname'. If 'provider' already is a full linkname 'provider' name
 * is stored in 'linkname'.
 */
int
dlpi_makelink(char *linkname, const char *provider, uint_t ppa)
{
	int provlen = strlen(provider);

	if (linkname == NULL || provlen == 0 || provlen >= DLPI_LINKNAME_MAX)
		return (DLPI_ELINKNAMEINVAL);

	if (!isdigit(provider[provlen - 1])) {
		(void) snprintf(linkname, DLPI_LINKNAME_MAX, "%s%d", provider,
		    ppa);
	} else {
		(void) strlcpy(linkname, provider, DLPI_LINKNAME_MAX);
	}

	return (DLPI_SUCCESS);
}

int
dlpi_bind(dlpi_handle_t dh, uint_t sap, uint_t *boundsap)
{
	int		retval;
	dlpi_msg_t	req, ack;
	dl_bind_req_t	*bindreqp;
	dl_bind_ack_t	*bindackp;
	dlpi_impl_t	*dip = (dlpi_impl_t *)dh;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	DLPI_MSG_CREATE(req, DL_BIND_REQ);
	DLPI_MSG_CREATE(ack, DL_BIND_ACK);
	bindreqp = &(req.dlm_msg->bind_req);

	/*
	 * If 'sap' is DLPI_ANY_SAP, bind to SAP 2 on token ring, else 0 on
	 * other interface types (SAP 0 has special significance on token ring).
	 */
	if (sap == DLPI_ANY_SAP)
		bindreqp->dl_sap = ((dip->dli_mactype == DL_TPR) ? 2 : 0);
	else
		bindreqp->dl_sap = sap;

	bindreqp->dl_service_mode = DL_CLDLS;
	bindreqp->dl_conn_mgmt = 0;
	bindreqp->dl_max_conind = 0;
	bindreqp->dl_xidtest_flg = 0;

	retval = i_dlpi_msg_common(dip, &req, &ack, DL_BIND_ACK_SIZE, 0);
	if (retval != DLPI_SUCCESS)
		return (retval);

	bindackp = &(ack.dlm_msg->bind_ack);
	/*
	 * Received a DLPI_BIND_ACK, now verify that the bound SAP
	 * is equal to the SAP requested. Some DLPI MAC type may bind
	 * to a different SAP than requested, in this case 'boundsap'
	 * returns the actual bound SAP. For the case where 'boundsap'
	 * is NULL and 'sap' is not DLPI_ANY_SAP, dlpi_bind fails.
	 */
	if (boundsap != NULL) {
		*boundsap = bindackp->dl_sap;
	} else if (sap != DLPI_ANY_SAP && bindackp->dl_sap != sap) {
		if (dlpi_unbind(dh) != DLPI_SUCCESS)
			return (DLPI_FAILURE);
		else
			return (DLPI_EUNAVAILSAP);
	}

	dip->dli_sap = bindackp->dl_sap;	/* save sap value in handle */
	return (DLPI_SUCCESS);
}

int
dlpi_unbind(dlpi_handle_t dh)
{
	dlpi_msg_t	req, ack;
	dlpi_impl_t	*dip = (dlpi_impl_t *)dh;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	DLPI_MSG_CREATE(req, DL_UNBIND_REQ);
	DLPI_MSG_CREATE(ack, DL_OK_ACK);

	return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0));
}

/*
 * This function is invoked by dlpi_enabmulti() or dlpi_disabmulti() and
 * based on the "op" value, multicast address is enabled/disabled.
 */
static int
i_dlpi_multi(dlpi_handle_t dh, t_uscalar_t op, const uint8_t *addrp,
    size_t addrlen)
{
	dlpi_msg_t		req, ack;
	dl_enabmulti_req_t	*multireqp;
	dlpi_impl_t		*dip = (dlpi_impl_t *)dh;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	if (addrlen > DLPI_PHYSADDR_MAX)
		return (DLPI_EINVAL);

	DLPI_MSG_CREATE(req, op);
	DLPI_MSG_CREATE(ack, DL_OK_ACK);

	multireqp = &(req.dlm_msg->enabmulti_req);
	multireqp->dl_addr_length = addrlen;
	multireqp->dl_addr_offset = sizeof (dl_enabmulti_req_t);
	(void) memcpy(&multireqp[1], addrp, addrlen);

	return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0));
}

int
dlpi_enabmulti(dlpi_handle_t dh, const void *addrp, size_t addrlen)
{
	return (i_dlpi_multi(dh, DL_ENABMULTI_REQ, addrp, addrlen));
}

int
dlpi_disabmulti(dlpi_handle_t dh, const void *addrp, size_t addrlen)
{
	return (i_dlpi_multi(dh, DL_DISABMULTI_REQ, addrp, addrlen));
}

/*
 * This function is invoked by dlpi_promiscon() or dlpi_promiscoff(). Based
 * on the value of 'op', promiscuous mode is turned on/off at the specified
 * 'level'.
 */
static int
i_dlpi_promisc(dlpi_handle_t dh, t_uscalar_t op, uint_t level)
{
	dlpi_msg_t		req, ack;
	dl_promiscon_req_t	*promiscreqp;
	dlpi_impl_t		*dip = (dlpi_impl_t *)dh;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	DLPI_MSG_CREATE(req, op);
	DLPI_MSG_CREATE(ack, DL_OK_ACK);

	promiscreqp = &(req.dlm_msg->promiscon_req);
	promiscreqp->dl_level = level;

	return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0));
}

int
dlpi_promiscon(dlpi_handle_t dh, uint_t level)
{
	return (i_dlpi_promisc(dh, DL_PROMISCON_REQ, level));
}

int
dlpi_promiscoff(dlpi_handle_t dh, uint_t level)
{
	return (i_dlpi_promisc(dh, DL_PROMISCOFF_REQ, level));
}

int
dlpi_get_physaddr(dlpi_handle_t dh, uint_t type, void *addrp, size_t *addrlenp)
{
	int			retval;
	dlpi_msg_t  		req, ack;
	dl_phys_addr_req_t	*physreqp;
	dl_phys_addr_ack_t	*physackp;
	t_uscalar_t		dataoff, datalen;
	caddr_t			datap, physackendp;
	dlpi_impl_t		*dip = (dlpi_impl_t *)dh;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	if (addrlenp == NULL || addrp == NULL || *addrlenp < DLPI_PHYSADDR_MAX)
		return (DLPI_EINVAL);

	DLPI_MSG_CREATE(req, DL_PHYS_ADDR_REQ);
	DLPI_MSG_CREATE(ack, DL_PHYS_ADDR_ACK);

	physreqp = &(req.dlm_msg->physaddr_req);
	physreqp->dl_addr_type = type;

	retval = i_dlpi_msg_common(dip, &req, &ack, DL_PHYS_ADDR_ACK_SIZE, 0);
	if (retval != DLPI_SUCCESS)
		return (retval);

	/* Received DL_PHYS_ADDR_ACK, store the physical address and length. */
	physackp = &(ack.dlm_msg->physaddr_ack);
	physackendp = (caddr_t)ack.dlm_msg + ack.dlm_msgsz;
	dataoff = physackp->dl_addr_offset;
	datalen = physackp->dl_addr_length;
	if (dataoff != 0 && datalen != 0) {
		datap = (caddr_t)physackp + dataoff;
		if (datalen > DLPI_PHYSADDR_MAX)
			return (DL_BADADDR);
		if (dataoff < DL_PHYS_ADDR_ACK_SIZE ||
		    datap + datalen > physackendp)
			return (DLPI_EBADMSG);

		*addrlenp = physackp->dl_addr_length;
		(void) memcpy(addrp, datap, datalen);
	} else {
		*addrlenp = datalen;
	}

	return (DLPI_SUCCESS);
}

int
dlpi_set_physaddr(dlpi_handle_t dh, uint_t type, const void *addrp,
    size_t addrlen)
{
	dlpi_msg_t  		req, ack;
	dl_set_phys_addr_req_t	*setphysreqp;
	dlpi_impl_t		*dip = (dlpi_impl_t *)dh;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	if (addrp == NULL || type != DL_CURR_PHYS_ADDR ||
	    addrlen > DLPI_PHYSADDR_MAX)
		return (DLPI_EINVAL);

	DLPI_MSG_CREATE(req, DL_SET_PHYS_ADDR_REQ);
	DLPI_MSG_CREATE(ack, DL_OK_ACK);

	setphysreqp = &(req.dlm_msg->set_physaddr_req);
	setphysreqp->dl_addr_length = addrlen;
	setphysreqp->dl_addr_offset = sizeof (dl_set_phys_addr_req_t);
	(void) memcpy(&setphysreqp[1], addrp, addrlen);

	return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0));
}

int
dlpi_send(dlpi_handle_t dh, const void *daddrp, size_t daddrlen,
    const void *msgbuf, size_t msglen, const dlpi_sendinfo_t *sendp)
{
	dlpi_msg_t		req;
	dl_unitdata_req_t	*udatareqp;
	uint_t			sap;
	dlpi_impl_t		*dip = (dlpi_impl_t *)dh;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	if (dip->dli_oflags & DLPI_RAW)
		return (i_dlpi_strputmsg(dip, NULL, msgbuf, msglen, 0));

	if ((daddrlen > 0 && daddrp == NULL) || daddrlen > DLPI_PHYSADDR_MAX)
		return (DLPI_EINVAL);

	DLPI_MSG_CREATE(req, DL_UNITDATA_REQ);
	udatareqp = &(req.dlm_msg->unitdata_req);

	/* Set priority to default priority range. */
	udatareqp->dl_priority.dl_min = 0;
	udatareqp->dl_priority.dl_max = 0;

	/* Use SAP value if specified otherwise use bound SAP value. */
	if (sendp != NULL) {
		sap = sendp->dsi_sap;
		if (sendp->dsi_prio.dl_min != DL_QOS_DONT_CARE)
			udatareqp->dl_priority.dl_min = sendp->dsi_prio.dl_min;
		if (sendp->dsi_prio.dl_max != DL_QOS_DONT_CARE)
			udatareqp->dl_priority.dl_max = sendp->dsi_prio.dl_max;
	} else {
		sap = dip->dli_sap;
	}

	udatareqp->dl_dest_addr_length = daddrlen + dip->dli_saplen;
	udatareqp->dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE;

	/*
	 * Since `daddrp' only has the link-layer destination address,
	 * we must prepend or append the SAP (according to dli_sapbefore)
	 * to make a full DLPI address.
	 */
	if (dip->dli_sapbefore) {
		i_dlpi_writesap(&udatareqp[1], sap, dip->dli_saplen);
		(void) memcpy((caddr_t)&udatareqp[1] + dip->dli_saplen,
		    daddrp, daddrlen);
	} else {
		(void) memcpy(&udatareqp[1], daddrp, daddrlen);
		i_dlpi_writesap((caddr_t)&udatareqp[1] + daddrlen, sap,
		    dip->dli_saplen);
	}

	return (i_dlpi_strputmsg(dip, &req, msgbuf, msglen, 0));
}

int
dlpi_recv(dlpi_handle_t dh, void *saddrp, size_t *saddrlenp, void *msgbuf,
    size_t *msglenp, int msec, dlpi_recvinfo_t *recvp)
{
	int			retval;
	dlpi_msg_t		ind;
	size_t			totmsglen;
	dl_unitdata_ind_t	*udatap;
	t_uscalar_t		dataoff, datalen;
	caddr_t			datap, indendp;
	dlpi_impl_t		*dip = (dlpi_impl_t *)dh;

	if (dip == NULL)
		return (DLPI_EINHANDLE);
	/*
	 * If handle is in raw mode ignore everything except total message
	 * length.
	 */
	if (dip->dli_oflags & DLPI_RAW) {
		retval = i_dlpi_strgetmsg(dip, msec, NULL, 0, 0, 0, msgbuf,
		    msglenp, &totmsglen);

		if (retval == DLPI_SUCCESS && recvp != NULL)
			recvp->dri_totmsglen = totmsglen;
		return (retval);
	}

	DLPI_MSG_CREATE(ind, DL_UNITDATA_IND);
	udatap = &(ind.dlm_msg->unitdata_ind);
	indendp = (caddr_t)ind.dlm_msg + ind.dlm_msgsz;

	if ((retval = i_dlpi_strgetmsg(dip, msec, &ind, DL_UNITDATA_IND,
	    DL_UNITDATA_IND, DL_UNITDATA_IND_SIZE, msgbuf,
	    msglenp, &totmsglen)) != DLPI_SUCCESS)
		return (retval);

	/*
	 * If DLPI link provides source address, store source address in
	 * 'saddrp' and source length in 'saddrlenp', else set saddrlenp to 0.
	 */
	if (saddrp != NULL && saddrlenp != NULL)  {
		if (*saddrlenp < DLPI_PHYSADDR_MAX)
			return (DLPI_EINVAL);

		dataoff = udatap->dl_src_addr_offset;
		datalen = udatap->dl_src_addr_length;
		if (dataoff != 0 && datalen != 0) {
			datap = (caddr_t)udatap + dataoff;
			if (dataoff < DL_UNITDATA_IND_SIZE ||
			    datap + datalen > indendp)
				return (DLPI_EBADMSG);

			*saddrlenp = datalen - dip->dli_saplen;
			if (*saddrlenp > DLPI_PHYSADDR_MAX)
				return (DL_BADADDR);

			if (dip->dli_sapbefore)
				datap += dip->dli_saplen;
			(void) memcpy(saddrp, datap, *saddrlenp);
		} else {
			*saddrlenp = 0;
		}
	}

	/*
	 * If destination address requested, check and save destination
	 * address, if any.
	 */
	if (recvp != NULL) {
		dataoff = udatap->dl_dest_addr_offset;
		datalen = udatap->dl_dest_addr_length;
		if (dataoff != 0 && datalen != 0) {
			datap = (caddr_t)udatap + dataoff;
			if (dataoff < DL_UNITDATA_IND_SIZE ||
			    datap + datalen > indendp)
				return (DLPI_EBADMSG);

			recvp->dri_destaddrlen = datalen - dip->dli_saplen;
			if (recvp->dri_destaddrlen > DLPI_PHYSADDR_MAX)
				return (DL_BADADDR);

			if (dip->dli_sapbefore)
				datap += dip->dli_saplen;
			(void) memcpy(recvp->dri_destaddr, datap,
			    recvp->dri_destaddrlen);
		} else {
			recvp->dri_destaddrlen = 0;
		}

		recvp->dri_destaddrtype = udatap->dl_group_address;
		recvp->dri_totmsglen = totmsglen;
	}

	return (DLPI_SUCCESS);
}

int
dlpi_enabnotify(dlpi_handle_t dh, uint_t notes, dlpi_notifyfunc_t *funcp,
    void *arg, dlpi_notifyid_t *id)
{
	int			retval;
	dlpi_msg_t		req, ack;
	dl_notify_req_t		*notifyreqp;
	dlpi_impl_t		*dip = (dlpi_impl_t *)dh;
	dlpi_notifyent_t	*newnotifp;
	dlpi_info_t 		dlinfo;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	retval = dlpi_info((dlpi_handle_t)dip, &dlinfo, 0);
	if (retval != DLPI_SUCCESS)
		return (retval);

	if (dip->dli_note_processing)
		return (DLPI_FAILURE);

	if (funcp == NULL || id == NULL)
		return (DLPI_EINVAL);

	if ((~DLPI_NOTIFICATION_TYPES & notes) ||
	    !(notes & DLPI_NOTIFICATION_TYPES))
		return (DLPI_ENOTEINVAL);

	DLPI_MSG_CREATE(req, DL_NOTIFY_REQ);
	DLPI_MSG_CREATE(ack, DL_NOTIFY_ACK);

	notifyreqp = &(req.dlm_msg->notify_req);
	notifyreqp->dl_notifications = notes;
	notifyreqp->dl_timelimit = 0;

	retval = i_dlpi_msg_common(dip, &req, &ack, DL_NOTIFY_ACK_SIZE, 0);
	if (retval == DL_NOTSUPPORTED)
		return (DLPI_ENOTENOTSUP);

	if (retval != DLPI_SUCCESS)
		return (retval);

	if ((newnotifp = calloc(1, sizeof (dlpi_notifyent_t))) == NULL)
		return (DL_SYSERR);

	/* Register notification information. */
	newnotifp->dln_fnp = funcp;
	newnotifp->dln_notes = notes;
	newnotifp->arg = arg;
	newnotifp->dln_rm = B_FALSE;

	/* Insert notification node at head */
	newnotifp->dln_next = dip->dli_notifylistp;
	dip->dli_notifylistp = newnotifp;

	*id = (dlpi_notifyid_t)newnotifp;
	return (DLPI_SUCCESS);
}

int
dlpi_disabnotify(dlpi_handle_t dh, dlpi_notifyid_t id, void **argp)
{
	dlpi_impl_t		*dip = (dlpi_impl_t *)dh;
	dlpi_notifyent_t	*remid = (dlpi_notifyent_t *)id;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	/* Walk the notifyentry list to find matching id. */
	if (!(i_dlpi_notifyidexists(dip, remid)))
		return (DLPI_ENOTEIDINVAL);

	if (argp != NULL)
		*argp = remid->arg;

	remid->dln_rm = B_TRUE;
	/* Delete node if callbacks are not being processed. */
	if (!dip->dli_note_processing)
		i_dlpi_deletenotifyid(dip);

	return (DLPI_SUCCESS);
}

int
dlpi_fd(dlpi_handle_t dh)
{
	dlpi_impl_t	*dip = (dlpi_impl_t *)dh;

	return (dip != NULL ? dip->dli_fd : -1);
}

int
dlpi_set_timeout(dlpi_handle_t dh, int sec)
{
	dlpi_impl_t	*dip = (dlpi_impl_t *)dh;

	if (dip == NULL)
		return (DLPI_EINHANDLE);

	dip->dli_timeout = sec;
	return (DLPI_SUCCESS);
}

const char *
dlpi_linkname(dlpi_handle_t dh)
{
	dlpi_impl_t	*dip = (dlpi_impl_t *)dh;

	return (dip != NULL ? dip->dli_linkname : NULL);
}

/*
 * Returns DLPI style stored in the handle.
 * Note: This function is used for test purposes only. Do not remove without
 * fixing the DLPI testsuite.
 */
uint_t
dlpi_style(dlpi_handle_t dh)
{
	dlpi_impl_t	*dip = (dlpi_impl_t *)dh;

	return (dip->dli_style);
}

uint_t
dlpi_arptype(uint_t dlpitype)
{
	switch (dlpitype) {

	case DL_ETHER:
		return (ARPHRD_ETHER);

	case DL_FRAME:
		return (ARPHRD_FRAME);

	case DL_ATM:
		return (ARPHRD_ATM);

	case DL_IPATM:
		return (ARPHRD_IPATM);

	case DL_HDLC:
		return (ARPHRD_HDLC);

	case DL_FC:
		return (ARPHRD_FC);

	case DL_CSMACD:				/* ieee 802 networks */
	case DL_TPB:
	case DL_TPR:
	case DL_METRO:
	case DL_FDDI:
		return (ARPHRD_IEEE802);

	case DL_IB:
		return (ARPHRD_IB);

	case DL_IPV4:
	case DL_IPV6:
		return (ARPHRD_TUNNEL);
	}

	return (0);
}

uint_t
dlpi_iftype(uint_t dlpitype)
{
	switch (dlpitype) {

	case DL_ETHER:
		return (IFT_ETHER);

	case DL_ATM:
		return (IFT_ATM);

	case DL_CSMACD:
		return (IFT_ISO88023);

	case DL_TPB:
		return (IFT_ISO88024);

	case DL_TPR:
		return (IFT_ISO88025);

	case DL_FDDI:
		return (IFT_FDDI);

	case DL_IB:
		return (IFT_IB);

	case DL_OTHER:
		return (IFT_OTHER);
	}

	return (0);
}

/*
 * This function attempts to open a device under the following namespaces:
 *	/dev/ipnet	- if DLPI_DEVIPNET is specified
 *      /dev/net	- if a data-link with the specified name exists
 *	/dev		- if DLPI_DEVONLY is specified, or if there is no
 *			  data-link with the specified name (could be /dev/ip)
 *
 * In particular, if DLPI_DEVIPNET is not specified, this function is used to
 * open a data-link node, or "/dev/ip" node. It is usually be called firstly
 * with style1 being B_TRUE, and if that fails and the return value is not
 * DLPI_ENOTSTYLE2, the function will again be called with style1 being
 * B_FALSE (style-1 open attempt first, then style-2 open attempt).
 *
 * If DLPI_DEVONLY is specified, both attempt will try to open the /dev node
 * directly.
 *
 * Otherwise, for style-1 attempt, the function will try to open the style-1
 * /dev/net node, and perhaps fallback to open the style-1 /dev node if the
 * give name is not a data-link name (e.g., it is /dev/ip). Note that the
 * fallback and the subsequent style-2 attempt will not happen if:
 * 1. style-1 opening of the /dev/net node succeeds;
 * 2. style-1 opening of the /dev/net node fails with errno other than ENOENT,
 *    which means that the specific /dev/net node exist, but the attempt fails
 *    for some other reason;
 * 3. style-1 openning of the /dev/net fails with ENOENT, but the name is
 *    a known device name or its VLAN PPA hack name. (for example, assuming
 *    device bge0 is renamed to net0, opening /dev/net/bge1000 would return
 *    ENOENT, but we should not fallback to open /dev/bge1000 in this case,
 *    as VLAN 1 over the bge0 device should be named as net1000.
 *
 * DLPI_ENOTSTYLE2 will be returned in case 2 and 3 to indicate not to proceed
 * the second style-2 open attempt.
 */
static int
i_dlpi_open(const char *provider, int *fd, uint_t flags, boolean_t style1)
{
	char		path[MAXPATHLEN];
	int		oflags;

	errno = ENOENT;
	oflags = O_RDWR;
	if (flags & DLPI_EXCL)
		oflags |= O_EXCL;

	if (flags & DLPI_DEVIPNET) {
		(void) snprintf(path, sizeof (path), "/dev/ipnet/%s", provider);
		if ((*fd = open(path, oflags)) != -1)
			return (DLPI_SUCCESS);
		else
			return (errno == ENOENT ? DLPI_ENOLINK : DL_SYSERR);
	} else if (style1 && !(flags & DLPI_DEVONLY)) {
		char		driver[DLPI_LINKNAME_MAX];
		char		device[DLPI_LINKNAME_MAX];
		datalink_id_t	linkid;
		uint_t		ppa;
		dladm_handle_t	handle;

		/*
		 * This is not a valid style-1 name. It could be "ip" module
		 * for example. Fallback to open the /dev node.
		 */
		if (dlpi_parselink(provider, driver, &ppa) != DLPI_SUCCESS)
			goto fallback;

		(void) snprintf(path, sizeof (path), "/dev/net/%s", provider);
		if ((*fd = open(path, oflags)) != -1)
			return (DLPI_SUCCESS);

		/*
		 * We don't fallback to open the /dev node when it returns
		 * error codes other than ENOENT. In that case, DLPI_ENOTSTYLE2
		 * is returned to indicate not to continue the style-2 open.
		 */
		if (errno != ENOENT)
			return (DLPI_ENOTSTYLE2);

		/*
		 * We didn't find the /dev/net node. Then we check whether
		 * the given name is a device name or its VLAN PPA hack name
		 * of a known link. If the answer is yes, and this link
		 * supports vanity naming, then the link (or the VLAN) should
		 * also have its /dev/net node but perhaps with another vanity
		 * name (for example, when bge0 is renamed to net0). In this
		 * case, although attempt to open the /dev/net/<devname> fails,
		 * we should not fallback to open the /dev/<devname> node.
		 */
		(void) snprintf(device, DLPI_LINKNAME_MAX, "%s%d", driver,
		    ppa >= 1000 ? ppa % 1000 : ppa);

		/* open libdladm handle rather than taking it as input */
		if (dladm_open(&handle) != DLADM_STATUS_OK)
			goto fallback;

		if (dladm_dev2linkid(handle, device, &linkid) ==
		    DLADM_STATUS_OK) {
			dladm_phys_attr_t dpa;

			if ((dladm_phys_info(handle, linkid, &dpa,
			    DLADM_OPT_ACTIVE)) == DLADM_STATUS_OK &&
			    !dpa.dp_novanity) {
				dladm_close(handle);
				return (DLPI_ENOTSTYLE2);
			}
		}
		dladm_close(handle);
	}

fallback:
	(void) snprintf(path, sizeof (path), "/dev/%s", provider);
	if ((*fd = open(path, oflags)) != -1)
		return (DLPI_SUCCESS);

	return (errno == ENOENT ? DLPI_ENOLINK : DL_SYSERR);
}

/*
 * Open a style 1 link. PPA is implicitly attached.
 */
static int
i_dlpi_style1_open(dlpi_impl_t *dip)
{
	int		retval, save_errno;
	int		fd;

	retval = i_dlpi_open(dip->dli_linkname, &fd, dip->dli_oflags, B_TRUE);
	if (retval != DLPI_SUCCESS)
		return (retval);
	dip->dli_fd = fd;

	if ((retval = i_dlpi_checkstyle(dip, DL_STYLE1)) != DLPI_SUCCESS) {
		save_errno = errno;
		(void) close(dip->dli_fd);
		errno = save_errno;
	}

	return (retval);
}

/*
 * Open a style 2 link. PPA must be explicitly attached.
 */
static int
i_dlpi_style2_open(dlpi_impl_t *dip)
{
	int 		fd;
	int 		retval, save_errno;

	retval = i_dlpi_open(dip->dli_provider, &fd, dip->dli_oflags, B_FALSE);
	if (retval != DLPI_SUCCESS)
		return (retval);
	dip->dli_fd = fd;

	/*
	 * Special case: DLPI_SERIAL flag (synchronous serial lines) is not a
	 * DLPI link so attach and ignore rest.
	 */
	if (dip->dli_oflags & DLPI_SERIAL)
		goto attach;

	if ((retval = i_dlpi_checkstyle(dip, DL_STYLE2)) != DLPI_SUCCESS)
		goto failure;

	/*
	 * Succeeded opening the link and verified it is style2. Now attach to
	 * PPA only if DLPI_NOATTACH is not set.
	 */
	if (dip->dli_oflags & DLPI_NOATTACH)
		return (DLPI_SUCCESS);

attach:
	if ((retval = i_dlpi_attach(dip)) == DLPI_SUCCESS)
		return (DLPI_SUCCESS);

failure:
	save_errno = errno;
	(void) close(dip->dli_fd);
	errno = save_errno;
	return (retval);
}

/*
 * Verify with DLPI that the link is the expected DLPI 'style' device,
 * dlpi_info sets the DLPI style in the DLPI handle.
 */
static int
i_dlpi_checkstyle(dlpi_impl_t *dip, t_uscalar_t style)
{
	int retval;
	dlpi_info_t dlinfo;

	retval = dlpi_info((dlpi_handle_t)dip, &dlinfo, 0);
	if (retval == DLPI_SUCCESS && dip->dli_style != style)
		retval = DLPI_EBADLINK;

	return (retval);
}

/*
 * For DLPI style 2 providers, an explicit attach of PPA is required.
 */
static int
i_dlpi_attach(dlpi_impl_t *dip)
{
	dlpi_msg_t		req, ack;
	dl_attach_req_t		*attachreqp;

	/*
	 * Special case: DLPI_SERIAL flag (synchronous serial lines)
	 * is not a DLPI link so ignore DLPI style.
	 */
	if (dip->dli_style != DL_STYLE2 && !(dip->dli_oflags & DLPI_SERIAL))
		return (DLPI_ENOTSTYLE2);

	DLPI_MSG_CREATE(req, DL_ATTACH_REQ);
	DLPI_MSG_CREATE(ack, DL_OK_ACK);

	attachreqp = &(req.dlm_msg->attach_req);
	attachreqp->dl_ppa = dip->dli_ppa;

	return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0));
}

/*
 * Enable DLPI passive mode on a DLPI handle. We intentionally do not care
 * if this request fails, as this indicates the underlying DLPI device does
 * not support link aggregation (pre-GLDV3 device drivers), and thus will
 * see the expected behavior without failing with DL_SYSERR/EBUSY when issuing
 * DLPI primitives like DL_BIND_REQ. For further info see dlpi(7p).
 */
static void
i_dlpi_passive(dlpi_impl_t *dip)
{
	dlpi_msg_t		req, ack;

	DLPI_MSG_CREATE(req, DL_PASSIVE_REQ);
	DLPI_MSG_CREATE(ack, DL_OK_ACK);

	(void) i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0);
}

/*
 * Send a dlpi control message and/or data message on a stream. The inputs
 * for this function are:
 * 	dlpi_impl_t *dip: internal dlpi handle to open stream
 *	const dlpi_msg_t *dlreqp: request message structure
 *	void *databuf:	data buffer
 *	size_t datalen:	data buffer len
 *	int flags:	flags to set for putmsg()
 * Returns DLPI_SUCCESS if putmsg() succeeds, otherwise DL_SYSERR on failure.
 */
static int
i_dlpi_strputmsg(dlpi_impl_t *dip, const dlpi_msg_t *dlreqp,
    const void *databuf, size_t datalen, int flags)
{
	int		retval;
	int		fd = dip->dli_fd;
	struct strbuf	ctl;
	struct strbuf   data;

	if (dlreqp != NULL) {
		ctl.buf = (void *)dlreqp->dlm_msg;
		ctl.len = dlreqp->dlm_msgsz;
	}

	data.buf = (void *)databuf;
	data.len = datalen;

	retval = putmsg(fd, (dlreqp == NULL ? NULL: &ctl),
	    (databuf == NULL ? NULL : &data), flags);

	return ((retval == 0) ? DLPI_SUCCESS : DL_SYSERR);
}

/*
 * Get a DLPI control message and/or data message from a stream. The inputs
 * for this function are:
 * 	dlpi_impl_t *dip: 	internal dlpi handle
 * 	int msec: 		timeout to wait for message
 *	dlpi_msg_t *dlreplyp:	reply message structure, the message size
 *				member on return stores actual size received
 *	t_uscalar_t dlreqprim: 	requested primitive
 *	t_uscalar_t dlreplyprim:acknowledged primitive in response to request
 *	size_t dlreplyminsz:	minimum size of acknowledged primitive size
 *	void *databuf: 		data buffer
 *	size_t *datalenp:	data buffer len
 *	size_t *totdatalenp: 	total data received. Greater than 'datalenp' if
 *				actual data received is larger than 'databuf'
 * Function returns DLPI_SUCCESS if requested message is retrieved
 * otherwise returns error code or timeouts. If a notification arrives on
 * the stream the callback is notified. However, error returned during the
 * handling of notification is ignored as it would be confusing to actual caller
 * of this function.
 */
static int
i_dlpi_strgetmsg(dlpi_impl_t *dip, int msec, dlpi_msg_t *dlreplyp,
    t_uscalar_t dlreqprim, t_uscalar_t dlreplyprim, size_t dlreplyminsz,
    void *databuf, size_t *datalenp, size_t *totdatalenp)
{
	int			retval;
	int			flags;
	int			fd = dip->dli_fd;
	struct strbuf		ctl, data;
	struct pollfd		pfd;
	hrtime_t		start, current;
	long			bufc[DLPI_CHUNKSIZE / sizeof (long)];
	long			bufd[DLPI_CHUNKSIZE / sizeof (long)];
	union DL_primitives	*dlprim;
	dl_notify_ind_t		*dlnotif;
	boolean_t		infinite = (msec < 0);	/* infinite timeout */

	/*
	 * dlreplyp and databuf can be NULL at the same time, to force a check
	 * for pending events on the DLPI link instance; dlpi_enabnotify(3DLPI).
	 * this will be true more so for DLPI_RAW mode with notifications
	 * enabled.
	 */
	if ((databuf == NULL && datalenp != NULL) ||
	    (databuf != NULL && datalenp == NULL))
		return (DLPI_EINVAL);

	pfd.fd = fd;
	pfd.events = POLLIN | POLLPRI;

	ctl.buf = (dlreplyp == NULL) ? bufc : (void *)dlreplyp->dlm_msg;
	ctl.len = 0;
	ctl.maxlen = (dlreplyp == NULL) ? sizeof (bufc) : dlreplyp->dlm_msgsz;

	data.buf = (databuf == NULL) ? bufd : databuf;
	data.len = 0;
	data.maxlen = (databuf == NULL) ? sizeof (bufd): *datalenp;

	for (;;) {
		if (!infinite)
			start = gethrtime() / (NANOSEC / MILLISEC);

		switch (poll(&pfd, 1, msec)) {
		default:
			if (pfd.revents & POLLHUP)
				return (DL_SYSERR);
			break;
		case 0:
			return (DLPI_ETIMEDOUT);
		case -1:
			return (DL_SYSERR);
		}

		flags = 0;
		if ((retval = getmsg(fd, &ctl, &data, &flags)) < 0)
			return (DL_SYSERR);

		if (totdatalenp != NULL)
			*totdatalenp = data.len;

		/*
		 * The supplied DLPI_CHUNKSIZE sized buffers are large enough
		 * to retrieve all valid DLPI responses in one iteration.
		 * If MORECTL or MOREDATA is set, we are not interested in the
		 * remainder of the message. Temporary buffers are used to
		 * drain the remainder of this message.
		 * The special case we have to account for is if
		 * a higher priority messages is enqueued  whilst handling
		 * this condition. We use a change in the flags parameter
		 * returned by getmsg() to indicate the message has changed.
		 */
		while (retval & (MORECTL | MOREDATA)) {
			struct strbuf   cscratch, dscratch;
			int		oflags = flags;

			cscratch.buf = (char *)bufc;
			dscratch.buf = (char *)bufd;
			cscratch.len = dscratch.len = 0;
			cscratch.maxlen = dscratch.maxlen =
			    sizeof (bufc);

			if ((retval = getmsg(fd, &cscratch, &dscratch,
			    &flags)) < 0)
				return (DL_SYSERR);

			if (totdatalenp != NULL)
				*totdatalenp += dscratch.len;
			/*
			 * In the special case of higher priority
			 * message received, the low priority message
			 * received earlier is discarded, if no data
			 * or control message is left.
			 */
			if ((flags != oflags) &&
			    !(retval & (MORECTL | MOREDATA)) &&
			    (cscratch.len != 0)) {
				ctl.len = MIN(cscratch.len, DLPI_CHUNKSIZE);
				if (dlreplyp != NULL)
					(void) memcpy(dlreplyp->dlm_msg, bufc,
					    ctl.len);
				break;
			}
		}

		/*
		 * Check if DL_NOTIFY_IND message received. If there is one,
		 * notify the callback function(s) and continue processing the
		 * requested message.
		 */
		if (dip->dli_notifylistp != NULL &&
		    ctl.len >= (int)(sizeof (t_uscalar_t)) &&
		    *(t_uscalar_t *)(void *)ctl.buf == DL_NOTIFY_IND) {
			/* process properly-formed DL_NOTIFY_IND messages */
			if (ctl.len >= DL_NOTIFY_IND_SIZE) {
				dlnotif = (dl_notify_ind_t *)(void *)ctl.buf;
				(void) i_dlpi_notifyind_process(dip, dlnotif);
			}
			goto update_timer;
		}

		/*
		 * If we were expecting a data message, and we got one, set
		 * *datalenp.  If we aren't waiting on a control message, then
		 * we're done.
		 */
		if (databuf != NULL && data.len >= 0) {
			*datalenp = data.len;
			if (dlreplyp == NULL)
				break;
		}

		/*
		 * If we were expecting a control message, and the message
		 * we received is at least big enough to be a DLPI message,
		 * then verify it's a reply to something we sent.  If it
		 * is a reply to something we sent, also verify its size.
		 */
		if (dlreplyp != NULL && ctl.len >= sizeof (t_uscalar_t)) {
			dlprim = dlreplyp->dlm_msg;
			if (dlprim->dl_primitive == dlreplyprim) {
				if (ctl.len < dlreplyminsz)
					return (DLPI_EBADMSG);
				dlreplyp->dlm_msgsz = ctl.len;
				break;
			} else if (dlprim->dl_primitive == DL_ERROR_ACK) {
				if (ctl.len < DL_ERROR_ACK_SIZE)
					return (DLPI_EBADMSG);

				/* Is it ours? */
				if (dlprim->error_ack.dl_error_primitive ==
				    dlreqprim)
					break;
			}
		}
update_timer:
		if (!infinite) {
			current = gethrtime() / (NANOSEC / MILLISEC);
			msec -= (current - start);

			if (msec <= 0)
				return (DLPI_ETIMEDOUT);
		}
	}

	return (DLPI_SUCCESS);
}

/*
 * Common routine invoked by all DLPI control routines. The inputs for this
 * function are:
 * 	dlpi_impl_t *dip: internal dlpi handle
 *	const dlpi_msg_t *dlreqp: request message structure
 *	dlpi_msg_t *dlreplyp: reply message structure
 *	size_t dlreplyminsz: minimum size of reply primitive
 *	int flags: flags to be set to send a message
 * This routine succeeds if the message is an expected request/acknowledged
 * message. However, if DLPI notification has been enabled via
 * dlpi_enabnotify(), DL_NOTIFY_IND messages are handled before handling
 * expected messages. Otherwise, any other unexpected asynchronous messages will
 * be discarded.
 */
static int
i_dlpi_msg_common(dlpi_impl_t *dip, const dlpi_msg_t *dlreqp,
    dlpi_msg_t *dlreplyp, size_t dlreplyminsz, int flags)
{
	int		retval;
	t_uscalar_t	dlreqprim = dlreqp->dlm_msg->dl_primitive;
	t_uscalar_t 	dlreplyprim = dlreplyp->dlm_msg->dl_primitive;

	/* Put the requested primitive on the stream. */
	retval = i_dlpi_strputmsg(dip, dlreqp, NULL, 0, flags);
	if (retval != DLPI_SUCCESS)
		return (retval);

	/* Retrieve acknowledged message for requested primitive. */
	retval = i_dlpi_strgetmsg(dip, (dip->dli_timeout * MILLISEC),
	    dlreplyp, dlreqprim, dlreplyprim, dlreplyminsz, NULL, NULL, NULL);
	if (retval != DLPI_SUCCESS)
		return (retval);

	/*
	 * If primitive is DL_ERROR_ACK, set errno.
	 */
	if (dlreplyp->dlm_msg->dl_primitive == DL_ERROR_ACK) {
		errno = dlreplyp->dlm_msg->error_ack.dl_unix_errno;
		retval = dlreplyp->dlm_msg->error_ack.dl_errno;
	}

	return (retval);
}

/*
 * DLPI error codes.
 */
static const char *dlpi_errlist[] = {
	"bad LSAP selector",				/* DL_BADSAP  0x00 */
	"DLSAP address in improper format or invalid",	/* DL_BADADDR 0x01 */
	"improper permissions for request",		/* DL_ACCESS  0x02 */
	"primitive issued in improper state",		/* DL_OUTSTATE 0x03 */
	NULL,						/* DL_SYSERR  0x04 */
	"sequence number not from outstanding DL_CONN_IND",
							/* DL_BADCORR 0x05 */
	"user data exceeded provider limit",		/* DL_BADDATA 0x06 */
	"requested service not supplied by provider",
						/* DL_UNSUPPORTED 0x07 */
	"specified PPA was invalid", 			/* DL_BADPPA 0x08 */
	"primitive received not known by provider",	/* DL_BADPRIM 0x09 */
	"QoS parameters contained invalid values",
						/* DL_BADQOSPARAM 0x0a */
	"QoS structure type is unknown/unsupported",	/* DL_BADQOSTYPE 0x0b */
	"token used not an active stream", 		/* DL_BADTOKEN 0x0c */
	"attempted second bind with dl_max_conind",	/* DL_BOUND 0x0d */
	"physical link initialization failed",		/* DL_INITFAILED 0x0e */
	"provider couldn't allocate alternate address",	/* DL_NOADDR 0x0f */
	"physical link not initialized",		/* DL_NOTINIT 0x10 */
	"previous data unit could not be delivered",
						/* DL_UNDELIVERABLE 0x11 */
	"primitive is known but unsupported",
						/* DL_NOTSUPPORTED 0x12 */
	"limit exceeded",				/* DL_TOOMANY 0x13 */
	"promiscuous mode not enabled",			/* DL_NOTENAB 0x14 */
	"other streams for PPA in post-attached",	/* DL_BUSY 0x15 */
	"automatic handling XID&TEST unsupported",	/* DL_NOAUTO 0x16 */
	"automatic handling of XID unsupported",	/* DL_NOXIDAUTO 0x17 */
	"automatic handling of TEST unsupported",	/* DL_NOTESTAUTO 0x18 */
	"automatic handling of XID response",		/* DL_XIDAUTO 0x19 */
	"automatic handling of TEST response", 		/* DL_TESTAUTO 0x1a */
	"pending outstanding connect indications"	/* DL_PENDING 0x1b */
};

/*
 * libdlpi error codes.
 */
static const char *libdlpi_errlist[] = {
	"DLPI operation succeeded",		/* DLPI_SUCCESS */
	"invalid argument",			/* DLPI_EINVAL */
	"invalid DLPI linkname",		/* DLPI_ELINKNAMEINVAL */
	"DLPI link does not exist",		/* DLPI_ENOLINK */
	"bad DLPI link",			/* DLPI_EBADLINK */
	"invalid DLPI handle",			/* DLPI_EINHANDLE */
	"DLPI operation timed out",		/* DLPI_ETIMEDOUT */
	"unsupported DLPI version",		/* DLPI_EVERNOTSUP */
	"unsupported DLPI connection mode",	/* DLPI_EMODENOTSUP */
	"unavailable DLPI SAP",			/* DLPI_EUNAVAILSAP */
	"DLPI operation failed",		/* DLPI_FAILURE */
	"DLPI style-2 node reports style-1",	/* DLPI_ENOTSTYLE2 */
	"bad DLPI message",			/* DLPI_EBADMSG */
	"DLPI raw mode not supported",		/* DLPI_ERAWNOTSUP */
	"DLPI notification not supported by link",
						/* DLPI_ENOTENOTSUP */
	"invalid DLPI notification type",	/* DLPI_ENOTEINVAL */
	"invalid DLPI notification id",		/* DLPI_ENOTEIDINVAL */
	"DLPI_IPNETINFO not supported"		/* DLPI_EIPNETINFONOTSUP */
};

const char *
dlpi_strerror(int err)
{
	if (err == DL_SYSERR)
		return (strerror(errno));
	else if (err >= 0 && err < NELEMS(dlpi_errlist))
		return (dgettext(TEXT_DOMAIN, dlpi_errlist[err]));
	else if (err >= DLPI_SUCCESS && err < DLPI_ERRMAX)
		return (dgettext(TEXT_DOMAIN, libdlpi_errlist[err -
		    DLPI_SUCCESS]));
	else
		return (dgettext(TEXT_DOMAIN, "Unknown DLPI error"));
}

/*
 * Each table entry comprises a DLPI/Private mactype and the description.
 */
static const dlpi_mactype_t dlpi_mactypes[] = {
	{ DL_CSMACD,		"CSMA/CD"		},
	{ DL_TPB,		"Token Bus"		},
	{ DL_TPR,		"Token Ring"		},
	{ DL_METRO,		"Metro Net"		},
	{ DL_ETHER,		"Ethernet"		},
	{ DL_HDLC,		"HDLC"			},
	{ DL_CHAR,		"Sync Character"	},
	{ DL_CTCA,		"CTCA"			},
	{ DL_FDDI,		"FDDI"			},
	{ DL_FRAME,		"Frame Relay (LAPF)"	},
	{ DL_MPFRAME,		"MP Frame Relay"	},
	{ DL_ASYNC,		"Async Character"	},
	{ DL_IPX25,		"X.25 (Classic IP)"	},
	{ DL_LOOP,		"Software Loopback"	},
	{ DL_FC,		"Fiber Channel"		},
	{ DL_ATM,		"ATM"			},
	{ DL_IPATM,		"ATM (Classic IP)"	},
	{ DL_X25,		"X.25 (LAPB)"		},
	{ DL_ISDN,		"ISDN"			},
	{ DL_HIPPI,		"HIPPI"			},
	{ DL_100VG,		"100BaseVG Ethernet"	},
	{ DL_100VGTPR,		"100BaseVG Token Ring"	},
	{ DL_ETH_CSMA,		"Ethernet/IEEE 802.3"	},
	{ DL_100BT,		"100BaseT"		},
	{ DL_IB,		"Infiniband"		},
	{ DL_IPV4,		"IPv4 Tunnel"		},
	{ DL_IPV6,		"IPv6 Tunnel"		},
	{ DL_WIFI,		"IEEE 802.11"		},
	{ DL_IPNET,		"IPNET"			}
};

const char *
dlpi_mactype(uint_t mactype)
{
	int i;

	for (i = 0; i < NELEMS(dlpi_mactypes); i++) {
		if (dlpi_mactypes[i].dm_mactype == mactype)
			return (dlpi_mactypes[i].dm_desc);
	}

	return ("Unknown MAC Type");
}

/*
 * Each table entry comprises a DLPI primitive and the maximum buffer
 * size needed, in bytes, for the DLPI message (see <sys/dlpi.h> for details).
 */
static const dlpi_primsz_t dlpi_primsizes[] = {
{ DL_INFO_REQ,		DL_INFO_REQ_SIZE				},
{ DL_INFO_ACK,		DL_INFO_ACK_SIZE + (2 * DLPI_PHYSADDR_MAX) +
			DLPI_SAPLEN_MAX + (2 * sizeof (union DL_qos_types))},
{ DL_ATTACH_REQ,	DL_ATTACH_REQ_SIZE				},
{ DL_BIND_REQ,		DL_BIND_REQ_SIZE				},
{ DL_BIND_ACK, 		DL_BIND_ACK_SIZE + DLPI_PHYSADDR_MAX +
			DLPI_SAPLEN_MAX					},
{ DL_UNBIND_REQ, 	DL_UNBIND_REQ_SIZE				},
{ DL_ENABMULTI_REQ, 	DL_ENABMULTI_REQ_SIZE + DLPI_PHYSADDR_MAX	},
{ DL_DISABMULTI_REQ, 	DL_DISABMULTI_REQ_SIZE + DLPI_PHYSADDR_MAX	},
{ DL_PROMISCON_REQ, 	DL_PROMISCON_REQ_SIZE				},
{ DL_PROMISCOFF_REQ,	DL_PROMISCOFF_REQ_SIZE				},
{ DL_PASSIVE_REQ, 	DL_PASSIVE_REQ_SIZE				},
{ DL_UNITDATA_REQ, 	DL_UNITDATA_REQ_SIZE + DLPI_PHYSADDR_MAX +
			DLPI_SAPLEN_MAX					},
{ DL_UNITDATA_IND, 	DL_UNITDATA_IND_SIZE + (2 * (DLPI_PHYSADDR_MAX +
			DLPI_SAPLEN_MAX))				},
{ DL_PHYS_ADDR_REQ, 	DL_PHYS_ADDR_REQ_SIZE				},
{ DL_PHYS_ADDR_ACK, 	DL_PHYS_ADDR_ACK_SIZE + DLPI_PHYSADDR_MAX	},
{ DL_SET_PHYS_ADDR_REQ, DL_SET_PHYS_ADDR_REQ_SIZE + DLPI_PHYSADDR_MAX	},
{ DL_OK_ACK,		MAX(DL_ERROR_ACK_SIZE, DL_OK_ACK_SIZE)		},
{ DL_NOTIFY_REQ,	DL_NOTIFY_REQ_SIZE				},
{ DL_NOTIFY_ACK,	MAX(DL_ERROR_ACK_SIZE, DL_NOTIFY_ACK_SIZE)	},
{ DL_NOTIFY_IND,	DL_NOTIFY_IND_SIZE + DLPI_PHYSADDR_MAX +
			DLPI_SAPLEN_MAX					}
};

/*
 * Refers to the dlpi_primsizes[] table to return corresponding maximum
 * buffer size.
 */
static size_t
i_dlpi_getprimsize(t_uscalar_t prim)
{
	int	i;

	for (i = 0; i < NELEMS(dlpi_primsizes); i++) {
		if (dlpi_primsizes[i].dp_prim == prim)
			return (dlpi_primsizes[i].dp_primsz);
	}

	return (sizeof (t_uscalar_t));
}

/*
 * sap values vary in length and are in host byte order, build sap value
 * by writing saplen bytes, so that the sap value is left aligned.
 */
static uint_t
i_dlpi_buildsap(uint8_t *sapp, uint_t saplen)
{
	int i;
	uint_t sap = 0;

#ifdef _LITTLE_ENDIAN
	for (i = saplen - 1; i >= 0; i--) {
#else
	for (i = 0; i < saplen; i++) {
#endif
		sap <<= 8;
		sap |= sapp[i];
	}

	return (sap);
}

/*
 * Copy sap value to a buffer in host byte order. saplen is the number of
 * bytes to copy.
 */
static void
i_dlpi_writesap(void *dstbuf, uint_t sap, uint_t saplen)
{
	uint8_t *sapp;

#ifdef _LITTLE_ENDIAN
	sapp = (uint8_t *)&sap;
#else
	sapp = (uint8_t *)&sap + (sizeof (sap) - saplen);
#endif

	(void) memcpy(dstbuf, sapp, saplen);
}

/*
 * Fill notification payload and callback each registered functions.
 * Delete nodes if any was called while processing.
 */
static int
i_dlpi_notifyind_process(dlpi_impl_t *dip, dl_notify_ind_t *dlnotifyindp)
{
	dlpi_notifyinfo_t	notifinfo;
	t_uscalar_t		dataoff, datalen;
	caddr_t			datap;
	dlpi_notifyent_t	*dnp;
	uint_t			note = dlnotifyindp->dl_notification;
	uint_t			deletenode = B_FALSE;

	notifinfo.dni_note = note;

	switch (note) {
	case DL_NOTE_SPEED:
		notifinfo.dni_speed = dlnotifyindp->dl_data;
		break;
	case DL_NOTE_SDU_SIZE:
		notifinfo.dni_size = dlnotifyindp->dl_data;
		break;
	case DL_NOTE_PHYS_ADDR:
		/*
		 * libdlpi currently only supports notifications for
		 * DL_CURR_PHYS_ADDR.
		 */
		if (dlnotifyindp->dl_data != DL_CURR_PHYS_ADDR)
			return (DLPI_ENOTENOTSUP);

		dataoff = dlnotifyindp->dl_addr_offset;
		datalen = dlnotifyindp->dl_addr_length;

		if (dataoff == 0 || datalen == 0)
			return (DLPI_EBADMSG);

		datap = (caddr_t)dlnotifyindp + dataoff;
		if (dataoff < DL_NOTIFY_IND_SIZE)
			return (DLPI_EBADMSG);

		notifinfo.dni_physaddrlen = datalen - dip->dli_saplen;

		if (notifinfo.dni_physaddrlen > DLPI_PHYSADDR_MAX)
			return (DL_BADADDR);

		(void) memcpy(notifinfo.dni_physaddr, datap,
		    notifinfo.dni_physaddrlen);
		break;
	}

	dip->dli_note_processing = B_TRUE;

	for (dnp = dip->dli_notifylistp; dnp != NULL; dnp = dnp->dln_next) {
		if (note & dnp->dln_notes)
			dnp->dln_fnp((dlpi_handle_t)dip, &notifinfo, dnp->arg);
		if (dnp->dln_rm)
			deletenode = B_TRUE;
	}

	dip->dli_note_processing = B_FALSE;

	/* Walk the notifyentry list to unregister marked entries. */
	if (deletenode)
		i_dlpi_deletenotifyid(dip);

	return (DLPI_SUCCESS);
}
/*
 * Find registered notification.
 */
static boolean_t
i_dlpi_notifyidexists(dlpi_impl_t *dip, dlpi_notifyent_t *id)
{
	dlpi_notifyent_t	*dnp;

	for (dnp = dip->dli_notifylistp; dnp != NULL; dnp = dnp->dln_next) {
		if (id == dnp)
			return (B_TRUE);
	}

	return (B_FALSE);
}

/*
 * Walk the list of notifications and deleted nodes marked to be deleted.
 */
static void
i_dlpi_deletenotifyid(dlpi_impl_t *dip)
{
	dlpi_notifyent_t	 *prev, *dnp;

	prev = NULL;
	dnp = dip->dli_notifylistp;
	while (dnp != NULL) {
		if (!dnp->dln_rm) {
			prev = dnp;
			dnp = dnp->dln_next;
		} else if (prev == NULL) {
			dip->dli_notifylistp = dnp->dln_next;
			free(dnp);
			dnp = dip->dli_notifylistp;
		} else {
			prev->dln_next = dnp->dln_next;
			free(dnp);
			dnp = prev->dln_next;
		}
	}
}