xref: /illumos-gate/usr/src/uts/common/io/pfmod.c (revision e5803b76927480e8f9b67b22201c484ccf4c2bcf)
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
150 _init(void)
151 {
152 	return (mod_install(&modlinkage));
153 }
154 
155 int
156 _fini(void)
157 {
158 	return (mod_remove(&modlinkage));
159 }
160 
161 int
162 _info(struct modinfo *modinfop)
163 {
164 	return (mod_info(&modlinkage, modinfop));
165 }
166 
167 /*ARGSUSED*/
168 static int
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
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
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
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
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
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