libdlpi.c revision 3ab45760e29dbab3ec3197fc452899c4d4b1c4c4
0N/A/*
1472N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
0N/A * Common Development and Distribution License (the "License").
0N/A * You may not use this file except in compliance with the License.
0N/A *
0N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
0N/A * or http://www.opensolaris.org/os/licensing.
0N/A * See the License for the specific language governing permissions
0N/A * and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
0N/A * If applicable, add the following below this CDDL HEADER, with the
0N/A * fields enclosed by brackets "[]" replaced with your own identifying
0N/A * information: Portions Copyright [yyyy] [name of copyright owner]
0N/A *
1472N/A * CDDL HEADER END
1472N/A */
1472N/A/*
0N/A * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
0N/A * Use is subject to license terms.
0N/A */
0N/A
0N/A#pragma ident "%Z%%M% %I% %E% SMI"
0N/A
0N/A/*
0N/A * Data-Link Provider Interface (Version 2)
0N/A */
0N/A#include <stdio.h>
0N/A#include <stdlib.h>
0N/A#include <string.h>
0N/A#include <sys/types.h>
0N/A#include <sys/stat.h>
0N/A#include <fcntl.h>
0N/A#include <unistd.h>
0N/A#include <poll.h>
0N/A#include <stropts.h>
0N/A#include <sys/dlpi.h>
0N/A#include <errno.h>
0N/A#include <alloca.h>
0N/A#include <sys/sysmacros.h>
0N/A#include <ctype.h>
0N/A#include <net/if_types.h>
0N/A#include <netinet/arp.h>
1387N/A#include <libdlpi.h>
1387N/A#include <libintl.h>
0N/A#include <libinetutil.h>
0N/A
0N/A#include "libdlpi_impl.h"
0N/A
0N/Astatic int i_dlpi_open(const char *, int *, uint_t);
0N/Astatic int i_dlpi_style1_open(dlpi_impl_t *);
0N/Astatic int i_dlpi_style2_open(dlpi_impl_t *);
0N/Astatic int i_dlpi_checkstyle(dlpi_impl_t *, t_uscalar_t);
0N/Astatic int i_dlpi_remove_ppa(char *);
0N/Astatic int i_dlpi_attach(dlpi_impl_t *);
0N/Astatic void i_dlpi_passive(dlpi_impl_t *);
0N/A
0N/Astatic int i_dlpi_strputmsg(dlpi_impl_t *, const dlpi_msg_t *, const void *,
0N/A size_t, int);
0N/Astatic int i_dlpi_strgetmsg(dlpi_impl_t *, int, dlpi_msg_t *, t_uscalar_t,
0N/A t_uscalar_t, size_t, void *, size_t *, size_t *);
0N/Astatic int i_dlpi_msg_common(dlpi_impl_t *, const dlpi_msg_t *, dlpi_msg_t *,
0N/A size_t, int);
0N/A
0N/Astatic size_t i_dlpi_getprimsize(t_uscalar_t);
0N/Astatic int i_dlpi_multi(dlpi_handle_t, t_uscalar_t, const uint8_t *, size_t);
0N/Astatic int i_dlpi_promisc(dlpi_handle_t, t_uscalar_t, uint_t);
0N/Astatic uint_t i_dlpi_buildsap(uint8_t *, uint_t);
0N/Astatic void i_dlpi_writesap(void *, uint_t, uint_t);
0N/Astatic int i_dlpi_notifyind_process(dlpi_impl_t *, dl_notify_ind_t *);
0N/Astatic boolean_t i_dlpi_notifyidexists(dlpi_impl_t *, dlpi_notifyent_t *);
0N/Astatic void i_dlpi_deletenotifyid(dlpi_impl_t *);
0N/A
0N/Aint
0N/Adlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags)
0N/A{
0N/A int retval;
0N/A int cnt;
0N/A ifspec_t ifsp;
0N/A dlpi_impl_t *dip;
0N/A
0N/A /*
0N/A * Validate linkname, fail if logical unit number (lun) is specified,
0N/A * otherwise decompose the contents into ifsp.
0N/A */
0N/A if (linkname == NULL || (strchr(linkname, ':') != NULL) ||
0N/A !ifparse_ifspec(linkname, &ifsp))
0N/A return (DLPI_ELINKNAMEINVAL);
0N/A
0N/A /* Allocate a new dlpi_impl_t. */
0N/A if ((dip = calloc(1, sizeof (dlpi_impl_t))) == NULL)
0N/A return (DL_SYSERR);
0N/A
0N/A /* Fill in known/default libdlpi handle values. */
0N/A dip->dli_timeout = DLPI_DEF_TIMEOUT;
0N/A dip->dli_ppa = ifsp.ifsp_ppa;
0N/A dip->dli_mod_cnt = ifsp.ifsp_modcnt;
0N/A dip->dli_oflags = flags;
0N/A dip->dli_notifylistp = NULL;
0N/A dip->dli_note_processing = B_FALSE;
0N/A
0N/A for (cnt = 0; cnt != dip->dli_mod_cnt; cnt++) {
0N/A (void) strlcpy(dip->dli_modlist[cnt], ifsp.ifsp_mods[cnt],
0N/A DLPI_LINKNAME_MAX);
0N/A }
0N/A
0N/A /* Copy linkname provided to the function. */
0N/A if (strlcpy(dip->dli_linkname, linkname, sizeof (dip->dli_linkname)) >=
0N/A sizeof (dip->dli_linkname)) {
0N/A free(dip);
0N/A return (DLPI_ELINKNAMEINVAL);
0N/A }
0N/A
0N/A /* Copy provider name. */
0N/A (void) strlcpy(dip->dli_provider, ifsp.ifsp_devnm,
0N/A sizeof (dip->dli_provider));
0N/A
0N/A /*
0N/A * Special case: DLPI_SERIAL flag is set to indicate a synchronous
0N/A * serial line interface (see syncinit(1M), syncstat(1M),
0N/A * syncloop(1M)), which is not a DLPI link.
0N/A */
0N/A if (dip->dli_oflags & DLPI_SERIAL) {
0N/A if ((retval = i_dlpi_style2_open(dip)) != DLPI_SUCCESS) {
0N/A free(dip);
0N/A return (retval);
0N/A }
0N/A
0N/A *dhp = (dlpi_handle_t)dip;
0N/A return (retval);
0N/A }
0N/A
0N/A if (i_dlpi_style1_open(dip) != DLPI_SUCCESS) {
0N/A if ((retval = i_dlpi_style2_open(dip)) != DLPI_SUCCESS) {
0N/A free(dip);
0N/A return (retval);
0N/A }
0N/A }
0N/A
0N/A if (dip->dli_oflags & DLPI_PASSIVE)
0N/A i_dlpi_passive(dip);
0N/A
0N/A if ((dip->dli_oflags & DLPI_RAW) &&
0N/A ioctl(dip->dli_fd, DLIOCRAW, 0) < 0) {
0N/A dlpi_close((dlpi_handle_t)dip);
0N/A return (DLPI_ERAWNOTSUP);
0N/A }
0N/A
0N/A /*
0N/A * We intentionally do not care if this request fails, as this
0N/A * indicates the underlying DLPI device does not support Native mode
0N/A * (pre-GLDV3 device drivers).
0N/A */
0N/A if (dip->dli_oflags & DLPI_NATIVE) {
0N/A if ((retval = ioctl(dip->dli_fd, DLIOCNATIVE, 0)) > 0)
0N/A dip->dli_mactype = retval;
0N/A }
0N/A
0N/A *dhp = (dlpi_handle_t)dip;
0N/A return (DLPI_SUCCESS);
0N/A}
0N/A
0N/Avoid
0N/Adlpi_close(dlpi_handle_t dh)
0N/A{
0N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
0N/A dlpi_notifyent_t *next, *dnp;
0N/A
1387N/A if (dip != NULL) {
1387N/A for (dnp = dip->dli_notifylistp; dnp != NULL; dnp = next) {
1387N/A next = dnp->dln_next;
1387N/A free(dnp);
1387N/A }
1387N/A
1387N/A (void) close(dip->dli_fd);
1387N/A free(dip);
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * NOTE: The opt argument must be zero and is reserved for future use to extend
0N/A * fields to the dlpi_info_t structure (see dlpi_info(3DLPI)).
0N/A */
0N/Aint
0N/Adlpi_info(dlpi_handle_t dh, dlpi_info_t *infop, uint_t opt)
0N/A{
0N/A int retval;
0N/A dlpi_msg_t req, ack;
0N/A dl_info_ack_t *infoackp;
0N/A uint8_t *sapp, *addrp;
0N/A caddr_t ackendp, datap;
0N/A t_uscalar_t dataoff, datalen;
0N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
0N/A
0N/A if (dip == NULL)
0N/A return (DLPI_EINHANDLE);
0N/A
0N/A if (infop == NULL || opt != 0)
0N/A 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)
{
ifspec_t ifsp;
if (linkname == NULL || !ifparse_ifspec(linkname, &ifsp))
return (DLPI_ELINKNAMEINVAL);
if (provider != NULL)
(void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX);
if (ppa != NULL)
*ppa = ifsp.ifsp_ppa;
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 (dlinfo.di_state != DL_IDLE)
return (DL_OUTSTATE);
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 linkname under the following namespaces:
* - /dev
* - /devices
* If open doesn't succeed and link doesn't exist (ENOENT), this function
* returns DLPI_ENOLINK, otherwise returns DL_SYSERR.
*/
static int
i_dlpi_open(const char *provider, int *fd, uint_t flags)
{
char path[MAXPATHLEN];
int oflags;
oflags = O_RDWR;
if (flags & DLPI_EXCL)
oflags |= O_EXCL;
(void) snprintf(path, sizeof (path), "/dev/%s", provider);
if ((*fd = open(path, oflags)) != -1)
return (DLPI_SUCCESS);
/*
* On diskless boot, it's possible the /dev links have not yet
* been created; fallback to /devices. When /dev links are
* created on demand, this code can be removed.
*/
(void) snprintf(path, sizeof (path), "/devices/pseudo/clone@0:%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;
/*
* In order to support open of syntax like device[.module[.module...]]
* where modules need to be pushed onto the device stream, open only
* device name, otherwise open the full linkname.
*/
retval = i_dlpi_open((dip->dli_mod_cnt != 0) ? dip->dli_provider :
dip->dli_linkname, &fd, dip->dli_oflags);
if (retval != DLPI_SUCCESS) {
dip->dli_mod_pushed = 0;
return (retval);
}
dip->dli_fd = fd;
/*
* Try to push modules (if any) onto the device stream. If I_PUSH
* fails, we increment count of modules pushed (dli_mod_pushed)
* expecting it is last module to be pushed and thus will be pushed
* in i_dlpi_style2_open().
*/
for (dip->dli_mod_pushed = 0; dip->dli_mod_pushed < dip->dli_mod_cnt;
dip->dli_mod_pushed++) {
if (ioctl(fd, I_PUSH,
dip->dli_modlist[dip->dli_mod_pushed]) == -1) {
dip->dli_mod_pushed++;
return (DLPI_FAILURE);
}
}
if ((retval = i_dlpi_checkstyle(dip, DL_STYLE1)) != DLPI_SUCCESS) {
save_errno = errno;
(void) close(dip->dli_fd);
errno = save_errno;
dip->dli_mod_pushed = 0;
return (retval);
}
return (DLPI_SUCCESS);
}
/*
* 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;
/*
* If style 1 open failed, we need to determine how far it got and
* finish up the open() call as a style 2 open.
*
* If no modules were pushed (mod_pushed == 0), then we need to
* open it as a style 2 link.
*
* If the pushing of the last module failed, we need to
* try pushing it as a style 2 module. Decrement dli_mod_pushed
* count so it can be pushed onto the stream.
*
* Otherwise we failed during the push of an intermediate module and
* must fail out and close the link.
*/
if (dip->dli_mod_pushed == 0) {
if ((retval = i_dlpi_open(dip->dli_provider, &fd,
dip->dli_oflags)) != DLPI_SUCCESS)
return (retval);
dip->dli_fd = fd;
} else if (dip->dli_mod_pushed == dip->dli_mod_cnt) {
if (i_dlpi_remove_ppa(dip->dli_modlist[dip->dli_mod_cnt - 1])
!= DLPI_SUCCESS)
return (DLPI_ELINKNAMEINVAL);
dip->dli_mod_pushed--;
fd = dip->dli_fd;
} else {
return (DLPI_ELINKNAMEINVAL);
}
/* Try and push modules (if any) onto the device stream. */
for (; dip->dli_mod_pushed < dip->dli_mod_cnt; dip->dli_mod_pushed++) {
if (ioctl(fd, I_PUSH,
dip->dli_modlist[dip->dli_mod_pushed]) == -1) {
retval = DL_SYSERR;
goto failure;
}
}
/*
* 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)
goto failure;
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);
}
/*
* Remove PPA from end of linkname.
* Return DLPI_SUCCESS if found, else return DLPI_FAILURE.
*/
static int
i_dlpi_remove_ppa(char *linkname)
{
int i = strlen(linkname) - 1;
if (i == -1 || !isdigit(linkname[i--]))
return (DLPI_FAILURE);
while (i >= 0 && isdigit(linkname[i]))
i--;
linkname[i + 1] = '\0';
return (DLPI_SUCCESS);
}
/*
* 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 = 0;
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 */
if ((dlreplyp == NULL && databuf == NULL) ||
(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);
}
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 &&
dlreplyp->dlm_msg->dl_primitive == DL_NOTIFY_IND) {
if (ctl.len < DL_NOTIFY_IND_SIZE)
continue;
dlnotif = &(dlreplyp->dlm_msg->notify_ind);
(void) i_dlpi_notifyind_process(dip, dlnotif);
continue;
}
/*
* 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;
}
}
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 */
};
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" }
};
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:
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;
}
}
}