libdlpi.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Data-Link Provider Interface (Version 2)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <stropts.h>
#include <sys/dlpi.h>
#include <errno.h>
#include <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));
}
static int
strgetmsg(int fd, int timeout, char *ctl_buf,
size_t *ctl_lenp, char *data_buf, size_t *data_lenp)
{
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;
data.buf = data_buf;
data.len = 0;
data.maxlen = (data_lenp != NULL) ? *data_lenp : 0;
if ((res = getmsg(fd, &ctl, &data, &flags)) < 0)
goto failed;
if (ctl_buf != NULL) {
if (res & MORECTL) {
errno = E2BIG;
goto failed;
}
*ctl_lenp = ctl.len;
}
if (data_buf != NULL) {
if (res & MOREDATA) {
errno = E2BIG;
goto failed;
}
*data_lenp = data.len;
}
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;
(void) snprintf(devname, MAXPATHLEN, "/dev/%s", provider);
if ((fd = open(devname, O_RDWR)) != -1)
return (fd);
(void) snprintf(devname, MAXPATHLEN, "/devices/pseudo/dld@0:%s",
provider);
if ((fd = open(devname, O_RDWR)) != -1)
return (fd);
(void) snprintf(path, MAXPATHLEN, "/devices/pseudo/clone@0:%s",
provider);
if (stat(path, &st) == 0) {
(void) strlcpy(devname, path, sizeof (devname));
if ((fd = open(devname, O_RDWR)) != -1)
return (fd);
}
return (-1);
}
int
dlpi_close(int fd)
{
return (close(fd));
}
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)
{
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;
}
dliap = (dl_info_ack_t *)buf;
if (dliap->dl_primitive != DL_INFO_ACK ||
dliap->dl_version != DL_VERSION_2) {
errno = EPROTO;
goto done;
}
(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);
}
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);
}
if (dliap->dl_brdcst_addr_offset != 0) {
if (dliap->dl_brdcst_addr_length == 0) {
errno = EPROTO;
goto done;
}
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);
}
rc = 0; /* success */
done:
free(buf);
return (rc);
}
int
dlpi_attach(int fd, int timeout, 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;
size = 0;
size = MAX(sizeof (dl_ok_ack_t), size);
size = MAX(sizeof (dl_error_ack_t), size);
if ((buf = malloc(size)) == NULL)
return (-1);
dlar.dl_primitive = DL_ATTACH_REQ;
dlar.dl_ppa = ppa;
if (strputmsg(fd, (uint8_t *)&dlar, DL_ATTACH_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;
}
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_BADPPA:
errno = EINVAL;
break;
case DL_ACCESS:
errno = EPERM;
break;
case DL_SYSERR:
errno = dleap->dl_unix_errno;
break;
default:
errno = EPROTO;
break;
}
goto done;
default:
errno = EBADMSG;
goto done;
}
rc = 0; /* success */
done:
free(buf);
return (rc);
}
int
dlpi_detach(int fd, int timeout)
{
int rc = -1;
size_t size;
dl_detach_req_t dldr;
dl_error_ack_t *dleap;
union DL_primitives *buf;
union DL_primitives *udlp;
size = 0;
size = MAX(sizeof (dl_ok_ack_t), size);
size = MAX(sizeof (dl_error_ack_t), size);
if ((buf = malloc(size)) == NULL)
return (-1);
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;
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;
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;
default:
errno = EPROTO;
break;
}
goto done;
default:
errno = EBADMSG;
goto done;
}
rc = 0; /* success */
done:
free(buf);
return (rc);
}
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)
{
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;
}
udlp = (union DL_primitives *)buf;
switch (udlp->dl_primitive) {
case DL_BIND_ACK:
if (size < DL_BIND_ACK_SIZE) {
errno = EBADMSG;
goto done;
}
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 (dlbap->dl_addr_offset != 0) {
if (dlbap->dl_addr_length == 0) {
errno = EPROTO;
goto done;
}
if (addrlenp != NULL)
*addrlenp = dlbap->dl_addr_length;
if (addrp != NULL)
(void) memcpy(addrp,
(char *)buf + dlbap->dl_addr_offset,
dlbap->dl_addr_length);
}
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_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;
default:
errno = EBADMSG;
goto done;
}
rc = 0; /* success */
done:
free(buf);
return (rc);
}
int
dlpi_unbind(int fd, int timeout)
{
int rc = -1;
size_t size;
dl_unbind_req_t dlubr;
dl_error_ack_t *dleap;
union DL_primitives *buf;
union DL_primitives *udlp;
size = 0;
size = MAX(sizeof (dl_ok_ack_t), size);
size = MAX(sizeof (dl_error_ack_t), size);
if ((buf = malloc(size)) == NULL)
return (-1);
dlubr.dl_primitive = DL_UNBIND_REQ;
if (strputmsg(fd, (uint8_t *)&dlubr, DL_UNBIND_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;
}
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;
default:
errno = EPROTO;
break;
}
goto done;
default:
errno = EBADMSG;
goto done;
}
rc = 0; /* success */
done:
free(buf);
return (rc);
}
static int
i_dlpi_multi(int fd, int timeout, dlpi_multi_op_t op,
uint8_t *addrp, size_t addr_length)
{
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);
}
if (strputmsg(fd, (uint8_t *)buf, 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;
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_BADADDR:
errno = EINVAL;
break;
case DL_TOOMANY:
errno = ENOSPC;
break;
case DL_NOTSUPPORTED:
errno = ENOTSUP;
break;
case DL_NOTENAB:
errno = EINVAL;
break;
case DL_SYSERR:
errno = dleap->dl_unix_errno;
break;
default:
errno = EPROTO;
break;
}
goto done;
default:
errno = EBADMSG;
goto done;
}
rc = 0; /* success */
done:
free(buf);
return (rc);
}
int
dlpi_enabmulti(int fd, int timeout, uint8_t *addrp,
size_t addr_length)
{
return (i_dlpi_multi(fd, timeout, DLPI_MULTI_ENABLE, addrp,
addr_length));
}
int
dlpi_disabmulti(int fd, int timeout, uint8_t *addrp,
size_t addr_length)
{
return (i_dlpi_multi(fd, timeout, DLPI_MULTI_DISABLE, addrp,
addr_length));
}
static int
i_dlpi_promisc(int fd, int timeout, dlpi_promisc_op_t op,
uint_t level)
{
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;
}
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;
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_NOTSUPPORTED:
case DL_UNSUPPORTED:
errno = ENOTSUP;
break;
case DL_NOTENAB:
errno = EINVAL;
break;
case DL_SYSERR:
errno = dleap->dl_unix_errno;
break;
default:
errno = EPROTO;
break;
}
goto done;
default:
errno = EBADMSG;
goto done;
}
rc = 0; /* success */
done:
free(buf);
return (rc);
}
int
dlpi_promiscon(int fd, int timeout, uint_t level)
{
return (i_dlpi_promisc(fd, timeout, DLPI_PROMISC_ON, level));
}
int
dlpi_promiscoff(int fd, int timeout, uint_t level)
{
return (i_dlpi_promisc(fd, timeout, DLPI_PROMISC_OFF, level));
}
int
dlpi_phys_addr(int fd, int timeout, uint_t type, uint8_t *addrp,
size_t *addrlenp)
{
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;
size = 0;
size = MAX(sizeof (dl_phys_addr_ack_t) + MAXADDRLEN, size);
size = MAX(sizeof (dl_error_ack_t), size);
if ((buf = malloc(size)) == NULL)
return (-1);
dlpar.dl_primitive = DL_PHYS_ADDR_REQ;
dlpar.dl_addr_type = type;
if (strputmsg(fd, (uint8_t *)&dlpar, DL_PHYS_ADDR_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;
}
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;
}
dlpaap = (dl_phys_addr_ack_t *)buf;
if (dlpaap->dl_addr_offset != 0) {
if (dlpaap->dl_addr_length == 0) {
errno = EPROTO;
goto done;
}
if (addrlenp != NULL)
*addrlenp = dlpaap->dl_addr_length;
if (addrp != NULL)
(void) memcpy(addrp,
(char *)buf + dlpaap->dl_addr_offset,
dlpaap->dl_addr_length);
}
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;
default:
errno = EPROTO;
break;
}
goto done;
default:
errno = EBADMSG;
goto done;
}
rc = 0; /* success */
done:
free(buf);
return (rc);
}
int
dlpi_set_phys_addr(int fd, int timeout, uint8_t *addrp,
size_t addr_length)
{
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;
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_BADADDR:
errno = EINVAL;
break;
case DL_NOTSUPPORTED:
errno = ENOTSUP;
break;
case DL_SYSERR:
errno = dleap->dl_unix_errno;
break;
default:
errno = EPROTO;
break;
}
goto done;
default:
errno = EBADMSG;
goto done;
}
rc = 0; /* success */
done:
free(buf);
return (rc);
}
void
dlpi_passive(int fd, int timeout)
{
size_t size;
dl_passive_req_t dlpr;
union DL_primitives *buf;
size = MAX(sizeof (dl_ok_ack_t), sizeof (dl_error_ack_t));
if ((buf = malloc(size)) == NULL)
return;
dlpr.dl_primitive = DL_PASSIVE_REQ;
/*
* 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);
}
static int
i_dlpi_style1_open(dlpi_if_attr_t *diap)
{
int fd;
int cnt;
dl_info_ack_t dlia;
/* 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;
}
/*
* 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);
}
}
if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL, NULL) == -1)
goto failed;
if (dlia.dl_provider_style != DL_STYLE1)
goto failed;
diap->style = DL_STYLE1;
return (fd);
failed:
(void) dlpi_close(fd);
return (-1);
}
static int
i_dlpi_style2_open(dlpi_if_attr_t *diap)
{
int fd;
uint_t ppa;
dl_info_ack_t dlia;
/*
* 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);
}
/*
* Try and push modules (if any) onto the device stream
*/
for (; diap->mod_pushed < diap->mod_cnt; diap->mod_pushed++) {
if (ioctl(fd, I_PUSH,
diap->modlist[diap->mod_pushed]) == -1)
goto failed;
}
if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL,
NULL) == -1)
goto failed;
if (dlia.dl_provider_style != DL_STYLE2)
goto failed;
diap->style = DL_STYLE2;
if (dlpi_attach(fd, -1, diap->ppa) < 0)
goto failed;
return (fd);
failed:
(void) dlpi_close(fd);
return (-1);
}
static int
i_dlpi_ifname_parse(const char *ifname, dlpi_if_attr_t *diap)
{
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;
}
}
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;
}
}
diap->mod_cnt = cnt;
if (strlcpy(diap->devname, modbuf, sizeof (diap->devname)) >=
sizeof (diap->devname))
return (-1);
return (0);
}
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);
}
if (!force_style2) {
if ((fd = i_dlpi_style1_open(diap)) != -1)
return (fd);
}
if ((fd = i_dlpi_style2_open(diap)) == -1)
return (-1);
return (fd);
}
int
dlpi_if_parse(const char *ifname, char *provider, int *ppap)
{
dlpi_if_attr_t diap;
if (i_dlpi_ifname_parse(ifname, &diap) == -1) {
errno = EINVAL;
return (-1);
}
if (strlcpy(provider, diap.provider, LIFNAMSIZ) > LIFNAMSIZ)
return (-1);
if (ppap != NULL)
*ppap = diap.ppa;
return (0);
}
/*
* 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
*/
static int
i_dlpi_ifrm_num(char *fname, unsigned int *ppa)
{
int i;
uint_t p = 0;
unsigned int m = 1;
i = strlen(fname) - 1;
while (i >= 0 && isdigit(fname[i])) {
p += (fname[i] - '0')*m;
m *= 10;
i--;
}
if (m == 1) {
return (-1);
}
fname[i + 1] = '\0';
*ppa = p;
return (0);
}