#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/errno.h>
#include <sys/debug.h>
#include <sys/time.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/kmem.h>
#include <sys/strsun.h>
#include <sys/pfmod.h>
#include <sys/modctl.h>
struct epacketfilt {
struct Pf_ext_packetfilt pf;
#define pf_Priority pf.Pf_Priority
#define pf_FilterLen pf.Pf_FilterLen
#define pf_Filter pf.Pf_Filter
ushort_t *pf_FilterEnd;
ushort_t pf_PByteLen;
};
struct packdesc {
ushort_t *pd_hdr;
uint_t pd_hdrlen;
ushort_t *pd_body;
uint_t pd_bodylen;
};
static int pfopen(queue_t *, dev_t *, int, int, cred_t *);
static int pfclose(queue_t *, int, cred_t *);
static void pfioctl(queue_t *wq, mblk_t *mp);
static int FilterPacket(struct packdesc *, struct epacketfilt *);
static int pfwput(queue_t *, mblk_t *);
static int pfrput(queue_t *, mblk_t *);
static struct module_info pf_minfo = {
22,
"pfmod",
0,
INFPSZ,
0,
0
};
static struct qinit pf_rinit = {
pfrput,
NULL,
pfopen,
pfclose,
NULL,
&pf_minfo,
NULL
};
static struct qinit pf_winit = {
pfwput,
NULL,
NULL,
NULL,
NULL,
&pf_minfo,
NULL
};
static struct streamtab pf_info = {
&pf_rinit,
&pf_winit,
NULL,
NULL
};
static struct fmodsw fsw = {
"pfmod",
&pf_info,
D_MTQPAIR | D_MP
};
static struct modlstrmod modlstrmod = {
&mod_strmodops, "streams packet filter module", &fsw
};
static struct modlinkage modlinkage = {
MODREV_1, &modlstrmod, NULL
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
pfopen(queue_t *rq, dev_t *dev, int oflag, int sflag, cred_t *crp)
{
struct epacketfilt *pfp;
ASSERT(rq);
if (sflag != MODOPEN)
return (EINVAL);
if (rq->q_ptr)
return (0);
pfp = kmem_alloc(sizeof (struct epacketfilt), KM_SLEEP);
rq->q_ptr = WR(rq)->q_ptr = (char *)pfp;
qprocson(rq);
return (0);
}
static int
pfclose(queue_t *rq, int flags __unused, cred_t *credp __unused)
{
struct epacketfilt *pfp = (struct epacketfilt *)rq->q_ptr;
ASSERT(pfp);
qprocsoff(rq);
kmem_free(pfp, sizeof (struct epacketfilt));
rq->q_ptr = WR(rq)->q_ptr = NULL;
return (0);
}
static int
pfwput(queue_t *wq, mblk_t *mp)
{
switch (mp->b_datap->db_type) {
case M_IOCTL:
pfioctl(wq, mp);
break;
default:
putnext(wq, mp);
break;
}
return (0);
}
static int
pfrput(queue_t *rq, mblk_t *mp)
{
struct epacketfilt *pfp = (struct epacketfilt *)rq->q_ptr;
mblk_t *mbp, *mpp;
struct packdesc pd;
int need;
ASSERT(pfp);
switch (DB_TYPE(mp)) {
case M_PROTO:
case M_DATA:
for (mpp = mp; mp && (DB_TYPE(mp) == M_PROTO); mp = mp->b_cont)
;
if (mp == NULL) {
putnext(rq, mpp);
break;
}
mbp = mp->b_cont;
need = pfp->pf_PByteLen;
if (mbp && (MBLKL(mbp) < need)) {
int len = msgdsize(mbp);
if (pullupmsg(mbp, MIN(need, len)) == 0) {
freemsg(mpp);
break;
}
}
if (((uintptr_t)mp->b_rptr & (sizeof (ushort_t) - 1)) ||
(mbp != NULL &&
((uintptr_t)mbp->b_rptr & (sizeof (ushort_t) - 1)))) {
freemsg(mpp);
break;
}
pd.pd_hdr = (ushort_t *)mp->b_rptr;
pd.pd_hdrlen = (mp->b_wptr - mp->b_rptr) / sizeof (ushort_t);
if (mbp) {
pd.pd_body = (ushort_t *)mbp->b_rptr;
pd.pd_bodylen = (mbp->b_wptr - mbp->b_rptr) /
sizeof (ushort_t);
} else {
pd.pd_body = NULL;
pd.pd_bodylen = 0;
}
if (FilterPacket(&pd, pfp))
putnext(rq, mpp);
else
freemsg(mpp);
break;
default:
putnext(rq, mp);
break;
}
return (0);
}
static void
pfioctl(queue_t *wq, mblk_t *mp)
{
struct epacketfilt *pfp = (struct epacketfilt *)wq->q_ptr;
struct Pf_ext_packetfilt *upfp;
struct packetfilt *opfp;
ushort_t *fwp;
int arg;
int maxoff = 0;
int maxoffreg = 0;
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
int error;
switch (iocp->ioc_cmd) {
case PFIOCSETF:
switch (iocp->ioc_count) {
case sizeof (struct Pf_ext_packetfilt):
error = miocpullup(mp,
sizeof (struct Pf_ext_packetfilt));
if (error != 0) {
miocnak(wq, mp, 0, error);
return;
}
upfp = (struct Pf_ext_packetfilt *)mp->b_cont->b_rptr;
if (upfp->Pf_FilterLen > PF_MAXFILTERS) {
miocnak(wq, mp, 0, EINVAL);
return;
}
bcopy(upfp, pfp, sizeof (struct Pf_ext_packetfilt));
pfp->pf_FilterEnd = &pfp->pf_Filter[pfp->pf_FilterLen];
break;
case sizeof (struct packetfilt):
error = miocpullup(mp, sizeof (struct packetfilt));
if (error != 0) {
miocnak(wq, mp, 0, error);
return;
}
opfp = (struct packetfilt *)mp->b_cont->b_rptr;
if (opfp->Pf_FilterLen - 1 >= ENMAXFILTERS) {
miocnak(wq, mp, 0, EINVAL);
return;
}
pfp->pf.Pf_Priority = opfp->Pf_Priority;
pfp->pf.Pf_FilterLen = (unsigned int)opfp->Pf_FilterLen;
bcopy(opfp->Pf_Filter, pfp->pf.Pf_Filter,
sizeof (opfp->Pf_Filter));
pfp->pf_FilterEnd = &pfp->pf_Filter[pfp->pf_FilterLen];
break;
default:
miocnak(wq, mp, 0, EINVAL);
return;
}
for (fwp = pfp->pf_Filter; fwp < pfp->pf_FilterEnd; fwp++) {
arg = *fwp & ((1 << ENF_NBPA) - 1);
switch (arg) {
default:
if ((arg -= ENF_PUSHWORD) > maxoff)
maxoff = arg;
break;
case ENF_LOAD_OFFSET:
fwp++;
if (*fwp > maxoffreg)
maxoffreg = *fwp;
break;
case ENF_PUSHLIT:
case ENF_BRTR:
case ENF_BRFL:
fwp++;
break;
case ENF_PUSHZERO:
case ENF_PUSHONE:
case ENF_PUSHFFFF:
case ENF_PUSHFF00:
case ENF_PUSH00FF:
case ENF_NOPUSH:
case ENF_POP:
break;
}
}
pfp->pf_PByteLen = (maxoff + maxoffreg + 1) * sizeof (ushort_t);
miocack(wq, mp, 0, 0);
break;
default:
putnext(wq, mp);
break;
}
}
#ifdef INNERDEBUG
#define enprintf(a) printf a
#else
#define enprintf(a)
#endif
#define opx(i) ((i) >> ENF_NBPA)
static int
FilterPacket(struct packdesc *pp, struct epacketfilt *pfp)
{
int maxhdr = pp->pd_hdrlen;
int maxword = maxhdr + pp->pd_bodylen;
ushort_t *sp;
ushort_t *fp;
ushort_t *fpe;
unsigned op;
unsigned arg;
unsigned offreg = 0;
ushort_t stack[ENMAXFILTERS+1];
fp = &pfp->pf_Filter[0];
fpe = pfp->pf_FilterEnd;
enprintf(("FilterPacket(%p, %p, %p, %p):\n", pp, pfp, fp, fpe));
sp = &stack[ENMAXFILTERS];
*sp = 1;
while (fp < fpe) {
op = *fp >> ENF_NBPA;
arg = *fp & ((1 << ENF_NBPA) - 1);
fp++;
switch (arg) {
default:
arg -= ENF_PUSHWORD;
if (arg + offreg < maxhdr)
*--sp = pp->pd_hdr[arg + offreg];
else if (arg + offreg < maxword)
*--sp = pp->pd_body[arg - maxhdr + offreg];
else {
enprintf(("=>0(len)\n"));
return (0);
}
break;
case ENF_PUSHLIT:
*--sp = *fp++;
break;
case ENF_PUSHZERO:
*--sp = 0;
break;
case ENF_PUSHONE:
*--sp = 1;
break;
case ENF_PUSHFFFF:
*--sp = 0xffff;
break;
case ENF_PUSHFF00:
*--sp = 0xff00;
break;
case ENF_PUSH00FF:
*--sp = 0x00ff;
break;
case ENF_LOAD_OFFSET:
offreg = *fp++;
break;
case ENF_BRTR:
if (*sp != 0)
fp += *fp;
else
fp++;
if (fp >= fpe) {
enprintf(("BRTR: fp>=fpe\n"));
return (0);
}
break;
case ENF_BRFL:
if (*sp == 0)
fp += *fp;
else
fp++;
if (fp >= fpe) {
enprintf(("BRFL: fp>=fpe\n"));
return (0);
}
break;
case ENF_POP:
++sp;
if (sp > &stack[ENMAXFILTERS]) {
enprintf(("stack underflow\n"));
return (0);
}
break;
case ENF_NOPUSH:
break;
}
if (sp < &stack[2]) {
enprintf(("=>0(--sp)\n"));
return (0);
}
if (op == ENF_NOP)
continue;
if (sp > &stack[ENMAXFILTERS-2]) {
enprintf(("=>0(sp++)\n"));
return (0);
}
arg = *sp++;
switch (op) {
default:
enprintf(("=>0(def)\n"));
return (0);
case opx(ENF_AND):
*sp &= arg;
break;
case opx(ENF_OR):
*sp |= arg;
break;
case opx(ENF_XOR):
*sp ^= arg;
break;
case opx(ENF_EQ):
*sp = (*sp == arg);
break;
case opx(ENF_NEQ):
*sp = (*sp != arg);
break;
case opx(ENF_LT):
*sp = (*sp < arg);
break;
case opx(ENF_LE):
*sp = (*sp <= arg);
break;
case opx(ENF_GT):
*sp = (*sp > arg);
break;
case opx(ENF_GE):
*sp = (*sp >= arg);
break;
case opx(ENF_COR):
if (*sp++ == arg) {
enprintf(("=>COR %x\n", *sp));
return (1);
}
break;
case opx(ENF_CAND):
if (*sp++ != arg) {
enprintf(("=>CAND %x\n", *sp));
return (0);
}
break;
case opx(ENF_CNOR):
if (*sp++ == arg) {
enprintf(("=>COR %x\n", *sp));
return (0);
}
break;
case opx(ENF_CNAND):
if (*sp++ != arg) {
enprintf(("=>CNAND %x\n", *sp));
return (1);
}
break;
}
}
enprintf(("=>%x\n", *sp));
return (*sp);
}