#define RCSID "$Id: sppp_dlpi.c,v 1.0 2000/05/08 01:10:12 masputra Exp $"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/cmn_err.h>
#include <sys/conf.h>
#include <sys/dlpi.h>
#include <sys/ddi.h>
#include <sys/kstat.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/ethernet.h>
#include <net/ppp_defs.h>
#include <netinet/in.h>
#include <net/pppio.h>
#include "s_common.h"
#include "sppp.h"
static int sppp_dlattachreq(queue_t *, mblk_t *, spppstr_t *);
static int sppp_dldetachreq(queue_t *, mblk_t *, spppstr_t *);
static int sppp_dlbindreq(queue_t *, mblk_t *, spppstr_t *);
static int sppp_dlunbindreq(queue_t *, mblk_t *, spppstr_t *);
static int sppp_dlinforeq(queue_t *, mblk_t *, spppstr_t *);
static int sppp_dlunitdatareq(queue_t *, mblk_t *, spppstr_t *);
static int sppp_dlpromisconreq(queue_t *, mblk_t *, spppstr_t *);
static int sppp_dlpromiscoffreq(queue_t *, mblk_t *, spppstr_t *);
static int sppp_dlphyreq(queue_t *, mblk_t *, spppstr_t *);
static void sppp_dl_attach_upper(queue_t *, mblk_t *);
static void sppp_dl_detach_upper(queue_t *, mblk_t *);
static void sppp_dl_bind(queue_t *, mblk_t *);
static void sppp_dl_unbind(queue_t *, mblk_t *);
static void sppp_dl_promiscon(queue_t *, mblk_t *);
static void sppp_dl_promiscoff(queue_t *, mblk_t *);
static mblk_t *sppp_dladdether(spppstr_t *, mblk_t *, t_scalar_t);
static struct sppp_dlpi_pinfo_t dl_pinfo[DL_MAXPRIM + 1];
#if 0
#define DBGERROR(x) cmn_err x
#else
#define DBGERROR(x) ((void)0)
#endif
#ifdef DBG_DLPI
struct sppp_dlpi_entry {
uint32_t sde_val;
const char *sde_name;
};
static const struct sppp_dlpi_entry sppp_dlpi_list[] = {
{ DL_INFO_REQ, "DL_INFO_REQ" },
{ DL_INFO_ACK, "DL_INFO_ACK" },
{ DL_ATTACH_REQ, "DL_ATTACH_REQ" },
{ DL_DETACH_REQ, "DL_DETACH_REQ" },
{ DL_BIND_REQ, "DL_BIND_REQ" },
{ DL_BIND_ACK, "DL_BIND_ACK" },
{ DL_UNBIND_REQ, "DL_UNBIND_REQ" },
{ DL_OK_ACK, "DL_OK_ACK" },
{ DL_ERROR_ACK, "DL_ERROR_ACK" },
{ DL_SUBS_BIND_REQ, "DL_SUBS_BIND_REQ" },
{ DL_SUBS_BIND_ACK, "DL_SUBS_BIND_ACK" },
{ DL_SUBS_UNBIND_REQ, "DL_SUBS_UNBIND_REQ" },
{ DL_ENABMULTI_REQ, "DL_ENABMULTI_REQ" },
{ DL_DISABMULTI_REQ, "DL_DISABMULTI_REQ" },
{ DL_PROMISCON_REQ, "DL_PROMISCON_REQ" },
{ DL_PROMISCOFF_REQ, "DL_PROMISCOFF_REQ" },
{ DL_UNITDATA_REQ, "DL_UNITDATA_REQ" },
{ DL_UNITDATA_IND, "DL_UNITDATA_IND" },
{ DL_UDERROR_IND, "DL_UDERROR_IND" },
{ DL_UDQOS_REQ, "DL_UDQOS_REQ" },
{ DL_CONNECT_REQ, "DL_CONNECT_REQ" },
{ DL_CONNECT_IND, "DL_CONNECT_IND" },
{ DL_CONNECT_RES, "DL_CONNECT_RES" },
{ DL_CONNECT_CON, "DL_CONNECT_CON" },
{ DL_TOKEN_REQ, "DL_TOKEN_REQ" },
{ DL_TOKEN_ACK, "DL_TOKEN_ACK" },
{ DL_DISCONNECT_REQ, "DL_DISCONNECT_REQ" },
{ DL_DISCONNECT_IND, "DL_DISCONNECT_IND" },
{ DL_RESET_REQ, "DL_RESET_REQ" },
{ DL_RESET_IND, "DL_RESET_IND" },
{ DL_RESET_RES, "DL_RESET_RES" },
{ DL_RESET_CON, "DL_RESET_CON" },
{ DL_DATA_ACK_REQ, "DL_DATA_ACK_REQ" },
{ DL_DATA_ACK_IND, "DL_DATA_ACK_IND" },
{ DL_DATA_ACK_STATUS_IND, "DL_DATA_ACK_STATUS_IND" },
{ DL_REPLY_REQ, "DL_REPLY_REQ" },
{ DL_REPLY_IND, "DL_REPLY_IND" },
{ DL_REPLY_STATUS_IND, "DL_REPLY_STATUS_IND" },
{ DL_REPLY_UPDATE_REQ, "DL_REPLY_UPDATE_REQ" },
{ DL_REPLY_UPDATE_STATUS_IND, "DL_REPLY_UPDATE_STATUS_IND" },
{ DL_XID_REQ, "DL_XID_REQ" },
{ DL_XID_IND, "DL_XID_IND" },
{ DL_XID_RES, "DL_XID_RES" },
{ DL_XID_CON, "DL_XID_CON" },
{ DL_TEST_REQ, "DL_TEST_REQ" },
{ DL_TEST_IND, "DL_TEST_IND" },
{ DL_TEST_RES, "DL_TEST_RES" },
{ DL_TEST_CON, "DL_TEST_CON" },
{ DL_PHYS_ADDR_REQ, "DL_PHYS_ADDR_REQ" },
{ DL_PHYS_ADDR_ACK, "DL_PHYS_ADDR_ACK" },
{ DL_SET_PHYS_ADDR_REQ, "DL_SET_PHYS_ADDR_REQ" },
{ DL_GET_STATISTICS_REQ, "DL_GET_STATISTICS_REQ" },
{ DL_GET_STATISTICS_ACK, "DL_GET_STATISTICS_ACK" },
{ 0, NULL }
};
static const struct sppp_dlpi_entry sppp_state_list[] = {
{ DL_UNBOUND, "DL_UNBOUND" },
{ DL_BIND_PENDING, "DL_BIND_PENDING" },
{ DL_UNBIND_PENDING, "DL_UNBIND_PENDING" },
{ DL_IDLE, "DL_IDLE" },
{ DL_UNATTACHED, "DL_UNATTACHED" },
{ DL_ATTACH_PENDING, "DL_ATTACH_PENDING" },
{ DL_DETACH_PENDING, "DL_DETACH_PENDING" },
{ DL_UDQOS_PENDING, "DL_UDQOS_PENDING" },
{ DL_OUTCON_PENDING, "DL_OUTCON_PENDING" },
{ DL_INCON_PENDING, "DL_INCON_PENDING" },
{ DL_CONN_RES_PENDING, "DL_CONN_RES_PENDING" },
{ DL_DATAXFER, "DL_DATAXFER" },
{ DL_USER_RESET_PENDING, "DL_USER_RESET_PENDING" },
{ DL_PROV_RESET_PENDING, "DL_PROV_RESET_PENDING" },
{ DL_RESET_RES_PENDING, "DL_RESET_RES_PENDING" },
{ DL_DISCON8_PENDING, "DL_DISCON8_PENDING" },
{ DL_DISCON9_PENDING, "DL_DISCON9_PENDING" },
{ DL_DISCON11_PENDING, "DL_DISCON11_PENDING" },
{ DL_DISCON12_PENDING, "DL_DISCON12_PENDING" },
{ DL_DISCON13_PENDING, "DL_DISCON13_PENDING" },
{ DL_SUBS_BIND_PND, "DL_SUBS_BIND_PND" },
{ DL_SUBS_UNBIND_PND, "DL_SUBS_UNBIND_PND" },
{ 0, NULL }
};
static const char *
prim2name(uint32_t prim)
{
const struct sppp_dlpi_entry *sde;
for (sde = sppp_dlpi_list; sde->sde_name != NULL; sde++)
if (sde->sde_val == prim)
break;
return (sde->sde_name);
}
static const char *
state2name(uint32_t state)
{
const struct sppp_dlpi_entry *sde;
for (sde = sppp_state_list; sde->sde_name != NULL; sde++)
if (sde->sde_val == state)
break;
return (sde->sde_name);
}
#define DBGDLPI(x) cmn_err x
#else
#define DBGDLPI(x) ((void)0)
#endif
static dl_info_ack_t sppp_infoack = {
DL_INFO_ACK,
PPP_MAXMTU,
0,
SPPP_ADDRL,
DL_ETHER,
0,
0,
SPPP_SAPL,
DL_CLDLS,
0,
0,
0,
0,
DL_STYLE2,
sizeof (dl_info_ack_t),
DL_VERSION_2,
0,
0,
0
};
void
sppp_dlpi_pinfoinit(void)
{
bzero(dl_pinfo, sizeof (dl_pinfo));
dl_pinfo[DL_ATTACH_REQ].pi_minlen = sizeof (dl_attach_req_t);
dl_pinfo[DL_ATTACH_REQ].pi_state = DL_UNATTACHED;
dl_pinfo[DL_ATTACH_REQ].pi_funcp = sppp_dlattachreq;
dl_pinfo[DL_DETACH_REQ].pi_minlen = sizeof (dl_detach_req_t);
dl_pinfo[DL_DETACH_REQ].pi_state = DL_UNBOUND;
dl_pinfo[DL_DETACH_REQ].pi_funcp = sppp_dldetachreq;
dl_pinfo[DL_BIND_REQ].pi_minlen = sizeof (dl_bind_req_t);
dl_pinfo[DL_BIND_REQ].pi_state = DL_UNBOUND;
dl_pinfo[DL_BIND_REQ].pi_funcp = sppp_dlbindreq;
dl_pinfo[DL_UNBIND_REQ].pi_minlen = sizeof (dl_unbind_req_t);
dl_pinfo[DL_UNBIND_REQ].pi_state = DL_IDLE;
dl_pinfo[DL_UNBIND_REQ].pi_funcp = sppp_dlunbindreq;
dl_pinfo[DL_INFO_REQ].pi_minlen = sizeof (dl_info_req_t);
dl_pinfo[DL_INFO_REQ].pi_state = -1;
dl_pinfo[DL_INFO_REQ].pi_funcp = sppp_dlinforeq;
dl_pinfo[DL_UNITDATA_REQ].pi_minlen = sizeof (dl_unitdata_req_t);
dl_pinfo[DL_UNITDATA_REQ].pi_state = DL_IDLE;
dl_pinfo[DL_UNITDATA_REQ].pi_funcp = sppp_dlunitdatareq;
dl_pinfo[DL_PROMISCON_REQ].pi_minlen = sizeof (dl_promiscon_req_t);
dl_pinfo[DL_PROMISCON_REQ].pi_state = -1;
dl_pinfo[DL_PROMISCON_REQ].pi_funcp = sppp_dlpromisconreq;
dl_pinfo[DL_PROMISCOFF_REQ].pi_minlen = sizeof (dl_promiscoff_req_t);
dl_pinfo[DL_PROMISCOFF_REQ].pi_state = -1;
dl_pinfo[DL_PROMISCOFF_REQ].pi_funcp = sppp_dlpromiscoffreq;
dl_pinfo[DL_PHYS_ADDR_REQ].pi_minlen = sizeof (dl_phys_addr_req_t);
dl_pinfo[DL_PHYS_ADDR_REQ].pi_state = -1;
dl_pinfo[DL_PHYS_ADDR_REQ].pi_funcp = sppp_dlphyreq;
}
int
sppp_mproto(queue_t *q, mblk_t *mp, spppstr_t *sps)
{
union DL_primitives *dlp;
struct sppp_dlpi_pinfo_t *dpi;
t_uscalar_t prim;
int len;
int error = 0;
ASSERT(!IS_SPS_CONTROL(sps));
if ((len = MBLKL(mp)) < sizeof (t_uscalar_t)) {
DBGERROR((CE_CONT, "bad mproto: block length %d\n", len));
merror(q, mp, EPROTO);
return (0);
}
dlp = (union DL_primitives *)mp->b_rptr;
prim = dlp->dl_primitive;
if (prim > DL_MAXPRIM) {
DBGERROR((CE_CONT, "bad mproto: primitive %d > %d\n", prim,
DL_MAXPRIM));
error = DL_BADPRIM;
} else {
dpi = &dl_pinfo[prim];
if (dpi->pi_funcp == NULL) {
DBGERROR((CE_CONT,
"bad mproto: primitive %d not supported\n", prim));
error = DL_NOTSUPPORTED;
} else if (len < dpi->pi_minlen) {
DBGERROR((CE_CONT,
"bad mproto: primitive len %d < %d\n", len,
dpi->pi_minlen));
error = DL_BADPRIM;
} else if (dpi->pi_state != -1 &&
sps->sps_dlstate != dpi->pi_state) {
DBGERROR((CE_CONT,
"bad state %d != %d for primitive %d\n",
sps->sps_dlstate, dpi->pi_state, prim));
error = DL_OUTSTATE;
}
}
if (error != 0) {
dlerrorack(q, mp, dlp->dl_primitive, error, 0);
return (0);
}
#ifdef DBG_DLPI
{
const char *cp = prim2name(prim);
if (cp != NULL)
cmn_err(CE_CONT, "/%d: Dispatching %s\n",
sps->sps_mn_id, cp);
else
cmn_err(CE_CONT,
"/%d: Dispatching unknown primitive %d\n",
sps->sps_mn_id, prim);
}
#endif
return ((*dpi->pi_funcp)(q, mp, sps));
}
static int
sppp_dlattachreq(queue_t *q, mblk_t *mp, spppstr_t *sps)
{
int error = 0;
union DL_primitives *dlp;
ASSERT(q != NULL && q->q_ptr != NULL);
ASSERT(mp != NULL && mp->b_rptr != NULL);
dlp = (union DL_primitives *)mp->b_rptr;
ASSERT(sps != NULL);
ASSERT(sps->sps_dlstate == DL_UNATTACHED);
if (IS_SPS_PIOATTACH(sps)) {
DBGERROR((CE_CONT, "DLPI attach: already attached\n"));
error = EINVAL;
}
if (error != 0) {
dlerrorack(q, mp, dlp->dl_primitive, DL_OUTSTATE, error);
} else {
qwriter(q, mp, sppp_dl_attach_upper, PERIM_OUTER);
}
return (0);
}
static void
sppp_dl_attach_upper(queue_t *q, mblk_t *mp)
{
sppa_t *ppa;
spppstr_t *sps = q->q_ptr;
union DL_primitives *dlp;
int err = ENOMEM;
cred_t *cr;
zoneid_t zoneid;
ASSERT(!IS_SPS_PIOATTACH(sps));
dlp = (union DL_primitives *)mp->b_rptr;
if (sps->sps_ppa != NULL) {
sppp_remove_ppa(sps);
}
if ((cr = msg_getcred(mp, NULL)) == NULL)
zoneid = sps->sps_zoneid;
else
zoneid = crgetzoneid(cr);
ppa = sppp_find_ppa(dlp->attach_req.dl_ppa);
if (ppa == NULL) {
ppa = sppp_create_ppa(dlp->attach_req.dl_ppa, zoneid);
} else if (ppa->ppa_zoneid != zoneid) {
ppa = NULL;
err = EPERM;
}
if (ppa == NULL) {
DBGERROR((CE_CONT, "DLPI attach: cannot create ppa %u\n",
dlp->attach_req.dl_ppa));
dlerrorack(q, mp, dlp->dl_primitive, DL_SYSERR, err);
return;
}
if ((sps->sps_hangup = allocb(1, BPRI_MED)) == NULL) {
DBGERROR((CE_CONT, "DLPI attach: cannot allocate hangup\n"));
dlerrorack(q, mp, dlp->dl_primitive, DL_SYSERR, ENOSR);
return;
}
sps->sps_dlstate = DL_UNBOUND;
sps->sps_ppa = ppa;
rw_enter(&ppa->ppa_sib_lock, RW_WRITER);
ppa->ppa_refcnt++;
sps->sps_nextsib = ppa->ppa_streams;
ppa->ppa_streams = sps;
if (IS_SPS_PROMISC(sps)) {
ppa->ppa_promicnt++;
}
rw_exit(&ppa->ppa_sib_lock);
DBGDLPI((CE_CONT, "/%d: attached to ppa %d\n", sps->sps_mn_id,
ppa->ppa_ppa_id));
dlokack(q, mp, DL_ATTACH_REQ);
}
static int
sppp_dldetachreq(queue_t *q, mblk_t *mp, spppstr_t *sps)
{
ASSERT(q != NULL && q->q_ptr != NULL);
ASSERT(mp != NULL && mp->b_rptr != NULL);
ASSERT(sps != NULL);
ASSERT(sps->sps_dlstate == DL_UNBOUND);
ASSERT(!IS_SPS_PIOATTACH(sps));
qwriter(q, mp, sppp_dl_detach_upper, PERIM_INNER);
return (0);
}
static void
sppp_dl_detach_upper(queue_t *q, mblk_t *mp)
{
spppstr_t *sps;
ASSERT(q != NULL && q->q_ptr != NULL);
ASSERT(mp != NULL && mp->b_rptr != NULL);
sps = (spppstr_t *)q->q_ptr;
sps->sps_flags &= ~SPS_PROMISC;
sps->sps_dlstate = DL_UNATTACHED;
dlokack(q, mp, DL_DETACH_REQ);
}
static int
sppp_dlbindreq(queue_t *q, mblk_t *mp, spppstr_t *sps)
{
sppa_t *ppa;
union DL_primitives *dlp;
spppreqsap_t req_sap;
int error = 0;
ASSERT(q != NULL && q->q_ptr != NULL);
ASSERT(mp != NULL && mp->b_rptr != NULL);
dlp = (union DL_primitives *)mp->b_rptr;
req_sap = dlp->bind_req.dl_sap;
ASSERT(sps != NULL);
ASSERT(!IS_SPS_PIOATTACH(sps));
ASSERT(sps->sps_dlstate == DL_UNBOUND);
ppa = sps->sps_ppa;
if (ppa == NULL) {
DBGERROR((CE_CONT, "DLPI bind: no attached ppa\n"));
error = DL_OUTSTATE;
} else if ((req_sap != ETHERTYPE_IP) && (req_sap != ETHERTYPE_IPV6) &&
(req_sap != ETHERTYPE_ALLSAP)) {
DBGERROR((CE_CONT, "DLPI bind: unknown SAP %x\n", req_sap));
error = DL_BADADDR;
}
if (error != 0) {
dlerrorack(q, mp, dlp->dl_primitive, error, 0);
} else {
qwriter(q, mp, sppp_dl_bind, PERIM_INNER);
}
return (0);
}
static void
sppp_dl_bind(queue_t *q, mblk_t *mp)
{
spppstr_t *sps;
sppa_t *ppa;
union DL_primitives *dlp;
t_scalar_t sap;
spppreqsap_t req_sap;
mblk_t *lsmp;
ASSERT(q != NULL && q->q_ptr != NULL);
sps = (spppstr_t *)q->q_ptr;
ASSERT(mp != NULL && mp->b_rptr != NULL);
dlp = (union DL_primitives *)mp->b_rptr;
ppa = sps->sps_ppa;
ASSERT(ppa != NULL);
req_sap = dlp->bind_req.dl_sap;
ASSERT((req_sap == ETHERTYPE_IP) || (req_sap == ETHERTYPE_IPV6) ||
(req_sap == ETHERTYPE_ALLSAP));
if (req_sap == ETHERTYPE_IP) {
sap = PPP_IP;
} else if (req_sap == ETHERTYPE_IPV6) {
sap = PPP_IPV6;
} else if (req_sap == ETHERTYPE_ALLSAP) {
sap = PPP_ALLSAP;
}
lsmp = NULL;
if (sap != PPP_ALLSAP) {
if ((sap == PPP_IP) && (ppa->ppa_ip_cache == NULL)) {
ppa->ppa_ip_cache = sps;
if (ppa->ppa_ctl != NULL) {
lsmp = create_lsmsg(PPP_LINKSTAT_IPV4_BOUND);
}
} else if ((sap == PPP_IPV6) && (ppa->ppa_ip6_cache == NULL)) {
ppa->ppa_ip6_cache = sps;
if (ppa->ppa_ctl != NULL) {
lsmp = create_lsmsg(PPP_LINKSTAT_IPV6_BOUND);
}
} else {
DBGERROR((CE_CONT, "DLPI bind: bad SAP %x\n", sap));
dlerrorack(q, mp, dlp->dl_primitive, DL_NOADDR,
EEXIST);
return;
}
sps->sps_flags |= SPS_CACHED;
}
if (lsmp != NULL && ppa->ppa_ctl != NULL) {
#ifdef DBG_DLPI
cmn_err(CE_CONT, "sending up %s\n",
((sap == PPP_IP) ? "PPP_LINKSTAT_IPV4_BOUND" :
"PPP_LINKSTAT_IPV6_BOUND"));
#endif
putnext(ppa->ppa_ctl->sps_rq, lsmp);
}
DBGDLPI((CE_CONT, "/%d: bound to sap %X (req %X)\n", sps->sps_mn_id,
sap, req_sap));
sps->sps_req_sap = req_sap;
sps->sps_sap = sap;
sps->sps_dlstate = DL_IDLE;
dlbindack(q, mp, req_sap, &sap, sizeof (int32_t), 0, 0);
}
static int
sppp_dlunbindreq(queue_t *q, mblk_t *mp, spppstr_t *sps)
{
ASSERT(q != NULL && q->q_ptr != NULL);
ASSERT(mp != NULL && mp->b_rptr != NULL);
ASSERT(sps != NULL);
ASSERT(!IS_SPS_PIOATTACH(sps));
ASSERT(sps->sps_dlstate == DL_IDLE);
qwriter(q, mp, sppp_dl_unbind, PERIM_INNER);
return (0);
}
static void
sppp_dl_unbind(queue_t *q, mblk_t *mp)
{
spppstr_t *sps;
sppa_t *ppa;
t_scalar_t sap;
mblk_t *msg;
boolean_t saydown;
ASSERT(q != NULL && q->q_ptr != NULL);
sps = (spppstr_t *)q->q_ptr;
ppa = sps->sps_ppa;
ASSERT(mp != NULL && mp->b_rptr != NULL);
sap = sps->sps_sap;
ASSERT((sap == PPP_IP) || (sap == PPP_IPV6) || (sap == PPP_ALLSAP));
flushq(WR(q), FLUSHALL);
flushq(RD(q), FLUSHALL);
if ((ppa != NULL) && IS_SPS_CACHED(sps)) {
sps->sps_flags &= ~SPS_CACHED;
msg = NULL;
saydown = (ppa->ppa_ctl != NULL &&
(sps->sps_npmode == NPMODE_PASS ||
sps->sps_npmode == NPMODE_QUEUE));
if (sap == PPP_IP) {
ppa->ppa_ip_cache = NULL;
if (saydown)
msg = create_lsmsg(PPP_LINKSTAT_IPV4_UNBOUND);
} else if (sap == PPP_IPV6) {
ppa->ppa_ip6_cache = NULL;
if (saydown)
msg = create_lsmsg(PPP_LINKSTAT_IPV6_UNBOUND);
}
if (msg != NULL) {
#ifdef DBG_DLPI
cmn_err(CE_CONT, "sending up %s\n",
((sap == PPP_IP) ? "PPP_LINKSTAT_IPV4_UNBOUND" :
"PPP_LINKSTAT_IPV6_UNBOUND"));
#endif
putnext(ppa->ppa_ctl->sps_rq, msg);
}
}
DBGDLPI((CE_CONT, "/%d: unbound from sap %X (req %X)\n", sps->sps_mn_id,
sps->sps_sap, sps->sps_req_sap));
sps->sps_req_sap = 0;
sps->sps_sap = -1;
sps->sps_dlstate = DL_UNBOUND;
dlokack(q, mp, DL_UNBIND_REQ);
}
static int
sppp_dlinforeq(queue_t *q, mblk_t *mp, spppstr_t *sps)
{
dl_info_ack_t *dlip;
uint32_t size;
uint32_t addr_size;
sppa_t *ppa;
ASSERT(q != NULL && q->q_ptr != NULL);
ASSERT(mp != NULL && mp->b_rptr != NULL);
ASSERT(sps != NULL);
ppa = sps->sps_ppa;
addr_size = SPPP_ADDRL;
size = sizeof (dl_info_ack_t) + addr_size;
if ((mp = mexchange(q, mp, size, M_PCPROTO, DL_INFO_ACK)) == NULL) {
DBGERROR((CE_CONT, "DLPI info: mexchange failed\n"));
return (0);
}
dlip = (dl_info_ack_t *)mp->b_rptr;
*dlip = sppp_infoack;
dlip->dl_current_state = sps->sps_dlstate;
dlip->dl_max_sdu = ppa != NULL ? ppa->ppa_mtu : PPP_MAXMTU;
#ifdef DBG_DLPI
{
const char *cp = state2name(dlip->dl_current_state);
if (cp != NULL)
cmn_err(CE_CONT, "info returns state %s, max sdu %d\n",
cp, dlip->dl_max_sdu);
else
cmn_err(CE_CONT, "info returns state %d, max sdu %d\n",
dlip->dl_current_state, dlip->dl_max_sdu);
}
#endif
qreply(q, mp);
return (0);
}
static int
sppp_dlunitdatareq(queue_t *q, mblk_t *mp, spppstr_t *sps)
{
sppa_t *ppa;
mblk_t *hdrmp;
mblk_t *pktmp;
dl_unitdata_req_t *dludp;
int dladdroff;
int dladdrlen;
int msize;
int error = 0;
boolean_t is_promisc;
ASSERT(q != NULL && q->q_ptr != NULL);
ASSERT(mp != NULL && mp->b_rptr != NULL);
ASSERT((MTYPE(mp) == M_PCPROTO) || (MTYPE(mp) == M_PROTO));
dludp = (dl_unitdata_req_t *)mp->b_rptr;
dladdroff = dludp->dl_dest_addr_offset;
dladdrlen = dludp->dl_dest_addr_length;
ASSERT(sps != NULL);
ASSERT(!IS_SPS_PIOATTACH(sps));
ASSERT(sps->sps_dlstate == DL_IDLE);
ASSERT(q->q_ptr == sps);
ppa = sps->sps_ppa;
if (ppa == NULL) {
DBGERROR((CE_CONT, "DLPI unitdata: no attached ppa\n"));
error = ENOLINK;
} else if (mp->b_cont == NULL) {
DBGERROR((CE_CONT, "DLPI unitdata: missing data\n"));
error = EPROTO;
}
if (error != 0) {
dluderrorind(q, mp, mp->b_rptr + dladdroff, dladdrlen,
DL_BADDATA, error);
return (0);
}
ASSERT(mp->b_cont->b_rptr != NULL);
msize = msgdsize(mp);
if (msize > ppa->ppa_mtu) {
mutex_enter(&ppa->ppa_sta_lock);
ppa->ppa_otoolongs++;
mutex_exit(&ppa->ppa_sta_lock);
}
if (IS_SPS_KDEBUG(sps)) {
SPDEBUG(PPP_DRV_NAME
"/%d: DL_UNITDATA_REQ (%d bytes) sps=0x%p flags=0x%b "
"ppa=0x%p flags=0x%b\n", sps->sps_mn_id, msize,
(void *)sps, sps->sps_flags, SPS_FLAGS_STR,
(void *)ppa, ppa->ppa_flags, PPA_FLAGS_STR);
}
if ((hdrmp = allocb(PPP_HDRLEN, BPRI_MED)) == NULL) {
mutex_enter(&ppa->ppa_sta_lock);
ppa->ppa_allocbfail++;
mutex_exit(&ppa->ppa_sta_lock);
DBGERROR((CE_CONT,
"DLPI unitdata: can't allocate header buffer\n"));
dluderrorind(q, mp, mp->b_rptr + dladdroff, dladdrlen,
DL_SYSERR, ENOSR);
return (0);
}
rw_enter(&ppa->ppa_sib_lock, RW_READER);
is_promisc = ppa->ppa_promicnt;
if (is_promisc) {
ASSERT(ppa->ppa_streams != NULL);
sppp_dlprsendup(ppa->ppa_streams, mp->b_cont, sps->sps_sap,
B_FALSE);
}
rw_exit(&ppa->ppa_sib_lock);
pktmp = mp->b_cont;
mp->b_cont = NULL;
freemsg(mp);
mp = hdrmp;
*(uchar_t *)mp->b_wptr++ = PPP_ALLSTATIONS;
*(uchar_t *)mp->b_wptr++ = PPP_UI;
*(uchar_t *)mp->b_wptr++ = ((uint16_t)sps->sps_sap >> 8) & 0xff;
*(uchar_t *)mp->b_wptr++ = ((uint16_t)sps->sps_sap) & 0xff;
ASSERT(MBLKL(mp) == PPP_HDRLEN);
linkb(mp, pktmp);
if (IS_PPA_TIMESTAMP(ppa)) {
ppa->ppa_lasttx = gethrtime();
}
if (putq(q, mp) == 0) {
mutex_enter(&ppa->ppa_sta_lock);
ppa->ppa_oqdropped++;
mutex_exit(&ppa->ppa_sta_lock);
freemsg(mp);
} else {
qenable(q);
}
return (0);
}
static int
sppp_dlpromisconreq(queue_t *q, mblk_t *mp, spppstr_t *sps)
{
t_uscalar_t level;
ASSERT(q != NULL && q->q_ptr != NULL);
ASSERT(mp != NULL && mp->b_rptr != NULL);
level = ((dl_promiscon_req_t *)mp->b_rptr)->dl_level;
ASSERT(sps != NULL);
if (IS_SPS_PROMISC(sps)) {
dlokack(q, mp, DL_PROMISCON_REQ);
} else if ((level != DL_PROMISC_PHYS) && (level != DL_PROMISC_SAP) &&
(level != DL_PROMISC_MULTI)) {
DBGERROR((CE_CONT, "DLPI promiscon: bad level %d\n", level));
dlerrorack(q, mp, DL_PROMISCON_REQ, DL_NOTSUPPORTED, 0);
} else {
qwriter(q, mp, sppp_dl_promiscon, PERIM_INNER);
}
return (0);
}
static void
sppp_dl_promiscon(queue_t *q, mblk_t *mp)
{
spppstr_t *sps;
sppa_t *ppa;
ASSERT(q != NULL && q->q_ptr != NULL);
sps = (spppstr_t *)q->q_ptr;
ASSERT(!IS_SPS_PROMISC(sps));
ASSERT(mp != NULL && mp->b_rptr != NULL);
ppa = sps->sps_ppa;
sps->sps_flags |= SPS_PROMISC;
if (ppa != NULL) {
rw_enter(&ppa->ppa_sib_lock, RW_WRITER);
ppa->ppa_promicnt++;
rw_exit(&ppa->ppa_sib_lock);
}
DBGDLPI((CE_CONT, "/%d: promiscuous mode on\n", sps->sps_mn_id));
dlokack(q, mp, DL_PROMISCON_REQ);
}
static int
sppp_dlpromiscoffreq(queue_t *q, mblk_t *mp, spppstr_t *sps)
{
t_uscalar_t level;
ASSERT(q != NULL && q->q_ptr != NULL);
ASSERT(mp != NULL && mp->b_rptr != NULL);
level = ((dl_promiscoff_req_t *)mp->b_rptr)->dl_level;
ASSERT(sps != NULL);
if (!IS_SPS_PROMISC(sps)) {
DBGERROR((CE_CONT, "DLPI promiscoff: not promiscuous\n"));
dlerrorack(q, mp, DL_PROMISCOFF_REQ, DL_NOTENAB, 0);
} else if ((level != DL_PROMISC_PHYS) && (level != DL_PROMISC_SAP) &&
(level != DL_PROMISC_MULTI)) {
dlerrorack(q, mp, DL_PROMISCOFF_REQ, DL_NOTSUPPORTED, 0);
DBGERROR((CE_CONT, "DLPI promiscoff: bad level %d\n", level));
} else {
qwriter(q, mp, sppp_dl_promiscoff, PERIM_INNER);
}
return (0);
}
static void
sppp_dl_promiscoff(queue_t *q, mblk_t *mp)
{
spppstr_t *sps;
sppa_t *ppa;
ASSERT(q != NULL && q->q_ptr != NULL);
sps = (spppstr_t *)q->q_ptr;
ASSERT(IS_SPS_PROMISC(sps));
ASSERT(mp != NULL && mp->b_rptr != NULL);
ppa = sps->sps_ppa;
sps->sps_flags &= ~SPS_PROMISC;
if (ppa != NULL) {
rw_enter(&ppa->ppa_sib_lock, RW_WRITER);
ASSERT(ppa->ppa_promicnt > 0);
ppa->ppa_promicnt--;
rw_exit(&ppa->ppa_sib_lock);
}
DBGDLPI((CE_CONT, "/%d: promiscuous mode off\n", sps->sps_mn_id));
dlokack(q, mp, DL_PROMISCOFF_REQ);
}
static int
sppp_dlphyreq(queue_t *q, mblk_t *mp, spppstr_t *us)
{
static struct ether_addr addr = { 0 };
dlphysaddrack(q, mp, (char *)&addr, ETHERADDRL);
return (0);
}
static mblk_t *
sppp_dladdether(spppstr_t *sps, mblk_t *mp, t_scalar_t proto)
{
mblk_t *eh;
t_scalar_t type;
if ((eh = allocb(sizeof (struct ether_header), BPRI_MED)) == NULL) {
freemsg(mp);
return (NULL);
}
if (proto == PPP_IP) {
type = ETHERTYPE_IP;
} else if (proto == PPP_IPV6) {
type = ETHERTYPE_IPV6;
} else {
type = ETHERTYPE_PPP;
mp->b_rptr -= PPP_HDRLEN;
ASSERT(mp->b_rptr >= mp->b_datap->db_base);
}
eh->b_wptr += sizeof (struct ether_header);
bzero((caddr_t)eh->b_rptr, sizeof (struct ether_header));
((struct ether_header *)eh->b_rptr)->ether_type = htons((int16_t)type);
linkb(eh, mp);
return (eh);
}
mblk_t *
sppp_dladdud(spppstr_t *sps, mblk_t *mp, t_scalar_t proto, boolean_t promisc)
{
dl_unitdata_ind_t *dlu;
mblk_t *dh;
size_t size;
t_scalar_t type;
size = sizeof (dl_unitdata_ind_t) + (2 * SPPP_ADDRL);
if ((dh = allocb(size, BPRI_MED)) == NULL) {
freemsg(mp);
return (NULL);
}
dh->b_datap->db_type = M_PROTO;
dh->b_wptr = dh->b_datap->db_lim;
dh->b_rptr = dh->b_wptr - size;
dlu = (dl_unitdata_ind_t *)dh->b_rptr;
dlu->dl_primitive = DL_UNITDATA_IND;
dlu->dl_dest_addr_length = SPPP_ADDRL;
dlu->dl_dest_addr_offset = sizeof (dl_unitdata_ind_t);
dlu->dl_src_addr_length = SPPP_ADDRL;
dlu->dl_src_addr_offset = sizeof (dl_unitdata_ind_t) + SPPP_ADDRL;
dlu->dl_group_address = 0;
if (promisc) {
if (proto == PPP_IP) {
type = ETHERTYPE_IP;
} else if (proto == PPP_IPV6) {
type = ETHERTYPE_IPV6;
} else {
type = ETHERTYPE_PPP;
mp->b_rptr -= PPP_HDRLEN;
ASSERT(mp->b_rptr >= mp->b_datap->db_base);
}
} else {
type = sps->sps_req_sap;
}
((spppreqsap_t *)(dlu + 1))[0] = type;
((spppreqsap_t *)(dlu + 1))[1] = type;
linkb(dh, mp);
return (dh);
}
void
sppp_dlprsendup(spppstr_t *sps, mblk_t *mp, t_scalar_t proto, boolean_t header)
{
sppa_t *ppa;
mblk_t *dmp;
ASSERT(sps != NULL);
ASSERT(mp != NULL && mp->b_rptr != NULL);
ppa = sps->sps_ppa;
ASSERT(ppa != NULL);
ASSERT(RW_READ_HELD(&ppa->ppa_sib_lock));
for (; sps != NULL; sps = sps->sps_nextsib) {
if (IS_SPS_PROMISC(sps) && (sps->sps_dlstate == DL_IDLE) &&
canputnext(sps->sps_rq)) {
if ((dmp = dupmsg(mp)) == NULL) {
mutex_enter(&ppa->ppa_sta_lock);
ppa->ppa_allocbfail++;
mutex_exit(&ppa->ppa_sta_lock);
continue;
}
if (header) {
dmp->b_rptr += PPP_HDRLEN;
}
if (IS_SPS_RAWDATA(sps)) {
dmp = sppp_dladdether(sps, dmp, proto);
} else {
dmp = sppp_dladdud(sps, dmp, proto, B_TRUE);
}
if (dmp != NULL) {
putnext(sps->sps_rq, dmp);
} else {
mutex_enter(&ppa->ppa_sta_lock);
ppa->ppa_allocbfail++;
mutex_exit(&ppa->ppa_sta_lock);
}
}
}
}