1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 /*
29 * STREAMS Packet Filter Module
30 *
31 * This module applies a filter to messages arriving on its read
32 * queue, passing on messages that the filter accepts adn discarding
33 * the others. It supports ioctls for setting the filter.
34 *
35 * On the write side, the module simply passes everything through
36 * unchanged.
37 *
38 * Based on SunOS 4.x version. This version has minor changes:
39 * - general SVR4 porting stuff
40 * - change name and prefixes from "nit" buffer to streams buffer
41 * - multithreading assumes configured as D_MTQPAIR
42 */
43
44 #include <sys/types.h>
45 #include <sys/sysmacros.h>
46 #include <sys/errno.h>
47 #include <sys/debug.h>
48 #include <sys/time.h>
49 #include <sys/stropts.h>
50 #include <sys/stream.h>
51 #include <sys/conf.h>
52 #include <sys/ddi.h>
53 #include <sys/sunddi.h>
54 #include <sys/kmem.h>
55 #include <sys/strsun.h>
56 #include <sys/pfmod.h>
57 #include <sys/modctl.h>
58
59 /*
60 * Expanded version of the Packetfilt structure that includes
61 * some additional fields that aid filter execution efficiency.
62 */
63 struct epacketfilt {
64 struct Pf_ext_packetfilt pf;
65 #define pf_Priority pf.Pf_Priority
66 #define pf_FilterLen pf.Pf_FilterLen
67 #define pf_Filter pf.Pf_Filter
68 /* pointer to word immediately past end of filter */
69 ushort_t *pf_FilterEnd;
70 /* length in bytes of packet prefix the filter examines */
71 ushort_t pf_PByteLen;
72 };
73
74 /*
75 * (Internal) packet descriptor for FilterPacket
76 */
77 struct packdesc {
78 ushort_t *pd_hdr; /* header starting address */
79 uint_t pd_hdrlen; /* header length in shorts */
80 ushort_t *pd_body; /* body starting address */
81 uint_t pd_bodylen; /* body length in shorts */
82 };
83
84
85 /*
86 * Function prototypes.
87 */
88 static int pfopen(queue_t *, dev_t *, int, int, cred_t *);
89 static int pfclose(queue_t *);
90 static void pfioctl(queue_t *wq, mblk_t *mp);
91 static int FilterPacket(struct packdesc *, struct epacketfilt *);
92 /*
93 * To save instructions, since STREAMS ignores the return value
94 * from these functions, they are defined as void here. Kind of icky, but...
95 */
96 static void pfwput(queue_t *, mblk_t *);
97 static void pfrput(queue_t *, mblk_t *);
98
99 static struct module_info pf_minfo = {
100 22, /* mi_idnum */
101 "pfmod", /* mi_idname */
102 0, /* mi_minpsz */
103 INFPSZ, /* mi_maxpsz */
104 0, /* mi_hiwat */
105 0 /* mi_lowat */
106 };
107
108 static struct qinit pf_rinit = {
109 (int (*)())pfrput, /* qi_putp */
110 NULL,
111 pfopen, /* qi_qopen */
112 pfclose, /* qi_qclose */
113 NULL, /* qi_qadmin */
114 &pf_minfo, /* qi_minfo */
115 NULL /* qi_mstat */
116 };
117
118 static struct qinit pf_winit = {
119 (int (*)())pfwput, /* qi_putp */
120 NULL, /* qi_srvp */
121 NULL, /* qi_qopen */
122 NULL, /* qi_qclose */
123 NULL, /* qi_qadmin */
124 &pf_minfo, /* qi_minfo */
125 NULL /* qi_mstat */
126 };
127
128 static struct streamtab pf_info = {
129 &pf_rinit, /* st_rdinit */
130 &pf_winit, /* st_wrinit */
131 NULL, /* st_muxrinit */
132 NULL /* st_muxwinit */
133 };
134
135 static struct fmodsw fsw = {
136 "pfmod",
137 &pf_info,
138 D_MTQPAIR | D_MP
139 };
140
141 static struct modlstrmod modlstrmod = {
142 &mod_strmodops, "streams packet filter module", &fsw
143 };
144
145 static struct modlinkage modlinkage = {
146 MODREV_1, &modlstrmod, NULL
147 };
148
149 int
_init(void)150 _init(void)
151 {
152 return (mod_install(&modlinkage));
153 }
154
155 int
_fini(void)156 _fini(void)
157 {
158 return (mod_remove(&modlinkage));
159 }
160
161 int
_info(struct modinfo * modinfop)162 _info(struct modinfo *modinfop)
163 {
164 return (mod_info(&modlinkage, modinfop));
165 }
166
167 /*ARGSUSED*/
168 static int
pfopen(queue_t * rq,dev_t * dev,int oflag,int sflag,cred_t * crp)169 pfopen(queue_t *rq, dev_t *dev, int oflag, int sflag, cred_t *crp)
170 {
171 struct epacketfilt *pfp;
172
173 ASSERT(rq);
174
175 if (sflag != MODOPEN)
176 return (EINVAL);
177
178 if (rq->q_ptr)
179 return (0);
180
181 /*
182 * Allocate and initialize per-Stream structure.
183 */
184 pfp = kmem_alloc(sizeof (struct epacketfilt), KM_SLEEP);
185 rq->q_ptr = WR(rq)->q_ptr = (char *)pfp;
186
187 qprocson(rq);
188
189 return (0);
190 }
191
192 static int
pfclose(queue_t * rq)193 pfclose(queue_t *rq)
194 {
195 struct epacketfilt *pfp = (struct epacketfilt *)rq->q_ptr;
196
197 ASSERT(pfp);
198
199 qprocsoff(rq);
200
201 kmem_free(pfp, sizeof (struct epacketfilt));
202 rq->q_ptr = WR(rq)->q_ptr = NULL;
203
204 return (0);
205 }
206
207 /*
208 * Write-side put procedure. Its main task is to detect ioctls.
209 * Other message types are passed on through.
210 */
211 static void
pfwput(queue_t * wq,mblk_t * mp)212 pfwput(queue_t *wq, mblk_t *mp)
213 {
214 switch (mp->b_datap->db_type) {
215 case M_IOCTL:
216 pfioctl(wq, mp);
217 break;
218
219 default:
220 putnext(wq, mp);
221 break;
222 }
223 }
224
225 /*
226 * Read-side put procedure. It's responsible for applying the
227 * packet filter and passing upstream message on or discarding it
228 * depending upon the results.
229 *
230 * Upstream messages can start with zero or more M_PROTO mblks
231 * which are skipped over before executing the packet filter
232 * on any remaining M_DATA mblks.
233 */
234 static void
pfrput(queue_t * rq,mblk_t * mp)235 pfrput(queue_t *rq, mblk_t *mp)
236 {
237 struct epacketfilt *pfp = (struct epacketfilt *)rq->q_ptr;
238 mblk_t *mbp, *mpp;
239 struct packdesc pd;
240 int need;
241
242 ASSERT(pfp);
243
244 switch (DB_TYPE(mp)) {
245 case M_PROTO:
246 case M_DATA:
247 /*
248 * Skip over protocol information and find the start
249 * of the message body, saving the overall message
250 * start in mpp.
251 */
252 for (mpp = mp; mp && (DB_TYPE(mp) == M_PROTO); mp = mp->b_cont)
253 ;
254
255 /*
256 * Null body (exclusive of M_PROTO blocks) ==> accept.
257 * Note that a null body is not the same as an empty body.
258 */
259 if (mp == NULL) {
260 putnext(rq, mpp);
261 break;
262 }
263
264 /*
265 * Pull the packet up to the length required by
266 * the filter. Note that doing so destroys sharing
267 * relationships, which is unfortunate, since the
268 * results of pulling up here are likely to be useful
269 * for shared messages applied to a filter on a sibling
270 * stream.
271 *
272 * Most packet sources will provide the packet in two
273 * logical pieces: an initial header in a single mblk,
274 * and a body in a sequence of mblks hooked to the
275 * header. We're prepared to deal with variant forms,
276 * but in any case, the pullup applies only to the body
277 * part.
278 */
279 mbp = mp->b_cont;
280 need = pfp->pf_PByteLen;
281 if (mbp && (MBLKL(mbp) < need)) {
282 int len = msgdsize(mbp);
283
284 /* XXX discard silently on pullupmsg failure */
285 if (pullupmsg(mbp, MIN(need, len)) == 0) {
286 freemsg(mpp);
287 break;
288 }
289 }
290
291 /*
292 * Misalignment (not on short boundary) ==> reject.
293 */
294 if (((uintptr_t)mp->b_rptr & (sizeof (ushort_t) - 1)) ||
295 (mbp != NULL &&
296 ((uintptr_t)mbp->b_rptr & (sizeof (ushort_t) - 1)))) {
297 freemsg(mpp);
298 break;
299 }
300
301 /*
302 * These assignments are distasteful, but necessary,
303 * since the packet filter wants to work in terms of
304 * shorts. Odd bytes at the end of header or data can't
305 * participate in the filtering operation.
306 */
307 pd.pd_hdr = (ushort_t *)mp->b_rptr;
308 pd.pd_hdrlen = (mp->b_wptr - mp->b_rptr) / sizeof (ushort_t);
309 if (mbp) {
310 pd.pd_body = (ushort_t *)mbp->b_rptr;
311 pd.pd_bodylen = (mbp->b_wptr - mbp->b_rptr) /
312 sizeof (ushort_t);
313 } else {
314 pd.pd_body = NULL;
315 pd.pd_bodylen = 0;
316 }
317
318 /*
319 * Apply the filter.
320 */
321 if (FilterPacket(&pd, pfp))
322 putnext(rq, mpp);
323 else
324 freemsg(mpp);
325
326 break;
327
328 default:
329 putnext(rq, mp);
330 break;
331 }
332
333 }
334
335 /*
336 * Handle write-side M_IOCTL messages.
337 */
338 static void
pfioctl(queue_t * wq,mblk_t * mp)339 pfioctl(queue_t *wq, mblk_t *mp)
340 {
341 struct epacketfilt *pfp = (struct epacketfilt *)wq->q_ptr;
342 struct Pf_ext_packetfilt *upfp;
343 struct packetfilt *opfp;
344 ushort_t *fwp;
345 int arg;
346 int maxoff = 0;
347 int maxoffreg = 0;
348 struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
349 int error;
350
351 switch (iocp->ioc_cmd) {
352 case PFIOCSETF:
353 /*
354 * Verify argument length. Since the size of packet filter
355 * got increased (ENMAXFILTERS was bumped up to 2047), to
356 * maintain backwards binary compatibility, we need to
357 * check for both possible sizes.
358 */
359 switch (iocp->ioc_count) {
360 case sizeof (struct Pf_ext_packetfilt):
361 error = miocpullup(mp,
362 sizeof (struct Pf_ext_packetfilt));
363 if (error != 0) {
364 miocnak(wq, mp, 0, error);
365 return;
366 }
367 upfp = (struct Pf_ext_packetfilt *)mp->b_cont->b_rptr;
368 if (upfp->Pf_FilterLen > PF_MAXFILTERS) {
369 miocnak(wq, mp, 0, EINVAL);
370 return;
371 }
372
373 bcopy(upfp, pfp, sizeof (struct Pf_ext_packetfilt));
374 pfp->pf_FilterEnd = &pfp->pf_Filter[pfp->pf_FilterLen];
375 break;
376
377 case sizeof (struct packetfilt):
378 error = miocpullup(mp, sizeof (struct packetfilt));
379 if (error != 0) {
380 miocnak(wq, mp, 0, error);
381 return;
382 }
383 opfp = (struct packetfilt *)mp->b_cont->b_rptr;
384 /* this strange comparison keeps gcc from complaining */
385 if (opfp->Pf_FilterLen - 1 >= ENMAXFILTERS) {
386 miocnak(wq, mp, 0, EINVAL);
387 return;
388 }
389
390 pfp->pf.Pf_Priority = opfp->Pf_Priority;
391 pfp->pf.Pf_FilterLen = (unsigned int)opfp->Pf_FilterLen;
392
393 bcopy(opfp->Pf_Filter, pfp->pf.Pf_Filter,
394 sizeof (opfp->Pf_Filter));
395 pfp->pf_FilterEnd = &pfp->pf_Filter[pfp->pf_FilterLen];
396 break;
397
398 default:
399 miocnak(wq, mp, 0, EINVAL);
400 return;
401 }
402
403 /*
404 * Find and record maximum byte offset that the
405 * filter users. We use this when executing the
406 * filter to determine how much of the packet
407 * body to pull up. This code depends on the
408 * filter encoding.
409 */
410 for (fwp = pfp->pf_Filter; fwp < pfp->pf_FilterEnd; fwp++) {
411 arg = *fwp & ((1 << ENF_NBPA) - 1);
412 switch (arg) {
413 default:
414 if ((arg -= ENF_PUSHWORD) > maxoff)
415 maxoff = arg;
416 break;
417
418 case ENF_LOAD_OFFSET:
419 /* Point to the offset */
420 fwp++;
421 if (*fwp > maxoffreg)
422 maxoffreg = *fwp;
423 break;
424
425 case ENF_PUSHLIT:
426 case ENF_BRTR:
427 case ENF_BRFL:
428 /* Skip over the literal. */
429 fwp++;
430 break;
431
432 case ENF_PUSHZERO:
433 case ENF_PUSHONE:
434 case ENF_PUSHFFFF:
435 case ENF_PUSHFF00:
436 case ENF_PUSH00FF:
437 case ENF_NOPUSH:
438 case ENF_POP:
439 break;
440 }
441 }
442
443 /*
444 * Convert word offset to length in bytes.
445 */
446 pfp->pf_PByteLen = (maxoff + maxoffreg + 1) * sizeof (ushort_t);
447 miocack(wq, mp, 0, 0);
448 break;
449
450 default:
451 putnext(wq, mp);
452 break;
453 }
454 }
455
456 /* #define DEBUG 1 */
457 /* #define INNERDEBUG 1 */
458
459 #ifdef INNERDEBUG
460 #define enprintf(a) printf a
461 #else
462 #define enprintf(a)
463 #endif
464
465 /*
466 * Apply the packet filter given by pfp to the packet given by
467 * pp. Return nonzero iff the filter accepts the packet.
468 *
469 * The packet comes in two pieces, a header and a body, since
470 * that's the most convenient form for our caller. The header
471 * is in contiguous memory, whereas the body is in a mbuf.
472 * Our caller will have adjusted the mbuf chain so that its first
473 * min(MLEN, length(body)) bytes are guaranteed contiguous. For
474 * the sake of efficiency (and some laziness) the filter is prepared
475 * to examine only these two contiguous pieces. Furthermore, it
476 * assumes that the header length is even, so that there's no need
477 * to glue the last byte of header to the first byte of data.
478 */
479
480 #define opx(i) ((i) >> ENF_NBPA)
481
482 static int
FilterPacket(struct packdesc * pp,struct epacketfilt * pfp)483 FilterPacket(struct packdesc *pp, struct epacketfilt *pfp)
484 {
485 int maxhdr = pp->pd_hdrlen;
486 int maxword = maxhdr + pp->pd_bodylen;
487 ushort_t *sp;
488 ushort_t *fp;
489 ushort_t *fpe;
490 unsigned op;
491 unsigned arg;
492 unsigned offreg = 0;
493 ushort_t stack[ENMAXFILTERS+1];
494
495 fp = &pfp->pf_Filter[0];
496 fpe = pfp->pf_FilterEnd;
497
498 enprintf(("FilterPacket(%p, %p, %p, %p):\n", pp, pfp, fp, fpe));
499
500 /*
501 * Push TRUE on stack to start. The stack size is chosen such
502 * that overflow can't occur -- each operation can push at most
503 * one item on the stack, and the stack size equals the maximum
504 * program length.
505 */
506 sp = &stack[ENMAXFILTERS];
507 *sp = 1;
508
509 while (fp < fpe) {
510 op = *fp >> ENF_NBPA;
511 arg = *fp & ((1 << ENF_NBPA) - 1);
512 fp++;
513
514 switch (arg) {
515 default:
516 arg -= ENF_PUSHWORD;
517 /*
518 * Since arg is unsigned,
519 * if it were less than ENF_PUSHWORD before,
520 * it would now be huge.
521 */
522 if (arg + offreg < maxhdr)
523 *--sp = pp->pd_hdr[arg + offreg];
524 else if (arg + offreg < maxword)
525 *--sp = pp->pd_body[arg - maxhdr + offreg];
526 else {
527 enprintf(("=>0(len)\n"));
528 return (0);
529 }
530 break;
531 case ENF_PUSHLIT:
532 *--sp = *fp++;
533 break;
534 case ENF_PUSHZERO:
535 *--sp = 0;
536 break;
537 case ENF_PUSHONE:
538 *--sp = 1;
539 break;
540 case ENF_PUSHFFFF:
541 *--sp = 0xffff;
542 break;
543 case ENF_PUSHFF00:
544 *--sp = 0xff00;
545 break;
546 case ENF_PUSH00FF:
547 *--sp = 0x00ff;
548 break;
549 case ENF_LOAD_OFFSET:
550 offreg = *fp++;
551 break;
552 case ENF_BRTR:
553 if (*sp != 0)
554 fp += *fp;
555 else
556 fp++;
557 if (fp >= fpe) {
558 enprintf(("BRTR: fp>=fpe\n"));
559 return (0);
560 }
561 break;
562 case ENF_BRFL:
563 if (*sp == 0)
564 fp += *fp;
565 else
566 fp++;
567 if (fp >= fpe) {
568 enprintf(("BRFL: fp>=fpe\n"));
569 return (0);
570 }
571 break;
572 case ENF_POP:
573 ++sp;
574 if (sp > &stack[ENMAXFILTERS]) {
575 enprintf(("stack underflow\n"));
576 return (0);
577 }
578 break;
579 case ENF_NOPUSH:
580 break;
581 }
582
583 if (sp < &stack[2]) { /* check stack overflow: small yellow zone */
584 enprintf(("=>0(--sp)\n"));
585 return (0);
586 }
587
588 if (op == ENF_NOP)
589 continue;
590
591 /*
592 * all non-NOP operators binary, must have at least two operands
593 * on stack to evaluate.
594 */
595 if (sp > &stack[ENMAXFILTERS-2]) {
596 enprintf(("=>0(sp++)\n"));
597 return (0);
598 }
599
600 arg = *sp++;
601 switch (op) {
602 default:
603 enprintf(("=>0(def)\n"));
604 return (0);
605 case opx(ENF_AND):
606 *sp &= arg;
607 break;
608 case opx(ENF_OR):
609 *sp |= arg;
610 break;
611 case opx(ENF_XOR):
612 *sp ^= arg;
613 break;
614 case opx(ENF_EQ):
615 *sp = (*sp == arg);
616 break;
617 case opx(ENF_NEQ):
618 *sp = (*sp != arg);
619 break;
620 case opx(ENF_LT):
621 *sp = (*sp < arg);
622 break;
623 case opx(ENF_LE):
624 *sp = (*sp <= arg);
625 break;
626 case opx(ENF_GT):
627 *sp = (*sp > arg);
628 break;
629 case opx(ENF_GE):
630 *sp = (*sp >= arg);
631 break;
632
633 /* short-circuit operators */
634
635 case opx(ENF_COR):
636 if (*sp++ == arg) {
637 enprintf(("=>COR %x\n", *sp));
638 return (1);
639 }
640 break;
641 case opx(ENF_CAND):
642 if (*sp++ != arg) {
643 enprintf(("=>CAND %x\n", *sp));
644 return (0);
645 }
646 break;
647 case opx(ENF_CNOR):
648 if (*sp++ == arg) {
649 enprintf(("=>COR %x\n", *sp));
650 return (0);
651 }
652 break;
653 case opx(ENF_CNAND):
654 if (*sp++ != arg) {
655 enprintf(("=>CNAND %x\n", *sp));
656 return (1);
657 }
658 break;
659 }
660 }
661 enprintf(("=>%x\n", *sp));
662 return (*sp);
663 }
664