#include <sys/types.h>
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/sysmacros.h>
#include <sys/errno.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/crc32.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <net/ppp_defs.h>
#include <net/pppio.h>
#include "s_common.h"
#ifdef DEBUG
#define REPORT_CRC_TYPE
#endif
#include "spppasyn.h"
#ifdef INTERNAL_BUILD
const char spppasyn_module_description[] = "PPP 4.0 AHDLC";
#else
const char spppasyn_module_description[] = "ANU PPP AHDLC $Revision: 1.16$";
static const char buildtime[] = "Built " __DATE__ " at " __TIME__
#ifdef DEBUG
" DEBUG"
#endif
"\n";
#endif
static int spppasyn_open(queue_t *, dev_t *, int, int, cred_t *);
static int spppasyn_close(queue_t *, int, cred_t *);
static int spppasyn_wput(queue_t *, mblk_t *);
static int spppasyn_rput(queue_t *, mblk_t *);
static mblk_t *ahdlc_encode(queue_t *, mblk_t *);
static mblk_t *ahdlc_decode(queue_t *, mblk_t *);
static void spppasyn_timer(void *);
static mblk_t *spppasyn_inpkt(queue_t *, mblk_t *);
static mblk_t *spppasyn_muxencode(queue_t *, mblk_t *);
#define RESET_MUX_VALUES(x) { \
x->sa_mqhead = x->sa_mqtail = NULL; \
x->sa_proto = 0; \
x->sa_mqlen = 0; \
}
#define IS_XMUX_ENABLED(x) \
((x)->sa_flags & X_MUXMASK)
#define IS_RMUX_ENABLED(x) \
((x)->sa_flags & R_MUXMASK)
#define IS_COMP_AC(x) \
((x)->sa_flags & SAF_XCOMP_AC)
#define IS_COMP_PROT(x) \
((x)->sa_flags & SAF_XCOMP_PROT)
#define IS_DECOMP_PROT(x) \
((x)->sa_flags & SAF_RDECOMP_PROT)
#define FLAG_TIME 1500000000ul
#define LCP_USE_DFLT(mp) ((code = MSG_BYTE((mp), 4)) < 9 || code > 11)
#define IN_TX_MAP(c, m) \
((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
#define IN_RX_MAP(c, m) (((c) < 0x20) && ((m) & (1 << (c))))
static struct module_info spppasyn_modinfo = {
AHDLC_MOD_ID,
AHDLC_MOD_NAME,
0,
INFPSZ,
0,
0
};
static struct qinit spppasyn_rinit = {
spppasyn_rput,
NULL,
spppasyn_open,
spppasyn_close,
NULL,
&spppasyn_modinfo,
NULL
};
static struct qinit spppasyn_winit = {
spppasyn_wput,
NULL,
NULL,
NULL,
NULL,
&spppasyn_modinfo,
NULL
};
struct streamtab spppasyn_tab = {
&spppasyn_rinit,
&spppasyn_winit,
NULL,
NULL,
};
static const char *kstat_names[] = {
"ioctls", "ioctlsfwd", "ioctlserr", "ctls",
"ctlsfwd", "ctlserr", "inbadchars", "inbadcharmask",
"inaborts", "inrunts", "inallocfails", "intoolongs",
"outrunts", "outallocfails", "incrcerrs", "unknownwrs",
"unknownrds", "hangups", "datain", "dataout",
"extrabufs", "sentmux", "recvmux", "inmuxerrs",
#ifdef REPORT_CRC_TYPE
"incrctype", "outcrctype",
#endif
};
#define KVAL(vn) state->sa_kstats.vn.value.ui32
#define KSET(vn, v) KVAL(vn) = (v)
#define KADD(vn, v) KSET(vn, KVAL(vn) + (v))
#define KOR(vn, v) KSET(vn, KVAL(vn) | (v))
#define KINCR(vn) KADD(vn, 1)
static void ppp_dump_frame(sppp_ahdlc_t *state, mblk_t *mptr,
const char *msg);
#define RCV_FLAGS (RCV_B7_1 | RCV_B7_0 | RCV_ODDP | RCV_EVNP)
static ushort_t fcstab[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
static uchar_t charflags[256] = {
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP
};
static mblk_t *
sppp_mappend(mblk_t *m1, mblk_t *m2)
{
mblk_t *mret;
if (m1 == NULL)
return (m2);
if (m2 == NULL)
return (m1);
mret = m1;
while (m1->b_next != NULL)
m1 = m1->b_next;
m1->b_next = m2;
return (mret);
}
static mblk_t *
sppp_mcat(mblk_t *m1, mblk_t *m2)
{
mblk_t *mret;
if (m1 == NULL)
return (m2);
if (m2 == NULL)
return (m1);
mret = m1;
while (m1->b_cont != NULL)
m1 = m1->b_cont;
m1->b_cont = m2;
return (mret);
}
static int
spppasyn_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
{
sppp_ahdlc_t *state;
ASSERT(q != NULL);
if (q->q_ptr != NULL) {
return (0);
}
if (sflag != MODOPEN) {
return (EINVAL);
}
state = (sppp_ahdlc_t *)kmem_zalloc(sizeof (sppp_ahdlc_t), KM_SLEEP);
ASSERT(state != NULL);
q->q_ptr = (caddr_t)state;
WR(q)->q_ptr = (caddr_t)state;
state->sa_xaccm[0] = 0xffffffff;
state->sa_xaccm[3] = 0x60000000;
state->sa_mru = PPP_MRU;
qprocson(q);
return (0);
}
static int
spppasyn_close(queue_t *q, int flag, cred_t *credp)
{
sppp_ahdlc_t *state;
ASSERT(q != NULL);
state = (sppp_ahdlc_t *)q->q_ptr;
ASSERT(state != NULL);
qprocsoff(q);
if (state->sa_rx_buf != NULL) {
freemsg(state->sa_rx_buf);
state->sa_rx_buf = NULL;
}
if (state->sa_ksp != NULL) {
kstat_delete(state->sa_ksp);
state->sa_ksp = NULL;
}
if (state->sa_mqhead != NULL)
freemsg(state->sa_mqhead);
if (state->sa_timeout_id != 0)
(void) quntimeout(q, state->sa_timeout_id);
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
kmem_free(state, sizeof (sppp_ahdlc_t));
return (0);
}
static void
create_kstats(sppp_ahdlc_t *state)
{
kstat_t *ksp;
char unitname[KSTAT_STRLEN];
int nstat, i;
kstat_named_t *knt;
nstat = sizeof (state->sa_kstats) / sizeof (kstat_named_t);
knt = (kstat_named_t *)&state->sa_kstats;
for (i = 0; i < nstat; i++, knt++) {
#ifdef DEBUG
if (i >= sizeof (kstat_names) / sizeof (kstat_names[0]))
(void) sprintf(knt->name, "unknown%d", i);
else
#endif
(void) strncpy(knt->name, kstat_names[i],
sizeof (knt->name));
knt->data_type = KSTAT_DATA_UINT32;
}
(void) sprintf(unitname, "%s" "%d", AHDLC_MOD_NAME, state->sa_unit);
ksp = kstat_create(AHDLC_MOD_NAME, state->sa_unit, unitname, "net",
KSTAT_TYPE_NAMED, nstat, KSTAT_FLAG_VIRTUAL);
if (ksp != NULL) {
ksp->ks_data = (void *)&state->sa_kstats;
kstat_install(ksp);
}
state->sa_ksp = ksp;
#ifdef REPORT_CRC_TYPE
KSET(pks_outcrctype, 16);
KSET(pks_incrctype, 16);
#endif
}
static void
spppasyn_inner_ioctl(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
struct iocblk *iop;
int error;
int flagval;
int len;
uint32_t mux_flags;
uint32_t mask;
int flagmask;
ASSERT(q != NULL && mp != NULL);
state = (sppp_ahdlc_t *)q->q_ptr;
iop = (struct iocblk *)mp->b_rptr;
ASSERT(state != NULL && iop != NULL);
error = EINVAL;
len = 0;
switch (iop->ioc_cmd) {
case PPPIO_XFCS:
if (iop->ioc_count != sizeof (uint32_t) || mp->b_cont == NULL)
break;
flagval = *(uint32_t *)mp->b_cont->b_rptr;
if (flagval < PPPFCS_16 || flagval > PPPFCS_NONE)
break;
state->sa_flags &= ~SAF_XMITCRC32 & ~SAF_XMITCRCNONE;
if (flagval == PPPFCS_32) {
#ifdef REPORT_CRC_TYPE
KSET(pks_outcrctype, 32);
#endif
state->sa_flags |= SAF_XMITCRC32;
} else if (flagval == PPPFCS_NONE) {
#ifdef REPORT_CRC_TYPE
KSET(pks_outcrctype, 0);
#endif
state->sa_flags |= SAF_XMITCRCNONE;
}
#ifdef REPORT_CRC_TYPE
else {
KSET(pks_outcrctype, 16);
}
#endif
error = 0;
break;
case PPPIO_RFCS:
if (iop->ioc_count != sizeof (uint32_t) || mp->b_cont == NULL)
break;
flagval = *(uint32_t *)mp->b_cont->b_rptr;
if (flagval < PPPFCS_16 || flagval > PPPFCS_NONE)
break;
state->sa_flags &= ~SAF_RECVCRC32 & ~SAF_RECVCRCNONE;
if (flagval == PPPFCS_32) {
#ifdef REPORT_CRC_TYPE
KSET(pks_incrctype, 32);
#endif
state->sa_flags |= SAF_RECVCRC32;
} else if (flagval == PPPFCS_NONE) {
#ifdef REPORT_CRC_TYPE
KSET(pks_incrctype, 0);
#endif
state->sa_flags |= SAF_RECVCRCNONE;
}
#ifdef REPORT_CRC_TYPE
else {
KSET(pks_incrctype, 16);
}
#endif
error = 0;
break;
case PPPIO_XACCM:
if (iop->ioc_count < sizeof (uint32_t) ||
iop->ioc_count > sizeof (ext_accm) ||
mp->b_cont == NULL)
break;
bcopy((caddr_t)mp->b_cont->b_rptr,
(caddr_t)state->sa_xaccm, iop->ioc_count);
state->sa_xaccm[2] &= ~0x40000000;
state->sa_xaccm[3] |= 0x60000000;
error = 0;
break;
case PPPIO_RACCM:
if (iop->ioc_count != sizeof (uint32_t) ||
mp->b_cont == NULL)
break;
state->sa_raccm = *(uint32_t *)mp->b_cont->b_rptr;
error = 0;
break;
case PPPIO_LASTMOD:
state->sa_flags |= SAF_LASTMOD;
error = 0;
break;
case PPPIO_MUX:
if (iop->ioc_count != 2 * sizeof (uint32_t) ||
mp->b_cont == NULL)
break;
mux_flags = ((uint32_t *)mp->b_cont->b_rptr)[0];
mask = ((uint32_t *)mp->b_cont->b_rptr)[1];
if (mux_flags != 0)
state->sa_flags = (state->sa_flags & ~mask) | (mask);
if (mask & R_MUXMASK)
state->sa_timeout_usec = mux_flags;
error = 0;
break;
case PPPIO_CFLAGS:
if (iop->ioc_count != 2 * sizeof (uint32_t) ||
mp->b_cont == NULL)
break;
flagval = (((uint32_t *)mp->b_cont->b_rptr)[0] << 20) &
(SAF_RDECOMP_PROT | SAF_RDECOMP_AC | SAF_XCOMP_PROT |
SAF_XCOMP_AC);
flagmask = (((uint32_t *)mp->b_cont->b_rptr)[1] << 20) &
(SAF_RDECOMP_PROT | SAF_RDECOMP_AC | SAF_XCOMP_PROT |
SAF_XCOMP_AC);
state->sa_flags = flagval | (state->sa_flags & ~flagmask);
*(uint32_t *)mp->b_cont->b_rptr = state->sa_flags >> 20;
len = sizeof (uint32_t);
error = 0;
break;
case PPPIO_DEBUG:
if (iop->ioc_count != sizeof (uint32_t) || mp->b_cont == NULL)
break;
flagval = *(uint32_t *)mp->b_cont->b_rptr;
if (flagval != PPPDBG_LOG + PPPDBG_AHDLC) {
putnext(q, mp);
return;
}
cmn_err(CE_CONT, AHDLC_MOD_NAME "%d: debug log enabled\n",
state->sa_unit);
state->sa_flags |= SAF_XMITDUMP | SAF_RECVDUMP;
error = 0;
break;
}
if (error == 0) {
if (mp->b_cont == NULL)
len = 0;
else
mp->b_cont->b_wptr = mp->b_cont->b_rptr + len;
miocack(q, mp, len, 0);
} else {
KINCR(pks_ioctlserr);
miocnak(q, mp, 0, error);
}
}
static void
spppasyn_inner_mctl(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
int msglen;
int error;
ASSERT(q != NULL && mp != NULL);
state = (sppp_ahdlc_t *)q->q_ptr;
ASSERT(state != NULL);
msglen = MBLKL(mp);
error = 0;
switch (*mp->b_rptr) {
case PPPCTL_MTU:
break;
case PPPCTL_MRU:
if (msglen != 4)
error = EINVAL;
else
state->sa_mru =
((ushort_t *)mp->b_rptr)[1];
break;
case PPPCTL_UNIT:
if (state->sa_ksp != NULL) {
error = EINVAL;
break;
}
if (msglen == 2)
state->sa_unit = mp->b_rptr[1];
else if (msglen == 8)
state->sa_unit =
((uint32_t *)mp->b_rptr)[1];
else
error = EINVAL;
if (error == 0 && state->sa_ksp == NULL)
create_kstats(state);
break;
}
if (error > 0) {
KINCR(pks_ctlserr);
}
if (state->sa_flags & SAF_LASTMOD) {
freemsg(mp);
} else {
KINCR(pks_ctlsfwd);
putnext(q, mp);
}
}
static int
spppasyn_wput(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
struct iocblk *iop;
int error;
mblk_t *np;
struct ppp_stats64 *psp;
int msglen;
ASSERT(q != NULL && mp != NULL);
state = (sppp_ahdlc_t *)q->q_ptr;
ASSERT(state != NULL);
switch (MTYPE(mp)) {
case M_DATA:
if (IS_XMUX_ENABLED(state))
mp = spppasyn_muxencode(q, mp);
else
mp = ahdlc_encode(q, mp);
if (mp != NULL)
putnext(q, mp);
break;
case M_IOCTL:
KINCR(pks_ioctls);
iop = (struct iocblk *)mp->b_rptr;
msglen = 0;
switch (iop->ioc_cmd) {
case PPPIO_XFCS:
case PPPIO_RFCS:
case PPPIO_XACCM:
case PPPIO_RACCM:
case PPPIO_LASTMOD:
case PPPIO_DEBUG:
case PPPIO_MUX:
case PPPIO_CFLAGS:
spppasyn_inner_ioctl(q, mp);
return (0);
case PPPIO_GCLEAN:
np = allocb(sizeof (uint32_t), BPRI_HI);
if (np == NULL) {
error = ENOSR;
break;
}
if (mp->b_cont != NULL) {
freemsg(mp->b_cont);
}
mp->b_cont = np;
*(uint32_t *)np->b_wptr = state->sa_flags & RCV_FLAGS;
msglen = sizeof (uint32_t);
np->b_wptr += msglen;
error = 0;
break;
case PPPIO_GETSTAT:
error = EINVAL;
break;
case PPPIO_GETSTAT64:
np = allocb(sizeof (*psp), BPRI_HI);
if (np == NULL) {
error = ENOSR;
break;
}
if (mp->b_cont != NULL) {
freemsg(mp->b_cont);
}
mp->b_cont = np;
psp = (struct ppp_stats64 *)np->b_wptr;
bzero((caddr_t)psp, sizeof (*psp));
psp->p = state->sa_stats;
msglen = sizeof (*psp);
np->b_wptr += msglen;
error = 0;
break;
case PPPIO_GTYPE:
np = allocb(sizeof (uint32_t), BPRI_HI);
if (np == NULL) {
error = ENOSR;
break;
}
if (mp->b_cont != NULL) {
freemsg(mp->b_cont);
}
mp->b_cont = np;
*(uint32_t *)np->b_wptr = PPPTYP_AHDLC;
msglen = sizeof (uint32_t);
np->b_wptr += msglen;
error = 0;
break;
default:
KINCR(pks_ioctlsfwd);
putnext(q, mp);
return (0);
}
if (error == 0) {
miocack(q, mp, msglen, 0);
} else {
KINCR(pks_ioctlserr);
miocnak(q, mp, 0, error);
}
break;
case M_CTL:
KINCR(pks_ctls);
spppasyn_inner_mctl(q, mp);
break;
default:
if (state->sa_flags & (SAF_XMITDUMP|SAF_RECVDUMP))
cmn_err(CE_CONT,
"spppasyn_wpur: unknown buffer type %d",
MTYPE(mp));
KINCR(pks_unknownwrs);
putnext(q, mp);
break;
}
return (0);
}
static int
spppasyn_rput(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
mblk_t *mpnext;
ASSERT(q != NULL && mp != NULL);
state = (sppp_ahdlc_t *)q->q_ptr;
ASSERT(state != NULL);
switch (MTYPE(mp)) {
case M_DATA:
mp = ahdlc_decode(q, mp);
while (mp != NULL) {
mpnext = mp->b_next;
mp->b_next = NULL;
putnext(q, mp);
mp = mpnext;
}
break;
case M_HANGUP:
KINCR(pks_hangups);
state->sa_flags |= SAF_IFLUSH;
putnext(q, mp);
break;
default:
if (state->sa_flags & (SAF_XMITDUMP|SAF_RECVDUMP)) {
if (MTYPE(mp) == M_IOCTL)
cmn_err(CE_CONT,
"spppasyn_rput: unexpected ioctl %X",
((struct iocblk *)mp->b_rptr)->ioc_cmd);
else
cmn_err(CE_CONT,
"spppasyn_rput: unknown buffer type %d",
MTYPE(mp));
}
KINCR(pks_unknownrds);
putnext(q, mp);
break;
}
return (0);
}
static mblk_t *
ahdlc_encode(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
uint32_t loc_xaccm[8];
ushort_t fcs16;
uint32_t fcs32;
size_t msglen;
size_t outmp_len;
mblk_t *outmp;
mblk_t *curout;
mblk_t *tmp;
uchar_t *ep;
uchar_t *dp;
uchar_t *tp;
uchar_t *tpmax;
#if defined(lint) || defined(_lint)
uchar_t chr;
#else
int chr;
#endif
int is_lcp, is_ctrl;
int code;
hrtime_t hrtime;
uint32_t flags;
state = (sppp_ahdlc_t *)q->q_ptr;
msglen = msgsize(mp);
if (msglen < 4) {
KINCR(pks_outrunts);
freemsg(mp);
(void) putnextctl1(RD(q), M_CTL, PPPCTL_OERROR);
return (NULL);
}
outmp_len = msglen + (msglen >> 2) + 16;
outmp = allocb(outmp_len, BPRI_MED);
if (outmp == NULL)
goto outallocfail;
tp = outmp->b_wptr;
hrtime = gethrtime();
if (ABS(hrtime - state->sa_hrtime) > FLAG_TIME) {
*tp++ = PPP_FLAG;
}
state->sa_hrtime = hrtime;
bcopy((caddr_t)state->sa_xaccm, (caddr_t)loc_xaccm, sizeof (loc_xaccm));
flags = state->sa_flags;
is_lcp = is_ctrl = 0;
code = MSG_BYTE(mp, 0);
if (code == PPP_ALLSTATIONS) {
if (MSG_BYTE(mp, 1) == PPP_UI) {
code = MSG_BYTE(mp, 2);
if (code == (PPP_LCP >> 8) &&
MSG_BYTE(mp, 3) == (PPP_LCP & 0xFF)) {
if (LCP_USE_DFLT(mp))
is_lcp = 2;
else
is_lcp = 1;
} else if (!(code & 1) && code > 0x3F)
is_ctrl = 1;
}
} else if (!(code & 1) && code > 0x3F)
is_ctrl = 1;
if (is_lcp > 1) {
loc_xaccm[0] = 0xffffffff;
loc_xaccm[4] = 0xffffffff;
}
fcs16 = PPPINITFCS16;
fcs32 = PPPINITFCS32;
curout = outmp;
tpmax = outmp->b_datap->db_lim;
do {
dp = mp->b_rptr;
while (dp < (ep = mp->b_wptr)) {
outmp_len = (tpmax - tp) / 2;
if (dp + outmp_len < ep)
ep = dp + outmp_len;
if (flags & SAF_XMITCRC32) {
while (dp < ep) {
chr = *dp++;
fcs32 = PPPFCS32(fcs32, chr);
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
}
} else {
while (dp < ep) {
chr = *dp++;
fcs16 = PPPFCS16(fcs16, chr);
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
}
}
if (ep != mp->b_wptr && tpmax - tp < 5) {
KINCR(pks_extrabufs);
outmp_len = (mp->b_wptr - dp) +
msgsize(mp->b_cont);
outmp_len = (outmp_len + PPP_FCS32LEN) * 2 + 1;
curout = allocb(outmp_len, BPRI_MED);
if ((outmp->b_cont = curout) == NULL)
goto outallocfail;
outmp->b_wptr = tp;
tp = curout->b_wptr;
tpmax = curout->b_datap->db_lim;
}
}
tmp = mp->b_cont;
freeb(mp);
mp = tmp;
} while (mp != NULL);
outmp_len = PPP_FCS32LEN * 2 + 1;
if (tpmax - tp < outmp_len) {
KINCR(pks_extrabufs);
curout = allocb(outmp_len, BPRI_MED);
if ((outmp->b_cont = curout) == NULL)
goto outallocfail;
outmp->b_wptr = tp;
tp = curout->b_wptr;
tpmax = curout->b_datap->db_lim;
}
if ((flags & SAF_XMITCRCNONE) && !is_lcp && !is_ctrl)
goto nocrc;
if (!(flags & SAF_XMITCRC32))
fcs32 = fcs16;
fcs32 = ~fcs32;
chr = fcs32 & 0xff;
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
chr = (fcs32 >> 8) & 0xff;
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
if (flags & SAF_XMITCRC32) {
chr = (fcs32 >> 16) & 0xff;
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
chr = (fcs32 >> 24) & 0xff;
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
}
nocrc:
*tp++ = PPP_FLAG;
ASSERT(tp < tpmax);
curout->b_wptr = tp;
state->sa_stats.ppp_obytes += msgsize(outmp);
state->sa_stats.ppp_opackets++;
if (state->sa_flags & SAF_XMITDUMP)
ppp_dump_frame(state, outmp, "sent");
KINCR(pks_dataout);
return (outmp);
outallocfail:
KINCR(pks_outallocfails);
state->sa_stats.ppp_oerrors++;
freemsg(outmp);
freemsg(mp);
(void) putnextctl1(RD(q), M_CTL, PPPCTL_OERROR);
return (NULL);
}
static mblk_t *
receive_frame(queue_t *q, mblk_t *outmp, ushort_t fcs16, uint32_t fcs32)
{
sppp_ahdlc_t *state = (sppp_ahdlc_t *)q->q_ptr;
uchar_t *cp, *ep;
int is_lcp, is_ctrl, crclen;
ushort_t proto;
int i;
cp = outmp->b_rptr;
if (cp[0] == PPP_ALLSTATIONS && cp[1] == PPP_UI)
cp += 2;
proto = *cp++;
if ((proto & 1) == 0)
proto = (proto << 8) + *cp++;
is_lcp = (proto == PPP_LCP);
is_ctrl = (proto >= 0x4000);
if ((is_lcp && (fcs16 == PPPGOODFCS16 || fcs32 == PPPGOODFCS32)) ||
((fcs16 == PPPGOODFCS16 && !(state->sa_flags & SAF_RECVCRC32)) ||
(fcs32 == PPPGOODFCS32 &&
(state->sa_flags & SAF_RECVCRC32))) ||
(!is_ctrl && !is_lcp && (state->sa_flags & SAF_RECVCRCNONE))) {
state->sa_stats.ppp_ipackets++;
if (is_lcp) {
crclen = (fcs16 == PPPGOODFCS16) ?
PPP_FCSLEN : PPP_FCS32LEN;
} else {
crclen = (state->sa_flags & SAF_RECVCRC32) ?
PPP_FCS32LEN : PPP_FCSLEN;
if (!is_ctrl && (state->sa_flags & SAF_RECVCRCNONE))
crclen = 0;
}
if (crclen != 0) {
i = adjmsg(outmp, -crclen);
ASSERT(i != 0);
#if defined(lint) || defined(_lint)
i = i;
#endif
}
if (proto == PPP_MUX) {
KINCR(pks_recvmux);
outmp->b_rptr = cp;
return (spppasyn_inpkt(q, outmp));
}
if (is_lcp && *cp == 2) {
ep = outmp->b_wptr;
i = (cp[2] << 8) | cp[3];
if (i > ep - cp)
ep = cp;
else if (i < ep - cp)
ep = cp + i;
cp += 4;
while (cp + 2 < ep) {
if ((i = cp[1]) < 2)
i = 2;
if (cp + i > ep)
i = ep - cp;
if (cp[0] == 2 && i >= 6) {
state->sa_raccm = (cp[2] << 24) |
(cp[3] << 16) | (cp[4] << 8) |
cp[5];
break;
}
cp += i;
}
}
return (outmp);
} else {
KINCR(pks_incrcerrs);
cmn_err(CE_CONT, PPP_DRV_NAME "%d: bad fcs (len=%ld)\n",
state->sa_unit, msgsize(outmp));
if (state->sa_flags & SAF_RECVDUMP)
ppp_dump_frame(state, outmp, "bad data");
freemsg(outmp);
state->sa_stats.ppp_ierrors++;
(void) putnextctl1(q, M_CTL, PPPCTL_IERROR);
return (NULL);
}
}
static mblk_t *
ahdlc_decode(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
mblk_t *retmp;
mblk_t *outmp;
mblk_t *mpnext;
uchar_t *dp;
uchar_t *dpend;
uchar_t *tp;
uchar_t *tpmax;
int flagtmp;
#if defined(lint) || defined(_lint)
uchar_t chr;
#else
int chr;
#endif
ushort_t fcs16;
uint32_t fcs32;
#ifdef HANDLE_ZERO_LENGTH
size_t nprocessed;
#endif
state = (sppp_ahdlc_t *)q->q_ptr;
KINCR(pks_datain);
state->sa_stats.ppp_ibytes += msgsize(mp);
if (state->sa_flags & SAF_RECVDUMP)
ppp_dump_frame(state, mp, "rcvd");
flagtmp = state->sa_flags;
fcs16 = state->sa_infcs16;
fcs32 = state->sa_infcs32;
outmp = state->sa_rx_buf;
if (outmp == NULL) {
tp = tpmax = NULL;
} else {
tp = outmp->b_wptr;
tpmax = outmp->b_datap->db_lim;
}
#ifdef HANDLE_ZERO_LENGTH
nprocessed = 0;
#endif
retmp = NULL;
while (mp != NULL) {
dpend = mp->b_wptr;
dp = mp->b_rptr;
#ifdef HANDLE_ZERO_LENGTH
nprocessed += dpend - dp;
#endif
for (; dp < dpend; dp++) {
chr = *dp;
flagtmp |= charflags[chr];
if (chr == PPP_FLAG) {
if ((flagtmp & SAF_IFLUSH) || outmp == NULL) {
flagtmp &= ~SAF_IFLUSH & ~SAF_ESCAPED;
continue;
}
if ((flagtmp & SAF_ESCAPED) ||
tp - outmp->b_rptr < 2 + PPP_FCSLEN) {
if (flagtmp & SAF_ESCAPED)
KINCR(pks_inaborts);
else
KINCR(pks_inrunts);
if (state->sa_flags & SAF_RECVDUMP) {
outmp->b_wptr = tp;
ppp_dump_frame(state, outmp,
"runt");
}
freemsg(outmp);
flagtmp &= ~SAF_ESCAPED;
} else {
outmp->b_wptr = tp;
outmp = receive_frame(q, outmp, fcs16,
fcs32);
retmp = sppp_mappend(retmp, outmp);
}
outmp = NULL;
tp = tpmax = NULL;
continue;
}
if (flagtmp & SAF_IFLUSH) {
continue;
}
if (outmp == NULL) {
int maxlen;
if ((maxlen = state->sa_mru) < PPP_MRU)
maxlen = PPP_MRU;
maxlen += PPP_FCS32LEN + 32;
outmp = allocb(maxlen, BPRI_MED);
if (outmp == NULL) {
KINCR(pks_inallocfails);
flagtmp |= SAF_IFLUSH;
continue;
}
tp = outmp->b_wptr;
tpmax = outmp->b_datap->db_lim;
flagtmp &= ~(SAF_IFLUSH | SAF_ESCAPED);
fcs16 = PPPINITFCS16;
fcs32 = PPPINITFCS32;
}
if (IN_RX_MAP(chr, state->sa_raccm)) {
KINCR(pks_inbadchars);
KOR(pks_inbadcharmask, 1 << chr);
continue;
}
if (flagtmp & SAF_ESCAPED) {
chr ^= PPP_TRANS;
flagtmp &= ~SAF_ESCAPED;
} else if (chr == PPP_ESCAPE) {
flagtmp |= SAF_ESCAPED;
continue;
}
if (tp < tpmax) {
fcs16 = PPPFCS16(fcs16, chr);
fcs32 = PPPFCS32(fcs32, chr);
*tp++ = chr;
} else {
KINCR(pks_intoolongs);
cmn_err(CE_CONT, PPP_DRV_NAME
"%d: frame too long (%d bytes)\n",
state->sa_unit,
(int)(tpmax - outmp->b_rptr));
freemsg(outmp);
outmp = NULL;
tp = tpmax = NULL;
flagtmp |= SAF_IFLUSH;
}
}
mpnext = mp->b_cont;
freeb(mp);
mp = mpnext;
}
state->sa_flags = flagtmp;
if ((state->sa_rx_buf = outmp) != NULL)
outmp->b_wptr = tp;
state->sa_infcs16 = fcs16;
state->sa_infcs32 = fcs32;
#ifdef HANDLE_ZERO_LENGTH
if (nprocessed <= 0) {
outmp = allocb(0, BPRI_MED);
if (outmp != NULL) {
outmp->b_datap->db_type = M_HANGUP;
retmp = sppp_mappend(retmp, outmp);
}
}
#endif
return (retmp);
}
#define BYTES_PER_LINE 8
static void
ppp_dump_frame(sppp_ahdlc_t *state, mblk_t *mptr, const char *msg)
{
char buf[3 * BYTES_PER_LINE + 2 + BYTES_PER_LINE + 1];
uchar_t *rptr, *eptr;
int i, chr;
char *bp;
static const char digits[] = "0123456789abcdef";
cmn_err(CE_CONT, "!ppp_async%d: %s %ld bytes\n", state->sa_unit,
msg, msgsize(mptr));
i = 0;
bp = buf;
buf[3 * BYTES_PER_LINE] = ' ';
buf[3 * BYTES_PER_LINE + 1] = ' ';
buf[sizeof (buf) - 1] = '\0';
while (mptr != NULL) {
rptr = mptr->b_rptr;
eptr = mptr->b_wptr;
while (rptr < eptr) {
chr = *rptr++;
*bp++ = digits[chr >> 4];
*bp++ = digits[chr & 0xf];
*bp++ = ' ';
buf[3 * BYTES_PER_LINE + 2 + i] =
(chr >= 0x20 && chr <= 0x7E) ? (char)chr : '.';
i++;
if (i >= BYTES_PER_LINE) {
cmn_err(CE_CONT, "!ppp%d: %s\n", state->sa_unit,
buf);
bp = buf;
i = 0;
}
}
mptr = mptr->b_cont;
}
if (bp > buf) {
while (bp < buf + 3 * BYTES_PER_LINE)
*bp++ = ' ';
buf[3 * BYTES_PER_LINE + 2 + i] = '\0';
cmn_err(CE_CONT, "!ppp%d: %s\n", state->sa_unit, buf);
}
}
static mblk_t *
spppasyn_muxencode(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state = (sppp_ahdlc_t *)q->q_ptr;
uint32_t len;
uint32_t nlen;
ushort_t protolen;
uint32_t hdrlen;
ushort_t proto;
mblk_t *new_frame;
mblk_t *tmp;
mblk_t *send_frame;
ushort_t i;
len = msgdsize(mp);
i = 0;
protolen = 1;
proto = MSG_BYTE(mp, i);
if (proto == PPP_ALLSTATIONS) {
len -= 2;
i += 2;
proto = MSG_BYTE(mp, i);
}
++i;
if ((proto & 1) == 0) {
proto = (proto << 8) + MSG_BYTE(mp, i);
protolen++;
}
hdrlen = i - 1;
send_frame = NULL;
if (len > PPP_MAX_MUX_LEN || (proto & 0x8000)) {
if (state->sa_mqhead != NULL) {
if (state->sa_mqtail != NULL)
KINCR(pks_sentmux);
send_frame = ahdlc_encode(q, state->sa_mqhead);
}
mp = ahdlc_encode(q, mp);
send_frame = sppp_mcat(send_frame, mp);
RESET_MUX_VALUES(state);
return (send_frame);
}
nlen = len + 1;
if (state->sa_proto == proto)
nlen -= protolen;
send_frame = NULL;
if ((state->sa_mqlen + nlen) >= state->sa_mru) {
if (state->sa_mqhead != NULL) {
if (state->sa_mqtail != NULL)
KINCR(pks_sentmux);
send_frame = ahdlc_encode(q, state->sa_mqhead);
}
RESET_MUX_VALUES(state);
}
if (state->sa_mqhead != NULL) {
if (state->sa_mqtail == NULL) {
if ((new_frame = allocb(PPP_HDRLEN+1,
BPRI_MED)) == NULL) {
return (send_frame);
}
if (!IS_COMP_AC(state)) {
*new_frame->b_wptr++ = PPP_ALLSTATIONS;
*new_frame->b_wptr++ = PPP_UI;
}
if (IS_COMP_PROT(state)) {
*new_frame->b_wptr++ = PPP_MUX;
} else {
*new_frame->b_wptr++ = 0;
*new_frame->b_wptr++ = PPP_MUX;
}
*new_frame->b_wptr++ = PFF |
(state->sa_mqlen - protolen - 1);
if (DB_REF(mp) > 1) {
tmp = copymsg(state->sa_mqhead);
freemsg(state->sa_mqhead);
if ((state->sa_mqhead = tmp) == NULL) {
return (send_frame);
}
}
if (state->sa_mqhead->b_rptr[0] == PPP_ALLSTATIONS)
state->sa_mqhead->b_rptr += 2;
linkb(new_frame, state->sa_mqhead);
state->sa_mqtail = state->sa_mqhead;
while (state->sa_mqtail->b_cont != NULL)
state->sa_mqtail = state->sa_mqtail->b_cont;
state->sa_mqhead = new_frame;
}
if (state->sa_proto == proto) {
if (DB_REF(mp) > 1) {
tmp = copymsg(mp);
freemsg(mp);
if ((mp = tmp) == NULL) {
return (send_frame);
}
}
mp->b_rptr += hdrlen;
mp->b_rptr += (protolen - 1);
*mp->b_rptr = (len - protolen) & 0xff;
} else {
if (DB_REF(mp) > 1) {
tmp = copymsg(mp);
freemsg(mp);
if ((mp = tmp) == NULL) {
return (send_frame);
}
}
if (hdrlen != 0) {
mp->b_rptr += (hdrlen-1);
*mp->b_rptr = PFF | (len);
} else if (state->sa_mqtail->b_wptr <
DB_LIM(state->sa_mqtail)) {
*state->sa_mqtail->b_wptr++ = PFF |len;
} else {
if ((new_frame = allocb(1, BPRI_MED))
== NULL) {
freemsg(mp);
return (send_frame);
}
*new_frame->b_wptr++ = PFF | (len);
linkb(state->sa_mqtail, new_frame);
}
state->sa_proto = proto;
}
linkb(state->sa_mqtail, mp);
state->sa_mqtail = mp;
while (state->sa_mqtail->b_cont != NULL)
state->sa_mqtail = state->sa_mqtail->b_cont;
state->sa_mqlen += nlen;
} else {
state->sa_mqhead = mp;
state->sa_mqlen = len + protolen + 1;
state->sa_proto = proto;
}
if (state->sa_timeout_id == 0) {
state->sa_timeout_id = qtimeout(q, spppasyn_timer, q,
(drv_usectohz(state->sa_timeout_usec)));
}
return (send_frame);
}
static mblk_t *
spppasyn_inpkt(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state = (sppp_ahdlc_t *)q->q_ptr;
ushort_t proto;
ushort_t prev_proto;
uint32_t len;
uchar_t muxhdr;
mblk_t *hdrmp;
mblk_t *subframe;
mblk_t *retmp;
if (!(mp->b_rptr[0] & PFF)) {
KINCR(pks_inmuxerrs);
(void) putnextctl1(q, M_CTL, PPPCTL_IERROR);
freemsg(mp);
return (NULL);
}
prev_proto = 0;
retmp = NULL;
while (mp->b_rptr < mp->b_wptr) {
muxhdr = mp->b_rptr[0];
len = muxhdr & ~PFF;
mp->b_rptr++;
if (MBLKL(mp) < len) {
KINCR(pks_inmuxerrs);
(void) putnextctl1(q, M_CTL, PPPCTL_IERROR);
break;
}
if ((hdrmp = allocb(PPP_HDRLEN, BPRI_MED)) == NULL) {
KINCR(pks_inallocfails);
break;
}
*hdrmp->b_wptr++ = PPP_ALLSTATIONS;
*hdrmp->b_wptr++ = PPP_UI;
if (muxhdr & PFF) {
proto = MSG_BYTE(mp, 0);
if ((proto & 1) == 0)
proto = (proto << 8) + MSG_BYTE(mp, 1);
prev_proto = proto;
} else {
if (!IS_DECOMP_PROT(state))
*hdrmp->b_wptr++ = prev_proto >> 8;
*hdrmp->b_wptr++ = (prev_proto & 0xff);
}
subframe = dupmsg(mp);
subframe->b_wptr = mp->b_rptr + len;
linkb(hdrmp, subframe);
retmp = sppp_mappend(retmp, hdrmp);
mp->b_rptr += len;
}
freemsg(mp);
return (retmp);
}
static void
spppasyn_timer(void *arg)
{
queue_t *q;
sppp_ahdlc_t *state;
mblk_t *mp;
ASSERT(arg);
q = (queue_t *)arg;
state = (sppp_ahdlc_t *)q->q_ptr;
if (state->sa_mqhead != NULL) {
if (state->sa_mqtail != NULL)
KINCR(pks_sentmux);
if ((mp = ahdlc_encode(q, state->sa_mqhead)) != NULL)
putnext(q, mp);
RESET_MUX_VALUES(state);
}
state->sa_timeout_id = 0;
}