#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/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,
LSAP_SNAP,
CNTL_LLC_UI,
0x00, 0x00, 0x00,
0x00
};
#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)
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;
ASSERT(macinfo->gldm_type == DL_ETHER);
ASSERT(macinfo->gldm_addrlen == 6);
ASSERT(macinfo->gldm_saplen == -2);
#ifndef lint
ASSERT(sizeof (struct ether_header) == 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);
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);
}
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_header *mh;
gld_mac_pvt_t *mac_pvt = (gld_mac_pvt_t *)macinfo->gldm_mac_pvt;
struct llc_snap_hdr *snaphdr;
mblk_t *pmp = NULL, *savemp = mp;
unsigned short typelen;
int ret = 0;
if (flags == GLD_RXQUICK) {
pktinfo->pktLen = msgdsize(mp);
if (MBLKL(mp) < sizeof (struct ether_header))
return (-1);
mh = (struct ether_header *)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_header);
return (0);
}
bzero((void *)pktinfo, sizeof (*pktinfo));
pktinfo->pktLen = msgdsize(mp);
if (pktinfo->pktLen < sizeof (struct ether_header))
return (-1);
if (MBLKL(mp) < sizeof (struct ether_header)) {
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;
}
mh = (struct ether_header *)mp->b_rptr;
if (mac_eq(&mh->ether_dhost, ether_broadcast, macinfo->gldm_addrlen))
pktinfo->isBroadcast = 1;
else if (mh->ether_dhost.ether_addr_octet[0] & 1)
pktinfo->isMulticast = 1;
typelen = REF_NET_USHORT(mh->ether_type);
if (flags == GLD_TX) {
if ((typelen == ETHERTYPE_VLAN) &&
(macinfo->gldm_send_tagged != NULL)) {
struct ether_vlan_header *evhp;
uint16_t tci;
if ((MBLKL(mp) < sizeof (struct ether_vlan_header)) &&
(pullupmsg(mp, sizeof (struct ether_vlan_header))
== 0)) {
ret = -1;
goto out;
}
evhp = (struct ether_vlan_header *)mp->b_rptr;
tci = REF_NET_USHORT(evhp->ether_tci);
if ((GLD_VTAG_PRI((int32_t)tci) == 0 &&
GLD_VTAG_VID((int32_t)tci) == VLAN_VID_NONE) ||
(GLD_VTAG_CFI((uint32_t)tci)) != VLAN_CFI_ETHER) {
ret = -1;
goto out;
}
GLD_SAVE_MBLK_VTAG(savemp, GLD_TCI2VTAG(tci));
ovbcopy(mp->b_rptr, mp->b_rptr + VTAG_SIZE,
2 * ETHERADDRL);
mp->b_rptr += VTAG_SIZE;
}
goto out;
}
ASSERT(GLDM_LOCK_HELD(macinfo));
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_header);
if (typelen > ETHERMTU) {
pktinfo->ethertype = typelen;
goto out;
}
{
int delta = pktinfo->pktLen -
(sizeof (struct ether_header) + typelen);
if (delta > 0 && adjmsg(mp, -delta))
pktinfo->pktLen -= delta;
}
if (pktinfo->pktLen < pktinfo->macLen + LLC_HDR1_LEN)
goto out;
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_header) + LLC_SNAP_HDR_LEN &&
MBLKL(mp) < pktinfo->pktLen) {
ASSERT(pmp == NULL);
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;
}
mp = pmp;
}
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 (ret);
}
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_header *mh;
int hdrlen;
uint32_t vptag;
gld_vlan_t *gld_vlan;
ASSERT(macinfo);
mac_copy(gldp->glda_addr, dhost, macinfo->gldm_addrlen);
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;
if (typelen <= ETHERMTU)
typelen = msgdsize(mp);
hdrlen = sizeof (struct ether_header);
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;
}
nmp = mp->b_cont;
if (DB_REF(nmp) == 1 && MBLKHEAD(nmp) >= hdrlen) {
freeb(mp);
} else if (DB_REF(mp) == 1 && MBLKSIZE(mp) >= hdrlen) {
nmp = mp;
DB_TYPE(nmp) = M_DATA;
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
} else {
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);
}
nmp->b_rptr -= sizeof (typelen);
SET_NET_USHORT(*(uint16_t *)nmp->b_rptr, typelen);
if (hdrlen > sizeof (struct ether_header)) {
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_header *)nmp->b_rptr;
mac_copy(dhost, &mh->ether_dhost, macinfo->gldm_addrlen);
mac_copy(((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->curr_macaddr,
&mh->ether_shost, macinfo->gldm_addrlen);
return (nmp);
}
mblk_t *
gld_insert_vtag_ether(mblk_t *mp, uint32_t vtag)
{
struct ether_vlan_header *evhp;
struct ether_header *ehp;
mblk_t *nmp;
if (vtag == VLAN_VID_NONE)
return (mp);
if (DB_REF(mp) == 1 && MBLKHEAD(mp) >= VTAG_SIZE) {
nmp = mp;
ovbcopy(nmp->b_rptr, nmp->b_rptr - VTAG_SIZE, 2 * ETHERADDRL);
nmp->b_rptr -= VTAG_SIZE;
evhp = (struct ether_vlan_header *)nmp->b_rptr;
} else {
if ((nmp = allocb(sizeof (struct ether_vlan_header),
BPRI_MED)) == NULL) {
return (NULL);
}
nmp->b_wptr += sizeof (struct ether_vlan_header);
evhp = (struct ether_vlan_header *)nmp->b_rptr;
ehp = (struct ether_header *)mp->b_rptr;
mac_copy(&ehp->ether_dhost, &evhp->ether_dhost, ETHERADDRL);
mac_copy(&ehp->ether_shost, &evhp->ether_shost, ETHERADDRL);
bcopy(&ehp->ether_type, &evhp->ether_type, sizeof (uint16_t));
mp->b_rptr += sizeof (struct ether_header);
if (MBLKL(mp) == 0) {
nmp->b_cont = mp->b_cont;
freeb(mp);
} else {
nmp->b_cont = mp;
}
}
SET_NET_USHORT(evhp->ether_tci, vtag);
vtag >>= 16;
SET_NET_USHORT(evhp->ether_tpid, vtag);
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_header *mh;
int hdrlen;
uint32_t vptag;
gld_vlan_t *gld_vlan;
ASSERT(macinfo);
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;
if (typelen <= ETHERMTU)
return (NULL);
hdrlen = sizeof (struct ether_header);
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);
nmp->b_rptr -= sizeof (typelen);
SET_NET_USHORT(*(uint16_t *)nmp->b_rptr, typelen);
if (hdrlen > sizeof (struct ether_header)) {
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_header *)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);
}
void
gld_init_ib(gld_mac_info_t *macinfo)
{
ASSERT(macinfo->gldm_type == DL_IB);
ASSERT(macinfo->gldm_addrlen == IPOIB_ADDRL);
ASSERT(macinfo->gldm_saplen == -2);
}
void
gld_uninit_ib(gld_mac_info_t *macinfo)
{
}
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;
if (flags == GLD_RXQUICK) {
pktinfo->pktLen = msgdsize(mp) - IPOIB_GRH_SIZE;
if (MBLKL(mp) < (IPOIB_GRH_SIZE + IPOIB_HDRSIZE))
return (-1);
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);
}
}
ASSERT(flags != GLD_RXQUICK);
bzero((void *)pktinfo, sizeof (*pktinfo));
if (flags != GLD_RX) {
gldp = (ipoib_ptxhdr_t *)mp->b_rptr;
pktinfo->pktLen = msgdsize(mp);
if (pktinfo->pktLen < sizeof (ipoib_ptxhdr_t))
return (-1);
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);
}
mp = pmp;
}
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;
}
pktinfo->pktLen -= IPOIB_ADDRL;
if (flags == GLD_TX)
goto out;
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 {
ipoib_mac_t *mact, *tact;
ib_qpn_t dqpn;
pktinfo->pktLen = msgdsize(mp);
if (pktinfo->pktLen < (IPOIB_GRH_SIZE + IPOIB_HDRSIZE))
return (-1);
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);
}
mp = pmp;
}
grh = (ipoib_pgrh_t *)mp->b_rptr;
mp->b_rptr += IPOIB_GRH_SIZE;
pktinfo->pktLen -= IPOIB_GRH_SIZE;
if (grh->ipoib_vertcflow) {
mact = (ipoib_mac_t *)pktinfo->shost;
mac_copy(&grh->ipoib_sqpn, &mact->ipoib_qpn,
IPOIB_ADDRL);
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;
if (*(uchar_t *)(grh->ipoib_dgid_pref) == 0xFF) {
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;
dqpn = htonl(IB_MC_QPN);
mac_copy(&dqpn, &mact->ipoib_qpn,
sizeof (mact->ipoib_qpn));
} else {
mac_copy(&tact->ipoib_qpn, &mact->ipoib_qpn,
sizeof (mact->ipoib_qpn));
pktinfo->isForMe = 1;
}
} else {
pktinfo->nosource = 1;
mac_copy(mac_pvt->curr_macaddr, pktinfo->dhost,
IPOIB_ADDRL);
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);
}
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);
mac_copy(&gldp->ipoib_dest, &dhost, IPOIB_ADDRL);
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);
nmp = mp->b_cont;
if (DB_REF(nmp) == 1 && MBLKHEAD(nmp) >= hdrlen) {
freeb(mp);
} else if (DB_REF(mp) == 1 && MBLKSIZE(mp) >= hdrlen) {
nmp = mp;
DB_TYPE(nmp) = M_DATA;
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
} else {
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);
}
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);
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);
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);
}
void
gld_init_fddi(gld_mac_info_t *macinfo)
{
struct gldkstats *sp =
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->kstatp->ks_data;
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
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);
}
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;
if (flags == GLD_RXQUICK) {
pktinfo->pktLen = msgdsize(mp);
return (-1);
}
bzero((void *)pktinfo, sizeof (*pktinfo));
pktinfo->pktLen = msgdsize(mp);
if (pktinfo->pktLen < sizeof (struct fddi_mac_frm))
return (-1);
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;
}
mh = (struct fddi_mac_frm *)mp->b_rptr;
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;
ASSERT(GLDM_LOCK_HELD(macinfo));
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);
if (MBLKL(mp) < sizeof (struct fddi_mac_frm) + LLC_SNAP_HDR_LEN &&
MBLKL(mp) < pktinfo->pktLen) {
ASSERT(pmp == NULL);
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;
}
mp = pmp;
}
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);
mac_copy(gldp->glda_addr, dhost, macinfo->gldm_addrlen);
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);
if (type > GLD_MAX_802_SAP)
hdrlen += sizeof (struct llc_snap_hdr);
nmp = mp->b_cont;
if (DB_REF(nmp) == 1 && MBLKHEAD(nmp) >= hdrlen) {
freeb(mp);
} else if (DB_REF(mp) == 1 && MBLKSIZE(mp) >= hdrlen) {
nmp = mp;
DB_TYPE(nmp) = M_DATA;
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
} else {
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);
}
if (type > GLD_MAX_802_SAP) {
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);
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);
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);
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);
if (type > GLD_MAX_802_SAP) {
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);
}
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);
}
#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;
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;
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);
((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_enabled =
ddi_getprop(DDI_DEV_T_NONE, macinfo->gldm_devinfo, 0,
"gld_rde_enable", 1);
((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);
{
int t = ddi_getprop(DDI_DEV_T_NONE, macinfo->gldm_devinfo, 0,
"gld_rde_timeout", 10);
if (t < 1)
t = 1;
if (t > 600)
t = 600;
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);
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;
if (flags == GLD_RXQUICK) {
pktinfo->pktLen = msgdsize(mp);
return (-1);
}
bzero((void *)pktinfo, sizeof (*pktinfo));
pktinfo->pktLen = msgdsize(mp);
if (pktinfo->pktLen < sizeof (struct tr_mac_frm_nori))
return (-1);
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;
}
mh = (struct tr_mac_frm *)mp->b_rptr;
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;
ASSERT(GLDM_LOCK_HELD(macinfo));
mac_copy(mh->tr_dhost, pktinfo->dhost, macinfo->gldm_addrlen);
mac_copy(mh->tr_shost, pktinfo->shost, macinfo->gldm_addrlen);
pktinfo->shost[0] &= ~0x80;
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);
if (MBLKL(mp) < sizeof (struct tr_mac_frm) +
LLC_HDR1_LEN + sizeof (struct rde_pdu) &&
MBLKL(mp) < pktinfo->pktLen) {
ASSERT(pmp == NULL);
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;
}
mp = pmp;
mh = (struct tr_mac_frm *)mp->b_rptr;
}
if (mh->tr_shost[0] & 0x80) {
if (pktinfo->pktLen < sizeof (struct tr_mac_frm_nori) + 2)
goto out;
rh = (struct gld_ri *)&mh->tr_ri;
if ((rh->len & 1) || rh->len < 2) {
#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;
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;
}
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);
mac_copy(gldp->glda_addr, dhost, macinfo->gldm_addrlen);
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);
if (type > GLD_MAX_802_SAP)
hdrlen += sizeof (struct llc_snap_hdr);
llcmp = nmp = mp->b_cont;
ASSERT(nmp != NULL);
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) {
freeb(mp);
} else if (DB_REF(mp) == 1 && MBLKSIZE(mp) >= hdrlen) {
nmp = mp;
DB_TYPE(nmp) = M_DATA;
nmp->b_rptr = nmp->b_wptr = DB_LIM(nmp);
} else {
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);
}
if (type > GLD_MAX_802_SAP) {
struct llc_snap_hdr *snap;
llcmp = nmp;
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);
}
mutex_enter(GLD_SR_MUTEX(macinfo));
gld_rcc_send(macinfo, WR(gld->gld_qptr), dhost, &rh, llcmp->b_rptr);
if (rh != NULL) {
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));
if (pmp != NULL)
freemsg(pmp);
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);
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);
}
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 (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_enabled)
return (NULL);
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;
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);
if (type > GLD_MAX_802_SAP) {
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);
}
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);
}
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);
}
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 *);
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;
if (!pktinfo->isForMe)
return;
if (snaphdr->d_lsap == LSAP_RDE) {
struct rde_pdu *pdu = (struct rde_pdu *)(llcpkt + LLC_HDR1_LEN);
int pdulen = llcpktlen - LLC_HDR1_LEN;
if ((pdulen < sizeof (struct rde_pdu)) ||
(snaphdr->s_lsap != LSAP_RDE))
return;
if (snaphdr->control != CNTL_LLC_UI)
return;
switch (pdu->rde_ptype) {
case RDE_RQC:
gld_send_rqr(macinfo, pktinfo->shost, rh, pdu, pdulen);
case RDE_RQR:
case RDE_RS:
gld_rde_pdu_ind(macinfo, rh, pdu, pdulen);
break;
default:
return;
}
return;
}
if (rh == NULL)
return;
if ((rh->rt & 0x04) != 0)
return;
gld_rif_ind(macinfo, rh, pktinfo->shost, snaphdr->s_lsap,
snaphdr->d_lsap);
}
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;
ASSERT(GLDM_LOCK_HELD(macinfo));
if (pdulen < sizeof (struct rde_pdu))
return;
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;
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)) {
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 {
ASSERT(rh->len <= sizeof (struct gld_ri));
nmp->b_rptr -= rh->len;
nrh = (struct gld_ri *)(nmp->b_rptr);
bcopy(rh, nrh, rh->len);
nrh->rt = RT_SRF;
nrh->dir ^= 1;
}
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;
{
gld_vlan_t *vlan;
queue_t *q;
if ((vlan = gld_find_vlan(macinfo, VLAN_VID_NONE)) == NULL) {
freeb(nmp);
return;
}
q = vlan->gldv_str_next->gld_qptr;
(void) putbq(WR(q), nmp);
qenable(WR(q));
}
}
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);
ASSERT(mutex_owned(GLD_SR_MUTEX(macinfo)));
*rhp = (struct gld_ri *)NULL;
if (!((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_enabled) {
if (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->
rde_str_indicator_ste)
*rhp = &ri_ste_def;
return;
}
if (!(dhost[0] & 0x80)) {
if ((snaphdr->control & 0xef) == 0xe3) {
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) {
if (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->
rde_str_indicator_ste)
*rhp = &ri_ste_def;
}
}
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;
ASSERT(ptype == RDE_RQC);
ASSERT(rh == NULL);
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);
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
if (rh != NULL) {
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
if (((gld_mac_pvt_t *)macinfo->gldm_mac_pvt)->rde_str_indicator_ste) {
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;
}
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);
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;
(void) putbq(WR(q), nmp);
qenable(WR(q));
}
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) {
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;
return;
}
if (sr->sr_ri.len == 0) {
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_timer = t;
}
*rhp = NULL;
return;
}
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;
return;
}
if (sr->sr_ri.len == 2) {
*rhp = NULL;
return;
}
*rhp = &sr->sr_ri;
}
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();
}
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;
if (pdu->rde_ptype == RDE_RQC)
return;
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) {
otherhost = pdu->rde_target_mac;
} else {
ASSERT(pdu->rde_ptype == RDE_RS);
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;
}
if (pdu->rde_ptype == RDE_RQR) {
if (sr->sr_ri.len != 0 &&
sr->sr_ri.len <= rh->len) {
mutex_exit(GLD_SR_MUTEX(macinfo));
return;
}
}
bcopy((caddr_t)rh, (caddr_t)&sr->sr_ri, rh->len);
sr->sr_ri.rt = RT_SRF;
sr->sr_ri.dir ^= 1;
sr->sr_timer = ddi_get_lbolt();
mutex_exit(GLD_SR_MUTEX(macinfo));
}
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);
ASSERT((rh->rt & 0x04) == 0);
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;
}
bcopy((caddr_t)rh, (caddr_t)&sr->sr_ri, rh->len);
sr->sr_ri.rt = RT_SRF;
sr->sr_ri.dir ^= 1;
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));
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;
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));
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