gldutil.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"
/*
* gld - Generic LAN Driver
* media dependent routines
*/
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/kmem.h>
#include <sys/stat.h>
#include <sys/modctl.h>
#include <sys/kstat.h>
#include <sys/debug.h>
#include <sys/byteorder.h>
#include <sys/strsun.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <sys/multidata.h>
#include <sys/gld.h>
#include <sys/gldpriv.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sysmacros.h>
#include <sys/ib/clients/ibd/ibd.h>
#include <sys/pattr.h>
#define DLSAPLENGTH(macinfo) \
((macinfo)->gldm_addrlen + ABS((macinfo)->gldm_saplen))
#ifdef GLD_DEBUG
extern int gld_debug;
#endif
extern void gld_bitrevcopy(caddr_t src, caddr_t target, size_t n);
extern char *gld_macaddr_sprintf(char *, unsigned char *, int);
extern gld_vlan_t *gld_find_vlan(gld_mac_info_t *, uint32_t);
extern uint32_t gld_global_options;
static struct llc_snap_hdr llc_snap_def = {
LSAP_SNAP, /* DLSAP 0xaa */
LSAP_SNAP, /* SLSAP 0xaa */
CNTL_LLC_UI, /* Control 0x03 */
0x00, 0x00, 0x00, /* Org[3] */
0x00 /* Type */
};
#define ISETHERTYPE(snaphdr) \
(snaphdr->d_lsap == LSAP_SNAP && \
snaphdr->s_lsap == LSAP_SNAP && \
snaphdr->control == CNTL_LLC_UI && \
snaphdr->org[0] == 0 && \
snaphdr->org[1] == 0 && \
snaphdr->org[2] == 0)
/* ======== */
/* Ethernet */
/* ======== */
static mac_addr_t ether_broadcast = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
void
gld_init_ether(gld_mac_info_t *macinfo)
{
struct gldkstats *sp =
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->kstatp->ks_data;
/* Assumptions we make for this medium */
ASSERT(macinfo->gldm_type == DL_ETHER);
ASSERT(macinfo->gldm_addrlen == 6);
ASSERT(macinfo->gldm_saplen == -2);
#ifndef lint
ASSERT(sizeof (struct ether_mac_frm) == 14);
ASSERT(sizeof (mac_addr_t) == 6);
#endif
kstat_named_init(&sp->glds_frame, "align_errors", KSTAT_DATA_ULONG);
kstat_named_init(&sp->glds_crc, "fcs_errors", KSTAT_DATA_ULONG);
kstat_named_init(&sp->glds_collisions, "collisions", KSTAT_DATA_ULONG);
kstat_named_init(&sp->glds_nocarrier, "carrier_errors",
KSTAT_DATA_ULONG);
kstat_named_init(&sp->glds_defer, "defer_xmts", KSTAT_DATA_ULONG);
kstat_named_init(&sp->glds_xmtlatecoll, "tx_late_collisions",
KSTAT_DATA_ULONG);
kstat_named_init(&sp->glds_short, "runt_errors", KSTAT_DATA_ULONG);
kstat_named_init(&sp->glds_excoll, "ex_collisions", KSTAT_DATA_ULONG);
/*
* only initialize the new statistics if the driver
* knows about them.
*/
if (macinfo->gldm_driver_version != GLD_VERSION_200)
return;
kstat_named_init(&sp->glds_dot3_first_coll,
"first_collisions", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot3_multi_coll,
"multi_collisions", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot3_sqe_error,
"sqe_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot3_mac_xmt_error,
"macxmt_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot3_mac_rcv_error,
"macrcv_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot3_frame_too_long,
"toolong_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_duplex, "duplex", KSTAT_DATA_CHAR);
}
/*ARGSUSED*/
void
gld_uninit_ether(gld_mac_info_t *macinfo)
{
}
int
gld_interpret_ether(gld_mac_info_t *macinfo, mblk_t *mp, pktinfo_t *pktinfo,
packet_flag_t flags)
{
struct ether_mac_frm *mh;
gld_mac_pvt_t *mac_pvt = (gld_mac_pvt_t *)macinfo->gldm_mac_pvt;
struct llc_snap_hdr *snaphdr;
mblk_t *pmp = NULL;
unsigned short typelen;
/*
* Quickly handle receive fastpath for IPQ hack.
*/
if (flags == GLD_RXQUICK) {
pktinfo->pktLen = msgdsize(mp);
/*
* Check whether the header is contiguous, which
* also implicitly makes sure the packet is big enough.
*/
if (MBLKL(mp) < sizeof (struct ether_mac_frm))
return (-1);
mh = (struct ether_mac_frm *)mp->b_rptr;
pktinfo->ethertype = REF_NET_USHORT(mh->ether_type);
pktinfo->isForMe = mac_eq(mh->ether_dhost,
mac_pvt->curr_macaddr, macinfo->gldm_addrlen);
pktinfo->macLen = sizeof (struct ether_mac_frm);
return (0);
}
bzero((void *)pktinfo, sizeof (*pktinfo));
pktinfo->pktLen = msgdsize(mp);
/* make sure packet has at least a whole mac header */
if (pktinfo->pktLen < sizeof (struct ether_mac_frm))
return (-1);
/* make sure the mac header falls into contiguous memory */
if (MBLKL(mp) < sizeof (struct ether_mac_frm)) {
if ((pmp = msgpullup(mp, -1)) == NULL) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"GLD: interpret_ether cannot msgpullup");
#endif
return (-1);
}
mp = pmp; /* this mblk contains the whole mac header */
}
mh = (struct ether_mac_frm *)mp->b_rptr;
/* Check to see if the mac is a broadcast or multicast address. */
if (mac_eq(mh->ether_dhost, ether_broadcast, macinfo->gldm_addrlen))
pktinfo->isBroadcast = 1;
else if (mh->ether_dhost[0] & 1)
pktinfo->isMulticast = 1;
typelen = REF_NET_USHORT(mh->ether_type);
/*
* If the hardware is capable of VLAN tag insertion
* strip out the VLAN tag info. Knowing hardware is
* capable of VLAN can be established by the presance
* of non null 'macinfo->gldm_send_tagged'.
*/
if (flags == GLD_TX) {
if ((typelen == VLAN_TPID) &&
(macinfo->gldm_send_tagged != NULL)) {
ovbcopy(mp->b_rptr,
mp->b_rptr + VTAG_SIZE,
2 * ETHERADDRL);
mp->b_rptr += VTAG_SIZE;
}
goto out; /* Got all info we need for xmit case */
}
ASSERT(GLDM_LOCK_HELD(macinfo));
/*
* Deal with the mac header
*/
mac_copy(mh->ether_dhost, pktinfo->dhost, macinfo->gldm_addrlen);
mac_copy(mh->ether_shost, pktinfo->shost, macinfo->gldm_addrlen);
pktinfo->isLooped = mac_eq(pktinfo->shost,
mac_pvt->curr_macaddr, macinfo->gldm_addrlen);
pktinfo->isForMe = mac_eq(pktinfo->dhost,
mac_pvt->curr_macaddr, macinfo->gldm_addrlen);
pktinfo->macLen = sizeof (struct ether_mac_frm);
if (typelen > ETHERMTU) {
pktinfo->ethertype = typelen; /* use type interpretation */
goto out;
}
/*
* Packet is 802.3 so the ether type/length field
* specifies the number of bytes that should be present
* in the data field. Additional bytes are padding, and
* should be removed
*/
{
int delta = pktinfo->pktLen -
(sizeof (struct ether_mac_frm) + typelen);
if (delta > 0 && adjmsg(mp, -delta))
pktinfo->pktLen -= delta;
}
/*
* Before trying to look beyond the MAC header, make sure the LLC
* header exists, and that both it and any SNAP header are contiguous.
*/
if (pktinfo->pktLen < pktinfo->macLen + LLC_HDR1_LEN)
goto out; /* LLC hdr should have been there! */
pktinfo->isLLC = 1;
if (gld_global_options & GLD_OPT_NO_ETHRXSNAP ||
pktinfo->pktLen < pktinfo->macLen + LLC_SNAP_HDR_LEN)
goto out;
if (MBLKL(mp) < sizeof (struct ether_mac_frm) + LLC_SNAP_HDR_LEN &&
MBLKL(mp) < pktinfo->pktLen) {
/*
* we don't have the entire packet within the first mblk (and
* therefore we didn't do the msgpullup above), AND the first
* mblk may not contain all the data we need to look at.
*/
ASSERT(pmp == NULL); /* couldn't have done msgpullup above */
if ((pmp = msgpullup(mp, -1)) == NULL) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"GLD: interpret_ether cannot msgpullup2");
#endif
goto out; /* can't interpret this pkt further */
}
mp = pmp; /* this mblk should contain everything needed */
}
/*
* Check SAP/SNAP information for EtherType.
*/
snaphdr = (struct llc_snap_hdr *)(mp->b_rptr + pktinfo->macLen);
if (ISETHERTYPE(snaphdr)) {
pktinfo->ethertype = REF_NET_USHORT(snaphdr->type);
pktinfo->hdrLen = LLC_SNAP_HDR_LEN;
}
out:
if (pmp != NULL)
freemsg(pmp);
return (0);
}
mblk_t *
gld_unitdata_ether(gld_t *gld, mblk_t *mp)
{
gld_mac_info_t *macinfo = gld->gld_mac_info;
dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr;
struct gld_dlsap *gldp = DLSAP(dlp, dlp->dl_dest_addr_offset);
mac_addr_t dhost;
unsigned short typelen;
mblk_t *nmp;
struct ether_mac_frm *mh;
int hdrlen;
uint32_t vptag;
gld_vlan_t *gld_vlan;
ASSERT(macinfo);
/* extract needed info from the mblk before we maybe reuse it */
mac_copy(gldp->glda_addr, dhost, macinfo->gldm_addrlen);
/* look in the unitdata request for a sap, else use bound one */
if (dlp->dl_dest_addr_length >= DLSAPLENGTH(macinfo) &&
REF_HOST_USHORT(gldp->glda_sap) != 0)
typelen = REF_HOST_USHORT(gldp->glda_sap);
else
typelen = gld->gld_sap;
/*
* We take values less than or equal to ETHERMTU to mean that the
* packet should not have an encoded EtherType and so we use the
* IEEE 802.3 length interpretation of the type/length field.
*/
if (typelen <= ETHERMTU)
typelen = msgdsize(mp);
hdrlen = sizeof (struct ether_mac_frm);
/*
* Check to see if VLAN is enabled on this stream
* if so then make the header bigger to hold a clone
* vlan tag.
*/
gld_vlan = (gld_vlan_t *)gld->gld_vlan;
if (gld_vlan && (gld_vlan->gldv_id != VLAN_VID_NONE)) {
hdrlen += VTAG_SIZE;
vptag = gld_vlan->gldv_ptag;
}
/* need a buffer big enough for the headers */
nmp = mp->b_cont; /* where the packet payload M_DATA is */
if (DB_REF(nmp) == 1 && MBLKHEAD(nmp) >= hdrlen) {
/* it fits at the beginning of the first M_DATA block */
freeb(mp); /* don't need the M_PROTO anymore */
} else if (DB_REF(mp) == 1 && MBLKSIZE(mp) >= hdrlen) {
/* we can reuse the dl_unitdata_req M_PROTO mblk */
nmp = mp;
DB_TYPE(nmp) = M_DATA;
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
} else {
/* we need to allocate one */
if ((nmp = allocb(hdrlen, BPRI_MED)) == NULL)
return (NULL);
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
linkb(nmp, mp->b_cont);
freeb(mp);
}
/* Got the space, now copy in the header components */
nmp->b_rptr -= sizeof (typelen);
SET_NET_USHORT(*(uint16_t *)nmp->b_rptr, typelen);
if (hdrlen > sizeof (struct ether_mac_frm)) {
nmp->b_rptr -= sizeof (uint16_t);
SET_NET_USHORT(*(uint16_t *)nmp->b_rptr, vptag);
vptag >>= 16;
nmp->b_rptr -= sizeof (uint16_t);
SET_NET_USHORT(*(uint16_t *)nmp->b_rptr, vptag);
}
nmp->b_rptr -= (ETHERADDRL * 2);
mh = (struct ether_mac_frm *)nmp->b_rptr;
mac_copy(dhost, mh->ether_dhost, macinfo->gldm_addrlen);
/*
* We access the mac address without the mutex to prevent
* mutex contention (BUG 4211361)
*/
mac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
mh->ether_shost, macinfo->gldm_addrlen);
return (nmp);
}
mblk_t *
gld_fastpath_ether(gld_t *gld, mblk_t *mp)
{
gld_mac_info_t *macinfo = gld->gld_mac_info;
dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_cont->b_rptr;
struct gld_dlsap *gldp = DLSAP(dlp, dlp->dl_dest_addr_offset);
unsigned short typelen;
mblk_t *nmp;
struct ether_mac_frm *mh;
int hdrlen;
uint32_t vptag;
gld_vlan_t *gld_vlan;
ASSERT(macinfo);
/* look in the unitdata request for a sap, else use bound one */
if (dlp->dl_dest_addr_length >= DLSAPLENGTH(macinfo) &&
REF_HOST_USHORT(gldp->glda_sap) != 0)
typelen = REF_HOST_USHORT(gldp->glda_sap);
else
typelen = gld->gld_sap;
/*
* We only do fast-path for EtherType encoding because this is the only
* case where the media header will be consistent from packet to packet.
*/
if (typelen <= ETHERMTU)
return (NULL);
/*
* Initialize the fast path header to include the
* basic source address information and type field.
*/
hdrlen = sizeof (struct ether_mac_frm);
/*
* Check to see if VLAN is enabled on this stream
* if so then make the header bigger to hold a clone
* vlan tag.
*/
gld_vlan = (gld_vlan_t *)gld->gld_vlan;
if (gld_vlan && (gld_vlan->gldv_id != VLAN_VID_NONE)) {
hdrlen += VTAG_SIZE;
vptag = gld_vlan->gldv_ptag;
}
if ((nmp = allocb(hdrlen, BPRI_MED)) == NULL)
return (NULL);
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
/* Got the space, now copy in the header components */
nmp->b_rptr -= sizeof (typelen);
SET_NET_USHORT(*(uint16_t *)nmp->b_rptr, typelen);
/*
* If the header is for a VLAN stream, then add
* in the VLAN tag to the clone header.
*/
if (hdrlen > sizeof (struct ether_mac_frm)) {
nmp->b_rptr -= sizeof (uint16_t);
SET_NET_USHORT(*(uint16_t *)nmp->b_rptr, vptag);
vptag >>= 16;
nmp->b_rptr -= sizeof (uint16_t);
SET_NET_USHORT(*(uint16_t *)nmp->b_rptr, vptag);
}
nmp->b_rptr -= (ETHERADDRL * 2);
mh = (struct ether_mac_frm *)nmp->b_rptr;
mac_copy(gldp->glda_addr, mh->ether_dhost, macinfo->gldm_addrlen);
GLDM_LOCK(macinfo, RW_WRITER);
mac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
mh->ether_shost, macinfo->gldm_addrlen);
GLDM_UNLOCK(macinfo);
return (nmp);
}
/* == */
/* IB */
/* == */
void
gld_init_ib(gld_mac_info_t *macinfo)
{
/*
* Currently, the generic stats maintained by GLD is
* sufficient for IPoIB.
*/
/* Assumptions we make for this medium */
ASSERT(macinfo->gldm_type == DL_IB);
ASSERT(macinfo->gldm_addrlen == IPOIB_ADDRL);
ASSERT(macinfo->gldm_saplen == -2);
}
/* ARGSUSED */
void
gld_uninit_ib(gld_mac_info_t *macinfo)
{
}
/*
* The packet format sent to the driver is:
* IPOIB_ADDRL bytes dest addr :: 2b sap :: 2b 0s :: data
* The packet format received from the driver is:
* IPOIB_GRH_SIZE bytes pseudo GRH :: 2b sap :: 2b 0s :: data.
*/
int
gld_interpret_ib(gld_mac_info_t *macinfo, mblk_t *mp, pktinfo_t *pktinfo,
packet_flag_t flags)
{
ipoib_pgrh_t *grh;
ipoib_ptxhdr_t *gldp;
mblk_t *pmp = NULL;
gld_mac_pvt_t *mac_pvt = (gld_mac_pvt_t *)macinfo->gldm_mac_pvt;
/*
* Quickly handle receive fastpath for IPQ hack.
*/
if (flags == GLD_RXQUICK) {
pktinfo->pktLen = msgdsize(mp) - IPOIB_GRH_SIZE;
/*
* Check whether the header is contiguous, which
* also implicitly makes sure the packet is big enough.
*/
if (MBLKL(mp) < (IPOIB_GRH_SIZE + IPOIB_HDRSIZE))
return (-1);
/*
* Almost all times, unicast will not have
* a valid pgrh; quickly identify and ask for
* IPQ hack optimization only in that case.
*/
grh = (ipoib_pgrh_t *)mp->b_rptr;
if (grh->ipoib_vertcflow == 0) {
struct ipoib_header *ihp = (struct ipoib_header *)
(mp->b_rptr + IPOIB_GRH_SIZE);
pktinfo->isForMe = 1;
pktinfo->ethertype = REF_NET_USHORT(ihp->ipoib_type);
pktinfo->macLen = IPOIB_GRH_SIZE + IPOIB_HDRSIZE;
return (0);
} else {
return (-1);
}
}
/*
* Handle the GLD_TX, GLD_RX, GLD_RXLOOP cases now.
*/
ASSERT(flags != GLD_RXQUICK);
bzero((void *)pktinfo, sizeof (*pktinfo));
if (flags != GLD_RX) {
/*
* GLD_TX and GLD_RXLOOP cases.
*/
gldp = (ipoib_ptxhdr_t *)mp->b_rptr;
pktinfo->pktLen = msgdsize(mp);
/* make sure packet has at least a pseudo header */
if (pktinfo->pktLen < sizeof (ipoib_ptxhdr_t))
return (-1);
/* make sure the mac header falls into contiguous memory */
if (MBLKL(mp) < sizeof (ipoib_ptxhdr_t)) {
if ((pmp = msgpullup(mp, -1)) == NULL) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"GLD: interpret_ib "
"cannot msgpullup");
#endif
return (-1);
}
/* this mblk contains the whole mac header */
mp = pmp;
}
/*
* Check if mac is broadcast or multicast address; all these
* types of address have the top 4 bytes as 0x00FFFFFF.
*/
if (mac_eq(&gldp->ipoib_dest, macinfo->gldm_broadcast_addr,
sizeof (uint32_t))) {
if (mac_eq(&gldp->ipoib_dest,
macinfo->gldm_broadcast_addr, IPOIB_ADDRL))
pktinfo->isBroadcast = 1;
else
pktinfo->isMulticast = 1;
}
/*
* Only count bytes we will be sending over the wire
* or looping back.
*/
pktinfo->pktLen -= IPOIB_ADDRL;
if (flags == GLD_TX)
goto out; /* Got all info we need for xmit case */
/*
* Loopback case: this is a dup'ed message.
*/
mp->b_rptr += IPOIB_ADDRL;
mac_copy(&gldp->ipoib_dest, pktinfo->dhost, IPOIB_ADDRL);
mac_copy(mac_pvt->curr_macaddr, pktinfo->shost, IPOIB_ADDRL);
} else {
/*
* GLD_RX case; process packet sent from driver.
*/
ipoib_mac_t *mact, *tact;
ib_qpn_t dqpn;
pktinfo->pktLen = msgdsize(mp);
/* make sure packet has at least pgrh and mac header */
if (pktinfo->pktLen < (IPOIB_GRH_SIZE + IPOIB_HDRSIZE))
return (-1);
/* make sure the header falls into contiguous memory */
if (MBLKL(mp) < (IPOIB_GRH_SIZE + IPOIB_HDRSIZE)) {
if ((pmp = msgpullup(mp, -1)) == NULL) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"GLD: interpret_ib "
"cannot msgpullup2");
#endif
return (-1);
}
/* this mblk contains the whole mac header */
mp = pmp;
}
grh = (ipoib_pgrh_t *)mp->b_rptr;
mp->b_rptr += IPOIB_GRH_SIZE;
pktinfo->pktLen -= IPOIB_GRH_SIZE;
if (grh->ipoib_vertcflow) {
/*
* First, copy source address from grh.
*/
mact = (ipoib_mac_t *)pktinfo->shost;
mac_copy(&grh->ipoib_sqpn, &mact->ipoib_qpn,
IPOIB_ADDRL);
/*
* Then copy destination address from grh;
* first, the 16 bytes of GID.
*/
mact = (ipoib_mac_t *)pktinfo->dhost;
mac_copy(&grh->ipoib_dgid_pref,
&mact->ipoib_gidpref, IPOIB_ADDRL -
sizeof (mact->ipoib_qpn));
tact = (ipoib_mac_t *)mac_pvt->curr_macaddr;
/* Is this a multicast address */
if (*(uchar_t *)(grh->ipoib_dgid_pref) == 0xFF) {
/*
* Only check for hardware looping in
* multicast case. It is assumed higher
* layer code (IP) will stop unicast loops;
* ie will prevent a transmit to self.
*/
if (bcmp(&grh->ipoib_sqpn, tact,
IPOIB_ADDRL) == 0)
pktinfo->isLooped = 1;
tact = (ipoib_mac_t *)macinfo->
gldm_broadcast_addr;
if (mac_eq(tact->ipoib_gidpref,
grh->ipoib_dgid_pref,
IPOIB_ADDRL - sizeof (tact->ipoib_qpn)))
pktinfo->isBroadcast = 1;
else
pktinfo->isMulticast = 1;
/*
* Now copy the 4 bytes QPN part of the
* destination address.
*/
dqpn = htonl(IB_MC_QPN);
mac_copy(&dqpn, &mact->ipoib_qpn,
sizeof (mact->ipoib_qpn));
} else {
/*
* Now copy the 4 bytes QPN part of the
* destination address.
*/
mac_copy(&tact->ipoib_qpn, &mact->ipoib_qpn,
sizeof (mact->ipoib_qpn));
/*
* Any unicast packets received on IBA are
* for the node.
*/
pktinfo->isForMe = 1;
}
} else {
/*
* It can not be a IBA multicast packet.
* Must have been unicast to us. We do not
* have shost information, which is used in
* gld_addudind(); IP/ARP does not care.
*/
pktinfo->nosource = 1;
mac_copy(mac_pvt->curr_macaddr, pktinfo->dhost,
IPOIB_ADDRL);
/*
* Any unicast packets received on IBA are
* for the node.
*/
pktinfo->isForMe = 1;
}
}
ASSERT((flags == GLD_RX) || (flags == GLD_RXLOOP));
ASSERT(GLDM_LOCK_HELD(macinfo));
pktinfo->ethertype = REF_NET_USHORT(((ipoib_hdr_t *)
(mp->b_rptr))->ipoib_type);
pktinfo->macLen = IPOIB_HDRSIZE;
out:
if (pmp != NULL)
freemsg(pmp);
return (0);
}
/*
* The packet format sent to the driver is: 2b sap :: 2b 0s :: data
*/
void
gld_interpret_mdt_ib(gld_mac_info_t *macinfo, mblk_t *mp, pdescinfo_t *pinfo,
pktinfo_t *pktinfo, mdt_packet_flag_t flags)
{
gld_mac_pvt_t *mac_pvt;
multidata_t *dlmdp;
pattrinfo_t attr_info = { PATTR_DSTADDRSAP, };
pattr_t *patr;
ipoib_ptxhdr_t *dlap = NULL;
/*
* Per packet formatting.
*/
if (flags == GLD_MDT_TXPKT) {
ipoib_hdr_t *hptr;
uint_t seg;
if (PDESC_HDRL(pinfo) == 0)
return;
/*
* Update packet's link header.
*/
pinfo->hdr_rptr -= IPOIB_HDRSIZE;
hptr = (ipoib_hdr_t *)pinfo->hdr_rptr;
hptr->ipoib_mbz = htons(0);
hptr->ipoib_type = pktinfo->ethertype;
/*
* Total #bytes that will be put on wire.
*/
pktinfo->pktLen = PDESC_HDRL(pinfo);
for (seg = 0; seg < pinfo->pld_cnt; seg++)
pktinfo->pktLen += PDESC_PLDL(pinfo, seg);
return;
}
/*
* The following two cases of GLD_MDT_TX and GLD_MDT_RXLOOP are per
* MDT message processing.
*/
dlmdp = mmd_getmultidata(mp);
patr = mmd_getpattr(dlmdp, NULL, &attr_info);
ASSERT(patr != NULL);
ASSERT(macinfo->gldm_saplen == -2);
if (patr != NULL)
dlap = (ipoib_ptxhdr_t *)((pattr_addr_t *)attr_info.buf)->addr;
if (flags == GLD_MDT_TX) {
bzero((void *)pktinfo, sizeof (*pktinfo));
if (dlap == NULL)
return;
/*
* Check if mac is broadcast or multicast address; all these
* types of address have the top 4 bytes as 0x00FFFFFF.
*/
if (mac_eq(dlap, macinfo->gldm_broadcast_addr,
sizeof (uint32_t))) {
if (mac_eq(dlap, macinfo->gldm_broadcast_addr,
IPOIB_ADDRL))
pktinfo->isBroadcast = 1;
else
pktinfo->isMulticast = 1;
}
pktinfo->ethertype = REF_NET_USHORT(dlap->
ipoib_rhdr.ipoib_type);
} else {
ASSERT(flags == GLD_MDT_RXLOOP);
pktinfo->macLen = IPOIB_HDRSIZE;
mac_pvt = (gld_mac_pvt_t *)macinfo->gldm_mac_pvt;
mac_copy(mac_pvt->curr_macaddr, pktinfo->shost, IPOIB_ADDRL);
if (dlap == NULL)
return;
mac_copy(&dlap->ipoib_dest, pktinfo->dhost, IPOIB_ADDRL);
}
}
mblk_t *
gld_unitdata_ib(gld_t *gld, mblk_t *mp)
{
gld_mac_info_t *macinfo = gld->gld_mac_info;
dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr;
ipoib_ptxhdr_t *gldp = IPOIBDLSAP(dlp, dlp->dl_dest_addr_offset);
ipoib_mac_t dhost;
unsigned short type;
mblk_t *nmp;
int hdrlen;
ASSERT(macinfo != NULL);
/* extract needed info from the mblk before we maybe reuse it */
mac_copy(&gldp->ipoib_dest, &dhost, IPOIB_ADDRL);
/* look in the unitdata request for a sap, else use bound one */
if (dlp->dl_dest_addr_length >= DLSAPLENGTH(macinfo) &&
REF_HOST_USHORT(gldp->ipoib_rhdr.ipoib_type) != 0)
type = REF_HOST_USHORT(gldp->ipoib_rhdr.ipoib_type);
else
type = gld->gld_sap;
hdrlen = sizeof (ipoib_ptxhdr_t);
/* need a buffer big enough for the headers */
nmp = mp->b_cont; /* where the packet payload M_DATA is */
if (DB_REF(nmp) == 1 && MBLKHEAD(nmp) >= hdrlen) {
/* it fits at the beginning of the first M_DATA block */
freeb(mp); /* don't need the M_PROTO anymore */
} else if (DB_REF(mp) == 1 && MBLKSIZE(mp) >= hdrlen) {
/* we can reuse the dl_unitdata_req M_PROTO mblk */
nmp = mp;
DB_TYPE(nmp) = M_DATA;
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
} else {
/* we need to allocate one */
if ((nmp = allocb(hdrlen, BPRI_MED)) == NULL)
return (NULL);
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
linkb(nmp, mp->b_cont);
freeb(mp);
}
/* Got the space, now copy in the header components */
nmp->b_rptr -= sizeof (ipoib_ptxhdr_t);
gldp = (ipoib_ptxhdr_t *)nmp->b_rptr;
SET_NET_USHORT(gldp->ipoib_rhdr.ipoib_type, type);
gldp->ipoib_rhdr.ipoib_mbz = 0;
mac_copy(&dhost, &gldp->ipoib_dest, IPOIB_ADDRL);
return (nmp);
}
mblk_t *
gld_fastpath_ib(gld_t *gld, mblk_t *mp)
{
gld_mac_info_t *macinfo = gld->gld_mac_info;
dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_cont->b_rptr;
ipoib_ptxhdr_t *gldp = IPOIBDLSAP(dlp, dlp->dl_dest_addr_offset);
unsigned short type;
mblk_t *nmp;
ipoib_ptxhdr_t *tgldp;
int hdrlen;
ASSERT(macinfo != NULL);
/* look in the unitdata request for a sap, else use bound one */
if (dlp->dl_dest_addr_length >= DLSAPLENGTH(macinfo) &&
REF_HOST_USHORT(gldp->ipoib_rhdr.ipoib_type) != 0)
type = REF_HOST_USHORT(gldp->ipoib_rhdr.ipoib_type);
else
type = gld->gld_sap;
hdrlen = sizeof (ipoib_ptxhdr_t);
if ((nmp = allocb(hdrlen, BPRI_MED)) == NULL)
return (NULL);
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
/* Got the space, now copy in the header components */
nmp->b_rptr -= sizeof (ipoib_ptxhdr_t);
tgldp = (ipoib_ptxhdr_t *)nmp->b_rptr;
tgldp->ipoib_rhdr.ipoib_type = htons(type);
tgldp->ipoib_rhdr.ipoib_mbz = 0;
mac_copy(&gldp->ipoib_dest, &tgldp->ipoib_dest, IPOIB_ADDRL);
return (nmp);
}
/* ==== */
/* FDDI */
/* ==== */
void
gld_init_fddi(gld_mac_info_t *macinfo)
{
struct gldkstats *sp =
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->kstatp->ks_data;
/* Assumptions we make for this medium */
ASSERT(macinfo->gldm_type == DL_FDDI);
ASSERT(macinfo->gldm_addrlen == 6);
ASSERT(macinfo->gldm_saplen == -2);
#ifndef lint
ASSERT(sizeof (struct fddi_mac_frm) == 13);
ASSERT(sizeof (mac_addr_t) == 6);
#endif
/* Wire address format is bit reversed from canonical format */
macinfo->gldm_options |= GLDOPT_CANONICAL_ADDR;
kstat_named_init(&sp->glds_fddi_mac_error,
"mac_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_fddi_mac_lost,
"mac_lost_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_fddi_mac_token,
"mac_tokens", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_fddi_mac_tvx_expired,
"mac_tvx_expired", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_fddi_mac_late,
"mac_late", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_fddi_mac_ring_op,
"mac_ring_ops", KSTAT_DATA_UINT32);
}
/*ARGSUSED*/
void
gld_uninit_fddi(gld_mac_info_t *macinfo)
{
}
int
gld_interpret_fddi(gld_mac_info_t *macinfo, mblk_t *mp, pktinfo_t *pktinfo,
packet_flag_t flags)
{
struct fddi_mac_frm *mh;
gld_mac_pvt_t *mac_pvt;
struct llc_snap_hdr *snaphdr;
mblk_t *pmp = NULL;
/*
* Quickly handle receive fastpath; FDDI does not support IPQ hack.
*/
if (flags == GLD_RXQUICK) {
pktinfo->pktLen = msgdsize(mp);
return (-1);
}
bzero((void *)pktinfo, sizeof (*pktinfo));
pktinfo->pktLen = msgdsize(mp);
/* make sure packet has at least a whole mac header */
if (pktinfo->pktLen < sizeof (struct fddi_mac_frm))
return (-1);
/* make sure the mac header falls into contiguous memory */
if (MBLKL(mp) < sizeof (struct fddi_mac_frm)) {
if ((pmp = msgpullup(mp, -1)) == NULL) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"GLD: interpret_fddi cannot msgpullup");
#endif
return (-1);
}
mp = pmp; /* this mblk contains the whole mac header */
}
mh = (struct fddi_mac_frm *)mp->b_rptr;
/* Check to see if the mac is a broadcast or multicast address. */
/* NB we are still in wire format (non canonical) */
/* mac_eq works because ether_broadcast is the same either way */
if (mac_eq(mh->fddi_dhost, ether_broadcast, macinfo->gldm_addrlen))
pktinfo->isBroadcast = 1;
else if (mh->fddi_dhost[0] & 0x80)
pktinfo->isMulticast = 1;
if (flags == GLD_TX)
goto out; /* Got all info we need for xmit case */
ASSERT(GLDM_LOCK_HELD(macinfo));
/*
* Deal with the mac header
*/
cmac_copy(mh->fddi_dhost, pktinfo->dhost,
macinfo->gldm_addrlen, macinfo);
cmac_copy(mh->fddi_shost, pktinfo->shost,
macinfo->gldm_addrlen, macinfo);
mac_pvt = (gld_mac_pvt_t *)macinfo->gldm_mac_pvt;
pktinfo->isLooped = mac_eq(pktinfo->shost,
mac_pvt->curr_macaddr, macinfo->gldm_addrlen);
pktinfo->isForMe = mac_eq(pktinfo->dhost,
mac_pvt->curr_macaddr, macinfo->gldm_addrlen);
pktinfo->macLen = sizeof (struct fddi_mac_frm);
/*
* Before trying to look beyond the MAC header, make sure the LLC
* header exists, and that both it and any SNAP header are contiguous.
*/
if (MBLKL(mp) < sizeof (struct fddi_mac_frm) + LLC_SNAP_HDR_LEN &&
MBLKL(mp) < pktinfo->pktLen) {
/*
* we don't have the entire packet within the first mblk (and
* therefore we didn't do the msgpullup above), AND the first
* mblk may not contain all the data we need to look at.
*/
ASSERT(pmp == NULL); /* couldn't have done msgpullup above */
if ((pmp = msgpullup(mp, -1)) == NULL) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"GLD: interpret_fddi cannot msgpullup2");
#endif
goto out; /* can't interpret this pkt further */
}
mp = pmp; /* this mblk should contain everything needed */
}
/*
* Check SAP/SNAP information.
*/
if ((mh->fddi_fc & 0x70) == 0x50) {
if (pktinfo->pktLen < pktinfo->macLen + LLC_HDR1_LEN)
goto out;
pktinfo->isLLC = 1;
if (pktinfo->pktLen < pktinfo->macLen + LLC_SNAP_HDR_LEN)
goto out;
snaphdr = (struct llc_snap_hdr *)(mp->b_rptr + pktinfo->macLen);
if (ISETHERTYPE(snaphdr)) {
pktinfo->ethertype = REF_NET_USHORT(snaphdr->type);
pktinfo->hdrLen = LLC_SNAP_HDR_LEN;
}
}
out:
if (pmp != NULL)
freemsg(pmp);
return (0);
}
mblk_t *
gld_unitdata_fddi(gld_t *gld, mblk_t *mp)
{
gld_mac_info_t *macinfo = gld->gld_mac_info;
dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr;
struct gld_dlsap *gldp = DLSAP(dlp, dlp->dl_dest_addr_offset);
mac_addr_t dhost;
unsigned short type;
mblk_t *nmp;
struct fddi_mac_frm *mh;
int hdrlen;
ASSERT(macinfo);
/* extract needed info from the mblk before we maybe reuse it */
mac_copy(gldp->glda_addr, dhost, macinfo->gldm_addrlen);
/* look in the unitdata request for a sap, else use bound one */
if (dlp->dl_dest_addr_length >= DLSAPLENGTH(macinfo) &&
REF_HOST_USHORT(gldp->glda_sap) != 0)
type = REF_HOST_USHORT(gldp->glda_sap);
else
type = gld->gld_sap;
hdrlen = sizeof (struct fddi_mac_frm);
/*
* Check whether we need to do EtherType encoding or whether the packet
* is LLC.
*/
if (type > GLD_MAX_802_SAP)
hdrlen += sizeof (struct llc_snap_hdr);
/* need a buffer big enough for the headers */
nmp = mp->b_cont; /* where the packet payload M_DATA is */
if (DB_REF(nmp) == 1 && MBLKHEAD(nmp) >= hdrlen) {
/* it fits at the beginning of the first M_DATA block */
freeb(mp); /* don't need the M_PROTO anymore */
} else if (DB_REF(mp) == 1 && MBLKSIZE(mp) >= hdrlen) {
/* we can reuse the dl_unitdata_req M_PROTO mblk */
nmp = mp;
DB_TYPE(nmp) = M_DATA;
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
} else {
/* we need to allocate one */
if ((nmp = allocb(hdrlen, BPRI_MED)) == NULL)
return (NULL);
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
linkb(nmp, mp->b_cont);
freeb(mp);
}
/* Got the space, now copy in the header components */
if (type > GLD_MAX_802_SAP) {
/* create the snap header */
struct llc_snap_hdr *snap;
nmp->b_rptr -= sizeof (struct llc_snap_hdr);
snap = (struct llc_snap_hdr *)(nmp->b_rptr);
*snap = llc_snap_def;
SET_NET_USHORT(snap->type, type);
}
nmp->b_rptr -= sizeof (struct fddi_mac_frm);
mh = (struct fddi_mac_frm *)nmp->b_rptr;
mh->fddi_fc = 0x50;
cmac_copy(dhost, mh->fddi_dhost, macinfo->gldm_addrlen, macinfo);
/*
* We access the mac address without the mutex to prevent
* mutex contention (BUG 4211361)
*/
cmac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
mh->fddi_shost, macinfo->gldm_addrlen, macinfo);
return (nmp);
}
mblk_t *
gld_fastpath_fddi(gld_t *gld, mblk_t *mp)
{
gld_mac_info_t *macinfo = gld->gld_mac_info;
dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_cont->b_rptr;
struct gld_dlsap *gldp = DLSAP(dlp, dlp->dl_dest_addr_offset);
unsigned short type;
mblk_t *nmp;
struct fddi_mac_frm *mh;
int hdrlen;
ASSERT(macinfo);
/* look in the unitdata request for a sap, else use bound one */
if (dlp->dl_dest_addr_length >= DLSAPLENGTH(macinfo) &&
REF_HOST_USHORT(gldp->glda_sap) != 0)
type = REF_HOST_USHORT(gldp->glda_sap);
else
type = gld->gld_sap;
hdrlen = sizeof (struct fddi_mac_frm);
/*
* Check whether we need to do EtherType encoding or whether the packet
* will be LLC.
*/
if (type > GLD_MAX_802_SAP)
hdrlen += sizeof (struct llc_snap_hdr);
if ((nmp = allocb(hdrlen, BPRI_MED)) == NULL)
return (NULL);
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
/* Got the space, now copy in the header components */
if (type > GLD_MAX_802_SAP) {
/* create the snap header */
struct llc_snap_hdr *snap;
nmp->b_rptr -= sizeof (struct llc_snap_hdr);
snap = (struct llc_snap_hdr *)(nmp->b_rptr);
*snap = llc_snap_def;
snap->type = htons(type); /* we know it's aligned */
}
nmp->b_rptr -= sizeof (struct fddi_mac_frm);
mh = (struct fddi_mac_frm *)nmp->b_rptr;
mh->fddi_fc = 0x50;
cmac_copy(gldp->glda_addr, mh->fddi_dhost,
macinfo->gldm_addrlen, macinfo);
GLDM_LOCK(macinfo, RW_WRITER);
cmac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
mh->fddi_shost, macinfo->gldm_addrlen, macinfo);
GLDM_UNLOCK(macinfo);
return (nmp);
}
/* ========== */
/* Token Ring */
/* ========== */
#define GLD_SR_VAR(macinfo) \
(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->data)
#define GLD_SR_HASH(macinfo) ((struct srtab **)GLD_SR_VAR(macinfo))
#define GLD_SR_MUTEX(macinfo) \
(&((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->datalock)
static void gld_sr_clear(gld_mac_info_t *);
static void gld_rcc_receive(gld_mac_info_t *, pktinfo_t *, struct gld_ri *,
uchar_t *, int);
static void gld_rcc_send(gld_mac_info_t *, queue_t *, uchar_t *,
struct gld_ri **, uchar_t *);
static mac_addr_t tokenbroadcastaddr2 = { 0xc0, 0x00, 0xff, 0xff, 0xff, 0xff };
static struct gld_ri ri_ste_def;
void
gld_init_tr(gld_mac_info_t *macinfo)
{
struct gldkstats *sp =
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->kstatp->ks_data;
/* avoid endian-dependent code by initializing here instead of static */
ri_ste_def.len = 2;
ri_ste_def.rt = RT_STE;
ri_ste_def.mtu = RT_MTU_MAX;
ri_ste_def.dir = 0;
ri_ste_def.res = 0;
/* Assumptions we make for this medium */
ASSERT(macinfo->gldm_type == DL_TPR);
ASSERT(macinfo->gldm_addrlen == 6);
ASSERT(macinfo->gldm_saplen == -2);
#ifndef lint
ASSERT(sizeof (struct tr_mac_frm_nori) == 14);
ASSERT(sizeof (mac_addr_t) == 6);
#endif
mutex_init(GLD_SR_MUTEX(macinfo), NULL, MUTEX_DRIVER, NULL);
GLD_SR_VAR(macinfo) = kmem_zalloc(sizeof (struct srtab *)*SR_HASH_SIZE,
KM_SLEEP);
/* Default is RDE enabled for this medium */
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_enabled =
ddi_getprop(DDI_DEV_T_NONE, macinfo->gldm_devinfo, 0,
"gld_rde_enable", 1);
/*
* Default is to use STE for unknown paths if RDE is enabled.
* If RDE is disabled, default is to use NULL RIF fields.
*
* It's possible to force use of STE for ALL packets:
* disable RDE but enable STE. This may be useful for
* non-transparent bridges, when it is not desired to run
* the RDE algorithms.
*/
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_str_indicator_ste =
ddi_getprop(DDI_DEV_T_NONE, macinfo->gldm_devinfo, 0,
"gld_rde_str_indicator_ste",
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_enabled);
/* Default 10 second route timeout on lack of activity */
{
int t = ddi_getprop(DDI_DEV_T_NONE, macinfo->gldm_devinfo, 0,
"gld_rde_timeout", 10);
if (t < 1)
t = 1; /* Let's be reasonable */
if (t > 600)
t = 600; /* Let's be reasonable */
/* We're using ticks (lbolts) for our timeout -- convert from seconds */
t = drv_usectohz(1000000 * t);
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_timeout = t;
}
kstat_named_init(&sp->glds_dot5_line_error,
"line_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot5_burst_error,
"burst_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot5_signal_loss,
"signal_losses", KSTAT_DATA_UINT32);
/*
* only initialize the new statistics if the driver
* knows about them.
*/
if (macinfo->gldm_driver_version != GLD_VERSION_200)
return;
kstat_named_init(&sp->glds_dot5_ace_error,
"ace_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot5_internal_error,
"internal_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot5_lost_frame_error,
"lost_frame_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot5_frame_copied_error,
"frame_copied_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot5_token_error,
"token_errors", KSTAT_DATA_UINT32);
kstat_named_init(&sp->glds_dot5_freq_error,
"freq_errors", KSTAT_DATA_UINT32);
}
void
gld_uninit_tr(gld_mac_info_t *macinfo)
{
mutex_destroy(GLD_SR_MUTEX(macinfo));
gld_sr_clear(macinfo);
kmem_free(GLD_SR_VAR(macinfo), sizeof (struct srtab *) * SR_HASH_SIZE);
}
int
gld_interpret_tr(gld_mac_info_t *macinfo, mblk_t *mp, pktinfo_t *pktinfo,
packet_flag_t flags)
{
struct tr_mac_frm *mh;
gld_mac_pvt_t *mac_pvt;
struct llc_snap_hdr *snaphdr;
mblk_t *pmp = NULL;
struct gld_ri *rh;
/*
* Quickly handle receive fastpath; TR does not support IPQ hack.
*/
if (flags == GLD_RXQUICK) {
pktinfo->pktLen = msgdsize(mp);
return (-1);
}
bzero((void *)pktinfo, sizeof (*pktinfo));
pktinfo->pktLen = msgdsize(mp);
/* make sure packet has at least a whole mac header */
if (pktinfo->pktLen < sizeof (struct tr_mac_frm_nori))
return (-1);
/* make sure the mac header falls into contiguous memory */
if (MBLKL(mp) < sizeof (struct tr_mac_frm_nori)) {
if ((pmp = msgpullup(mp, -1)) == NULL) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"GLD: interpret_tr cannot msgpullup");
#endif
return (-1);
}
mp = pmp; /* this mblk contains the whole mac header */
}
mh = (struct tr_mac_frm *)mp->b_rptr;
/* Check to see if the mac is a broadcast or multicast address. */
if (mac_eq(mh->tr_dhost, ether_broadcast, macinfo->gldm_addrlen) ||
mac_eq(mh->tr_dhost, tokenbroadcastaddr2, macinfo->gldm_addrlen))
pktinfo->isBroadcast = 1;
else if (mh->tr_dhost[0] & 0x80)
pktinfo->isMulticast = 1;
if (flags == GLD_TX)
goto out; /* Got all info we need for xmit case */
ASSERT(GLDM_LOCK_HELD(macinfo));
/*
* Deal with the mac header
*/
mac_copy(mh->tr_dhost, pktinfo->dhost, macinfo->gldm_addrlen);
mac_copy(mh->tr_shost, pktinfo->shost, macinfo->gldm_addrlen);
pktinfo->shost[0] &= ~0x80; /* turn off RIF indicator */
mac_pvt = (gld_mac_pvt_t *)macinfo->gldm_mac_pvt;
pktinfo->isLooped = mac_eq(pktinfo->shost,
mac_pvt->curr_macaddr, macinfo->gldm_addrlen);
pktinfo->isForMe = mac_eq(pktinfo->dhost,
mac_pvt->curr_macaddr, macinfo->gldm_addrlen);
rh = (struct gld_ri *)NULL;
pktinfo->macLen = sizeof (struct tr_mac_frm_nori);
/*
* Before trying to look beyond the MAC header, make sure the data
* structures are all contiguously where we can conveniently look at
* them. We'll use a worst-case estimate of how many bytes into the
* packet data we'll be needing to look. Things will be more efficient
* if the driver puts at least this much into the first mblk.
*
* Even after this, we still will have to do checks against the total
* length of the packet. A bad incoming packet may not hold all the
* data structures it says it does.
*/
if (MBLKL(mp) < sizeof (struct tr_mac_frm) +
LLC_HDR1_LEN + sizeof (struct rde_pdu) &&
MBLKL(mp) < pktinfo->pktLen) {
/*
* we don't have the entire packet within the first mblk (and
* therefore we didn't do the msgpullup above), AND the first
* mblk may not contain all the data we need to look at.
*/
ASSERT(pmp == NULL); /* couldn't have done msgpullup above */
if ((pmp = msgpullup(mp, -1)) == NULL) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"GLD: interpret_tr cannot msgpullup2");
#endif
goto out; /* can't interpret this pkt further */
}
mp = pmp; /* this mblk should contain everything needed */
mh = (struct tr_mac_frm *)mp->b_rptr; /* to look at RIF */
}
if (mh->tr_shost[0] & 0x80) {
/* Routing Information Field (RIF) is present */
if (pktinfo->pktLen < sizeof (struct tr_mac_frm_nori) + 2)
goto out; /* RIF should have been there! */
rh = (struct gld_ri *)&mh->tr_ri;
if ((rh->len & 1) || rh->len < 2) {
/* Bogus RIF, don't handle this packet */
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"GLD: received TR packet with "
"bogus RIF length %d",
rh->len);
#endif
goto out;
}
if (pktinfo->pktLen < sizeof (struct tr_mac_frm_nori) + rh->len)
goto out; /* RIF should have been there! */
pktinfo->macLen += rh->len;
}
if ((mh->tr_fc & 0xc0) == 0x40) {
if (pktinfo->pktLen < pktinfo->macLen + LLC_HDR1_LEN)
goto out;
pktinfo->isLLC = 1;
if (pktinfo->pktLen < pktinfo->macLen + LLC_SNAP_HDR_LEN)
goto out;
snaphdr = (struct llc_snap_hdr *)(mp->b_rptr + pktinfo->macLen);
if (ISETHERTYPE(snaphdr)) {
pktinfo->ethertype = REF_NET_USHORT(snaphdr->type);
pktinfo->hdrLen = LLC_SNAP_HDR_LEN;
}
/* Inform the Route Control Component of received LLC frame */
gld_rcc_receive(macinfo, pktinfo, rh,
mp->b_rptr + pktinfo->macLen,
pktinfo->pktLen - pktinfo->macLen);
}
out:
if (pmp != NULL)
freemsg(pmp);
return (0);
}
mblk_t *
gld_unitdata_tr(gld_t *gld, mblk_t *mp)
{
gld_mac_info_t *macinfo = gld->gld_mac_info;
dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr;
struct gld_dlsap *gldp = DLSAP(dlp, dlp->dl_dest_addr_offset);
mac_addr_t dhost;
unsigned short type;
mblk_t *nmp, *llcmp, *pmp = NULL;
struct tr_mac_frm_nori *mh;
int hdrlen;
struct gld_ri *rh;
ASSERT(macinfo);
/* extract needed info from the mblk before we maybe reuse it */
mac_copy(gldp->glda_addr, dhost, macinfo->gldm_addrlen);
/* look in the unitdata request for a sap, else use bound one */
if (dlp->dl_dest_addr_length >= DLSAPLENGTH(macinfo) &&
REF_HOST_USHORT(gldp->glda_sap) != 0)
type = REF_HOST_USHORT(gldp->glda_sap);
else
type = gld->gld_sap;
/* includes maximum possible Routing Information Field (RIF) size */
hdrlen = sizeof (struct tr_mac_frm);
/*
* Check whether we need to do EtherType encoding or whether the packet
* is LLC.
*/
if (type > GLD_MAX_802_SAP)
hdrlen += sizeof (struct llc_snap_hdr);
/* need a buffer big enough for the headers */
llcmp = nmp = mp->b_cont; /* where the packet payload M_DATA is */
/*
* We are going to need to look at the LLC header, so make sure it
* is contiguously in a single mblk. If we're the ones who create
* the LLC header (below, in the case where sap > 0xff) then we don't
* have to worry about it here.
*/
ASSERT(nmp != NULL); /* gld_unitdata guarantees msgdsize > 0 */
if (type <= GLD_MAX_802_SAP) {
if (MBLKL(llcmp) < LLC_HDR1_LEN) {
llcmp = pmp = msgpullup(nmp, LLC_HDR1_LEN);
if (pmp == NULL) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"GLD: unitdata_tr "
"cannot msgpullup");
#endif
return (NULL);
}
}
}
if (DB_REF(nmp) == 1 && MBLKHEAD(nmp) >= hdrlen) {
/* it fits at the beginning of the first M_DATA block */
freeb(mp); /* don't need the M_PROTO anymore */
} else if (DB_REF(mp) == 1 && MBLKSIZE(mp) >= hdrlen) {
/* we can reuse the dl_unitdata_req M_PROTO mblk */
nmp = mp;
DB_TYPE(nmp) = M_DATA;
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
} else {
/* we need to allocate one */
if ((nmp = allocb(hdrlen, BPRI_MED)) == NULL) {
if (pmp != NULL)
freemsg(pmp);
return (NULL);
}
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
linkb(nmp, mp->b_cont);
freeb(mp);
}
/* Got the space, now copy in the header components */
if (type > GLD_MAX_802_SAP) {
/* create the snap header */
struct llc_snap_hdr *snap;
llcmp = nmp; /* LLC header is going to be in this mblk */
nmp->b_rptr -= sizeof (struct llc_snap_hdr);
snap = (struct llc_snap_hdr *)(nmp->b_rptr);
*snap = llc_snap_def;
SET_NET_USHORT(snap->type, type);
}
/* Hold SR tables still while we maybe point at an entry */
mutex_enter(GLD_SR_MUTEX(macinfo));
gld_rcc_send(macinfo, WR(gld->gld_qptr), dhost, &rh, llcmp->b_rptr);
if (rh != NULL) {
/* copy in the RIF */
ASSERT(rh->len <= sizeof (struct gld_ri));
nmp->b_rptr -= rh->len;
bcopy((caddr_t)rh, (caddr_t)nmp->b_rptr, rh->len);
}
mutex_exit(GLD_SR_MUTEX(macinfo));
/* no longer need the pulled-up mblk */
if (pmp != NULL)
freemsg(pmp);
/*
* fill in token ring header
*/
nmp->b_rptr -= sizeof (struct tr_mac_frm_nori);
mh = (struct tr_mac_frm_nori *)nmp->b_rptr;
mh->tr_ac = 0x10;
mh->tr_fc = 0x40;
mac_copy(dhost, mh->tr_dhost, macinfo->gldm_addrlen);
/*
* We access the mac address without the mutex to prevent
* mutex contention (BUG 4211361)
*/
mac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
mh->tr_shost, macinfo->gldm_addrlen);
if (rh != NULL)
mh->tr_shost[0] |= 0x80;
else
mh->tr_shost[0] &= ~0x80;
return (nmp);
}
/*
* We cannot have our client sending us "fastpath" M_DATA messages,
* because to do that we must provide to him a fixed MAC header to
* be prepended to each outgoing packet. But with Source Routing
* media, the length and content of the MAC header changes as the
* routes change, so there is no fixed header we can provide. So
* we decline to accept M_DATA messages if Source Routing is enabled.
*/
mblk_t *
gld_fastpath_tr(gld_t *gld, mblk_t *mp)
{
gld_mac_info_t *macinfo = gld->gld_mac_info;
dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_cont->b_rptr;
struct gld_dlsap *gldp = DLSAP(dlp, dlp->dl_dest_addr_offset);
unsigned short type;
mblk_t *nmp;
struct tr_mac_frm_nori *mh;
int hdrlen;
ASSERT(macinfo);
/*
* If we are doing Source Routing, then we cannot provide a fixed
* MAC header, so fail.
*/
if (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_enabled)
return (NULL);
/* look in the unitdata request for a sap, else use bound one */
if (dlp->dl_dest_addr_length >= DLSAPLENGTH(macinfo) &&
REF_HOST_USHORT(gldp->glda_sap) != 0)
type = REF_HOST_USHORT(gldp->glda_sap);
else
type = gld->gld_sap;
hdrlen = sizeof (struct tr_mac_frm_nori);
if (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_str_indicator_ste)
hdrlen += ri_ste_def.len;
/*
* Check whether we need to do EtherType encoding or whether the packet
* will be LLC.
*/
if (type > GLD_MAX_802_SAP)
hdrlen += sizeof (struct llc_snap_hdr);
if ((nmp = allocb(hdrlen, BPRI_MED)) == NULL)
return (NULL);
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
/* Got the space, now copy in the header components */
if (type > GLD_MAX_802_SAP) {
/* create the snap header */
struct llc_snap_hdr *snap;
nmp->b_rptr -= sizeof (struct llc_snap_hdr);
snap = (struct llc_snap_hdr *)(nmp->b_rptr);
*snap = llc_snap_def;
snap->type = htons(type); /* we know it's aligned */
}
/* RDE is disabled, use NULL RIF, or STE RIF */
if (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_str_indicator_ste) {
nmp->b_rptr -= ri_ste_def.len;
bcopy((caddr_t)&ri_ste_def, (caddr_t)nmp->b_rptr,
ri_ste_def.len);
}
/*
* fill in token ring header
*/
nmp->b_rptr -= sizeof (struct tr_mac_frm_nori);
mh = (struct tr_mac_frm_nori *)nmp->b_rptr;
mh->tr_ac = 0x10;
mh->tr_fc = 0x40;
mac_copy(gldp->glda_addr, mh->tr_dhost, macinfo->gldm_addrlen);
GLDM_LOCK(macinfo, RW_WRITER);
mac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
mh->tr_shost, macinfo->gldm_addrlen);
GLDM_UNLOCK(macinfo);
if (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_str_indicator_ste)
mh->tr_shost[0] |= 0x80;
else
mh->tr_shost[0] &= ~0x80;
return (nmp);
}
/*
* Route Determination Entity (ISO 8802-2 / IEEE 802.2 : 1994, Section 9)
*
* RDE is an LLC layer entity. GLD is a MAC layer entity. The proper
* solution to this architectural anomaly is to move RDE support out of GLD
* and into LLC where it belongs. In particular, only LLC has the knowledge
* necessary to reply to XID and TEST packets. If and when it comes time to
* move RDE out of GLD to LLC, the LLC-to-GLD interface should be modified
* to use MA_UNITDATA structures rather than DL_UNITDATA structures. Of
* course, GLD will still have to continue to also support the DL_ structures
* as long as IP is not layered over LLC. Another, perhaps better, idea
* would be to make RDE an autopush module on top of the token ring drivers:
* RDE would sit between LLC and GLD. It would then also sit between IP and
* GLD, providing services to all clients of GLD/tokenring. In that case,
* GLD would still have to continue to support the DL_ interface for non-
* Token Ring interfaces, using the MA_ interface only for media supporting
* Source Routing media.
*
* At present, Token Ring is the only source routing medium we support.
* Since Token Ring is not at this time a strategic network medium for Sun,
* rather than devote a large amount of resources to creating a proper
* architecture and implementation of RDE, we do the minimum necessary to
* get it to work. The interface between the above token ring code and the
* below RDE code is designed to make it relatively easy to change to an
* MA_UNITDATA model later should this ever become a priority.
*/
static void gld_send_rqr(gld_mac_info_t *, uchar_t *, struct gld_ri *,
struct rde_pdu *, int);
static void gld_rde_pdu_req(gld_mac_info_t *, queue_t *, uchar_t *,
struct gld_ri *, uchar_t, uchar_t, uchar_t);
static void gld_get_route(gld_mac_info_t *, queue_t *, uchar_t *,
struct gld_ri **, uchar_t, uchar_t);
static void gld_reset_route(gld_mac_info_t *, queue_t *,
uchar_t *, uchar_t, uchar_t);
static void gld_rde_pdu_ind(gld_mac_info_t *, struct gld_ri *, struct rde_pdu *,
int);
static void gld_rif_ind(gld_mac_info_t *, struct gld_ri *, uchar_t *,
uchar_t, uchar_t);
static struct srtab **gld_sr_hash(struct srtab **, uchar_t *, int);
static struct srtab *gld_sr_lookup_entry(gld_mac_info_t *, uchar_t *);
static struct srtab *gld_sr_create_entry(gld_mac_info_t *, uchar_t *);
/*
* This routine implements a modified subset of the 802.2 RDE RCC receive
* actions:
* we implement RCC receive events 3 to 12 (ISO 8802-2:1994 9.6.3.4);
* we omit special handling for the NULL SAP;
* we omit XID/TEST handling;
* we pass all packets (including RDE) upstream to LLC.
*/
static void
gld_rcc_receive(gld_mac_info_t *macinfo, pktinfo_t *pktinfo, struct gld_ri *rh,
uchar_t *llcpkt, int llcpktlen)
{
struct llc_snap_hdr *snaphdr = (struct llc_snap_hdr *)(llcpkt);
if (!((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_enabled)
return;
/*
* First, ensure this packet wasn't something we received just
* because we were in promiscuous mode. Since none of the below
* code wants to see group addressed packets anyway, we can do
* this check up front. Since we're doing that, we can omit the
* checks for group addressed packets below.
*/
if (!pktinfo->isForMe)
return; /* Event 6 */
/* Process a subset of Route Determination Entity (RDE) packets */
if (snaphdr->d_lsap == LSAP_RDE) {
struct rde_pdu *pdu = (struct rde_pdu *)(llcpkt + LLC_HDR1_LEN);
int pdulen = llcpktlen - LLC_HDR1_LEN;
/* sanity check the PDU */
if ((pdulen < sizeof (struct rde_pdu)) ||
(snaphdr->s_lsap != LSAP_RDE))
return;
/* we only handle route discovery PDUs, not XID/TEST/other */
if (snaphdr->control != CNTL_LLC_UI)
return;
switch (pdu->rde_ptype) {
case RDE_RQC: /* Route Query Command; Events 8 - 11 */
gld_send_rqr(macinfo, pktinfo->shost, rh, pdu, pdulen);
/* FALLTHROUGH */
case RDE_RQR: /* Route Query Response; Event 12 */
case RDE_RS: /* Route Selected; Event 7 */
gld_rde_pdu_ind(macinfo, rh, pdu, pdulen);
break;
default: /* ignore if unrecognized ptype */
return;
}
return;
}
/* Consider routes seen in other IA SRF packets */
if (rh == NULL)
return; /* no RIF; Event 3 */
if ((rh->rt & 0x04) != 0)
return; /* not SRF; Event 5 */
gld_rif_ind(macinfo, rh, pktinfo->shost, snaphdr->s_lsap,
snaphdr->d_lsap); /* Event 4 */
}
/*
* Send RQR: 802.2 9.6.3.4.2(9) RCC Receive Events 8-11
*
* The routing processing really doesn't belong here; it should be handled in
* the LLC layer above. If that were the case then RDE could just send down
* an extra MA_UNITDATA_REQ with the info needed to construct the packet. But
* at the time we get control here, it's not a particularly good time to be
* constructing packets and trying to send them. Specifically, at this layer
* we need to construct the full media packet, which means the below routine
* knows that it is dealing with Token Ring media. If this were instead done
* via a proper MA_UNITDATA interface, the RDE stuff could all be completely
* media independent. But since TR is the only source routing medium we
* support, this works even though it is not clean.
*
* We "know" that the only time we can get here is from the "interpret"
* routine, and only when it was called at receive time.
*/
static void
gld_send_rqr(gld_mac_info_t *macinfo, uchar_t *shost, struct gld_ri *rh,
struct rde_pdu *pdu, int pdulen)
{
mblk_t *nmp;
int nlen;
struct tr_mac_frm_nori *nmh;
struct gld_ri *nrh;
struct llc_snap_hdr *nsnaphdr;
struct rde_pdu *npdu;
/* We know and assume we're on the receive path */
ASSERT(GLDM_LOCK_HELD(macinfo));
if (pdulen < sizeof (struct rde_pdu))
return; /* Bad incoming PDU */
nlen = sizeof (struct tr_mac_frm) + LLC_HDR1_LEN +
sizeof (struct rde_pdu);
if ((nmp = allocb(nlen, BPRI_MED)) == NULL)
return;
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
nmp->b_rptr -= sizeof (struct rde_pdu);
npdu = (struct rde_pdu *)(nmp->b_rptr);
*npdu = *pdu; /* copy orig/target macaddr/saps */
npdu->rde_ver = 1;
npdu->rde_ptype = RDE_RQR;
mac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
npdu->rde_target_mac, macinfo->gldm_addrlen);
nmp->b_rptr -= LLC_HDR1_LEN;
nsnaphdr = (struct llc_snap_hdr *)(nmp->b_rptr);
nsnaphdr->s_lsap = nsnaphdr->d_lsap = LSAP_RDE;
nsnaphdr->control = CNTL_LLC_UI;
if (rh == NULL || (rh->rt & 0x06) == 0x06 ||
rh->len > sizeof (struct gld_ri)) {
/* no RIF (Event 8), or RIF type STE (Event 9): send ARE RQR */
nmp->b_rptr -= 2;
nrh = (struct gld_ri *)(nmp->b_rptr);
nrh->len = 2;
nrh->rt = RT_ARE;
nrh->dir = 0;
nrh->res = 0;
nrh->mtu = RT_MTU_MAX;
} else {
/*
* RIF must be ARE (Event 10) or SRF (Event 11):
* send SRF (reverse) RQR
*/
ASSERT(rh->len <= sizeof (struct gld_ri));
nmp->b_rptr -= rh->len;
nrh = (struct gld_ri *)(nmp->b_rptr);
bcopy(rh, nrh, rh->len); /* copy incoming RIF */
nrh->rt = RT_SRF; /* make it SRF */
nrh->dir ^= 1; /* reverse direction */
}
nmp->b_rptr -= sizeof (struct tr_mac_frm_nori);
nmh = (struct tr_mac_frm_nori *)(nmp->b_rptr);
nmh->tr_ac = 0x10;
nmh->tr_fc = 0x40;
mac_copy(shost, nmh->tr_dhost, macinfo->gldm_addrlen);
mac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
nmh->tr_shost, macinfo->gldm_addrlen);
nmh->tr_shost[0] |= 0x80; /* indicate RIF present */
/*
* Packet assembled; send it.
*
* As noted before, this is not really a good time to be trying to
* send out packets. We have no obvious queue to use if the packet
* can't be sent right away. We pick one arbitrarily.
*/
{
gld_vlan_t *vlan;
queue_t *q;
if ((vlan = gld_find_vlan(macinfo, VLAN_VID_NONE)) == NULL) {
/* oops, no vlan on the list for this macinfo! */
/* this should not happen */
freeb(nmp);
return;
}
q = vlan->gldv_str_next->gld_qptr;
/*
* Queue the packet and let gld_wsrv
* handle it, thus preventing a panic
* caused by v2 TR in promiscuous mode
* where it attempts to get the mutex
* in this thread while already holding
* it.
*/
(void) putbq(WR(q), nmp);
qenable(WR(q));
}
}
/*
* This routine implements a modified subset of the 802.2 RDE RCC send actions:
* we implement RCC send events 5 to 10 (ISO 8802-2:1994 9.6.3.5);
* we omit special handling for the NULL SAP;
* events 11 to 12 are handled by gld_rde_pdu_req below;
* we require an immediate response to our GET_ROUTE_REQUEST.
*/
static void
gld_rcc_send(gld_mac_info_t *macinfo, queue_t *q, uchar_t *dhost,
struct gld_ri **rhp, uchar_t *llcpkt)
{
struct llc_snap_hdr *snaphdr = (struct llc_snap_hdr *)(llcpkt);
/*
* Our caller has to take the mutex because: to avoid an extra bcopy
* of the RIF on every transmit, we pass back a pointer to our sr
* table entry via rhp. He has to keep the mutex until he has a
* chance to copy the RIF out into the outgoing packet, so that we
* don't modify the entry while he's trying to copy it. This is a
* little ugly, but saves the extra bcopy.
*/
ASSERT(mutex_owned(GLD_SR_MUTEX(macinfo)));
*rhp = (struct gld_ri *)NULL; /* start off clean (no RIF) */
if (!((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_enabled) {
/* RDE is disabled -- use NULL or STE always */
if (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->
rde_str_indicator_ste)
*rhp = &ri_ste_def; /* STE option */
return;
}
if (!(dhost[0] & 0x80)) {
/* individual address; Events 7 - 10 */
if ((snaphdr->control & 0xef) == 0xe3) {
/* TEST command, reset the route */
gld_reset_route(macinfo, q,
dhost, snaphdr->d_lsap, snaphdr->s_lsap);
}
gld_get_route(macinfo, q,
dhost, rhp, snaphdr->d_lsap, snaphdr->s_lsap);
}
if (*rhp == NULL) {
/*
* group address (Events 5 - 6),
* or no route available (Events 8 - 9):
* Need to send NSR or STE, as configured.
*/
if (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->
rde_str_indicator_ste)
*rhp = &ri_ste_def; /* STE option */
}
}
/*
* RCC send events 11 - 12
*
* At present we only handle the RQC ptype.
*
* We "know" that the only time we can get here is from the "unitdata"
* routine, called at wsrv time.
*
* If we ever implement the RS ptype (Event 13), this may no longer be true!
*/
static void
gld_rde_pdu_req(gld_mac_info_t *macinfo, queue_t *q, uchar_t *dhost,
struct gld_ri *rh, uchar_t dsap, uchar_t ssap, uchar_t ptype)
{
mblk_t *nmp;
int nlen;
struct tr_mac_frm_nori *nmh;
struct gld_ri *nrh;
struct llc_snap_hdr *nsnaphdr;
struct rde_pdu *npdu;
int srpresent = 0;
/* if you change this to process other types, review all code below */
ASSERT(ptype == RDE_RQC);
ASSERT(rh == NULL); /* RQC never uses SRF */
nlen = sizeof (struct tr_mac_frm) + LLC_HDR1_LEN +
sizeof (struct rde_pdu);
if ((nmp = allocb(nlen, BPRI_MED)) == NULL)
return;
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
nmp->b_rptr -= sizeof (struct rde_pdu);
npdu = (struct rde_pdu *)(nmp->b_rptr);
npdu->rde_ver = 1;
npdu->rde_ptype = ptype;
mac_copy(dhost, &npdu->rde_target_mac, 6);
/*
* access the mac address without a mutex - take a risk -
* to prevent mutex contention (BUG 4211361)
*/
mac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
&npdu->rde_orig_mac, 6);
npdu->rde_target_sap = dsap;
npdu->rde_orig_sap = ssap;
nmp->b_rptr -= LLC_HDR1_LEN;
nsnaphdr = (struct llc_snap_hdr *)(nmp->b_rptr);
nsnaphdr->s_lsap = nsnaphdr->d_lsap = LSAP_RDE;
nsnaphdr->control = CNTL_LLC_UI;
#if 0 /* we don't need this for now */
if (rh != NULL) {
/* send an SRF frame with specified RIF */
ASSERT(rh->len <= sizeof (struct gld_ri));
nmp->b_rptr -= rh->len;
nrh = (struct gld_ri *)(nmp->b_rptr);
bcopy(rh, nrh, rh->len);
ASSERT(nrh->rt == RT_SRF);
srpresent = 1;
} else
#endif
/* Need to send NSR or STE, as configured. */
if (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_str_indicator_ste) {
/* send an STE frame */
nmp->b_rptr -= 2;
nrh = (struct gld_ri *)(nmp->b_rptr);
nrh->len = 2;
nrh->rt = RT_STE;
nrh->dir = 0;
nrh->res = 0;
nrh->mtu = RT_MTU_MAX;
srpresent = 1;
} /* else send an NSR frame */
nmp->b_rptr -= sizeof (struct tr_mac_frm_nori);
nmh = (struct tr_mac_frm_nori *)(nmp->b_rptr);
nmh->tr_ac = 0x10;
nmh->tr_fc = 0x40;
mac_copy(dhost, nmh->tr_dhost, macinfo->gldm_addrlen);
/*
* access the mac address without a mutex - take a risk -
* to prevent mutex contention - BUG 4211361
*/
mac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
nmh->tr_shost, macinfo->gldm_addrlen);
if (srpresent)
nmh->tr_shost[0] |= 0x80;
else
nmh->tr_shost[0] &= ~0x80;
/*
* Packet assembled; send it.
*
* Since we own the SR_MUTEX, we don't want to take the maclock
* mutex (since they are acquired in the opposite order on the
* receive path, so deadlock could occur). We could rearrange
* the code in gld_get_route() and drop the SR_MUTEX around the
* call to gld_rde_pdu_req(), but that's kind of ugly. Rather,
* we just refrain from calling gld_start() from here, and
* instead just queue the packet for wsrv to send next. Besides,
* it's more important to get the packet we're working on out
* quickly than this RQC.
*/
(void) putbq(WR(q), nmp);
qenable(WR(q));
}
/*
* Route Determination Component (RDC)
*
* We do not implement separate routes for each SAP, as specified by
* ISO 8802-2; instead we implement only one route per remote mac address.
*/
static void
gld_get_route(gld_mac_info_t *macinfo, queue_t *q, uchar_t *dhost,
struct gld_ri **rhp, uchar_t dsap, uchar_t ssap)
{
struct srtab *sr;
clock_t t = ddi_get_lbolt();
ASSERT(mutex_owned(GLD_SR_MUTEX(macinfo)));
sr = gld_sr_lookup_entry(macinfo, dhost);
if (sr == NULL) {
/*
* we have no entry -- never heard of this address:
* create an empty entry and initiate RQC
*/
sr = gld_sr_create_entry(macinfo, dhost);
gld_rde_pdu_req(macinfo, q, dhost, (struct gld_ri *)NULL,
dsap, ssap, RDE_RQC);
if (sr)
sr->sr_timer = t;
*rhp = NULL; /* we have no route yet */
return;
}
/* we have an entry; see if we know a route yet */
if (sr->sr_ri.len == 0) {
/* Have asked RQC, but no reply (yet) */
if (t - sr->sr_timer >
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_timeout) {
/* RQR overdue, resend RQC */
gld_rde_pdu_req(macinfo, q, dhost,
(struct gld_ri *)NULL, dsap, ssap, RDE_RQC);
sr->sr_timer = t;
}
*rhp = NULL; /* we have no route yet */
return;
}
/* we know a route, or it's local */
/* if it might be stale, reset and get a new one */
if (t - sr->sr_timer >
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_timeout) {
gld_rde_pdu_req(macinfo, q, dhost,
(struct gld_ri *)NULL, dsap, ssap, RDE_RQC);
sr->sr_ri.len = 0;
sr->sr_timer = t;
*rhp = NULL; /* we have no route */
return;
}
if (sr->sr_ri.len == 2) {
/* the remote site is on our local ring -- no route needed */
*rhp = NULL;
return;
}
*rhp = &sr->sr_ri; /* we have a route, return it */
}
/*
* zap the specified entry and reinitiate RQC
*/
static void
gld_reset_route(gld_mac_info_t *macinfo, queue_t *q,
uchar_t *dhost, uchar_t dsap, uchar_t ssap)
{
struct srtab *sr;
ASSERT(mutex_owned(GLD_SR_MUTEX(macinfo)));
sr = gld_sr_create_entry(macinfo, dhost);
gld_rde_pdu_req(macinfo, q, dhost, (struct gld_ri *)NULL,
dsap, ssap, RDE_RQC);
if (sr == NULL)
return;
sr->sr_ri.len = 0;
sr->sr_timer = ddi_get_lbolt();
}
/*
* This routine is called when an RDE PDU is received from our peer.
* If it is an RS (Route Selected) PDU, we adopt the specified route.
* If it is an RQR (reply to our previous RQC), we evaluate the
* specified route in comparison with our current known route, if any,
* and we keep the "better" of the two routes.
*/
static void
gld_rde_pdu_ind(gld_mac_info_t *macinfo, struct gld_ri *rh, struct rde_pdu *pdu,
int pdulen)
{
struct srtab *sr;
uchar_t *otherhost;
if (pdulen < sizeof (struct rde_pdu))
return; /* Bad incoming PDU */
if (pdu->rde_ptype == RDE_RQC)
return; /* ignore RQC */
if (pdu->rde_ptype != RDE_RQR && pdu->rde_ptype != RDE_RS) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN, "gld: bogus RDE ptype 0x%x received",
pdu->rde_ptype);
#endif
return;
}
if (rh == NULL) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"gld: bogus NULL RIF, ptype 0x%x received",
pdu->rde_ptype);
#endif
return;
}
ASSERT(rh->len >= 2);
ASSERT(rh->len <= sizeof (struct gld_ri));
ASSERT((rh->len & 1) == 0);
if (pdu->rde_ptype == RDE_RQR) {
/* A reply to our RQC has his address as target mac */
otherhost = pdu->rde_target_mac;
} else {
ASSERT(pdu->rde_ptype == RDE_RS);
/* An RS has his address as orig mac */
otherhost = pdu->rde_orig_mac;
}
mutex_enter(GLD_SR_MUTEX(macinfo));
if ((sr = gld_sr_create_entry(macinfo, otherhost)) == NULL) {
mutex_exit(GLD_SR_MUTEX(macinfo));
return; /* oh well, out of memory */
}
if (pdu->rde_ptype == RDE_RQR) {
/* see if new route is better than what we may already have */
if (sr->sr_ri.len != 0 &&
sr->sr_ri.len <= rh->len) {
mutex_exit(GLD_SR_MUTEX(macinfo));
return; /* we have one, and new one is no shorter */
}
}
/* adopt the new route */
bcopy((caddr_t)rh, (caddr_t)&sr->sr_ri, rh->len); /* copy incom RIF */
sr->sr_ri.rt = RT_SRF; /* make it a clean SRF */
sr->sr_ri.dir ^= 1; /* reverse direction */
sr->sr_timer = ddi_get_lbolt();
mutex_exit(GLD_SR_MUTEX(macinfo));
}
/*
* This routine is called when a packet with a RIF is received. Our
* policy is to adopt the route.
*/
/* ARGSUSED3 */
static void
gld_rif_ind(gld_mac_info_t *macinfo, struct gld_ri *rh, uchar_t *shost,
uchar_t ssap, uchar_t dsap)
{
struct srtab *sr;
ASSERT(rh != NULL); /* ensure RIF */
ASSERT((rh->rt & 0x04) == 0); /* ensure SRF */
ASSERT(rh->len >= 2);
ASSERT(rh->len <= sizeof (struct gld_ri));
ASSERT((rh->len & 1) == 0);
mutex_enter(GLD_SR_MUTEX(macinfo));
if ((sr = gld_sr_create_entry(macinfo, shost)) == NULL) {
mutex_exit(GLD_SR_MUTEX(macinfo));
return; /* oh well, out of memory */
}
/* we have an entry; fill it in */
bcopy((caddr_t)rh, (caddr_t)&sr->sr_ri, rh->len); /* copy incom RIF */
sr->sr_ri.rt = RT_SRF; /* make it a clean SRF */
sr->sr_ri.dir ^= 1; /* reverse direction */
sr->sr_timer = ddi_get_lbolt();
mutex_exit(GLD_SR_MUTEX(macinfo));
}
static struct srtab **
gld_sr_hash(struct srtab **sr_hash_tbl, uchar_t *addr, int addr_length)
{
uint_t hashval = 0;
while (--addr_length >= 0)
hashval ^= *addr++;
return (&sr_hash_tbl[hashval % SR_HASH_SIZE]);
}
static struct srtab *
gld_sr_lookup_entry(gld_mac_info_t *macinfo, uchar_t *macaddr)
{
struct srtab *sr;
ASSERT(mutex_owned(GLD_SR_MUTEX(macinfo)));
for (sr = *gld_sr_hash(GLD_SR_HASH(macinfo), macaddr,
macinfo->gldm_addrlen); sr; sr = sr->sr_next)
if (mac_eq(macaddr, sr->sr_mac, macinfo->gldm_addrlen))
return (sr);
return ((struct srtab *)0);
}
static struct srtab *
gld_sr_create_entry(gld_mac_info_t *macinfo, uchar_t *macaddr)
{
struct srtab *sr;
struct srtab **srp;
ASSERT(!(macaddr[0] & 0x80)); /* no group addresses here */
ASSERT(mutex_owned(GLD_SR_MUTEX(macinfo)));
srp = gld_sr_hash(GLD_SR_HASH(macinfo), macaddr, macinfo->gldm_addrlen);
for (sr = *srp; sr; sr = sr->sr_next)
if (mac_eq(macaddr, sr->sr_mac, macinfo->gldm_addrlen))
return (sr);
if (!(sr = kmem_zalloc(sizeof (struct srtab), KM_NOSLEEP))) {
#ifdef GLD_DEBUG
if (gld_debug & GLDERRS)
cmn_err(CE_WARN,
"gld: gld_sr_create_entry kmem_alloc failed");
#endif
return ((struct srtab *)0);
}
bcopy((caddr_t)macaddr, (caddr_t)sr->sr_mac, macinfo->gldm_addrlen);
sr->sr_next = *srp;
*srp = sr;
return (sr);
}
static void
gld_sr_clear(gld_mac_info_t *macinfo)
{
int i;
struct srtab **sr_hash_tbl = GLD_SR_HASH(macinfo);
struct srtab **srp, *sr;
/*
* Walk through the table, deleting all entries.
*
* Only called from uninit, so don't need the mutex.
*/
for (i = 0; i < SR_HASH_SIZE; i++) {
for (srp = &sr_hash_tbl[i]; (sr = *srp) != NULL; ) {
*srp = sr->sr_next;
kmem_free((char *)sr, sizeof (struct srtab));
}
}
}
#ifdef DEBUG
void
gld_sr_dump(gld_mac_info_t *macinfo)
{
int i, j;
struct srtab **sr_hash_tbl;
struct srtab *sr;
sr_hash_tbl = GLD_SR_HASH(macinfo);
if (sr_hash_tbl == NULL)
return;
mutex_enter(GLD_SR_MUTEX(macinfo));
/*
* Walk through the table, printing all entries
*/
cmn_err(CE_NOTE, "GLD Source Routing Table (0x%p):", (void *)macinfo);
cmn_err(CE_CONT, "Addr len,rt,dir,mtu,res rng,brg0 rng,brg1...\n");
for (i = 0; i < SR_HASH_SIZE; i++) {
for (sr = sr_hash_tbl[i]; sr; sr = sr->sr_next) {
cmn_err(CE_CONT,
"%x:%x:%x:%x:%x:%x %d,%x,%x,%x,%x ",
sr->sr_mac[0], sr->sr_mac[1], sr->sr_mac[2],
sr->sr_mac[3], sr->sr_mac[4], sr->sr_mac[5],
sr->sr_ri.len, sr->sr_ri.rt, sr->sr_ri.dir,
sr->sr_ri.mtu, sr->sr_ri.res);
if (sr->sr_ri.len)
for (j = 0; j < (sr->sr_ri.len - 2) / 2; j++)
cmn_err(CE_CONT, "%x ",
REF_NET_USHORT(*(unsigned short *)
&sr->sr_ri.rd[j]));
cmn_err(CE_CONT, "\n");
}
}
mutex_exit(GLD_SR_MUTEX(macinfo));
}
#endif