/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* IDN DLPI support (based on QE implementation).
*/
#ifdef xxx_trace
#endif /* xxx_trace */
#ifdef IPV6
#else
#define IS_ETHERTYPE_IPV6(x) (0)
#endif /* IPV6 */
#ifdef IDN_TRACE
/*
*/
/*
* TR_FAC_IDN tags
*/
#define TR_IDN_OPEN 0
#else /* IDN_TRACE */
#endif /* IDN_TRACE */
#ifdef DEBUG
{ \
PR_DLPI("dlpi: ERRORACK: 0x%x(%s), err = 0x%x(%s)\n", \
}
{ \
}
{ \
PR_DLPI("dlpi: BINDACK: eth=%x:%x:%x:%x:%x:%x, sap=0x%x, l=%d\n", \
}
{ \
PR_DLPI("dlpi: PHYSACK: eth=%x:%x:%x:%x:%x:%x, l=%d\n", \
(ll)); \
}
static char *dlerrstr[] = {
"DL_BADSAP",
"DL_BADADDR",
"DL_ACCESS",
"DL_OUTSTATE",
"DL_SYSERR",
"DL_BADCORR",
"DL_BADDATA",
"DL_UNSUPPORTED",
"DL_BADPPA",
"DL_BADPRIM",
"DL_BADQOSPARAM",
"DL_BADQOSTYPE",
"DL_BADTOKEN",
"DL_BOUND",
"DL_INITFAILED",
"DL_NOADDR",
"DL_NOTINIT",
"DL_UNDELIVERABLE",
"DL_NOTSUPPORTED",
"DL_TOOMANY",
"DL_NOTENAB",
"DL_BUSY",
"DL_NOAUTO",
"DL_NOXIDAUTO",
"DL_NOTESTAUTO",
"DL_XIDAUTO",
"DL_TESTAUTO",
"DL_PENDING"
};
static char *
{
return ("unknown");
else
}
static char *
{
char *pstr;
switch (prim) {
case DL_SET_PHYS_ADDR_REQ:
pstr = "SET_PHYS_ADDR_REQ"; break;
default: pstr = "unsupported"; break;
}
return (pstr);
}
#else /* DEBUG */
#endif /* DEBUG */
/*
*/
if (IS_BROADCAST(ehp)) { \
} else if (IS_MULTICAST(ehp)) { \
}
if (IS_BROADCAST(ehp)) { \
} else if (IS_MULTICAST(ehp)) { \
}
/*
* Function prototypes.
*/
#ifdef notdef
#endif /* notdef */
struct ether_addr *, int, ulong_t);
static void idndl_setipq(struct idn *);
/*
* Our DL_INFO_ACK template.
*/
DL_INFO_ACK, /* dl_primitive */
0, /* dl_max_sdu (see idndl_dlpi_init()) */
0, /* dl_min_sdu */
IDNADDRL, /* dl_addr_length */
0, /* dl_reserved */
0, /* dl_current_state */
-2, /* dl_sap_length */
0, /* dl_qos_length */
0, /* dl_qos_offset */
0, /* dl_range_length */
0, /* dl_range_offset */
DL_STYLE2, /* dl_provider_style */
sizeof (dl_info_ack_t), /* dl_addr_offset */
DL_VERSION_2, /* dl_version */
ETHERADDRL, /* dl_brdcst_addr_length */
0 /* dl_growth */
};
/*
* Ethernet broadcast address definition.
*/
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/*
* --------------------------------------------------
*/
void
{
int rv;
int instance;
}
int
{
return (-1);
PR_DLPI("%s: dnetid = 0x%x, channel = 0x%x\n",
#ifdef notdef
PR_DLPI("%s: localetheraddr = %x:%x:%x:%x:%x:%x\n",
#endif /* notdef */
PR_DLPI("%s: domain %d: etheraddr = %x:%x:%x:%x:%x:%x\n",
return (0);
}
#ifdef DEBUG
/*
*/
static int
{
/*
* Note that (IDN_NIL_DOMID) will be returned if ether address is
* a broadcast 0xff.
*/
}
/*
*/
static struct idn *
{
int instance;
if (!IDNDL_ADDR_IS_MULTICAST(eap) &&
"IDN: 400: corrupted MAC header "
"(exp %x or 0xffff, act 0x%x)",
(IDNETHER_COOKIE1_VAL << 8) |
return (NULL);
}
if (IDNDL_ADDR_IS_MULTICAST(eap)) {
/*
* Received a broadcast. Need to manually
* find anybody the first running sip and use it.
* XXX - kind of kludgy - single threads broadcasts.
*/
} else {
}
return (sip);
}
#endif /* DEBUG */
void
{
PR_DLPI("%s: setting dl_max_sdu to %ld (0x%lx) bytes\n",
/*
* This field is dynamic because the user may
* want to dynamically set it _before_ an IDNnet
* has been established via ndd(1M).
*/
}
static int
{
if (rw == KSTAT_WRITE) {
#if 0
#endif /* 0 */
/*
* MIB II kstat variables
*/
/*
* PSARC 1997/198 : 64bit kstats
*/
/*
* PSARC 1997/247 : RFC 1643
*/
return (0);
}
/*
* MIB II kstat variables
*/
/*
* PSARC 1997/198 : 64bit kstats
*/
/*
* PSARC 1997/247 : RFC 1643
*/
return (0);
}
void
{
#ifdef kstat
sizeof (struct idn_kstat_named) / sizeof (kstat_named_t),
KSTAT_FLAG_PERSISTENT)) == NULL) {
#else
sizeof (struct idn_kstat_named) /
sizeof (kstat_named_t), 0)) == NULL) {
#endif /* kstat */
return;
}
/*
* MIB II kstat variables
*/
/*
* PSARC 1997/198 : 64bit kstats
*/
/*
* PSARC 1997/247 : RFC 1643
*/
}
void
{
/*
* Gotta at least have enough room to hold
* the primitive!
*/
return;
}
#ifdef DEBUG
PR_DLPI("%s: stp = 0x%p, wq = 0x%p, dlprim = 0x%x(%s)\n",
#endif /* DEBUG */
switch (prim) {
case DL_UNITDATA_REQ:
break;
case DL_ATTACH_REQ:
break;
case DL_DETACH_REQ:
break;
case DL_BIND_REQ:
break;
case DL_UNBIND_REQ:
break;
case DL_INFO_REQ:
break;
case DL_PROMISCON_REQ:
break;
case DL_PROMISCOFF_REQ:
break;
case DL_ENABMULTI_REQ:
break;
case DL_DISABMULTI_REQ:
break;
case DL_PHYS_ADDR_REQ:
break;
#ifdef notdef
/*
* We cannot allow this in IDN-land because we
* rely on the ethernet (physical) address to determine
* where to target the message. Recall that unlike
* ethernet we simply cannot dump junk on the wire and
* expect it to automatically find its destination.
* In the IDN we need to target the destination.
* Note that if we used POINT-TO-POINT then we wouldn't
* have to worry about the physical address since each
* domain connection would have a separate queue.
* However, ptp then requires multiple interfaces at
* the appl level as opposed to a single one for all
* of idn. We opt for the simpler single interface (idn0).
*/
case DL_SET_PHYS_ADDR_REQ:
break;
#endif /* notdef */
default:
break;
}
}
int
{
int rv = 0;
*argsizep = 0;
case DLIOCRAW: /* raw M_DATA mode */
break;
case DL_IOC_HDR_INFO: /* M_DATA "fastpath" info request */
break;
default:
break;
}
return (rv);
}
/*
* M_DATA "fastpath" info request.
* Following the M_IOCTL mblk should come a DL_UNITDATA_REQ mblk.
* We ack with an M_IOCACK pointing to the original DL_UNITDATA_REQ mblk
* followed by an mblk containing the raw ethernet header corresponding
* to the destination address. Subsequently, we may receive M_DATA
* msgs which start with this header and may send up
* up M_DATA msgs with b_rptr pointing to a (ulong) group address
* indicator followed by the network-layer data (IP packet header).
* This is all selectable on a per-Stream basis.
*/
static int
{
int padding = 0;
int error;
return (EINVAL);
}
if (error != 0) {
return (error);
}
/*
* Sanity check the DL_UNITDATA_REQ destination address
* offset and length values.
*/
PR_DLPI("%s: off(0x%x)/len(%d) error (ret EINVAL)\n",
return (EINVAL);
}
/*
* Allocate a new mblk to hold the ether header.
*/
return (ENOMEM);
}
/*
* Fill in the ether header.
*/
/*
* Link new mblk in after the "request" mblks.
*/
/*
* XXX Don't bother calling idndl_setipq() here.
*/
if (argsizep)
return (0);
}
static void
{
int ppa;
return;
}
return;
}
/*
* Valid ppa?
*/
return;
}
break;
}
/*
* Has device been initialized? Do so if necessary.
*/
if (idndl_init(sip)) {
DL_INITFAILED, 0);
return;
}
}
/*
* Set link to device and update our state.
*/
}
static void
{
return;
}
return;
}
}
/*
* Detach a Stream from an interface.
*/
void
{
int reinit = 0;
/*
* Disable promiscuous mode if on.
*/
reinit = 1;
}
/*
* Disable ALLMULTI mode if on.
*/
reinit = 1;
}
/*
* Disable any Multicast Addresses.
*/
stp->ss_mccount = 0;
reinit = 1;
}
/*
* Detach from device structure.
* Uninit the device when no other streams are attached to it.
*/
break;
else if (reinit)
(void) idndl_init(sip);
}
static void
{
int xidtest;
return;
}
return;
}
return;
}
if (xidtest) {
return;
}
if (sap > ETHERTYPE_MAX) {
return;
}
/*
* Save SAP value for this Stream and change state.
*/
if (IS_ETHERTYPE_IP(sap)) {
int channel;
channel =
if (idn_open_channel(channel)) {
PR_DLPI("%s: FAILED TO OPEN CHANNEL %d\n",
return;
}
}
}
static void
{
return;
}
return;
}
int channel;
channel =
/*
* We need to do an "soft" close since there's a
* potential that we've been called by one of the
* IDN data server/dispatcher threads! We'll deadlock
* if we attempt a "hard" close of the channel from here.
*/
}
}
static void
{
int size;
return;
}
/*
* Exchange current msg for a DL_INFO_ACK.
*/
return;
/*
* Fill in the DL_INFO_ACK fields and reply.
*/
*dlip = idninfoack;
} else {
}
}
static void
{
return;
}
case DL_PROMISC_PHYS:
break;
case DL_PROMISC_SAP:
break;
case DL_PROMISC_MULTI:
break;
default:
return;
}
}
static void
{
int flag;
return;
}
case DL_PROMISC_PHYS:
flag = IDNSALLPHYS;
break;
case DL_PROMISC_SAP:
flag = IDNSALLSAP;
break;
case DL_PROMISC_MULTI:
flag = IDNSALLMULTI;
break;
default:
return;
}
return;
}
}
static void
{
int off;
int len;
int i;
return;
}
return;
}
if ((len != ETHERADDRL) ||
return;
}
return;
}
/*
* Allocate table on first request.
*/
/*
* Check to see if the address is already in the table.
* Bug 1209733:
* If present in the table, add the entry to the end of the table
* and return without initializing the hardware.
*/
for (i = 0; i < stp->ss_mccount; i++) {
return;
}
}
}
static void
{
int off;
int len;
int i;
return;
}
return;
}
return;
}
/*
* Find the address in the multicast table for this Stream
* and delete it by shifting all subsequent multicast
* table entries over one.
*/
for (i = 0; i < stp->ss_mccount; i++)
((stp->ss_mccount - i) *
sizeof (struct ether_addr)));
stp->ss_mccount--;
return;
}
}
static void
{
int type;
return;
}
return;
}
switch (type) {
case DL_FACT_PHYS_ADDR:
break;
case DL_CURR_PHYS_ADDR:
break;
default:
return;
}
}
#ifdef notdef
static void
{
int off;
int len;
return;
}
return;
}
/*
* Error if length of address isn't right or the address
* specified is a multicast or broadcast address.
*/
if ((len != ETHERADDRL) ||
return;
}
/*
* Error if this stream is not attached to a device.
*/
return;
}
/*
* Set new interface local address and re-init device.
* This is destructive to any other streams attached
* to this device.
*/
}
#endif /* notdef */
static void
{
return;
}
/*
* Validate destination address format.
*/
return;
}
/*
* Error if no M_DATA follows.
*/
return;
}
/*
* Create ethernet header by either prepending it onto the
* next mblk if potential, or reusing the M_PROTO block if not.
*/
} else {
}
/*
* For transmitting, the driver looks at the
* sap field of the DL_BIND_REQ being 0 in addition to the type
* field in the range [0-1500]. If either is true, then the driver
* computes the length of the message, not including initial M_PROTO
* mblk (message block), of all subsequent DL_UNITDATA_REQ messages and
* transmits 802.3 frames that have this value in the MAC frame header
* length field.
*/
sizeof (struct ether_header));
else
/*
* The data transfer code requires only READ access (idn_wput_data).
*/
}
int
{
int rv = 0;
int flags;
int broadcast = 0;
int goagain = 0;
int goqueue = 0;
int msgcount;
char channel;
int domid;
if (!(flags & IDNRUNNING))
goto requeue;
}
/*
* Translate an IDN ethernet address into a domainid
* and idnaddr.
*/
/*
* update MIB II statistics
*/
PR_DLPI("%s: ether %x:%x:%x:%x:%x:%x (domid = %d)\n",
domid);
/*
* Caller wants to broadcast!
* XXX - Send to everybody but ourself???
*/
broadcast = 1;
if ((flags & IDNPROMISC) &&
}
} else if (domid != IDN_NIL_DOMID) {
if ((flags & IDNPROMISC) &&
}
} else {
#ifdef DEBUG
int netid;
netid = (int)
PR_DLPI("%s: no domain found for netid 0x%x\n",
#endif /* DEBUG */
goto bad;
}
PR_DLPI("%s: not connected to any domains!! Bailing\n",
proc);
goto bad;
}
/*
* XXX - Need to find a better way to handle broadcasting.
* Should be able to take advantage of the fact that
* we can broadcast XDC's (xdc_some). Need to use
* atomic counter (semaphore) instead of binary
* "owner" flag, or perhaps domain specific owner bytes.
*
* Transfer the data.
*/
msgcount = 0;
if (!broadcast)
goto noloop;
continue;
if (broadcast)
continue;
else
break;
}
switch (rv) {
case IDNXMIT_LOOP: /* handled in loopback */
msgcount++;
break;
case IDNXMIT_OKAY: /* handled, okay to free */
msgcount++;
break;
case IDNXMIT_RETRY:
if (!broadcast)
goto tryagain;
goagain++;
break;
case IDNXMIT_REQUEUE:
if (!broadcast)
goto requeue;
goqueue++;
break;
default:
if (!broadcast)
goto bad;
break;
}
if (!broadcast)
break;
}
if (msgcount == 0)
if (goqueue)
goto requeue;
else if (goagain)
goto tryagain;
else
goto bad;
PR_DLPI("%s: successful transmit to domainset 0x%x.\n",
return (0);
bad:
PR_DLPI("%s: bad transmission to domainset 0x%x, dropping msg.\n",
if (nmp)
return (1);
PR_DLPI("%s: requeue for domainset 0x%x, no qenable\n",
if (nmp)
return (1);
PR_DLPI("%s: try again to domainset 0x%x, putbq.\n",
if (nmp)
return (1);
}
/*
* Called by: idnh_recv_data, idn_recv_mboxdata.
*/
void
{
int pktlen;
PR_DLPI("%s: incoming msgsize = %lu, msgdsize = %lu\n",
/*
* If the sip is NULL, then I don't have a connection
* for this network. No point in sending the message
* up.
*/
PR_DLPI("%s: no plumbing to send message through.\n",
proc);
return;
}
/*
* update MIB II statistics
*/
ip4q &&
canputnext(ip4q)) {
/*LINTED*/
ip6q &&
canputnext(ip6q)) {
} else {
/*
* Strip the PADs for 802.3
*/
PR_DLPI("%s: stripping PADs for 802.3 (pktlen=%d)\n",
}
}
int
{
break;
}
}
}
void
{
int channel;
/*
* A uninit is a hard close of an interface.
*/
}
/*
* Send packet upstream.
* Assume mp->b_rptr points to ether_header.
*/
void
{
int type;
/*
* While holding a reader lock on the linked list of streams structures,
* attempt to match the address criteria for each stream
* and pass up the raw M_DATA ("fastpath") or a DL_UNITDATA_IND.
*/
return;
}
/*
* Loop on matching open streams until (*acceptfunc)() returns NULL.
*/
continue;
}
if (nmp) {
}
} else {
}
}
/*
* Do the last one.
*/
type, isgroupaddr))) {
}
} else {
}
}
/*
* Test upstream destination sap and address match.
*/
struct idnstr *
{
(flags & IDNSALLPHYS) ||
return (stp);
}
return (NULL);
}
/*
* Test upstream destination sap and address match for IDNSALLPHYS only.
*/
/* ARGSUSED3 */
struct idnstr *
{
(flags & IDNSALLPHYS))
return (stp);
}
return (NULL);
}
/*
* Set or clear the device ipq pointer.
* Assumes IPv4 and IPv6 are IDNSFAST.
*/
static void
{
ok4 = 0;
ok6 = 0;
break;
}
else
ok4 = 0;
/*LINTED*/
else
ok6 = 0;
}
}
}
if (ok4)
else
if (ok6)
else
}
/*
* Prefix msg with a DL_UNITDATA_IND mblk and return the new msg.
*/
static mblk_t *
{
int size;
/*
* Allocate an M_PROTO mblk for the DL_UNITDATA_IND.
*/
if (idn_debug)
return (NULL);
}
/*
* Construct a DL_UNITDATA_IND primitive.
*/
+ IDNADDRL);
/*
* Link the M_PROTO and M_DATA together.
*/
return (nmp);
}
/*
* Return TRUE if the given multicast address is one
* of those that this particular Stream is interested in.
*/
static int
{
register int mccount;
register int i;
/*
* Return FALSE if not a multicast address.
*/
if (!IDNDL_ADDR_IS_MULTICAST(addrp))
return (0);
/*
* Check if all multicasts have been enabled for this Stream
*/
return (1);
/*
* Return FALSE if no multicast addresses enabled for this Stream.
*/
if (stp->ss_mccount == 0)
return (0);
/*
* Otherwise, find it in the table.
*/
for (i = 0; i < mccount; i++)
return (1);
return (0);
}
/*
* Start xmit on any msgs previously enqueued on any write queues.
* If the caller passes NULL, then we need to check all
* our interfaces.
*/
void
{
/*
* Order of wantw accesses is important.
*/
do {
if (sip)
}
}
/*VARARGS*/
static void
{
static long last;
static char *lastfmt;
/*
* Don't print same error message too often.
*/
now = gethrestime_sec();
return;
}