2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Data-Link Provider Interface (Version 2)
2N/A */
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <fcntl.h>
2N/A#include <unistd.h>
2N/A#include <poll.h>
2N/A#include <stropts.h>
2N/A#include <sys/dlpi.h>
2N/A#include <errno.h>
2N/A#include <alloca.h>
2N/A#include <sys/sysmacros.h>
2N/A#include <ctype.h>
2N/A#include <net/if_types.h>
2N/A#include <netinet/arp.h>
2N/A#include <libdladm.h>
2N/A#include <libdllink.h>
2N/A#include <libdlpi.h>
2N/A#include <libintl.h>
2N/A#include <libinetutil.h>
2N/A#include <libnetcfg.h>
2N/A#include <dirent.h>
2N/A#include <zone.h>
2N/A
2N/A#include "libdlpi_impl.h"
2N/A
2N/Astatic int i_dlpi_open(const char *, int *, uint_t, boolean_t);
2N/Astatic int i_dlpi_style1_open(dlpi_impl_t *);
2N/Astatic int i_dlpi_style2_open(dlpi_impl_t *);
2N/Astatic int i_dlpi_checkstyle(dlpi_impl_t *, t_uscalar_t);
2N/Astatic int i_dlpi_attach(dlpi_impl_t *);
2N/Astatic void i_dlpi_passive(dlpi_impl_t *);
2N/A
2N/Astatic int i_dlpi_strputmsg(dlpi_impl_t *, const dlpi_msg_t *, const void *,
2N/A size_t, int);
2N/Astatic int i_dlpi_strgetmsg(dlpi_impl_t *, int, dlpi_msg_t *, t_uscalar_t,
2N/A t_uscalar_t, size_t, void *, size_t *, size_t *);
2N/Astatic int i_dlpi_msg_common(dlpi_impl_t *, const dlpi_msg_t *, dlpi_msg_t *,
2N/A size_t, int);
2N/A
2N/Astatic size_t i_dlpi_getprimsize(t_uscalar_t);
2N/Astatic int i_dlpi_multi(dlpi_handle_t, t_uscalar_t, const uint8_t *, size_t);
2N/Astatic int i_dlpi_promisc(dlpi_handle_t, t_uscalar_t, uint_t);
2N/Astatic uint_t i_dlpi_buildsap(uint8_t *, uint_t);
2N/Astatic void i_dlpi_writesap(void *, uint_t, uint_t);
2N/Astatic int i_dlpi_notifyind_process(dlpi_impl_t *, dl_notify_ind_t *);
2N/Astatic boolean_t i_dlpi_notifyidexists(dlpi_impl_t *, dlpi_notifyent_t *);
2N/Astatic void i_dlpi_deletenotifyid(dlpi_impl_t *);
2N/A
2N/Astatic int dlpi_parsezonelink(const char *, char *, uint_t, uint_t *);
2N/A
2N/Astruct i_dlpi_walklink_arg {
2N/A dlpi_walkfunc_t *fn;
2N/A void *arg;
2N/A};
2N/A
2N/Astatic int
2N/Ai_dlpi_walk_link(const char *name, void *arg)
2N/A{
2N/A struct i_dlpi_walklink_arg *warg = arg;
2N/A
2N/A return ((warg->fn(name, warg->arg)) ? DLADM_WALK_TERMINATE :
2N/A DLADM_WALK_CONTINUE);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Avoid
2N/Adlpi_walk(dlpi_walkfunc_t *fn, void *arg, uint_t flags)
2N/A{
2N/A struct i_dlpi_walklink_arg warg;
2N/A struct dirent *d;
2N/A DIR *dp;
2N/A dladm_handle_t handle;
2N/A
2N/A warg.fn = fn;
2N/A warg.arg = arg;
2N/A
2N/A if (flags & DLPI_DEVIPNET) {
2N/A if ((dp = opendir("/dev/ipnet")) == NULL)
2N/A return;
2N/A
2N/A while ((d = readdir(dp)) != NULL) {
2N/A if (d->d_name[0] == '.')
2N/A continue;
2N/A
2N/A if (warg.fn(d->d_name, warg.arg))
2N/A break;
2N/A }
2N/A
2N/A (void) closedir(dp);
2N/A } else {
2N/A /*
2N/A * Rather than have libdlpi take the libdladm handle,
2N/A * open the handle here.
2N/A */
2N/A if (dladm_open(&handle, NETADM_ACTIVE_PROFILE)
2N/A != DLADM_STATUS_OK)
2N/A return;
2N/A
2N/A (void) dladm_walk(i_dlpi_walk_link, handle, &warg,
2N/A DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
2N/A DLADM_OPT_ACTIVE);
2N/A
2N/A dladm_close(handle);
2N/A }
2N/A}
2N/A
2N/Aint
2N/Adlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags)
2N/A{
2N/A int retval, on = 1;
2N/A char provider[DLPI_LINKNAME_MAX];
2N/A uint_t ppa;
2N/A dlpi_impl_t *dip;
2N/A
2N/A /*
2N/A * Validate linkname and retrieve the DLPI provier name and PPA.
2N/A */
2N/A retval = dlpi_parsezonelink(linkname, provider,
2N/A sizeof (provider), &ppa);
2N/A if (retval != DLPI_SUCCESS)
2N/A return (retval);
2N/A
2N/A /*
2N/A * Ensure flags values are sane.
2N/A */
2N/A if ((flags & (DLPI_DEVIPNET|DLPI_DEVONLY)) ==
2N/A (DLPI_DEVIPNET|DLPI_DEVONLY))
2N/A return (DLPI_EINVAL);
2N/A
2N/A /* Allocate a new dlpi_impl_t. */
2N/A if ((dip = calloc(1, sizeof (dlpi_impl_t))) == NULL)
2N/A return (DL_SYSERR);
2N/A
2N/A /* Fill in known/default libdlpi handle values. */
2N/A dip->dli_timeout = DLPI_DEF_TIMEOUT;
2N/A dip->dli_ppa = ppa;
2N/A dip->dli_oflags = flags;
2N/A dip->dli_notifylistp = NULL;
2N/A dip->dli_note_processing = B_FALSE;
2N/A if (getenv("DLPI_DEVONLY") != NULL)
2N/A dip->dli_oflags |= DLPI_DEVONLY;
2N/A
2N/A /* Copy linkname provided to the function. */
2N/A if (strlcpy(dip->dli_linkname, linkname, sizeof (dip->dli_linkname)) >=
2N/A sizeof (dip->dli_linkname)) {
2N/A free(dip);
2N/A return (DLPI_ELINKNAMEINVAL);
2N/A }
2N/A
2N/A /* Copy provider name. */
2N/A (void) strlcpy(dip->dli_provider, provider, sizeof (dip->dli_provider));
2N/A
2N/A /*
2N/A * Special case: DLPI_SERIAL flag is set to indicate a synchronous
2N/A * serial line interface (see syncinit(1M), syncstat(1M),
2N/A * syncloop(1M)), which is not a DLPI link.
2N/A */
2N/A if (dip->dli_oflags & DLPI_SERIAL) {
2N/A if ((retval = i_dlpi_style2_open(dip)) != DLPI_SUCCESS) {
2N/A free(dip);
2N/A return (retval);
2N/A }
2N/A
2N/A *dhp = (dlpi_handle_t)dip;
2N/A return (retval);
2N/A }
2N/A
2N/A if ((retval = i_dlpi_style1_open(dip)) != DLPI_SUCCESS) {
2N/A if (retval == DLPI_ENOTSTYLE2) {
2N/A /*
2N/A * The error code indicates not to continue the
2N/A * style-2 open. Change the error code back to
2N/A * DL_SYSERR, so that one would know the cause
2N/A * of failure from errno.
2N/A */
2N/A retval = DL_SYSERR;
2N/A } else if (!(dip->dli_oflags & DLPI_DEVIPNET)) {
2N/A retval = i_dlpi_style2_open(dip);
2N/A }
2N/A if (retval != DLPI_SUCCESS) {
2N/A free(dip);
2N/A return (retval);
2N/A }
2N/A }
2N/A
2N/A if (dip->dli_oflags & DLPI_PASSIVE)
2N/A i_dlpi_passive(dip);
2N/A
2N/A if ((dip->dli_oflags & DLPI_RAW) &&
2N/A ioctl(dip->dli_fd, DLIOCRAW, 0) < 0) {
2N/A dlpi_close((dlpi_handle_t)dip);
2N/A return (DLPI_ERAWNOTSUP);
2N/A }
2N/A
2N/A if ((dip->dli_oflags & DLPI_IPNETINFO) &&
2N/A ioctl(dip->dli_fd, DLIOCIPNETINFO, &on) < 0) {
2N/A dlpi_close((dlpi_handle_t)dip);
2N/A return (DLPI_EIPNETINFONOTSUP);
2N/A }
2N/A
2N/A /*
2N/A * We intentionally do not care if this request fails, as this
2N/A * indicates the underlying DLPI device does not support Native mode
2N/A * (pre-GLDV3 device drivers).
2N/A */
2N/A if (dip->dli_oflags & DLPI_NATIVE) {
2N/A if ((retval = ioctl(dip->dli_fd, DLIOCNATIVE, 0)) > 0)
2N/A dip->dli_mactype = retval;
2N/A }
2N/A
2N/A *dhp = (dlpi_handle_t)dip;
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A
2N/Avoid
2N/Adlpi_close(dlpi_handle_t dh)
2N/A{
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A dlpi_notifyent_t *next, *dnp;
2N/A
2N/A if (dip != NULL) {
2N/A for (dnp = dip->dli_notifylistp; dnp != NULL; dnp = next) {
2N/A next = dnp->dln_next;
2N/A free(dnp);
2N/A }
2N/A
2N/A (void) close(dip->dli_fd);
2N/A free(dip);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * NOTE: The version argument must be less than the max supported value
2N/A * and is used to determine the fields of the dlpi_info_t structure. See
2N/A * dlpi_info(3DLPI).
2N/A */
2N/Aint
2N/Adlpi_info(dlpi_handle_t dh, dlpi_info_t *infop, uint_t version)
2N/A{
2N/A int retval;
2N/A dlpi_msg_t req, ack;
2N/A dl_info_ack_t *infoackp;
2N/A uint8_t *sapp, *addrp;
2N/A caddr_t ackendp, datap;
2N/A t_uscalar_t dataoff, datalen;
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A if (infop == NULL || version > DLPI_INFO_MAXVERSION)
2N/A return (DLPI_EINVAL);
2N/A
2N/A (void) memset(infop, 0, (version == DLPI_INFO_VERS1) ?
2N/A sizeof (dlpi_info_vers1_t) : sizeof (dlpi_info_t));
2N/A
2N/A /* Set QoS range parameters to default unsupported value. */
2N/A _DLPI_INFO_SET(infop, version, di_qos_range.dl_qos_type,
2N/A (t_uscalar_t)DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version,
2N/A di_qos_range.dl_trans_delay.dl_target_value, DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version,
2N/A di_qos_range.dl_trans_delay.dl_accept_value, DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version, di_qos_range.dl_priority.dl_min,
2N/A DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version, di_qos_range.dl_priority.dl_max,
2N/A DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version, di_qos_range.dl_protection.dl_min,
2N/A DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version, di_qos_range.dl_protection.dl_max,
2N/A DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version, di_qos_range.dl_residual_error,
2N/A DL_UNKNOWN);
2N/A
2N/A /* Set QoS parameters to default unsupported value. */
2N/A _DLPI_INFO_SET(infop, version, di_qos_sel.dl_qos_type,
2N/A (t_uscalar_t)DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version, di_qos_sel.dl_trans_delay, DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version, di_qos_sel.dl_priority, DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version, di_qos_sel.dl_protection, DL_UNKNOWN);
2N/A _DLPI_INFO_SET(infop, version, di_qos_sel.dl_residual_error,
2N/A DL_UNKNOWN);
2N/A
2N/A DLPI_MSG_CREATE(req, DL_INFO_REQ);
2N/A DLPI_MSG_CREATE(ack, DL_INFO_ACK);
2N/A
2N/A retval = i_dlpi_msg_common(dip, &req, &ack, DL_INFO_ACK_SIZE, RS_HIPRI);
2N/A if (retval != DLPI_SUCCESS)
2N/A return (retval);
2N/A
2N/A infoackp = &(ack.dlm_msg->info_ack);
2N/A if (infoackp->dl_version != DL_VERSION_2)
2N/A return (DLPI_EVERNOTSUP);
2N/A
2N/A if (infoackp->dl_service_mode != DL_CLDLS)
2N/A return (DLPI_EMODENOTSUP);
2N/A
2N/A dip->dli_style = infoackp->dl_provider_style;
2N/A dip->dli_mactype = infoackp->dl_mac_type;
2N/A
2N/A ackendp = (caddr_t)ack.dlm_msg + ack.dlm_msgsz;
2N/A
2N/A /* Check and save QoS selection information, if any. */
2N/A datalen = infoackp->dl_qos_length;
2N/A dataoff = infoackp->dl_qos_offset;
2N/A if (dataoff != 0 && datalen != 0) {
2N/A datap = (caddr_t)infoackp + dataoff;
2N/A if (datalen > sizeof (dl_qos_cl_sel1_t) ||
2N/A dataoff < DL_INFO_ACK_SIZE || datap + datalen > ackendp)
2N/A return (DLPI_EBADMSG);
2N/A
2N/A _DLPI_INFO_COPY(infop, version, di_qos_sel, datap, datalen);
2N/A if (_DLPI_INFO_GET(infop, version, di_qos_sel.dl_qos_type) !=
2N/A DL_QOS_CL_SEL1)
2N/A return (DLPI_EMODENOTSUP);
2N/A }
2N/A
2N/A /* Check and save QoS range information, if any. */
2N/A datalen = infoackp->dl_qos_range_length;
2N/A dataoff = infoackp->dl_qos_range_offset;
2N/A if (dataoff != 0 && datalen != 0) {
2N/A datap = (caddr_t)infoackp + dataoff;
2N/A if (datalen > sizeof (dl_qos_cl_range1_t) ||
2N/A dataoff < DL_INFO_ACK_SIZE || datap + datalen > ackendp)
2N/A return (DLPI_EBADMSG);
2N/A
2N/A _DLPI_INFO_COPY(infop, version, di_qos_range, datap, datalen);
2N/A if (_DLPI_INFO_GET(infop, version, di_qos_range.dl_qos_type) !=
2N/A DL_QOS_CL_RANGE1)
2N/A return (DLPI_EMODENOTSUP);
2N/A }
2N/A
2N/A /* Check and save physical address and SAP information. */
2N/A dip->dli_saplen = abs(infoackp->dl_sap_length);
2N/A dip->dli_sapbefore = (infoackp->dl_sap_length > 0);
2N/A _DLPI_INFO_SET(infop, version, di_physaddrlen,
2N/A infoackp->dl_addr_length - dip->dli_saplen);
2N/A
2N/A if (_DLPI_INFO_GET(infop, version, di_physaddrlen) >
2N/A DLPI_PHYSADDR_MAX || dip->dli_saplen > DLPI_SAPLEN_MAX)
2N/A return (DL_BADADDR);
2N/A
2N/A dataoff = infoackp->dl_addr_offset;
2N/A datalen = infoackp->dl_addr_length;
2N/A if (dataoff != 0 && datalen != 0) {
2N/A datap = (caddr_t)infoackp + dataoff;
2N/A if (dataoff < DL_INFO_ACK_SIZE || datap + datalen > ackendp)
2N/A return (DLPI_EBADMSG);
2N/A
2N/A sapp = addrp = (uint8_t *)datap;
2N/A if (dip->dli_sapbefore)
2N/A addrp += dip->dli_saplen;
2N/A else
2N/A sapp += _DLPI_INFO_GET(infop, version, di_physaddrlen);
2N/A
2N/A _DLPI_INFO_COPY(infop, version, di_physaddr, addrp,
2N/A _DLPI_INFO_GET(infop, version, di_physaddrlen));
2N/A _DLPI_INFO_SET(infop, version, di_sap,
2N/A i_dlpi_buildsap(sapp, dip->dli_saplen));
2N/A }
2N/A
2N/A /* Check and save broadcast address information, if any. */
2N/A datalen = infoackp->dl_brdcst_addr_length;
2N/A dataoff = infoackp->dl_brdcst_addr_offset;
2N/A if (dataoff != 0 && datalen != 0) {
2N/A datap = (caddr_t)infoackp + dataoff;
2N/A if (dataoff < DL_INFO_ACK_SIZE || datap + datalen > ackendp)
2N/A return (DLPI_EBADMSG);
2N/A if (datalen != _DLPI_INFO_GET(infop, version, di_physaddrlen))
2N/A return (DL_BADADDR);
2N/A
2N/A _DLPI_INFO_SET(infop, version, di_bcastaddrlen, datalen);
2N/A _DLPI_INFO_COPY(infop, version, di_bcastaddr, datap, datalen);
2N/A }
2N/A
2N/A _DLPI_INFO_SET(infop, version, di_max_sdu, infoackp->dl_max_sdu);
2N/A _DLPI_INFO_SET(infop, version, di_min_sdu, infoackp->dl_min_sdu);
2N/A _DLPI_INFO_SET(infop, version, di_state, infoackp->dl_current_state);
2N/A _DLPI_INFO_SET(infop, version, di_mactype, infoackp->dl_mac_type);
2N/A
2N/A /* Information retrieved from the handle. */
2N/A _DLPI_INFO_COPY(infop, version, di_linkname, dip->dli_linkname,
2N/A ((version == DLPI_INFO_VERS0) ? DLPI_LINKNAME_MAX :
2N/A MAXLINKNAMESPECIFIER));
2N/A _DLPI_INFO_SET(infop, version, di_timeout, dip->dli_timeout);
2N/A
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * This function parses 'linkname' and stores the 'provider' name and 'PPA'.
2N/A */
2N/Aint
2N/Adlpi_parselink(const char *linkname, char *provider, uint_t psize, uint_t *ppa)
2N/A{
2N/A return (dlparse_drvppa(linkname, provider, psize, ppa) ? DLPI_SUCCESS :
2N/A DLPI_ELINKNAMEINVAL);
2N/A}
2N/A
2N/Astatic int
2N/Adlpi_parsezonelink(const char *zonelinkname, char *provider, uint_t psize,
2N/A uint_t *ppa)
2N/A{
2N/A char devlinkname[MAXLINKNAMELEN];
2N/A
2N/A /*
2N/A * We support opening links using the zone/link format. Before we parse
2N/A * the linkname and retreive the DLPI provider name and PPA we strip the
2N/A * zonename prefix (if any) and call dlpi_parselink to retrieve the DLPI
2N/A * provider name and PPA.
2N/A */
2N/A if (!dlparse_zonelinkname(zonelinkname, devlinkname, NULL))
2N/A return (DLPI_ELINKNAMEINVAL);
2N/A
2N/A /*
2N/A * Validate linkname and retrieve the DLPI provier name and PPA.
2N/A */
2N/A return (dlpi_parselink(devlinkname, provider, psize, ppa));
2N/A}
2N/A
2N/A/*
2N/A * This function takes a provider name and a PPA and stores a full linkname
2N/A * as 'linkname'. If 'provider' already is a full linkname 'provider' name
2N/A * is stored in 'linkname'.
2N/A */
2N/Aint
2N/Adlpi_makelink(char *linkname, const char *provider, uint_t ppa)
2N/A{
2N/A int provlen = strlen(provider);
2N/A
2N/A if (linkname == NULL || provlen == 0 || provlen >= DLPI_LINKNAME_MAX)
2N/A return (DLPI_ELINKNAMEINVAL);
2N/A
2N/A if (!isdigit(provider[provlen - 1])) {
2N/A (void) snprintf(linkname, DLPI_LINKNAME_MAX, "%s%d", provider,
2N/A ppa);
2N/A } else {
2N/A (void) strlcpy(linkname, provider, DLPI_LINKNAME_MAX);
2N/A }
2N/A
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A
2N/Aint
2N/Adlpi_bind(dlpi_handle_t dh, uint_t sap, uint_t *boundsap)
2N/A{
2N/A int retval;
2N/A dlpi_msg_t req, ack;
2N/A dl_bind_req_t *bindreqp;
2N/A dl_bind_ack_t *bindackp;
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A DLPI_MSG_CREATE(req, DL_BIND_REQ);
2N/A DLPI_MSG_CREATE(ack, DL_BIND_ACK);
2N/A bindreqp = &(req.dlm_msg->bind_req);
2N/A
2N/A /*
2N/A * If 'sap' is DLPI_ANY_SAP, bind to SAP 2 on token ring, else 0 on
2N/A * other interface types (SAP 0 has special significance on token ring).
2N/A */
2N/A if (sap == DLPI_ANY_SAP)
2N/A bindreqp->dl_sap = ((dip->dli_mactype == DL_TPR) ?
2N/A DL_TPR_ANY_SAP: DL_ANY_SAP);
2N/A else
2N/A bindreqp->dl_sap = sap;
2N/A
2N/A bindreqp->dl_service_mode = DL_CLDLS;
2N/A bindreqp->dl_conn_mgmt = 0;
2N/A bindreqp->dl_max_conind = 0;
2N/A bindreqp->dl_xidtest_flg = 0;
2N/A
2N/A retval = i_dlpi_msg_common(dip, &req, &ack, DL_BIND_ACK_SIZE, 0);
2N/A if (retval != DLPI_SUCCESS)
2N/A return (retval);
2N/A
2N/A bindackp = &(ack.dlm_msg->bind_ack);
2N/A /*
2N/A * Received a DLPI_BIND_ACK, now verify that the bound SAP
2N/A * is equal to the SAP requested. Some DLPI MAC type may bind
2N/A * to a different SAP than requested, in this case 'boundsap'
2N/A * returns the actual bound SAP. For the case where 'boundsap'
2N/A * is NULL and 'sap' is not DLPI_ANY_SAP, dlpi_bind fails.
2N/A */
2N/A if (boundsap != NULL) {
2N/A *boundsap = bindackp->dl_sap;
2N/A } else if (sap != DLPI_ANY_SAP && bindackp->dl_sap != sap) {
2N/A if (dlpi_unbind(dh) != DLPI_SUCCESS)
2N/A return (DLPI_FAILURE);
2N/A else
2N/A return (DLPI_EUNAVAILSAP);
2N/A }
2N/A
2N/A dip->dli_sap = bindackp->dl_sap; /* save sap value in handle */
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A
2N/Aint
2N/Adlpi_unbind(dlpi_handle_t dh)
2N/A{
2N/A dlpi_msg_t req, ack;
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A DLPI_MSG_CREATE(req, DL_UNBIND_REQ);
2N/A DLPI_MSG_CREATE(ack, DL_OK_ACK);
2N/A
2N/A return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0));
2N/A}
2N/A
2N/A/*
2N/A * This function is invoked by dlpi_enabmulti() or dlpi_disabmulti() and
2N/A * based on the "op" value, multicast address is enabled/disabled.
2N/A */
2N/Astatic int
2N/Ai_dlpi_multi(dlpi_handle_t dh, t_uscalar_t op, const uint8_t *addrp,
2N/A size_t addrlen)
2N/A{
2N/A dlpi_msg_t req, ack;
2N/A dl_enabmulti_req_t *multireqp;
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A if (addrlen > DLPI_PHYSADDR_MAX)
2N/A return (DLPI_EINVAL);
2N/A
2N/A DLPI_MSG_CREATE(req, op);
2N/A DLPI_MSG_CREATE(ack, DL_OK_ACK);
2N/A
2N/A multireqp = &(req.dlm_msg->enabmulti_req);
2N/A multireqp->dl_addr_length = addrlen;
2N/A multireqp->dl_addr_offset = sizeof (dl_enabmulti_req_t);
2N/A (void) memcpy(&multireqp[1], addrp, addrlen);
2N/A
2N/A return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0));
2N/A}
2N/A
2N/Aint
2N/Adlpi_enabmulti(dlpi_handle_t dh, const void *addrp, size_t addrlen)
2N/A{
2N/A return (i_dlpi_multi(dh, DL_ENABMULTI_REQ, addrp, addrlen));
2N/A}
2N/A
2N/Aint
2N/Adlpi_disabmulti(dlpi_handle_t dh, const void *addrp, size_t addrlen)
2N/A{
2N/A return (i_dlpi_multi(dh, DL_DISABMULTI_REQ, addrp, addrlen));
2N/A}
2N/A
2N/A/*
2N/A * This function is invoked by dlpi_promiscon() or dlpi_promiscoff(). Based
2N/A * on the value of 'op', promiscuous mode is turned on/off at the specified
2N/A * 'level'.
2N/A */
2N/Astatic int
2N/Ai_dlpi_promisc(dlpi_handle_t dh, t_uscalar_t op, uint_t level)
2N/A{
2N/A dlpi_msg_t req, ack;
2N/A dl_promiscon_req_t *promiscreqp;
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A DLPI_MSG_CREATE(req, op);
2N/A DLPI_MSG_CREATE(ack, DL_OK_ACK);
2N/A
2N/A promiscreqp = &(req.dlm_msg->promiscon_req);
2N/A promiscreqp->dl_level = level;
2N/A
2N/A return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0));
2N/A}
2N/A
2N/Aint
2N/Adlpi_promiscon(dlpi_handle_t dh, uint_t level)
2N/A{
2N/A return (i_dlpi_promisc(dh, DL_PROMISCON_REQ, level));
2N/A}
2N/A
2N/Aint
2N/Adlpi_promiscoff(dlpi_handle_t dh, uint_t level)
2N/A{
2N/A return (i_dlpi_promisc(dh, DL_PROMISCOFF_REQ, level));
2N/A}
2N/A
2N/Aint
2N/Adlpi_get_physaddr(dlpi_handle_t dh, uint_t type, void *addrp, size_t *addrlenp)
2N/A{
2N/A int retval;
2N/A dlpi_msg_t req, ack;
2N/A dl_phys_addr_req_t *physreqp;
2N/A dl_phys_addr_ack_t *physackp;
2N/A t_uscalar_t dataoff, datalen;
2N/A caddr_t datap, physackendp;
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A if (addrlenp == NULL || addrp == NULL || *addrlenp < DLPI_PHYSADDR_MAX)
2N/A return (DLPI_EINVAL);
2N/A
2N/A DLPI_MSG_CREATE(req, DL_PHYS_ADDR_REQ);
2N/A DLPI_MSG_CREATE(ack, DL_PHYS_ADDR_ACK);
2N/A
2N/A physreqp = &(req.dlm_msg->physaddr_req);
2N/A physreqp->dl_addr_type = type;
2N/A
2N/A retval = i_dlpi_msg_common(dip, &req, &ack, DL_PHYS_ADDR_ACK_SIZE, 0);
2N/A if (retval != DLPI_SUCCESS)
2N/A return (retval);
2N/A
2N/A /* Received DL_PHYS_ADDR_ACK, store the physical address and length. */
2N/A physackp = &(ack.dlm_msg->physaddr_ack);
2N/A physackendp = (caddr_t)ack.dlm_msg + ack.dlm_msgsz;
2N/A dataoff = physackp->dl_addr_offset;
2N/A datalen = physackp->dl_addr_length;
2N/A if (dataoff != 0 && datalen != 0) {
2N/A datap = (caddr_t)physackp + dataoff;
2N/A if (datalen > DLPI_PHYSADDR_MAX)
2N/A return (DL_BADADDR);
2N/A if (dataoff < DL_PHYS_ADDR_ACK_SIZE ||
2N/A datap + datalen > physackendp)
2N/A return (DLPI_EBADMSG);
2N/A
2N/A *addrlenp = physackp->dl_addr_length;
2N/A (void) memcpy(addrp, datap, datalen);
2N/A } else {
2N/A *addrlenp = datalen;
2N/A }
2N/A
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A
2N/Aint
2N/Adlpi_set_physaddr(dlpi_handle_t dh, uint_t type, const void *addrp,
2N/A size_t addrlen)
2N/A{
2N/A dlpi_msg_t req, ack;
2N/A dl_set_phys_addr_req_t *setphysreqp;
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A if (addrp == NULL || type != DL_CURR_PHYS_ADDR ||
2N/A addrlen > DLPI_PHYSADDR_MAX)
2N/A return (DLPI_EINVAL);
2N/A
2N/A DLPI_MSG_CREATE(req, DL_SET_PHYS_ADDR_REQ);
2N/A DLPI_MSG_CREATE(ack, DL_OK_ACK);
2N/A
2N/A setphysreqp = &(req.dlm_msg->set_physaddr_req);
2N/A setphysreqp->dl_addr_length = addrlen;
2N/A setphysreqp->dl_addr_offset = sizeof (dl_set_phys_addr_req_t);
2N/A (void) memcpy(&setphysreqp[1], addrp, addrlen);
2N/A
2N/A return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0));
2N/A}
2N/A
2N/Aint
2N/Adlpi_send(dlpi_handle_t dh, const void *daddrp, size_t daddrlen,
2N/A const void *msgbuf, size_t msglen, const dlpi_sendinfo_t *sendp)
2N/A{
2N/A dlpi_msg_t req;
2N/A dl_unitdata_req_t *udatareqp;
2N/A uint_t sap;
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A if (dip->dli_oflags & DLPI_RAW)
2N/A return (i_dlpi_strputmsg(dip, NULL, msgbuf, msglen, 0));
2N/A
2N/A if ((daddrlen > 0 && daddrp == NULL) || daddrlen > DLPI_PHYSADDR_MAX)
2N/A return (DLPI_EINVAL);
2N/A
2N/A DLPI_MSG_CREATE(req, DL_UNITDATA_REQ);
2N/A udatareqp = &(req.dlm_msg->unitdata_req);
2N/A
2N/A /* Set priority to default priority range. */
2N/A udatareqp->dl_priority.dl_min = 0;
2N/A udatareqp->dl_priority.dl_max = 0;
2N/A
2N/A /* Use SAP value if specified otherwise use bound SAP value. */
2N/A if (sendp != NULL) {
2N/A sap = sendp->dsi_sap;
2N/A if (sendp->dsi_prio.dl_min != DL_QOS_DONT_CARE)
2N/A udatareqp->dl_priority.dl_min = sendp->dsi_prio.dl_min;
2N/A if (sendp->dsi_prio.dl_max != DL_QOS_DONT_CARE)
2N/A udatareqp->dl_priority.dl_max = sendp->dsi_prio.dl_max;
2N/A } else {
2N/A sap = dip->dli_sap;
2N/A }
2N/A
2N/A udatareqp->dl_dest_addr_length = daddrlen + dip->dli_saplen;
2N/A udatareqp->dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE;
2N/A
2N/A /*
2N/A * Since `daddrp' only has the link-layer destination address,
2N/A * we must prepend or append the SAP (according to dli_sapbefore)
2N/A * to make a full DLPI address.
2N/A */
2N/A if (dip->dli_sapbefore) {
2N/A i_dlpi_writesap(&udatareqp[1], sap, dip->dli_saplen);
2N/A (void) memcpy((caddr_t)&udatareqp[1] + dip->dli_saplen,
2N/A daddrp, daddrlen);
2N/A } else {
2N/A (void) memcpy(&udatareqp[1], daddrp, daddrlen);
2N/A i_dlpi_writesap((caddr_t)&udatareqp[1] + daddrlen, sap,
2N/A dip->dli_saplen);
2N/A }
2N/A
2N/A return (i_dlpi_strputmsg(dip, &req, msgbuf, msglen, 0));
2N/A}
2N/A
2N/Aint
2N/Adlpi_recv(dlpi_handle_t dh, void *saddrp, size_t *saddrlenp, void *msgbuf,
2N/A size_t *msglenp, int msec, dlpi_recvinfo_t *recvp)
2N/A{
2N/A int retval;
2N/A dlpi_msg_t ind;
2N/A size_t totmsglen;
2N/A dl_unitdata_ind_t *udatap;
2N/A t_uscalar_t dataoff, datalen;
2N/A caddr_t datap, indendp;
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A /*
2N/A * If handle is in raw mode ignore everything except total message
2N/A * length.
2N/A */
2N/A if (dip->dli_oflags & DLPI_RAW) {
2N/A retval = i_dlpi_strgetmsg(dip, msec, NULL, 0, 0, 0, msgbuf,
2N/A msglenp, &totmsglen);
2N/A
2N/A if (retval == DLPI_SUCCESS && recvp != NULL)
2N/A recvp->dri_totmsglen = totmsglen;
2N/A return (retval);
2N/A }
2N/A
2N/A DLPI_MSG_CREATE(ind, DL_UNITDATA_IND);
2N/A udatap = &(ind.dlm_msg->unitdata_ind);
2N/A indendp = (caddr_t)ind.dlm_msg + ind.dlm_msgsz;
2N/A
2N/A if ((retval = i_dlpi_strgetmsg(dip, msec, &ind, DL_UNITDATA_IND,
2N/A DL_UNITDATA_IND, DL_UNITDATA_IND_SIZE, msgbuf,
2N/A msglenp, &totmsglen)) != DLPI_SUCCESS)
2N/A return (retval);
2N/A
2N/A /*
2N/A * If DLPI link provides source address, store source address in
2N/A * 'saddrp' and source length in 'saddrlenp', else set saddrlenp to 0.
2N/A */
2N/A if (saddrp != NULL && saddrlenp != NULL) {
2N/A if (*saddrlenp < DLPI_PHYSADDR_MAX)
2N/A return (DLPI_EINVAL);
2N/A
2N/A dataoff = udatap->dl_src_addr_offset;
2N/A datalen = udatap->dl_src_addr_length;
2N/A if (dataoff != 0 && datalen != 0) {
2N/A datap = (caddr_t)udatap + dataoff;
2N/A if (dataoff < DL_UNITDATA_IND_SIZE ||
2N/A datap + datalen > indendp)
2N/A return (DLPI_EBADMSG);
2N/A
2N/A *saddrlenp = datalen - dip->dli_saplen;
2N/A if (*saddrlenp > DLPI_PHYSADDR_MAX)
2N/A return (DL_BADADDR);
2N/A
2N/A if (dip->dli_sapbefore)
2N/A datap += dip->dli_saplen;
2N/A (void) memcpy(saddrp, datap, *saddrlenp);
2N/A } else {
2N/A *saddrlenp = 0;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If destination address requested, check and save destination
2N/A * address, if any.
2N/A */
2N/A if (recvp != NULL) {
2N/A dataoff = udatap->dl_dest_addr_offset;
2N/A datalen = udatap->dl_dest_addr_length;
2N/A if (dataoff != 0 && datalen != 0) {
2N/A datap = (caddr_t)udatap + dataoff;
2N/A if (dataoff < DL_UNITDATA_IND_SIZE ||
2N/A datap + datalen > indendp)
2N/A return (DLPI_EBADMSG);
2N/A
2N/A recvp->dri_destaddrlen = datalen - dip->dli_saplen;
2N/A if (recvp->dri_destaddrlen > DLPI_PHYSADDR_MAX)
2N/A return (DL_BADADDR);
2N/A
2N/A if (dip->dli_sapbefore)
2N/A datap += dip->dli_saplen;
2N/A (void) memcpy(recvp->dri_destaddr, datap,
2N/A recvp->dri_destaddrlen);
2N/A } else {
2N/A recvp->dri_destaddrlen = 0;
2N/A }
2N/A
2N/A recvp->dri_destaddrtype = udatap->dl_group_address;
2N/A recvp->dri_totmsglen = totmsglen;
2N/A }
2N/A
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A
2N/Aint
2N/Adlpi_enabnotify(dlpi_handle_t dh, uint_t notes, dlpi_notifyfunc_t *funcp,
2N/A void *arg, dlpi_notifyid_t *id)
2N/A{
2N/A int retval;
2N/A dlpi_msg_t req, ack;
2N/A dl_notify_req_t *notifyreqp;
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A dlpi_notifyent_t *newnotifp;
2N/A dlpi_info_t dlinfo;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A retval = dlpi_info((dlpi_handle_t)dip, &dlinfo, DLPI_INFO_VERSION);
2N/A if (retval != DLPI_SUCCESS)
2N/A return (retval);
2N/A
2N/A if (dip->dli_note_processing)
2N/A return (DLPI_FAILURE);
2N/A
2N/A if (funcp == NULL || id == NULL)
2N/A return (DLPI_EINVAL);
2N/A
2N/A if ((~DLPI_NOTIFICATION_TYPES & notes) ||
2N/A !(notes & DLPI_NOTIFICATION_TYPES))
2N/A return (DLPI_ENOTEINVAL);
2N/A
2N/A DLPI_MSG_CREATE(req, DL_NOTIFY_REQ);
2N/A DLPI_MSG_CREATE(ack, DL_NOTIFY_ACK);
2N/A
2N/A notifyreqp = &(req.dlm_msg->notify_req);
2N/A notifyreqp->dl_notifications = notes;
2N/A notifyreqp->dl_timelimit = 0;
2N/A
2N/A retval = i_dlpi_msg_common(dip, &req, &ack, DL_NOTIFY_ACK_SIZE, 0);
2N/A if (retval == DL_NOTSUPPORTED)
2N/A return (DLPI_ENOTENOTSUP);
2N/A
2N/A if (retval != DLPI_SUCCESS)
2N/A return (retval);
2N/A
2N/A if ((newnotifp = calloc(1, sizeof (dlpi_notifyent_t))) == NULL)
2N/A return (DL_SYSERR);
2N/A
2N/A /* Register notification information. */
2N/A newnotifp->dln_fnp = funcp;
2N/A newnotifp->dln_notes = notes;
2N/A newnotifp->arg = arg;
2N/A newnotifp->dln_rm = B_FALSE;
2N/A
2N/A /* Insert notification node at head */
2N/A newnotifp->dln_next = dip->dli_notifylistp;
2N/A dip->dli_notifylistp = newnotifp;
2N/A
2N/A *id = (dlpi_notifyid_t)newnotifp;
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A
2N/Aint
2N/Adlpi_disabnotify(dlpi_handle_t dh, dlpi_notifyid_t id, void **argp)
2N/A{
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A dlpi_notifyent_t *remid = (dlpi_notifyent_t *)id;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A /* Walk the notifyentry list to find matching id. */
2N/A if (!(i_dlpi_notifyidexists(dip, remid)))
2N/A return (DLPI_ENOTEIDINVAL);
2N/A
2N/A if (argp != NULL)
2N/A *argp = remid->arg;
2N/A
2N/A remid->dln_rm = B_TRUE;
2N/A /* Delete node if callbacks are not being processed. */
2N/A if (!dip->dli_note_processing)
2N/A i_dlpi_deletenotifyid(dip);
2N/A
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A
2N/Aint
2N/Adlpi_fd(dlpi_handle_t dh)
2N/A{
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A return (dip != NULL ? dip->dli_fd : -1);
2N/A}
2N/A
2N/Aint
2N/Adlpi_set_timeout(dlpi_handle_t dh, int sec)
2N/A{
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A if (dip == NULL)
2N/A return (DLPI_EINHANDLE);
2N/A
2N/A dip->dli_timeout = sec;
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A
2N/Aconst char *
2N/Adlpi_linkname(dlpi_handle_t dh)
2N/A{
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A return (dip != NULL ? dip->dli_linkname : NULL);
2N/A}
2N/A
2N/A/*
2N/A * Returns DLPI style stored in the handle.
2N/A * Note: This function is used for test purposes only. Do not remove without
2N/A * fixing the DLPI testsuite.
2N/A */
2N/Auint_t
2N/Adlpi_style(dlpi_handle_t dh)
2N/A{
2N/A dlpi_impl_t *dip = (dlpi_impl_t *)dh;
2N/A
2N/A return (dip->dli_style);
2N/A}
2N/A
2N/Auint_t
2N/Adlpi_arptype(uint_t dlpitype)
2N/A{
2N/A switch (dlpitype) {
2N/A
2N/A case DL_ETHER:
2N/A return (ARPHRD_ETHER);
2N/A
2N/A case DL_FRAME:
2N/A return (ARPHRD_FRAME);
2N/A
2N/A case DL_ATM:
2N/A return (ARPHRD_ATM);
2N/A
2N/A case DL_IPATM:
2N/A return (ARPHRD_IPATM);
2N/A
2N/A case DL_HDLC:
2N/A return (ARPHRD_HDLC);
2N/A
2N/A case DL_FC:
2N/A return (ARPHRD_FC);
2N/A
2N/A case DL_CSMACD: /* ieee 802 networks */
2N/A case DL_TPB:
2N/A case DL_TPR:
2N/A case DL_METRO:
2N/A case DL_FDDI:
2N/A return (ARPHRD_IEEE802);
2N/A
2N/A case DL_IB:
2N/A return (ARPHRD_IB);
2N/A
2N/A case DL_IPV4:
2N/A case DL_IPV6:
2N/A return (ARPHRD_TUNNEL);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Auint_t
2N/Adlpi_iftype(uint_t dlpitype)
2N/A{
2N/A switch (dlpitype) {
2N/A
2N/A case DL_ETHER:
2N/A return (IFT_ETHER);
2N/A
2N/A case DL_ATM:
2N/A return (IFT_ATM);
2N/A
2N/A case DL_CSMACD:
2N/A return (IFT_ISO88023);
2N/A
2N/A case DL_TPB:
2N/A return (IFT_ISO88024);
2N/A
2N/A case DL_TPR:
2N/A return (IFT_ISO88025);
2N/A
2N/A case DL_FDDI:
2N/A return (IFT_FDDI);
2N/A
2N/A case DL_IB:
2N/A return (IFT_IB);
2N/A
2N/A case DL_OTHER:
2N/A return (IFT_OTHER);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function attempts to open a device under the following namespaces:
2N/A * /dev/ipnet - if DLPI_DEVIPNET is specified
2N/A * /dev/net/zone - if a data-link is specified with a zonename prefix
2N/A * /dev/net - if a data-link with the specified name exists
2N/A * /dev - if DLPI_DEVONLY is specified, or if there is no
2N/A * data-link with the specified name (could be /dev/ip)
2N/A *
2N/A * In particular, if DLPI_DEVIPNET is not specified, this function is used to
2N/A * open a data-link node, or "/dev/ip" node. It is usually be called firstly
2N/A * with style1 being B_TRUE, and if that fails and the return value is not
2N/A * DLPI_ENOTSTYLE2, the function will again be called with style1 being
2N/A * B_FALSE (style-1 open attempt first, then style-2 open attempt).
2N/A *
2N/A * If DLPI_DEVONLY is specified, both attempt will try to open the /dev node
2N/A * directly.
2N/A *
2N/A * Otherwise, for style-1 attempt, the function will try to open the style-1
2N/A * /dev/net/zone or /dev/net node, and perhaps fallback to open the style-1
2N/A * /dev node if the give name is not a data-link name (e.g., it is /dev/ip).
2N/A * Note that the fallback and the subsequent style-2 attempt will not happen if:
2N/A * 1. style-1 opening of the /dev/net or /dev/net/zone node succeeds;
2N/A * 2. style-1 opening of the /dev/net node or /dev/net/zone node fails with
2N/A * errno other than ENOENT, which means that the specific /dev/net or
2N/A * /dev/net/zone node exists, but the attempt fails for some other reason;
2N/A * 3. style-1 openning of the /dev/net node fails with ENOENT, but the name
2N/A * is a known device name or its VLAN PPA hack name. (for example, assuming
2N/A * device bge0 is renamed to net0, opening /dev/net/bge1000 would return
2N/A * ENOENT, but we should not fallback to open /dev/bge1000 in this case,
2N/A * as VLAN 1 over the bge0 device should be named as net1000.
2N/A *
2N/A * DLPI_ENOTSTYLE2 will be returned in case 2 and 3 to indicate not to proceed
2N/A * the second style-2 open attempt.
2N/A */
2N/Astatic int
2N/Ai_dlpi_open(const char *provider, int *fd, uint_t flags, boolean_t style1)
2N/A{
2N/A char path[MAXPATHLEN];
2N/A int oflags;
2N/A
2N/A errno = ENOENT;
2N/A oflags = O_RDWR;
2N/A if (flags & DLPI_EXCL)
2N/A oflags |= O_EXCL;
2N/A
2N/A if (flags & DLPI_DEVIPNET) {
2N/A (void) snprintf(path, sizeof (path), "/dev/ipnet/%s", provider);
2N/A if ((*fd = open(path, oflags)) != -1)
2N/A return (DLPI_SUCCESS);
2N/A else
2N/A return (errno == ENOENT ? DLPI_ENOLINK : DL_SYSERR);
2N/A } else if (style1 && !(flags & DLPI_DEVONLY)) {
2N/A char driver[DLPI_LINKNAME_MAX];
2N/A char device[DLPI_LINKNAME_MAX];
2N/A datalink_id_t linkid;
2N/A uint_t ppa;
2N/A dladm_handle_t handle;
2N/A
2N/A /*
2N/A * This is not a valid style-1 name. It could be "ip" module
2N/A * for example. Fallback to open the /dev node.
2N/A */
2N/A if (dlpi_parsezonelink(provider, driver, sizeof (driver),
2N/A &ppa) != DLPI_SUCCESS) {
2N/A goto fallback;
2N/A }
2N/A
2N/A if (strchr(provider, '/') != NULL) {
2N/A /*
2N/A * Open the device node in /dev/net at
2N/A * /dev/net/zone/{provider} if the provider passed
2N/A * has a zonename prefix in the format zonename/link.
2N/A */
2N/A (void) snprintf(path, sizeof (path),
2N/A "/dev/net/zone/%s", provider);
2N/A } else {
2N/A /*
2N/A * Otherwise open the device node in /dev/net
2N/A */
2N/A (void) snprintf(path, sizeof (path), "/dev/net/%s",
2N/A provider);
2N/A }
2N/A
2N/A if ((*fd = open(path, oflags)) != -1)
2N/A return (DLPI_SUCCESS);
2N/A
2N/A /*
2N/A * We don't fallback to open the /dev node when it returns
2N/A * error codes other than ENOENT. In that case, DLPI_ENOTSTYLE2
2N/A * is returned to indicate not to continue the style-2 open.
2N/A */
2N/A if (errno != ENOENT)
2N/A return (DLPI_ENOTSTYLE2);
2N/A
2N/A /*
2N/A * We didn't find the /dev/net node. Then we check whether
2N/A * the given name is a device name or its VLAN PPA hack name
2N/A * of a known link. If the answer is yes, and this link
2N/A * supports vanity naming, then the link (or the VLAN) should
2N/A * also have its /dev/net node but perhaps with another vanity
2N/A * name (for example, when bge0 is renamed to net0). In this
2N/A * case, although attempt to open the /dev/net/<devname> fails,
2N/A * we should not fallback to open the /dev/<devname> node.
2N/A */
2N/A (void) snprintf(device, DLPI_LINKNAME_MAX, "%s%d", driver,
2N/A ppa >= 1000 ? ppa % 1000 : ppa);
2N/A
2N/A /* open libdladm handle rather than taking it as input */
2N/A if (dladm_open(&handle, NETADM_ACTIVE_PROFILE)
2N/A != DLADM_STATUS_OK)
2N/A goto fallback;
2N/A
2N/A if (dladm_dev2linkid(handle, device, &linkid) ==
2N/A DLADM_STATUS_OK) {
2N/A dladm_phys_attr_t dpa;
2N/A
2N/A if ((dladm_phys_info(handle, linkid, &dpa,
2N/A DLADM_OPT_ACTIVE)) == DLADM_STATUS_OK &&
2N/A !dpa.dp_novanity) {
2N/A dladm_close(handle);
2N/A return (DLPI_ENOTSTYLE2);
2N/A }
2N/A }
2N/A dladm_close(handle);
2N/A }
2N/A
2N/Afallback:
2N/A (void) snprintf(path, sizeof (path), "/dev/%s", provider);
2N/A if ((*fd = open(path, oflags)) != -1)
2N/A return (DLPI_SUCCESS);
2N/A
2N/A return (errno == ENOENT ? DLPI_ENOLINK : DL_SYSERR);
2N/A}
2N/A
2N/A/*
2N/A * Open a style 1 link. PPA is implicitly attached.
2N/A */
2N/Astatic int
2N/Ai_dlpi_style1_open(dlpi_impl_t *dip)
2N/A{
2N/A int retval, save_errno;
2N/A int fd;
2N/A
2N/A retval = i_dlpi_open(dip->dli_linkname, &fd, dip->dli_oflags, B_TRUE);
2N/A if (retval != DLPI_SUCCESS)
2N/A return (retval);
2N/A dip->dli_fd = fd;
2N/A
2N/A if ((retval = i_dlpi_checkstyle(dip, DL_STYLE1)) != DLPI_SUCCESS) {
2N/A save_errno = errno;
2N/A (void) close(dip->dli_fd);
2N/A errno = save_errno;
2N/A }
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * Open a style 2 link. PPA must be explicitly attached.
2N/A */
2N/Astatic int
2N/Ai_dlpi_style2_open(dlpi_impl_t *dip)
2N/A{
2N/A int fd;
2N/A int retval, save_errno;
2N/A
2N/A retval = i_dlpi_open(dip->dli_provider, &fd, dip->dli_oflags, B_FALSE);
2N/A if (retval != DLPI_SUCCESS)
2N/A return (retval);
2N/A dip->dli_fd = fd;
2N/A
2N/A /*
2N/A * Special case: DLPI_SERIAL flag (synchronous serial lines) is not a
2N/A * DLPI link so attach and ignore rest.
2N/A */
2N/A if (dip->dli_oflags & DLPI_SERIAL)
2N/A goto attach;
2N/A
2N/A if ((retval = i_dlpi_checkstyle(dip, DL_STYLE2)) != DLPI_SUCCESS)
2N/A goto failure;
2N/A
2N/A /*
2N/A * Succeeded opening the link and verified it is style2. Now attach to
2N/A * PPA only if DLPI_NOATTACH is not set.
2N/A */
2N/A if (dip->dli_oflags & DLPI_NOATTACH)
2N/A return (DLPI_SUCCESS);
2N/A
2N/Aattach:
2N/A if ((retval = i_dlpi_attach(dip)) == DLPI_SUCCESS)
2N/A return (DLPI_SUCCESS);
2N/A
2N/Afailure:
2N/A save_errno = errno;
2N/A (void) close(dip->dli_fd);
2N/A errno = save_errno;
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * Verify with DLPI that the link is the expected DLPI 'style' device,
2N/A * dlpi_info sets the DLPI style in the DLPI handle.
2N/A */
2N/Astatic int
2N/Ai_dlpi_checkstyle(dlpi_impl_t *dip, t_uscalar_t style)
2N/A{
2N/A int retval;
2N/A dlpi_info_t dlinfo;
2N/A
2N/A retval = dlpi_info((dlpi_handle_t)dip, &dlinfo, DLPI_INFO_VERSION);
2N/A if (retval == DLPI_SUCCESS && dip->dli_style != style)
2N/A retval = DLPI_EBADLINK;
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * For DLPI style 2 providers, an explicit attach of PPA is required.
2N/A */
2N/Astatic int
2N/Ai_dlpi_attach(dlpi_impl_t *dip)
2N/A{
2N/A dlpi_msg_t req, ack;
2N/A dl_attach_req_t *attachreqp;
2N/A
2N/A /*
2N/A * Special case: DLPI_SERIAL flag (synchronous serial lines)
2N/A * is not a DLPI link so ignore DLPI style.
2N/A */
2N/A if (dip->dli_style != DL_STYLE2 && !(dip->dli_oflags & DLPI_SERIAL))
2N/A return (DLPI_ENOTSTYLE2);
2N/A
2N/A DLPI_MSG_CREATE(req, DL_ATTACH_REQ);
2N/A DLPI_MSG_CREATE(ack, DL_OK_ACK);
2N/A
2N/A attachreqp = &(req.dlm_msg->attach_req);
2N/A attachreqp->dl_ppa = dip->dli_ppa;
2N/A
2N/A return (i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0));
2N/A}
2N/A
2N/A/*
2N/A * Enable DLPI passive mode on a DLPI handle. We intentionally do not care
2N/A * if this request fails, as this indicates the underlying DLPI device does
2N/A * not support link aggregation (pre-GLDV3 device drivers), and thus will
2N/A * see the expected behavior without failing with DL_SYSERR/EBUSY when issuing
2N/A * DLPI primitives like DL_BIND_REQ. For further info see dlpi(7p).
2N/A */
2N/Astatic void
2N/Ai_dlpi_passive(dlpi_impl_t *dip)
2N/A{
2N/A dlpi_msg_t req, ack;
2N/A
2N/A DLPI_MSG_CREATE(req, DL_PASSIVE_REQ);
2N/A DLPI_MSG_CREATE(ack, DL_OK_ACK);
2N/A
2N/A (void) i_dlpi_msg_common(dip, &req, &ack, DL_OK_ACK_SIZE, 0);
2N/A}
2N/A
2N/A/*
2N/A * Send a dlpi control message and/or data message on a stream. The inputs
2N/A * for this function are:
2N/A * dlpi_impl_t *dip: internal dlpi handle to open stream
2N/A * const dlpi_msg_t *dlreqp: request message structure
2N/A * void *databuf: data buffer
2N/A * size_t datalen: data buffer len
2N/A * int flags: flags to set for putmsg()
2N/A * Returns DLPI_SUCCESS if putmsg() succeeds, otherwise DL_SYSERR on failure.
2N/A */
2N/Astatic int
2N/Ai_dlpi_strputmsg(dlpi_impl_t *dip, const dlpi_msg_t *dlreqp,
2N/A const void *databuf, size_t datalen, int flags)
2N/A{
2N/A int retval;
2N/A int fd = dip->dli_fd;
2N/A struct strbuf ctl;
2N/A struct strbuf data;
2N/A
2N/A if (dlreqp != NULL) {
2N/A ctl.buf = (void *)dlreqp->dlm_msg;
2N/A ctl.len = dlreqp->dlm_msgsz;
2N/A }
2N/A
2N/A data.buf = (void *)databuf;
2N/A data.len = datalen;
2N/A
2N/A retval = putmsg(fd, (dlreqp == NULL ? NULL: &ctl),
2N/A (databuf == NULL ? NULL : &data), flags);
2N/A
2N/A return ((retval == 0) ? DLPI_SUCCESS : DL_SYSERR);
2N/A}
2N/A
2N/A/*
2N/A * Get a DLPI control message and/or data message from a stream. The inputs
2N/A * for this function are:
2N/A * dlpi_impl_t *dip: internal dlpi handle
2N/A * int msec: timeout to wait for message
2N/A * dlpi_msg_t *dlreplyp: reply message structure, the message size
2N/A * member on return stores actual size received
2N/A * t_uscalar_t dlreqprim: requested primitive
2N/A * t_uscalar_t dlreplyprim:acknowledged primitive in response to request
2N/A * size_t dlreplyminsz: minimum size of acknowledged primitive size
2N/A * void *databuf: data buffer
2N/A * size_t *datalenp: data buffer len
2N/A * size_t *totdatalenp: total data received. Greater than 'datalenp' if
2N/A * actual data received is larger than 'databuf'
2N/A * Function returns DLPI_SUCCESS if requested message is retrieved
2N/A * otherwise returns error code or timeouts. If a notification arrives on
2N/A * the stream the callback is notified. However, error returned during the
2N/A * handling of notification is ignored as it would be confusing to actual caller
2N/A * of this function.
2N/A */
2N/Astatic int
2N/Ai_dlpi_strgetmsg(dlpi_impl_t *dip, int msec, dlpi_msg_t *dlreplyp,
2N/A t_uscalar_t dlreqprim, t_uscalar_t dlreplyprim, size_t dlreplyminsz,
2N/A void *databuf, size_t *datalenp, size_t *totdatalenp)
2N/A{
2N/A int retval;
2N/A int flags;
2N/A int fd = dip->dli_fd;
2N/A struct strbuf ctl, data;
2N/A struct pollfd pfd;
2N/A hrtime_t start, current;
2N/A long bufc[DLPI_CHUNKSIZE / sizeof (long)];
2N/A long bufd[DLPI_CHUNKSIZE / sizeof (long)];
2N/A union DL_primitives *dlprim;
2N/A dl_notify_ind_t *dlnotif;
2N/A boolean_t infinite = (msec < 0); /* infinite timeout */
2N/A
2N/A /*
2N/A * dlreplyp and databuf can be NULL at the same time, to force a check
2N/A * for pending events on the DLPI link instance; dlpi_enabnotify(3DLPI).
2N/A * this will be true more so for DLPI_RAW mode with notifications
2N/A * enabled.
2N/A */
2N/A if ((databuf == NULL && datalenp != NULL) ||
2N/A (databuf != NULL && datalenp == NULL))
2N/A return (DLPI_EINVAL);
2N/A
2N/A pfd.fd = fd;
2N/A pfd.events = POLLIN | POLLPRI;
2N/A
2N/A ctl.buf = (dlreplyp == NULL) ? bufc : (void *)dlreplyp->dlm_msg;
2N/A ctl.len = 0;
2N/A ctl.maxlen = (dlreplyp == NULL) ? sizeof (bufc) : dlreplyp->dlm_msgsz;
2N/A
2N/A data.buf = (databuf == NULL) ? bufd : databuf;
2N/A data.len = 0;
2N/A data.maxlen = (databuf == NULL) ? sizeof (bufd): *datalenp;
2N/A
2N/A for (;;) {
2N/A if (!infinite)
2N/A start = gethrtime() / (NANOSEC / MILLISEC);
2N/A
2N/A switch (poll(&pfd, 1, msec)) {
2N/A default:
2N/A if (pfd.revents & POLLHUP)
2N/A return (DL_SYSERR);
2N/A break;
2N/A case 0:
2N/A return (DLPI_ETIMEDOUT);
2N/A case -1:
2N/A return (DL_SYSERR);
2N/A }
2N/A
2N/A flags = 0;
2N/A if ((retval = getmsg(fd, &ctl, &data, &flags)) < 0)
2N/A return (DL_SYSERR);
2N/A
2N/A if (totdatalenp != NULL)
2N/A *totdatalenp = data.len;
2N/A
2N/A /*
2N/A * The supplied DLPI_CHUNKSIZE sized buffers are large enough
2N/A * to retrieve all valid DLPI responses in one iteration.
2N/A * If MORECTL or MOREDATA is set, we are not interested in the
2N/A * remainder of the message. Temporary buffers are used to
2N/A * drain the remainder of this message.
2N/A * The special case we have to account for is if
2N/A * a higher priority messages is enqueued whilst handling
2N/A * this condition. We use a change in the flags parameter
2N/A * returned by getmsg() to indicate the message has changed.
2N/A */
2N/A while (retval & (MORECTL | MOREDATA)) {
2N/A struct strbuf cscratch, dscratch;
2N/A int oflags = flags;
2N/A
2N/A cscratch.buf = (char *)bufc;
2N/A dscratch.buf = (char *)bufd;
2N/A cscratch.len = dscratch.len = 0;
2N/A cscratch.maxlen = dscratch.maxlen =
2N/A sizeof (bufc);
2N/A
2N/A if ((retval = getmsg(fd, &cscratch, &dscratch,
2N/A &flags)) < 0)
2N/A return (DL_SYSERR);
2N/A
2N/A if (totdatalenp != NULL)
2N/A *totdatalenp += dscratch.len;
2N/A /*
2N/A * In the special case of higher priority
2N/A * message received, the low priority message
2N/A * received earlier is discarded, if no data
2N/A * or control message is left.
2N/A */
2N/A if ((flags != oflags) &&
2N/A !(retval & (MORECTL | MOREDATA)) &&
2N/A (cscratch.len != 0)) {
2N/A ctl.len = MIN(cscratch.len, DLPI_CHUNKSIZE);
2N/A if (dlreplyp != NULL)
2N/A (void) memcpy(dlreplyp->dlm_msg, bufc,
2N/A ctl.len);
2N/A break;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Check if DL_NOTIFY_IND message received. If there is one,
2N/A * notify the callback function(s) and continue processing the
2N/A * requested message.
2N/A */
2N/A if (dip->dli_notifylistp != NULL &&
2N/A ctl.len >= (int)(sizeof (t_uscalar_t)) &&
2N/A *(t_uscalar_t *)(void *)ctl.buf == DL_NOTIFY_IND) {
2N/A /* process properly-formed DL_NOTIFY_IND messages */
2N/A if (ctl.len >= DL_NOTIFY_IND_SIZE) {
2N/A dlnotif = (dl_notify_ind_t *)(void *)ctl.buf;
2N/A (void) i_dlpi_notifyind_process(dip, dlnotif);
2N/A }
2N/A goto update_timer;
2N/A }
2N/A
2N/A /*
2N/A * If we were expecting a data message, and we got one, set
2N/A * *datalenp. If we aren't waiting on a control message, then
2N/A * we're done.
2N/A */
2N/A if (databuf != NULL && data.len >= 0) {
2N/A *datalenp = data.len;
2N/A if (dlreplyp == NULL)
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * If we were expecting a control message, and the message
2N/A * we received is at least big enough to be a DLPI message,
2N/A * then verify it's a reply to something we sent. If it
2N/A * is a reply to something we sent, also verify its size.
2N/A */
2N/A if (dlreplyp != NULL && ctl.len >= sizeof (t_uscalar_t)) {
2N/A dlprim = dlreplyp->dlm_msg;
2N/A if (dlprim->dl_primitive == dlreplyprim) {
2N/A if (ctl.len < dlreplyminsz)
2N/A return (DLPI_EBADMSG);
2N/A dlreplyp->dlm_msgsz = ctl.len;
2N/A break;
2N/A } else if (dlprim->dl_primitive == DL_ERROR_ACK) {
2N/A if (ctl.len < DL_ERROR_ACK_SIZE)
2N/A return (DLPI_EBADMSG);
2N/A
2N/A /* Is it ours? */
2N/A if (dlprim->error_ack.dl_error_primitive ==
2N/A dlreqprim)
2N/A break;
2N/A }
2N/A }
2N/Aupdate_timer:
2N/A if (!infinite) {
2N/A current = gethrtime() / (NANOSEC / MILLISEC);
2N/A msec -= (current - start);
2N/A
2N/A if (msec <= 0)
2N/A return (DLPI_ETIMEDOUT);
2N/A }
2N/A }
2N/A
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Common routine invoked by all DLPI control routines. The inputs for this
2N/A * function are:
2N/A * dlpi_impl_t *dip: internal dlpi handle
2N/A * const dlpi_msg_t *dlreqp: request message structure
2N/A * dlpi_msg_t *dlreplyp: reply message structure
2N/A * size_t dlreplyminsz: minimum size of reply primitive
2N/A * int flags: flags to be set to send a message
2N/A * This routine succeeds if the message is an expected request/acknowledged
2N/A * message. However, if DLPI notification has been enabled via
2N/A * dlpi_enabnotify(), DL_NOTIFY_IND messages are handled before handling
2N/A * expected messages. Otherwise, any other unexpected asynchronous messages will
2N/A * be discarded.
2N/A */
2N/Astatic int
2N/Ai_dlpi_msg_common(dlpi_impl_t *dip, const dlpi_msg_t *dlreqp,
2N/A dlpi_msg_t *dlreplyp, size_t dlreplyminsz, int flags)
2N/A{
2N/A int retval;
2N/A t_uscalar_t dlreqprim = dlreqp->dlm_msg->dl_primitive;
2N/A t_uscalar_t dlreplyprim = dlreplyp->dlm_msg->dl_primitive;
2N/A
2N/A /* Put the requested primitive on the stream. */
2N/A retval = i_dlpi_strputmsg(dip, dlreqp, NULL, 0, flags);
2N/A if (retval != DLPI_SUCCESS)
2N/A return (retval);
2N/A
2N/A /* Retrieve acknowledged message for requested primitive. */
2N/A retval = i_dlpi_strgetmsg(dip, (dip->dli_timeout * MILLISEC),
2N/A dlreplyp, dlreqprim, dlreplyprim, dlreplyminsz, NULL, NULL, NULL);
2N/A if (retval != DLPI_SUCCESS)
2N/A return (retval);
2N/A
2N/A /*
2N/A * If primitive is DL_ERROR_ACK, set errno.
2N/A */
2N/A if (dlreplyp->dlm_msg->dl_primitive == DL_ERROR_ACK) {
2N/A errno = dlreplyp->dlm_msg->error_ack.dl_unix_errno;
2N/A retval = dlreplyp->dlm_msg->error_ack.dl_errno;
2N/A }
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * DLPI error codes.
2N/A */
2N/Astatic const char *dlpi_errlist[] = {
2N/A "bad LSAP selector", /* DL_BADSAP 0x00 */
2N/A "DLSAP address in improper format or invalid", /* DL_BADADDR 0x01 */
2N/A "improper permissions for request", /* DL_ACCESS 0x02 */
2N/A "primitive issued in improper state", /* DL_OUTSTATE 0x03 */
2N/A NULL, /* DL_SYSERR 0x04 */
2N/A "sequence number not from outstanding DL_CONN_IND",
2N/A /* DL_BADCORR 0x05 */
2N/A "user data exceeded provider limit", /* DL_BADDATA 0x06 */
2N/A "requested service not supplied by provider",
2N/A /* DL_UNSUPPORTED 0x07 */
2N/A "specified PPA was invalid", /* DL_BADPPA 0x08 */
2N/A "primitive received not known by provider", /* DL_BADPRIM 0x09 */
2N/A "QoS parameters contained invalid values",
2N/A /* DL_BADQOSPARAM 0x0a */
2N/A "QoS structure type is unknown/unsupported", /* DL_BADQOSTYPE 0x0b */
2N/A "token used not an active stream", /* DL_BADTOKEN 0x0c */
2N/A "attempted second bind with dl_max_conind", /* DL_BOUND 0x0d */
2N/A "physical link initialization failed", /* DL_INITFAILED 0x0e */
2N/A "provider couldn't allocate alternate address", /* DL_NOADDR 0x0f */
2N/A "physical link not initialized", /* DL_NOTINIT 0x10 */
2N/A "previous data unit could not be delivered",
2N/A /* DL_UNDELIVERABLE 0x11 */
2N/A "primitive is known but unsupported",
2N/A /* DL_NOTSUPPORTED 0x12 */
2N/A "limit exceeded", /* DL_TOOMANY 0x13 */
2N/A "promiscuous mode not enabled", /* DL_NOTENAB 0x14 */
2N/A "other streams for PPA in post-attached", /* DL_BUSY 0x15 */
2N/A "automatic handling XID&TEST unsupported", /* DL_NOAUTO 0x16 */
2N/A "automatic handling of XID unsupported", /* DL_NOXIDAUTO 0x17 */
2N/A "automatic handling of TEST unsupported", /* DL_NOTESTAUTO 0x18 */
2N/A "automatic handling of XID response", /* DL_XIDAUTO 0x19 */
2N/A "automatic handling of TEST response", /* DL_TESTAUTO 0x1a */
2N/A "pending outstanding connect indications" /* DL_PENDING 0x1b */
2N/A};
2N/A
2N/A/*
2N/A * libdlpi error codes.
2N/A */
2N/Astatic const char *libdlpi_errlist[] = {
2N/A "DLPI operation succeeded", /* DLPI_SUCCESS */
2N/A "invalid argument", /* DLPI_EINVAL */
2N/A "invalid DLPI linkname", /* DLPI_ELINKNAMEINVAL */
2N/A "DLPI link does not exist", /* DLPI_ENOLINK */
2N/A "bad DLPI link", /* DLPI_EBADLINK */
2N/A "invalid DLPI handle", /* DLPI_EINHANDLE */
2N/A "DLPI operation timed out", /* DLPI_ETIMEDOUT */
2N/A "unsupported DLPI version", /* DLPI_EVERNOTSUP */
2N/A "unsupported DLPI connection mode", /* DLPI_EMODENOTSUP */
2N/A "unavailable DLPI SAP", /* DLPI_EUNAVAILSAP */
2N/A "DLPI operation failed", /* DLPI_FAILURE */
2N/A "DLPI style-2 node reports style-1", /* DLPI_ENOTSTYLE2 */
2N/A "bad DLPI message", /* DLPI_EBADMSG */
2N/A "DLPI raw mode not supported", /* DLPI_ERAWNOTSUP */
2N/A "DLPI notification not supported by link",
2N/A /* DLPI_ENOTENOTSUP */
2N/A "invalid DLPI notification type", /* DLPI_ENOTEINVAL */
2N/A "invalid DLPI notification id", /* DLPI_ENOTEIDINVAL */
2N/A "DLPI_IPNETINFO not supported" /* DLPI_EIPNETINFONOTSUP */
2N/A};
2N/A
2N/Aconst char *
2N/Adlpi_strerror(int err)
2N/A{
2N/A if (err == DL_SYSERR)
2N/A return (strerror(errno));
2N/A else if (err >= 0 && err < NELEMS(dlpi_errlist))
2N/A return (dgettext(TEXT_DOMAIN, dlpi_errlist[err]));
2N/A else if (err >= DLPI_SUCCESS && err < DLPI_ERRMAX)
2N/A return (dgettext(TEXT_DOMAIN, libdlpi_errlist[err -
2N/A DLPI_SUCCESS]));
2N/A else
2N/A return (dgettext(TEXT_DOMAIN, "Unknown DLPI error"));
2N/A}
2N/A
2N/A/*
2N/A * Each table entry comprises a DLPI/Private mactype and the description.
2N/A */
2N/Astatic const dlpi_mactype_t dlpi_mactypes[] = {
2N/A { DL_CSMACD, "CSMA/CD" },
2N/A { DL_TPB, "Token Bus" },
2N/A { DL_TPR, "Token Ring" },
2N/A { DL_METRO, "Metro Net" },
2N/A { DL_ETHER, "Ethernet" },
2N/A { DL_HDLC, "HDLC" },
2N/A { DL_CHAR, "Sync Character" },
2N/A { DL_CTCA, "CTCA" },
2N/A { DL_FDDI, "FDDI" },
2N/A { DL_FRAME, "Frame Relay (LAPF)" },
2N/A { DL_MPFRAME, "MP Frame Relay" },
2N/A { DL_ASYNC, "Async Character" },
2N/A { DL_IPX25, "X.25 (Classic IP)" },
2N/A { DL_LOOP, "Software Loopback" },
2N/A { DL_FC, "Fiber Channel" },
2N/A { DL_ATM, "ATM" },
2N/A { DL_IPATM, "ATM (Classic IP)" },
2N/A { DL_X25, "X.25 (LAPB)" },
2N/A { DL_ISDN, "ISDN" },
2N/A { DL_HIPPI, "HIPPI" },
2N/A { DL_100VG, "100BaseVG Ethernet" },
2N/A { DL_100VGTPR, "100BaseVG Token Ring" },
2N/A { DL_ETH_CSMA, "Ethernet/IEEE 802.3" },
2N/A { DL_100BT, "100BaseT" },
2N/A { DL_IB, "Infiniband" },
2N/A { DL_IPV4, "IPv4 Tunnel" },
2N/A { DL_IPV6, "IPv6 Tunnel" },
2N/A { DL_WIFI, "IEEE 802.11" },
2N/A { DL_IPNET, "IPNET" }
2N/A};
2N/A
2N/Aconst char *
2N/Adlpi_mactype(uint_t mactype)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < NELEMS(dlpi_mactypes); i++) {
2N/A if (dlpi_mactypes[i].dm_mactype == mactype)
2N/A return (dlpi_mactypes[i].dm_desc);
2N/A }
2N/A
2N/A return ("Unknown MAC Type");
2N/A}
2N/A
2N/A/*
2N/A * Each table entry comprises a DLPI primitive and the maximum buffer
2N/A * size needed, in bytes, for the DLPI message (see <sys/dlpi.h> for details).
2N/A */
2N/Astatic const dlpi_primsz_t dlpi_primsizes[] = {
2N/A{ DL_INFO_REQ, DL_INFO_REQ_SIZE },
2N/A{ DL_INFO_ACK, DL_INFO_ACK_SIZE + (2 * DLPI_PHYSADDR_MAX) +
2N/A DLPI_SAPLEN_MAX + (2 * sizeof (union DL_qos_types))},
2N/A{ DL_ATTACH_REQ, DL_ATTACH_REQ_SIZE },
2N/A{ DL_BIND_REQ, DL_BIND_REQ_SIZE },
2N/A{ DL_BIND_ACK, DL_BIND_ACK_SIZE + DLPI_PHYSADDR_MAX +
2N/A DLPI_SAPLEN_MAX },
2N/A{ DL_UNBIND_REQ, DL_UNBIND_REQ_SIZE },
2N/A{ DL_ENABMULTI_REQ, DL_ENABMULTI_REQ_SIZE + DLPI_PHYSADDR_MAX },
2N/A{ DL_DISABMULTI_REQ, DL_DISABMULTI_REQ_SIZE + DLPI_PHYSADDR_MAX },
2N/A{ DL_PROMISCON_REQ, DL_PROMISCON_REQ_SIZE },
2N/A{ DL_PROMISCOFF_REQ, DL_PROMISCOFF_REQ_SIZE },
2N/A{ DL_PASSIVE_REQ, DL_PASSIVE_REQ_SIZE },
2N/A{ DL_UNITDATA_REQ, DL_UNITDATA_REQ_SIZE + DLPI_PHYSADDR_MAX +
2N/A DLPI_SAPLEN_MAX },
2N/A{ DL_UNITDATA_IND, DL_UNITDATA_IND_SIZE + (2 * (DLPI_PHYSADDR_MAX +
2N/A DLPI_SAPLEN_MAX)) },
2N/A{ DL_PHYS_ADDR_REQ, DL_PHYS_ADDR_REQ_SIZE },
2N/A{ DL_PHYS_ADDR_ACK, DL_PHYS_ADDR_ACK_SIZE + DLPI_PHYSADDR_MAX },
2N/A{ DL_SET_PHYS_ADDR_REQ, DL_SET_PHYS_ADDR_REQ_SIZE + DLPI_PHYSADDR_MAX },
2N/A{ DL_OK_ACK, MAX(DL_ERROR_ACK_SIZE, DL_OK_ACK_SIZE) },
2N/A{ DL_NOTIFY_REQ, DL_NOTIFY_REQ_SIZE },
2N/A{ DL_NOTIFY_ACK, MAX(DL_ERROR_ACK_SIZE, DL_NOTIFY_ACK_SIZE) },
2N/A{ DL_NOTIFY_IND, DL_NOTIFY_IND_SIZE + DLPI_PHYSADDR_MAX +
2N/A DLPI_SAPLEN_MAX }
2N/A};
2N/A
2N/A/*
2N/A * Refers to the dlpi_primsizes[] table to return corresponding maximum
2N/A * buffer size.
2N/A */
2N/Astatic size_t
2N/Ai_dlpi_getprimsize(t_uscalar_t prim)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < NELEMS(dlpi_primsizes); i++) {
2N/A if (dlpi_primsizes[i].dp_prim == prim)
2N/A return (dlpi_primsizes[i].dp_primsz);
2N/A }
2N/A
2N/A return (sizeof (t_uscalar_t));
2N/A}
2N/A
2N/A/*
2N/A * sap values vary in length and are in host byte order, build sap value
2N/A * by writing saplen bytes, so that the sap value is left aligned.
2N/A */
2N/Astatic uint_t
2N/Ai_dlpi_buildsap(uint8_t *sapp, uint_t saplen)
2N/A{
2N/A int i;
2N/A uint_t sap = 0;
2N/A
2N/A#ifdef _LITTLE_ENDIAN
2N/A for (i = saplen - 1; i >= 0; i--) {
2N/A#else
2N/A for (i = 0; i < saplen; i++) {
2N/A#endif
2N/A sap <<= 8;
2N/A sap |= sapp[i];
2N/A }
2N/A
2N/A return (sap);
2N/A}
2N/A
2N/A/*
2N/A * Copy sap value to a buffer in host byte order. saplen is the number of
2N/A * bytes to copy.
2N/A */
2N/Astatic void
2N/Ai_dlpi_writesap(void *dstbuf, uint_t sap, uint_t saplen)
2N/A{
2N/A uint8_t *sapp;
2N/A
2N/A#ifdef _LITTLE_ENDIAN
2N/A sapp = (uint8_t *)&sap;
2N/A#else
2N/A sapp = (uint8_t *)&sap + (sizeof (sap) - saplen);
2N/A#endif
2N/A
2N/A (void) memcpy(dstbuf, sapp, saplen);
2N/A}
2N/A
2N/A/*
2N/A * Fill notification payload and callback each registered functions.
2N/A * Delete nodes if any was called while processing.
2N/A */
2N/Astatic int
2N/Ai_dlpi_notifyind_process(dlpi_impl_t *dip, dl_notify_ind_t *dlnotifyindp)
2N/A{
2N/A dlpi_notifyinfo_t notifinfo;
2N/A t_uscalar_t dataoff, datalen;
2N/A caddr_t datap;
2N/A dlpi_notifyent_t *dnp;
2N/A uint_t note = dlnotifyindp->dl_notification;
2N/A uint_t deletenode = B_FALSE;
2N/A dl_fc_info_t fcinfo;
2N/A
2N/A notifinfo.dni_note = note;
2N/A
2N/A switch (note) {
2N/A case DL_NOTE_SPEED:
2N/A notifinfo.dni_speed = dlnotifyindp->dl_data;
2N/A break;
2N/A case DL_NOTE_SDU_SIZE:
2N/A notifinfo.dni_size = dlnotifyindp->dl_data;
2N/A break;
2N/A case DL_NOTE_FC_MODE:
2N/A dataoff = dlnotifyindp->dl_addr_offset;
2N/A datalen = dlnotifyindp->dl_addr_length;
2N/A
2N/A if (dataoff == 0 || datalen == 0 ||
2N/A dataoff < DL_NOTIFY_IND_SIZE) {
2N/A return (DLPI_EBADMSG);
2N/A }
2N/A datap = (caddr_t)dlnotifyindp + dataoff;
2N/A (void) memcpy(&fcinfo, datap, sizeof (dl_fc_info_t));
2N/A notifinfo.dni_fc_mode = fcinfo.dfi_mode;
2N/A notifinfo.dni_fc_pfc = fcinfo.dfi_pfc;
2N/A notifinfo.dni_fc_ntc = fcinfo.dfi_ntc;
2N/A break;
2N/A case DL_NOTE_ETSBAT:
2N/A dataoff = dlnotifyindp->dl_addr_offset;
2N/A datalen = dlnotifyindp->dl_addr_length;
2N/A
2N/A if (dataoff == 0 || datalen == 0 ||
2N/A dataoff < DL_NOTIFY_IND_SIZE) {
2N/A return (DLPI_EBADMSG);
2N/A }
2N/A datap = (caddr_t)dlnotifyindp + dataoff;
2N/A (void) memcpy(notifinfo.dni_dcbbw, datap,
2N/A sizeof (notifinfo.dni_dcbbw));
2N/A break;
2N/A case DL_NOTE_PHYS_ADDR:
2N/A /*
2N/A * libdlpi currently only supports notifications for
2N/A * DL_CURR_PHYS_ADDR.
2N/A */
2N/A if (dlnotifyindp->dl_data != DL_CURR_PHYS_ADDR)
2N/A return (DLPI_ENOTENOTSUP);
2N/A
2N/A dataoff = dlnotifyindp->dl_addr_offset;
2N/A datalen = dlnotifyindp->dl_addr_length;
2N/A
2N/A if (dataoff == 0 || datalen == 0)
2N/A return (DLPI_EBADMSG);
2N/A
2N/A datap = (caddr_t)dlnotifyindp + dataoff;
2N/A if (dataoff < DL_NOTIFY_IND_SIZE)
2N/A return (DLPI_EBADMSG);
2N/A
2N/A notifinfo.dni_physaddrlen = datalen - dip->dli_saplen;
2N/A
2N/A if (notifinfo.dni_physaddrlen > DLPI_PHYSADDR_MAX)
2N/A return (DL_BADADDR);
2N/A
2N/A (void) memcpy(notifinfo.dni_physaddr, datap,
2N/A notifinfo.dni_physaddrlen);
2N/A break;
2N/A }
2N/A
2N/A dip->dli_note_processing = B_TRUE;
2N/A
2N/A for (dnp = dip->dli_notifylistp; dnp != NULL; dnp = dnp->dln_next) {
2N/A if (note & dnp->dln_notes)
2N/A dnp->dln_fnp((dlpi_handle_t)dip, &notifinfo, dnp->arg);
2N/A if (dnp->dln_rm)
2N/A deletenode = B_TRUE;
2N/A }
2N/A
2N/A dip->dli_note_processing = B_FALSE;
2N/A
2N/A /* Walk the notifyentry list to unregister marked entries. */
2N/A if (deletenode)
2N/A i_dlpi_deletenotifyid(dip);
2N/A
2N/A return (DLPI_SUCCESS);
2N/A}
2N/A/*
2N/A * Find registered notification.
2N/A */
2N/Astatic boolean_t
2N/Ai_dlpi_notifyidexists(dlpi_impl_t *dip, dlpi_notifyent_t *id)
2N/A{
2N/A dlpi_notifyent_t *dnp;
2N/A
2N/A for (dnp = dip->dli_notifylistp; dnp != NULL; dnp = dnp->dln_next) {
2N/A if (id == dnp)
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * Walk the list of notifications and deleted nodes marked to be deleted.
2N/A */
2N/Astatic void
2N/Ai_dlpi_deletenotifyid(dlpi_impl_t *dip)
2N/A{
2N/A dlpi_notifyent_t *prev, *dnp;
2N/A
2N/A prev = NULL;
2N/A dnp = dip->dli_notifylistp;
2N/A while (dnp != NULL) {
2N/A if (!dnp->dln_rm) {
2N/A prev = dnp;
2N/A dnp = dnp->dln_next;
2N/A } else if (prev == NULL) {
2N/A dip->dli_notifylistp = dnp->dln_next;
2N/A free(dnp);
2N/A dnp = dip->dli_notifylistp;
2N/A } else {
2N/A prev->dln_next = dnp->dln_next;
2N/A free(dnp);
2N/A dnp = prev->dln_next;
2N/A }
2N/A }
2N/A}