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