diff options
author | ss150715 <none@none> | 2007-02-09 16:50:26 -0800 |
---|---|---|
committer | ss150715 <none@none> | 2007-02-09 16:50:26 -0800 |
commit | c7e4935f5b755b4bbeaec416f1ad24337aeac7a4 (patch) | |
tree | b0f2f912e4898d18e49625b694ac3fee2ec32a85 /usr/src/lib/libdlpi/common/libdlpi.c | |
parent | 72b9fce97841381c0dae054e0dddd25d76672405 (diff) | |
download | illumos-joyent-c7e4935f5b755b4bbeaec416f1ad24337aeac7a4.tar.gz |
PSARC/2006/436 Public DLPI Library
PSARC/2006/634 Public DLPI Library Addendum
6266613 libdlpi should provide a default timeout
6336199 snoop should be ported to libdlpi
6340802 libdlpi needs a more robust mechanism of handling expected messages
6512059 public libdlpi library needed
--HG--
rename : usr/src/cmd/cmd-inet/usr.sbin/snoop/dlprims.c => deleted_files/usr/src/cmd/cmd-inet/usr.sbin/snoop/dlprims.c
Diffstat (limited to 'usr/src/lib/libdlpi/common/libdlpi.c')
-rw-r--r-- | usr/src/lib/libdlpi/common/libdlpi.c | 2242 |
1 files changed, 1182 insertions, 1060 deletions
diff --git a/usr/src/lib/libdlpi/common/libdlpi.c b/usr/src/lib/libdlpi/common/libdlpi.c index fc6dbe0f83..42ac808382 100644 --- a/usr/src/lib/libdlpi/common/libdlpi.c +++ b/usr/src/lib/libdlpi/common/libdlpi.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -28,7 +28,6 @@ /* * Data-Link Provider Interface (Version 2) */ - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -40,1285 +39,1408 @@ #include <stropts.h> #include <sys/dlpi.h> #include <errno.h> +#include <alloca.h> #include <sys/sysmacros.h> #include <ctype.h> #include <libdlpi.h> -#include <libdladm.h> - -typedef enum dlpi_multi_op { - DLPI_MULTI_DISABLE = 0, - DLPI_MULTI_ENABLE -} dlpi_multi_op_t; - -typedef enum dlpi_promisc_op { - DLPI_PROMISC_OFF = 0, - DLPI_PROMISC_ON -} dlpi_promisc_op_t; - -const char *i_dlpi_mac_type[] = { - "CSMA/CD", /* 0x00 */ - "Token Bus", /* 0x01 */ - "Token Ring", /* 0x02 */ - "Metro Net", /* 0x03 */ - "Ethernet", /* 0x04 */ - "HDLC", /* 0x05 */ - "Sync Character", /* 0x06 */ - "CTCA", /* 0x07 */ - "FDDI", /* 0x08 */ - "unknown", /* 0x09 */ - "Frame Relay (LAPF)", /* 0x0a */ - "MP Frame Relay", /* 0x0b */ - "Async Character", /* 0x0c */ - "X.25 (Classic IP)", /* 0x0d */ - "Software Loopback", /* 0x0e */ - "undefined", /* 0x0f */ - "Fiber Channel", /* 0x10 */ - "ATM", /* 0x11 */ - "ATM (Classic IP)", /* 0x12 */ - "X.25 (LAPB)", /* 0x13 */ - "ISDN", /* 0x14 */ - "HIPPI", /* 0x15 */ - "100BaseVG Ethernet", /* 0x16 */ - "100BaseVG Token Ring", /* 0x17 */ - "Ethernet/IEEE 802.3", /* 0x18 */ - "100BaseT", /* 0x19 */ - "Infiniband" /* 0x1a */ -}; - -static int i_dlpi_ifrm_num(char *, unsigned int *); - -const char * -dlpi_mac_type(uint_t type) -{ - if (type >= sizeof (i_dlpi_mac_type) / sizeof (i_dlpi_mac_type[0])) - return ("ERROR"); - - return (i_dlpi_mac_type[type]); -} - -static int -strputmsg(int fd, uint8_t *ctl_buf, size_t ctl_len, int flags) -{ - struct strbuf ctl; - - ctl.buf = (char *)ctl_buf; - ctl.len = ctl_len; - - return (putmsg(fd, &ctl, NULL, flags)); -} +#include <libintl.h> +#include <libinetutil.h> + +#include "libdlpi_impl.h" + +static int i_dlpi_open(const char *, int *, uint_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_remove_ppa(char *); +static int i_dlpi_attach(dlpi_impl_t *); +static void i_dlpi_passive(dlpi_impl_t *); + +static int i_dlpi_strputmsg(int, const dlpi_msg_t *, const void *, size_t, int); +static int i_dlpi_strgetmsg(int, 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 -strgetmsg(int fd, int timeout, char *ctl_buf, - size_t *ctl_lenp, char *data_buf, size_t *data_lenp) +int +dlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags) { - struct strbuf ctl; - struct strbuf data; - int res; - struct pollfd pfd; - int flags = 0; - - pfd.fd = fd; - pfd.events = POLLIN | POLLPRI; - - switch (poll(&pfd, 1, timeout)) { - default: - ctl.buf = ctl_buf; - ctl.len = 0; - ctl.maxlen = (ctl_lenp != NULL) ? *ctl_lenp : 0; + int retval; + int cnt; + ifspec_t ifsp; + dlpi_impl_t *dip; - data.buf = data_buf; - data.len = 0; - data.maxlen = (data_lenp != NULL) ? *data_lenp : 0; + /* + * 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); + + /* 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_mod_cnt = ifsp.ifsp_modcnt; + dip->dli_oflags = flags; + + for (cnt = 0; cnt != dip->dli_mod_cnt; cnt++) { + (void) strlcpy(dip->dli_modlist[cnt], ifsp.ifsp_mods[cnt], + DLPI_LINKNAME_MAX); + } - if ((res = getmsg(fd, &ctl, &data, &flags)) < 0) - goto failed; + /* 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); + } - if (ctl_buf != NULL) { - if (res & MORECTL) { - errno = E2BIG; - goto failed; - } + /* Copy provider name. */ + (void) strlcpy(dip->dli_provider, ifsp.ifsp_devnm, + sizeof (dip->dli_provider)); - *ctl_lenp = ctl.len; + /* + * 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); } - if (data_buf != NULL) { - if (res & MOREDATA) { - errno = E2BIG; - goto failed; - } + *dhp = (dlpi_handle_t)dip; + return (retval); + } - *data_lenp = data.len; + if (i_dlpi_style1_open(dip) != DLPI_SUCCESS) { + if ((retval = i_dlpi_style2_open(dip)) != DLPI_SUCCESS) { + free(dip); + return (retval); } - - break; - case 0: - errno = ETIME; - /*FALLTHRU*/ - case -1: - goto failed; } - return (0); -failed: - return (-1); -} - -int -dlpi_open(const char *provider) -{ - char devname[MAXPATHLEN]; - char path[MAXPATHLEN]; - int fd; - struct stat st; + if (dip->dli_oflags & DLPI_PASSIVE) + i_dlpi_passive(dip); - (void) snprintf(devname, MAXPATHLEN, "/dev/%s", provider); - - if ((fd = open(devname, O_RDWR)) != -1) - return (fd); - - (void) snprintf(path, MAXPATHLEN, "/devices/pseudo/clone@0:%s", - provider); + if ((dip->dli_oflags & DLPI_RAW) && + ioctl(dip->dli_fd, DLIOCRAW, 0) < 0) { + dlpi_close((dlpi_handle_t)dip); + return (DLPI_ERAWNOTSUP); + } - if (stat(path, &st) == 0) { - (void) strlcpy(devname, path, sizeof (devname)); - if ((fd = open(devname, O_RDWR)) != -1) - return (fd); + /* + * 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; } - return (-1); + *dhp = (dlpi_handle_t)dip; + return (DLPI_SUCCESS); } -int -dlpi_close(int fd) +void +dlpi_close(dlpi_handle_t dh) { - return (close(fd)); + dlpi_impl_t *dip = (dlpi_impl_t *)dh; + + if (dip != NULL) { + (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(int fd, int timeout, dl_info_ack_t *ackp, - union DL_qos_types *selp, union DL_qos_types *rangep, - uint8_t *addrp, size_t *addrlenp, uint8_t *brdcst_addrp, - size_t *brdcst_addrlenp) +dlpi_info(dlpi_handle_t dh, dlpi_info_t *infop, uint_t opt) { - int rc = -1; - size_t size; - dl_info_ack_t *buf; - dl_info_req_t dlir; - dl_info_ack_t *dliap; - union DL_qos_types *uqtp; - - size = sizeof (dl_info_ack_t); /* DL_INFO_ACK */ - size += sizeof (union DL_qos_types); /* QoS selections */ - size += sizeof (union DL_qos_types); /* QoS ranges */ - size += MAXADDRLEN + MAXSAPLEN; /* DLSAP Address */ - size += MAXADDRLEN; /* Broadcast Address */ - - if ((buf = malloc(size)) == NULL) - return (-1); - - dlir.dl_primitive = DL_INFO_REQ; - - if (strputmsg(fd, (uint8_t *)&dlir, DL_INFO_REQ_SIZE, RS_HIPRI) == -1) - goto done; - - if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1) - goto done; - - if (size < DL_INFO_ACK_SIZE) { - errno = EBADMSG; - goto done; + 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); } - dliap = (dl_info_ack_t *)buf; - if (dliap->dl_primitive != DL_INFO_ACK || - dliap->dl_version != DL_VERSION_2) { - errno = EPROTO; - goto done; + /* 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); } - (void) memcpy(ackp, buf, DL_INFO_ACK_SIZE); - - if (dliap->dl_qos_offset != 0) { - if (dliap->dl_qos_length < sizeof (t_uscalar_t)) { - errno = EPROTO; - goto done; - } - - uqtp = (union DL_qos_types *) - ((uintptr_t)buf + dliap->dl_qos_offset); - if (uqtp->dl_qos_type != DL_QOS_CO_SEL1 && - uqtp->dl_qos_type != DL_QOS_CL_SEL1) { - errno = EPROTO; - goto done; - } - - if (selp != NULL) - (void) memcpy(selp, (char *)buf + dliap->dl_qos_offset, - dliap->dl_qos_length); - } - - if (dliap->dl_qos_range_offset != 0) { - if (dliap->dl_qos_range_length < sizeof (t_uscalar_t)) { - errno = EPROTO; - goto done; - } - - uqtp = (union DL_qos_types *) - ((uintptr_t)buf + dliap->dl_qos_range_offset); - if (uqtp->dl_qos_type != DL_QOS_CO_RANGE1 && - uqtp->dl_qos_type != DL_QOS_CL_RANGE1) { - errno = EPROTO; - goto done; - } - - if (rangep != NULL) - (void) memcpy(rangep, - (char *)buf + dliap->dl_qos_range_offset, - dliap->dl_qos_range_length); + /* 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); } - if (dliap->dl_addr_offset != 0) { - if (dliap->dl_addr_length == 0) { - errno = EPROTO; - goto done; - } - - if (addrlenp != NULL) - *addrlenp = dliap->dl_addr_length; - if (addrp != NULL) - (void) memcpy(addrp, - (char *)buf + dliap->dl_addr_offset, - dliap->dl_addr_length); + /* 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); } - if (dliap->dl_brdcst_addr_offset != 0) { - if (dliap->dl_brdcst_addr_length == 0) { - errno = EPROTO; - goto done; - } + 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; - if (brdcst_addrlenp != NULL) - *brdcst_addrlenp = dliap->dl_brdcst_addr_length; - if (brdcst_addrp != NULL) - (void) memcpy(brdcst_addrp, - (char *)buf + dliap->dl_brdcst_addr_offset, - dliap->dl_brdcst_addr_length); - } + /* Information retrieved from the handle. */ + (void) strlcpy(infop->di_linkname, dip->dli_linkname, + sizeof (infop->di_linkname)); + infop->di_timeout = dip->dli_timeout; - rc = 0; /* success */ -done: - free(buf); - return (rc); + return (DLPI_SUCCESS); } +/* + * This function parses 'linkname' and stores the 'provider' name and 'PPA'. + */ int -dlpi_attach(int fd, int timeout, uint_t ppa) +dlpi_parselink(const char *linkname, char *provider, uint_t *ppa) { - int rc = -1; - size_t size; - dl_attach_req_t dlar; - dl_error_ack_t *dleap; - union DL_primitives *buf; - union DL_primitives *udlp; + ifspec_t ifsp; - size = 0; - size = MAX(sizeof (dl_ok_ack_t), size); - size = MAX(sizeof (dl_error_ack_t), size); + if (linkname == NULL || !ifparse_ifspec(linkname, &ifsp)) + return (DLPI_ELINKNAMEINVAL); - if ((buf = malloc(size)) == NULL) - return (-1); + if (provider != NULL) + (void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX); - dlar.dl_primitive = DL_ATTACH_REQ; - dlar.dl_ppa = ppa; + if (ppa != NULL) + *ppa = ifsp.ifsp_ppa; - if (strputmsg(fd, (uint8_t *)&dlar, DL_ATTACH_REQ_SIZE, 0) == -1) - goto done; + 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 (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1) - goto done; + if (linkname == NULL || provlen == 0 || provlen >= DLPI_LINKNAME_MAX) + return (DLPI_ELINKNAMEINVAL); - if (size < sizeof (t_uscalar_t)) { - errno = EBADMSG; - goto done; + if (!isdigit(provider[provlen - 1])) { + (void) snprintf(linkname, DLPI_LINKNAME_MAX, "%s%d", provider, + ppa); + } else { + (void) strlcpy(linkname, provider, DLPI_LINKNAME_MAX); } - udlp = (union DL_primitives *)buf; - switch (udlp->dl_primitive) { - case DL_OK_ACK: - if (size < DL_OK_ACK_SIZE) { - errno = EBADMSG; - goto done; - } - break; + return (DLPI_SUCCESS); +} - case DL_ERROR_ACK: - if (size < DL_ERROR_ACK_SIZE) { - errno = EBADMSG; - goto done; - } +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; - dleap = (dl_error_ack_t *)buf; - switch (dleap->dl_errno) { - case DL_BADPPA: - errno = EINVAL; - break; + if (dip == NULL) + return (DLPI_EINHANDLE); - case DL_ACCESS: - errno = EPERM; - break; + DLPI_MSG_CREATE(req, DL_BIND_REQ); + DLPI_MSG_CREATE(ack, DL_BIND_ACK); + bindreqp = &(req.dlm_msg->bind_req); - case DL_SYSERR: - errno = dleap->dl_unix_errno; - break; + /* + * 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; - default: - errno = EPROTO; - break; - } + bindreqp->dl_service_mode = DL_CLDLS; + bindreqp->dl_conn_mgmt = 0; + bindreqp->dl_max_conind = 0; + bindreqp->dl_xidtest_flg = 0; - goto done; + retval = i_dlpi_msg_common(dip, &req, &ack, DL_BIND_ACK_SIZE, 0); + if (retval != DLPI_SUCCESS) + return (retval); - default: - errno = EBADMSG; - goto done; + 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); } - rc = 0; /* success */ -done: - free(buf); - return (rc); + dip->dli_sap = bindackp->dl_sap; /* save sap value in handle */ + return (DLPI_SUCCESS); } int -dlpi_detach(int fd, int timeout) +dlpi_unbind(dlpi_handle_t dh) { - int rc = -1; - size_t size; - dl_detach_req_t dldr; - dl_error_ack_t *dleap; - union DL_primitives *buf; - union DL_primitives *udlp; + dlpi_msg_t req, ack; + dlpi_impl_t *dip = (dlpi_impl_t *)dh; - size = 0; - size = MAX(sizeof (dl_ok_ack_t), size); - size = MAX(sizeof (dl_error_ack_t), size); + if (dip == NULL) + return (DLPI_EINHANDLE); - if ((buf = malloc(size)) == NULL) - return (-1); + DLPI_MSG_CREATE(req, DL_UNBIND_REQ); + DLPI_MSG_CREATE(ack, DL_OK_ACK); - dldr.dl_primitive = DL_DETACH_REQ; - - if (strputmsg(fd, (uint8_t *)&dldr, DL_DETACH_REQ_SIZE, 0) == -1) - goto done; - - if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1) - goto done; + return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0)); +} - if (size < sizeof (t_uscalar_t)) { - errno = EBADMSG; - goto done; - } +/* + * 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; - udlp = (union DL_primitives *)buf; - switch (udlp->dl_primitive) { - case DL_OK_ACK: - if (size < DL_OK_ACK_SIZE) { - errno = EBADMSG; - goto done; - } - break; + if (dip == NULL) + return (DLPI_EINHANDLE); - case DL_ERROR_ACK: - if (size < DL_ERROR_ACK_SIZE) { - errno = EBADMSG; - goto done; - } + if (addrlen > DLPI_PHYSADDR_MAX) + return (DLPI_EINVAL); - dleap = (dl_error_ack_t *)buf; - switch (dleap->dl_errno) { - case DL_SYSERR: - errno = dleap->dl_unix_errno; - break; + DLPI_MSG_CREATE(req, op); + DLPI_MSG_CREATE(ack, DL_OK_ACK); - default: - errno = EPROTO; - break; - } - goto done; + 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); - default: - errno = EBADMSG; - goto done; - } + return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0)); +} - rc = 0; /* success */ -done: - free(buf); - return (rc); +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_bind(int fd, int timeout, uint_t sap, uint16_t mode, - boolean_t conn_mgmt, uint32_t *max_conn_ind, - uint32_t *xid_test, uint8_t *addrp, size_t *addrlenp) +dlpi_disabmulti(dlpi_handle_t dh, const void *addrp, size_t addrlen) { - int rc = -1; - size_t size; - dl_bind_req_t dlbr; - dl_bind_ack_t *dlbap; - dl_error_ack_t *dleap; - union DL_primitives *buf; - union DL_primitives *udlp; - - size = 0; - size = MAX(sizeof (dl_bind_ack_t) + MAXADDRLEN + MAXSAPLEN, size); - size = MAX(sizeof (dl_error_ack_t), size); - - if ((buf = malloc(size)) == NULL) - return (-1); - - dlbr.dl_primitive = DL_BIND_REQ; - dlbr.dl_sap = sap; - dlbr.dl_service_mode = mode; - dlbr.dl_conn_mgmt = (conn_mgmt) ? 1 : 0; - dlbr.dl_max_conind = (max_conn_ind != NULL) ? *max_conn_ind : 0; - dlbr.dl_xidtest_flg = (xid_test != NULL) ? *xid_test : 0; - - if (strputmsg(fd, (uint8_t *)&dlbr, DL_BIND_REQ_SIZE, 0) == -1) - goto done; - - if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1) - goto done; - - if (size < sizeof (t_uscalar_t)) { - errno = EBADMSG; - goto done; - } + return (i_dlpi_multi(dh, DL_DISABMULTI_REQ, addrp, addrlen)); +} - udlp = (union DL_primitives *)buf; - switch (udlp->dl_primitive) { - case DL_BIND_ACK: - if (size < DL_BIND_ACK_SIZE) { - errno = EBADMSG; - goto done; - } +/* + * 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; - dlbap = (dl_bind_ack_t *)buf; - if (max_conn_ind != NULL) - *max_conn_ind = dlbap->dl_max_conind; - if (xid_test != NULL) - *xid_test = dlbap->dl_xidtest_flg; + if (dip == NULL) + return (DLPI_EINHANDLE); - if (dlbap->dl_addr_offset != 0) { - if (dlbap->dl_addr_length == 0) { - errno = EPROTO; - goto done; - } + DLPI_MSG_CREATE(req, op); + DLPI_MSG_CREATE(ack, DL_OK_ACK); - if (addrlenp != NULL) - *addrlenp = dlbap->dl_addr_length; - if (addrp != NULL) - (void) memcpy(addrp, - (char *)buf + dlbap->dl_addr_offset, - dlbap->dl_addr_length); - } + promiscreqp = &(req.dlm_msg->promiscon_req); + promiscreqp->dl_level = level; - break; + return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0)); +} - case DL_ERROR_ACK: - if (size < DL_ERROR_ACK_SIZE) { - errno = EBADMSG; - goto done; - } +int +dlpi_promiscon(dlpi_handle_t dh, uint_t level) +{ + return (i_dlpi_promisc(dh, DL_PROMISCON_REQ, level)); +} - dleap = (dl_error_ack_t *)buf; - switch (dleap->dl_errno) { - case DL_BADADDR: - errno = EINVAL; - break; - - case DL_INITFAILED: - case DL_NOTINIT: - errno = EIO; - break; - - case DL_ACCESS: - errno = EACCES; - break; - - case DL_NOADDR: - errno = EFAULT; - break; - - case DL_UNSUPPORTED: - case DL_NOAUTO: - case DL_NOXIDAUTO: - case DL_NOTESTAUTO: - errno = ENOTSUP; - break; - - case DL_SYSERR: - errno = dleap->dl_unix_errno; - break; - - default: - errno = EPROTO; - break; - } - goto done; +int +dlpi_promiscoff(dlpi_handle_t dh, uint_t level) +{ + return (i_dlpi_promisc(dh, DL_PROMISCOFF_REQ, level)); +} - default: - errno = EBADMSG; - goto done; +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; } - rc = 0; /* success */ -done: - free(buf); - return (rc); + return (DLPI_SUCCESS); } int -dlpi_unbind(int fd, int timeout) +dlpi_set_physaddr(dlpi_handle_t dh, uint_t type, const void *addrp, + size_t addrlen) { - int rc = -1; - size_t size; - dl_unbind_req_t dlubr; - dl_error_ack_t *dleap; - union DL_primitives *buf; - union DL_primitives *udlp; + dlpi_msg_t req, ack; + dl_set_phys_addr_req_t *setphysreqp; + dlpi_impl_t *dip = (dlpi_impl_t *)dh; - size = 0; - size = MAX(sizeof (dl_ok_ack_t), size); - size = MAX(sizeof (dl_error_ack_t), size); + if (dip == NULL) + return (DLPI_EINHANDLE); - if ((buf = malloc(size)) == NULL) - return (-1); + if (addrp == NULL || type != DL_CURR_PHYS_ADDR || + addrlen > DLPI_PHYSADDR_MAX) + return (DLPI_EINVAL); - dlubr.dl_primitive = DL_UNBIND_REQ; + DLPI_MSG_CREATE(req, DL_SET_PHYS_ADDR_REQ); + DLPI_MSG_CREATE(ack, DL_OK_ACK); - if (strputmsg(fd, (uint8_t *)&dlubr, DL_UNBIND_REQ_SIZE, 0) == -1) - goto done; + 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); - if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1) - goto done; + return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0)); +} - if (size < sizeof (t_uscalar_t)) { - errno = EBADMSG; - goto done; +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->dli_fd, NULL, msgbuf, msglen, 0)); + + if (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; } - udlp = (union DL_primitives *)buf; - switch (udlp->dl_primitive) { - case DL_OK_ACK: - if (size < DL_OK_ACK_SIZE) { - errno = EBADMSG; - goto done; - } - break; - - case DL_ERROR_ACK: - if (size < DL_ERROR_ACK_SIZE) { - errno = EBADMSG; - goto done; - } - - dleap = (dl_error_ack_t *)buf; - switch (dleap->dl_errno) { - case DL_SYSERR: - errno = dleap->dl_unix_errno; - break; + udatareqp->dl_dest_addr_length = daddrlen + dip->dli_saplen; + udatareqp->dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE; - default: - errno = EPROTO; - break; - } - goto done; - - default: - errno = EBADMSG; - goto done; + /* + * 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); } - rc = 0; /* success */ -done: - free(buf); - return (rc); + return (i_dlpi_strputmsg(dip->dli_fd, &req, msgbuf, msglen, 0)); } -static int -i_dlpi_multi(int fd, int timeout, dlpi_multi_op_t op, - uint8_t *addrp, size_t addr_length) +int +dlpi_recv(dlpi_handle_t dh, void *saddrp, size_t *saddrlenp, void *msgbuf, + size_t *msglenp, int msec, dlpi_recvinfo_t *recvp) { - int rc = -1; - size_t opsize; - size_t size; - dl_enabmulti_req_t *dlemrp; - dl_disabmulti_req_t *dldmrp; - dl_error_ack_t *dleap; - union DL_primitives *buf; - union DL_primitives *udlp; - - opsize = (op == DLPI_MULTI_ENABLE) ? sizeof (dl_enabmulti_req_t) : - sizeof (dl_disabmulti_req_t); - opsize += addr_length; - - size = 0; - size = MAX(opsize, size); - size = MAX(sizeof (dl_ok_ack_t), size); - size = MAX(sizeof (dl_error_ack_t), size); - - if ((buf = malloc(size)) == NULL) - return (-1); - - if (op == DLPI_MULTI_ENABLE) { - dlemrp = (dl_enabmulti_req_t *)buf; - dlemrp->dl_primitive = DL_ENABMULTI_REQ; - dlemrp->dl_addr_length = addr_length; - dlemrp->dl_addr_offset = sizeof (dl_enabmulti_req_t); - (void) memcpy(&dlemrp[1], addrp, addr_length); - } else { - dldmrp = (dl_disabmulti_req_t *)buf; - dldmrp->dl_primitive = DL_DISABMULTI_REQ; - dldmrp->dl_addr_length = addr_length; - dldmrp->dl_addr_offset = sizeof (dl_disabmulti_req_t); - (void) memcpy(&dldmrp[1], addrp, addr_length); - } + 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->dli_fd, msec, NULL, 0, 0, 0, + msgbuf, msglenp, &totmsglen); - if (strputmsg(fd, (uint8_t *)buf, opsize, 0) == -1) - goto done; + if (retval == DLPI_SUCCESS && recvp != NULL) + recvp->dri_totmsglen = totmsglen; + return (retval); + } - if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1) - goto done; + DLPI_MSG_CREATE(ind, DL_UNITDATA_IND); + udatap = &(ind.dlm_msg->unitdata_ind); + indendp = (caddr_t)ind.dlm_msg + ind.dlm_msgsz; - if (size < sizeof (t_uscalar_t)) { - errno = EBADMSG; - goto done; - } + if ((retval = i_dlpi_strgetmsg(dip->dli_fd, msec, &ind, + DL_UNITDATA_IND, DL_UNITDATA_IND, DL_UNITDATA_IND_SIZE, + msgbuf, msglenp, &totmsglen)) != DLPI_SUCCESS) + return (retval); - udlp = (union DL_primitives *)buf; - switch (udlp->dl_primitive) { - case DL_OK_ACK: - if (size < DL_OK_ACK_SIZE) { - errno = EBADMSG; - goto done; + /* + * 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; } - break; + } - case DL_ERROR_ACK: - if (size < DL_ERROR_ACK_SIZE) { - errno = EBADMSG; - goto done; + /* + * 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; } - dleap = (dl_error_ack_t *)buf; - switch (dleap->dl_errno) { - case DL_BADADDR: - errno = EINVAL; - break; - - case DL_TOOMANY: - errno = ENOSPC; - break; + recvp->dri_dstaddrtype = udatap->dl_group_address; + recvp->dri_totmsglen = totmsglen; + } - case DL_NOTSUPPORTED: - errno = ENOTSUP; - break; + return (DLPI_SUCCESS); +} - case DL_NOTENAB: - errno = EINVAL; - break; +int +dlpi_fd(dlpi_handle_t dh) +{ + dlpi_impl_t *dip = (dlpi_impl_t *)dh; - case DL_SYSERR: - errno = dleap->dl_unix_errno; - break; + return (dip != NULL ? dip->dli_fd : -1); +} - default: - errno = EPROTO; - break; - } - goto done; +int +dlpi_set_timeout(dlpi_handle_t dh, int sec) +{ + dlpi_impl_t *dip = (dlpi_impl_t *)dh; - default: - errno = EBADMSG; - goto done; - } + if (dip == NULL) + return (DLPI_EINHANDLE); - rc = 0; /* success */ -done: - free(buf); - return (rc); + dip->dli_timeout = sec; + return (DLPI_SUCCESS); } -int -dlpi_enabmulti(int fd, int timeout, uint8_t *addrp, - size_t addr_length) +const char * +dlpi_linkname(dlpi_handle_t dh) { - return (i_dlpi_multi(fd, timeout, DLPI_MULTI_ENABLE, addrp, - addr_length)); + dlpi_impl_t *dip = (dlpi_impl_t *)dh; + + return (dip != NULL ? dip->dli_linkname : NULL); } -int -dlpi_disabmulti(int fd, int timeout, uint8_t *addrp, - size_t addr_length) +/* + * 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) { - return (i_dlpi_multi(fd, timeout, DLPI_MULTI_DISABLE, addrp, - addr_length)); + dlpi_impl_t *dip = (dlpi_impl_t *)dh; + + return (dip->dli_style); } +/* + * 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_promisc(int fd, int timeout, dlpi_promisc_op_t op, - uint_t level) +i_dlpi_open(const char *provider, int *fd, uint_t flags) { - int rc = -1; - size_t opsize; - size_t size; - dl_promiscon_req_t *dlpnrp; - dl_promiscoff_req_t *dlpfrp; - dl_error_ack_t *dleap; - union DL_primitives *buf; - union DL_primitives *udlp; - - opsize = (op == DLPI_PROMISC_ON) ? sizeof (dl_promiscon_req_t) : - sizeof (dl_promiscoff_req_t); - - size = 0; - size = MAX(opsize, size); - size = MAX(sizeof (dl_ok_ack_t), size); - size = MAX(sizeof (dl_error_ack_t), size); - - if ((buf = malloc(size)) == NULL) - return (-1); - - if (op == DLPI_PROMISC_ON) { - dlpnrp = (dl_promiscon_req_t *)buf; - dlpnrp->dl_primitive = DL_PROMISCON_REQ; - dlpnrp->dl_level = level; - - if (strputmsg(fd, (uint8_t *)dlpnrp, opsize, 0) == -1) - goto done; - } else { - dlpfrp = (dl_promiscoff_req_t *)buf; - dlpfrp->dl_primitive = DL_PROMISCOFF_REQ; - dlpfrp->dl_level = level; - - if (strputmsg(fd, (uint8_t *)dlpfrp, opsize, 0) == -1) - goto done; - } + char path[MAXPATHLEN]; + int oflags; - if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1) - goto done; + oflags = O_RDWR; + if (flags & DLPI_EXCL) + oflags |= O_EXCL; - if (size < sizeof (t_uscalar_t)) { - errno = EBADMSG; - goto done; - } + (void) snprintf(path, sizeof (path), "/dev/%s", provider); - udlp = (union DL_primitives *)buf; - switch (udlp->dl_primitive) { - case DL_OK_ACK: - if (size < DL_OK_ACK_SIZE) { - errno = EBADMSG; - goto done; - } - break; + if ((*fd = open(path, oflags)) != -1) + return (DLPI_SUCCESS); - case DL_ERROR_ACK: - if (size < DL_ERROR_ACK_SIZE) { - errno = EBADMSG; - goto done; - } + /* + * 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); - dleap = (dl_error_ack_t *)buf; - switch (dleap->dl_errno) { - case DL_NOTSUPPORTED: - case DL_UNSUPPORTED: - errno = ENOTSUP; - break; + if ((*fd = open(path, oflags)) != -1) + return (DLPI_SUCCESS); - case DL_NOTENAB: - errno = EINVAL; - break; + return (errno == ENOENT ? DLPI_ENOLINK : DL_SYSERR); +} - case DL_SYSERR: - errno = dleap->dl_unix_errno; - break; +/* + * 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; - default: - errno = EPROTO; - break; - } - goto done; + /* + * 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); - default: - errno = EBADMSG; - goto done; + if (retval != DLPI_SUCCESS) { + dip->dli_mod_pushed = 0; + return (retval); } + dip->dli_fd = fd; - rc = 0; /* success */ -done: - free(buf); - return (rc); -} + /* + * 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); + } + } -int -dlpi_promiscon(int fd, int timeout, uint_t level) -{ - return (i_dlpi_promisc(fd, timeout, DLPI_PROMISC_ON, level)); -} + 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); + } -int -dlpi_promiscoff(int fd, int timeout, uint_t level) -{ - return (i_dlpi_promisc(fd, timeout, DLPI_PROMISC_OFF, level)); + return (DLPI_SUCCESS); } -int -dlpi_phys_addr(int fd, int timeout, uint_t type, uint8_t *addrp, - size_t *addrlenp) +/* + * Open a style 2 link. PPA must be explicitly attached. + */ +static int +i_dlpi_style2_open(dlpi_impl_t *dip) { - int rc = -1; - size_t size; - dl_phys_addr_req_t dlpar; - dl_phys_addr_ack_t *dlpaap; - dl_error_ack_t *dleap; - union DL_primitives *buf; - union DL_primitives *udlp; + int fd; + int retval, save_errno; - size = 0; - size = MAX(sizeof (dl_phys_addr_ack_t) + MAXADDRLEN, size); - size = MAX(sizeof (dl_error_ack_t), size); + /* + * 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); + } - if ((buf = malloc(size)) == NULL) - return (-1); + /* 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; + } + } - dlpar.dl_primitive = DL_PHYS_ADDR_REQ; - dlpar.dl_addr_type = type; + /* + * 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 (strputmsg(fd, (uint8_t *)&dlpar, DL_PHYS_ADDR_REQ_SIZE, 0) == -1) - goto done; + if ((retval = i_dlpi_checkstyle(dip, DL_STYLE2)) != DLPI_SUCCESS) + goto failure; - if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1) - goto done; + /* + * 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); - if (size < sizeof (t_uscalar_t)) { - errno = EBADMSG; - goto done; - } +attach: + if ((retval = i_dlpi_attach(dip)) != DLPI_SUCCESS) + goto failure; - udlp = (union DL_primitives *)buf; - switch (udlp->dl_primitive) { - case DL_PHYS_ADDR_ACK: - if (size < DL_PHYS_ADDR_ACK_SIZE) { - errno = EBADMSG; - goto done; - } + return (DLPI_SUCCESS); - dlpaap = (dl_phys_addr_ack_t *)buf; - if (dlpaap->dl_addr_offset != 0) { - if (dlpaap->dl_addr_length == 0) { - errno = EPROTO; - goto done; - } +failure: + save_errno = errno; + (void) close(dip->dli_fd); + errno = save_errno; + return (retval); +} - if (addrlenp != NULL) - *addrlenp = dlpaap->dl_addr_length; +/* + * 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; - if (addrp != NULL) - (void) memcpy(addrp, - (char *)buf + dlpaap->dl_addr_offset, - dlpaap->dl_addr_length); - } - break; + retval = dlpi_info((dlpi_handle_t)dip, &dlinfo, 0); + if (retval == DLPI_SUCCESS && dip->dli_style != style) + retval = DLPI_EBADLINK; - case DL_ERROR_ACK: - if (size < DL_ERROR_ACK_SIZE) { - errno = EBADMSG; - goto done; - } + return (retval); +} - dleap = (dl_error_ack_t *)buf; - switch (dleap->dl_errno) { - case DL_SYSERR: - errno = dleap->dl_unix_errno; - break; +/* + * 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; - default: - errno = EPROTO; - break; - } - goto done; + if (i == -1 || !isdigit(linkname[i--])) + return (DLPI_FAILURE); - default: - errno = EBADMSG; - goto done; - } + while (i >= 0 && isdigit(linkname[i])) + i--; - rc = 0; /* success */ -done: - free(buf); - return (rc); + linkname[i + 1] = '\0'; + return (DLPI_SUCCESS); } -int -dlpi_set_phys_addr(int fd, int timeout, uint8_t *addrp, - size_t addr_length) +/* + * For DLPI style 2 providers, an explicit attach of PPA is required. + */ +static int +i_dlpi_attach(dlpi_impl_t *dip) { - int rc = -1; - size_t opsize; - size_t size; - dl_set_phys_addr_req_t *dlspap; - dl_error_ack_t *dleap; - union DL_primitives *buf; - union DL_primitives *udlp; - - opsize = sizeof (dl_set_phys_addr_req_t) + addr_length; - - size = 0; - size = MAX(opsize, size); - size = MAX(sizeof (dl_ok_ack_t), size); - size = MAX(sizeof (dl_error_ack_t), size); - - if ((buf = malloc(size)) == NULL) - return (-1); - - dlspap = (dl_set_phys_addr_req_t *)buf; - dlspap->dl_primitive = DL_SET_PHYS_ADDR_REQ; - dlspap->dl_addr_length = addr_length; - dlspap->dl_addr_offset = sizeof (dl_set_phys_addr_req_t); - (void) memcpy(&dlspap[1], addrp, addr_length); - - if (strputmsg(fd, (uint8_t *)dlspap, opsize, 0) == -1) - goto done; - - if (strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL) == -1) - goto done; - - if (size < sizeof (t_uscalar_t)) { - errno = EBADMSG; - goto done; - } - - udlp = (union DL_primitives *)buf; - switch (udlp->dl_primitive) { - case DL_OK_ACK: - if (size < DL_OK_ACK_SIZE) { - errno = EBADMSG; - goto done; - } - break; + dlpi_msg_t req, ack; + dl_attach_req_t *attachreqp; - case DL_ERROR_ACK: - if (size < DL_ERROR_ACK_SIZE) { - errno = EBADMSG; - goto done; - } + /* + * 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); - dleap = (dl_error_ack_t *)buf; - switch (dleap->dl_errno) { - case DL_BADADDR: - errno = EINVAL; - break; + DLPI_MSG_CREATE(req, DL_ATTACH_REQ); + DLPI_MSG_CREATE(ack, DL_OK_ACK); - case DL_NOTSUPPORTED: - errno = ENOTSUP; - break; + attachreqp = &(req.dlm_msg->attach_req); + attachreqp->dl_ppa = dip->dli_ppa; - case DL_SYSERR: - errno = dleap->dl_unix_errno; - break; + return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0)); +} - default: - errno = EPROTO; - break; - } - goto done; +/* + * 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; - default: - errno = EBADMSG; - goto done; - } + DLPI_MSG_CREATE(req, DL_PASSIVE_REQ); + DLPI_MSG_CREATE(ack, DL_OK_ACK); - rc = 0; /* success */ -done: - free(buf); - return (rc); + (void) i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0); } -void -dlpi_passive(int fd, int timeout) +/* + * Send a dlpi control message and/or data message on a stream. The inputs + * for this function are: + * int fd: file descriptor of open stream to send message + * 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(int fd, const dlpi_msg_t *dlreqp, + const void *databuf, size_t datalen, int flags) { - size_t size; - dl_passive_req_t dlpr; - union DL_primitives *buf; + int retval; + struct strbuf ctl; + struct strbuf data; - size = MAX(sizeof (dl_ok_ack_t), sizeof (dl_error_ack_t)); + if (dlreqp != NULL) { + ctl.buf = (void *)dlreqp->dlm_msg; + ctl.len = dlreqp->dlm_msgsz; + } - if ((buf = malloc(size)) == NULL) - return; + data.buf = (void *)databuf; + data.len = datalen; - dlpr.dl_primitive = DL_PASSIVE_REQ; + retval = putmsg(fd, (dlreqp == NULL ? NULL: &ctl), + (databuf == NULL ? NULL : &data), flags); - /* - * We don't care about the outcome of this operation. We at least - * don't want to return until the operation completes or the - * timeout expires. - */ - if (strputmsg(fd, (uint8_t *)&dlpr, DL_PASSIVE_REQ_SIZE, 0) == 0) - (void) strgetmsg(fd, timeout, (char *)buf, &size, NULL, NULL); - free(buf); + 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: + * int fd: file descriptor of open stream + * 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. + */ static int -i_dlpi_style1_open(dlpi_if_attr_t *diap) +i_dlpi_strgetmsg(int fd, 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 fd; - int cnt; - dl_info_ack_t dlia; + int retval; + int flags = 0; + 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; + boolean_t infinite = (msec < 0); /* infinite timeout */ + + if ((dlreplyp == NULL && databuf == NULL) || + (databuf == NULL && datalenp != NULL) || + (databuf != NULL && datalenp == NULL)) + return (DLPI_EINVAL); - /* Open device */ - if ((fd = dlpi_open(diap->devname)) == -1) { - diap->style1_failed = B_TRUE; - diap->mod_pushed = 0; - return (-1); - } else { - diap->style1_fd = fd; - } + pfd.fd = fd; + pfd.events = POLLIN | POLLPRI; - /* - * Try to push modules (if any) onto the device stream - */ - for (cnt = 0; cnt < diap->mod_cnt; cnt++) { - if (ioctl(fd, I_PUSH, diap->modlist[cnt]) == -1) { - diap->mod_pushed = cnt+1; - return (-1); + 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: + break; + case 0: + return (DLPI_ETIMEDOUT); + case -1: + return (DL_SYSERR); } - } - if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL, NULL) == -1) - goto failed; + 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; + } + } + + /* + * 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 (dlia.dl_provider_style != DL_STYLE1) - goto failed; + if (!infinite) { + current = gethrtime() / (NANOSEC / MILLISEC); + msec -= (current - start); - diap->style = DL_STYLE1; - diap->style1_failed = B_FALSE; + if (msec <= 0) + return (DLPI_ETIMEDOUT); + } + } - return (fd); -failed: - diap->style1_failed = B_TRUE; - (void) dlpi_close(fd); - return (-1); + 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. Unexpected asynchronous messages (e.g. unexpected DL_NOTIFY_IND + * messages) will be discarded. + */ static int -i_dlpi_style2_open(dlpi_if_attr_t *diap) +i_dlpi_msg_common(dlpi_impl_t *dip, const dlpi_msg_t *dlreqp, + dlpi_msg_t *dlreplyp, size_t dlreplyminsz, int flags) { - int fd; - uint_t ppa; - dl_info_ack_t dlia; + int retval; + t_uscalar_t dlreqprim = dlreqp->dlm_msg->dl_primitive; + t_uscalar_t dlreplyprim = dlreplyp->dlm_msg->dl_primitive; - /* - * 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 - * strip off the ppa off the device name and open it as a style 2 - * device - * - * If the pushing of the last module failed, we need to strip off the - * ppa from that module and try pushing it as a style 2 module - * - * Otherwise we failed during the push of an intermediate module and - * must fail out and close the device. - * - * And if style1 did not fail (i.e. we called style2 open directly), - * just open the device - */ - if (diap->style1_failed) { - if (!diap->mod_pushed) { - if (i_dlpi_ifrm_num(diap->devname, &ppa) < 0) - return (-1); - if ((fd = dlpi_open(diap->devname)) == -1) - return (-1); - } else if (diap->mod_pushed == diap->mod_cnt) { - if (i_dlpi_ifrm_num( - diap->modlist[diap->mod_cnt - 1], &ppa) < 0) - return (-1); - diap->mod_pushed--; - fd = diap->style1_fd; - } else { - return (-1); - } - } else { - if ((fd = dlpi_open(diap->devname)) == -1) - return (-1); - } + /* Put the requested primitive on the stream. */ + retval = i_dlpi_strputmsg(dip->dli_fd, dlreqp, NULL, 0, flags); + if (retval != DLPI_SUCCESS) + return (retval); + + /* Retrieve acknowledged message for requested primitive. */ + retval = i_dlpi_strgetmsg(dip->dli_fd, (dip->dli_timeout * MILLISEC), + dlreplyp, dlreqprim, dlreplyprim, dlreplyminsz, NULL, NULL, NULL); + if (retval != DLPI_SUCCESS) + return (retval); /* - * Try and push modules (if any) onto the device stream + * If primitive is DL_ERROR_ACK, set errno. */ - for (; diap->mod_pushed < diap->mod_cnt; diap->mod_pushed++) { - if (ioctl(fd, I_PUSH, - diap->modlist[diap->mod_pushed]) == -1) - goto failed; + 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; } - if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL, - NULL) == -1) - goto failed; - - if (dlia.dl_provider_style != DL_STYLE2) - goto failed; + return (retval); +} - diap->style = DL_STYLE2; +/* + * 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 */ +}; - if (dlpi_attach(fd, -1, diap->ppa) < 0) - goto failed; +/* + * 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 */ +}; - return (fd); -failed: - (void) dlpi_close(fd); - return (-1); +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")); } -static int -i_dlpi_ifname_parse(const char *ifname, dlpi_if_attr_t *diap) +/* + * 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) { - char *modlist = NULL; /* list of modules to push */ - int cnt = 0; /* number of modules to push */ - char modbuf[LIFNAMSIZ + 32]; - char *nxtmod; - char *p; - int len; - - /* if lun is specified fail (backwards compat) */ - if (strchr(ifname, ':') != NULL) - return (-1); - - /* save copy of original device name */ - if (strlcpy(diap->ifname, ifname, sizeof (diap->ifname)) >= - sizeof (diap->ifname)) - return (-1); - - /* initialize ppa */ - diap->ppa = -1; - - /* get provider name and ppa from ifname */ - len = strlen(ifname); - for (p = (char *)ifname + len; --p != ifname; len--) { - if (!isdigit(*p)) { - (void) strlcpy(diap->provider, ifname, len + 1); - diap->ppa = atoi(p + 1); - break; - } - } + int i; - if (strlcpy(modbuf, diap->ifname, sizeof (modbuf)) >= - sizeof (modbuf)) - return (-1); - - /* parse '.' delimited module list */ - modlist = strchr(modbuf, '.'); - if (modlist != NULL) { - /* null-terminate interface name (device) */ - *modlist = '\0'; - modlist++; - while (modlist && cnt < MAX_MODS) { - if (*modlist == '\0') - return (-1); - - nxtmod = strchr(modlist, '.'); - if (nxtmod) { - *nxtmod = '\0'; - nxtmod++; - } - if (strlcpy(diap->modlist[cnt], modlist, - sizeof (diap->modlist[cnt])) >= - sizeof (diap->modlist[cnt])) - return (-1); - cnt++; - modlist = nxtmod; - } + for (i = 0; i < NELEMS(dlpi_mactypes); i++) { + if (dlpi_mactypes[i].dm_mactype == mactype) + return (dlpi_mactypes[i].dm_desc); } - diap->mod_cnt = cnt; - if (strlcpy(diap->devname, modbuf, sizeof (diap->devname)) >= - sizeof (diap->devname)) - return (-1); - - return (0); + return ("Unknown MAC Type"); } -int -dlpi_if_open(const char *ifname, dlpi_if_attr_t *diap, - boolean_t force_style2) -{ - int fd; - - if (i_dlpi_ifname_parse(ifname, diap) == -1) { - errno = EINVAL; - return (-1); - } +/* + * 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) } +}; - diap->style1_failed = B_TRUE; +/* + * Refers to the dlpi_primsizes[] table to return corresponding maximum + * buffer size. + */ +static size_t +i_dlpi_getprimsize(t_uscalar_t prim) +{ + int i; - if (!force_style2) { - if ((fd = i_dlpi_style1_open(diap)) != -1) - return (fd); + for (i = 0; i < NELEMS(dlpi_primsizes); i++) { + if (dlpi_primsizes[i].dp_prim == prim) + return (dlpi_primsizes[i].dp_primsz); } - if ((fd = i_dlpi_style2_open(diap)) == -1) - return (-1); - - return (fd); + return (sizeof (t_uscalar_t)); } -int -dlpi_if_parse(const char *ifname, char *provider, int *ppap) +/* + * 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) { - dlpi_if_attr_t diap; - - if (i_dlpi_ifname_parse(ifname, &diap) == -1) { - errno = EINVAL; - return (-1); + 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]; } - if (strlcpy(provider, diap.provider, LIFNAMSIZ) > LIFNAMSIZ) - return (-1); - - if (ppap != NULL) - *ppap = diap.ppa; - - return (0); + return (sap); } /* - * attempt to remove ppa from end of file name - * return -1 if none found - * return ppa if found and remove the ppa from the filename + * Copy sap value to a buffer in host byte order. saplen is the number of + * bytes to copy. */ -static int -i_dlpi_ifrm_num(char *fname, unsigned int *ppa) +static void +i_dlpi_writesap(void *dstbuf, uint_t sap, uint_t saplen) { - int i; - uint_t p = 0; - unsigned int m = 1; + uint8_t *sapp; - i = strlen(fname) - 1; - - while (i >= 0 && isdigit(fname[i])) { - p += (fname[i] - '0')*m; - m *= 10; - i--; - } - - if (m == 1) { - return (-1); - } +#ifdef _LITTLE_ENDIAN + sapp = (uint8_t *)&sap; +#else + sapp = (uint8_t *)&sap + (sizeof (sap) - saplen); +#endif - fname[i + 1] = '\0'; - *ppa = p; - return (0); + (void) memcpy(dstbuf, sapp, saplen); } |