stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * CDDL HEADER START |
| 3 | * |
| 4 | * The contents of this file are subject to the terms of the |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 5 | * Common Development and Distribution License (the "License"). |
| 6 | * You may not use this file except in compliance with the License. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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 | /* |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 22 | * Copyright 2006 Sun Microsystems, Inc. All rights reserved. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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; |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 345 | int arg; |
| 346 | int maxoff = 0; |
| 347 | int maxoffreg = 0; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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 | */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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 | |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 418 | case ENF_LOAD_OFFSET: |
| 419 | /* Point to the offset */ |
| 420 | fwp++; |
| 421 | if (*fwp > maxoffreg) |
| 422 | maxoffreg = *fwp; |
| 423 | break; |
| 424 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 425 | case ENF_PUSHLIT: |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 426 | case ENF_BRTR: |
| 427 | case ENF_BRFL: |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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: |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 438 | case ENF_POP: |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 439 | break; |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | /* |
| 444 | * Convert word offset to length in bytes. |
| 445 | */ |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 446 | pfp->pf_PByteLen = (maxoff + maxoffreg + 1) * sizeof (ushort_t); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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 |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 460 | #define enprintf(a) printf a |
| 461 | #else |
| 462 | #define enprintf(a) |
| 463 | #endif |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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; |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 492 | unsigned offreg = 0; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 493 | ushort_t stack[ENMAXFILTERS+1]; |
| 494 | |
| 495 | fp = &pfp->pf_Filter[0]; |
| 496 | fpe = pfp->pf_FilterEnd; |
| 497 | |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 498 | enprintf(("FilterPacket(%p, %p, %p, %p):\n", pp, pfp, fp, fpe)); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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 | */ |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 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]; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 526 | else { |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 527 | enprintf(("=>0(len)\n")); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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; |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 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; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 579 | case ENF_NOPUSH: |
| 580 | break; |
| 581 | } |
| 582 | |
| 583 | if (sp < &stack[2]) { /* check stack overflow: small yellow zone */ |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 584 | enprintf(("=>0(--sp)\n")); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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]) { |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 596 | enprintf(("=>0(sp++)\n")); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 597 | return (0); |
| 598 | } |
| 599 | |
| 600 | arg = *sp++; |
| 601 | switch (op) { |
| 602 | default: |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 603 | enprintf(("=>0(def)\n")); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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) { |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 637 | enprintf(("=>COR %x\n", *sp)); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 638 | return (1); |
| 639 | } |
| 640 | break; |
| 641 | case opx(ENF_CAND): |
| 642 | if (*sp++ != arg) { |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 643 | enprintf(("=>CAND %x\n", *sp)); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 644 | return (0); |
| 645 | } |
| 646 | break; |
| 647 | case opx(ENF_CNOR): |
| 648 | if (*sp++ == arg) { |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 649 | enprintf(("=>COR %x\n", *sp)); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 650 | return (0); |
| 651 | } |
| 652 | break; |
| 653 | case opx(ENF_CNAND): |
| 654 | if (*sp++ != arg) { |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 655 | enprintf(("=>CNAND %x\n", *sp)); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 656 | return (1); |
| 657 | } |
| 658 | break; |
| 659 | } |
| 660 | } |
dg199075 | 605445d | 2006-09-19 11:16:27 -0700 | [diff] [blame] | 661 | enprintf(("=>%x\n", *sp)); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 662 | return (*sp); |
| 663 | } |