Mercurial > illumos > onarm
annotate usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_filter.c @ 4:1a15d5aaf794
synchronized with onnv_86 (6202) in onnv-gate
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Mon, 31 Aug 2009 14:38:03 +0900 |
parents | c9caec207d52 |
children |
rev | line source |
---|---|
0 | 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 2007 Sun Microsystems, Inc. All rights reserved. | |
23 * Use is subject to license terms. | |
24 */ | |
25 | |
4
1a15d5aaf794
synchronized with onnv_86 (6202) in onnv-gate
Koji Uno <koji.uno@sun.com>
parents:
0
diff
changeset
|
26 #pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */ |
0 | 27 |
28 #include <stdio.h> | |
29 #include <stdlib.h> | |
30 #include <ctype.h> | |
31 #include <string.h> | |
32 #include <fcntl.h> | |
33 #include <string.h> | |
34 #include <sys/types.h> | |
35 #include <sys/time.h> | |
36 #include <stddef.h> | |
37 | |
38 #include <sys/socket.h> | |
39 #include <sys/sockio.h> | |
40 #include <sys/vlan.h> | |
41 #include <net/if.h> | |
42 #include <netinet/in_systm.h> | |
43 #include <netinet/in.h> | |
44 #include <netinet/ip.h> | |
45 #include <netinet/ip6.h> | |
46 #include <inet/ip6.h> | |
47 #include <inet/ip.h> | |
48 #include <netinet/if_ether.h> | |
49 #include <netinet/tcp.h> | |
50 #include <netinet/udp.h> | |
51 #include <netdb.h> | |
52 #include <arpa/inet.h> | |
53 #include <rpc/rpc.h> | |
54 #include <rpc/rpcent.h> | |
55 | |
56 #include <sys/dlpi.h> | |
57 #include <snoop.h> | |
58 #include "snoop_vlan.h" | |
59 | |
60 #define IPV4_ONLY 0 | |
61 #define IPV6_ONLY 1 | |
62 #define IPV4_AND_IPV6 2 | |
63 | |
64 /* | |
65 * The following constants represent the offsets in bytes from the beginning | |
66 * of the IP(v6) header of the source and destination IP(v6) addresses. | |
67 * These are useful when generating filter code. | |
68 */ | |
69 #define IPV4_SRCADDR_OFFSET 12 | |
70 #define IPV4_DSTADDR_OFFSET 16 | |
71 #define IPV6_SRCADDR_OFFSET 8 | |
72 #define IPV6_DSTADDR_OFFSET 24 | |
73 #define IP_VERS(p) (((*(uchar_t *)p) & 0xf0) >> 4) | |
74 #define MASKED_IPV4_VERS 0x40 | |
75 #define MASKED_IPV6_VERS 0x60 | |
76 #define IP_HDR_LEN(p) (((*(uchar_t *)p) & 0xf) * 4) | |
77 #define TCP_HDR_LEN(p) ((((*((uchar_t *)p+12)) >> 4) & 0xf) * 4) | |
78 | |
79 /* | |
80 * Coding the constant below is tacky, but the compiler won't let us | |
81 * be more clever. E.g., &((struct ip *)0)->ip_xxx | |
82 */ | |
83 #define IP_PROTO_OF(p) (((uchar_t *)p)[9]) | |
84 | |
85 /* | |
86 * AppleTalk uses 802.2 Ethernet encapsulation with LLC/SNAP headers, | |
87 * for 8 octets of overhead, and the common AppleTalk DDP Ethernet | |
88 * header is another 4 octets. | |
89 * | |
90 * The following constants represents the offsets in bytes from the beginning | |
91 * of the Ethernet payload to various parts of the DDP header. | |
92 */ | |
93 | |
94 #define AT_DST_NET_OFFSET 12 | |
95 #define AT_SRC_NET_OFFSET 14 | |
96 #define AT_DST_NODE_OFFSET 16 | |
97 #define AT_SRC_NODE_OFFSET 17 | |
98 | |
99 int eaddr; /* need ethernet addr */ | |
100 | |
101 int opstack; /* operand stack depth */ | |
102 | |
103 /* | |
104 * These are the operators of the user-level filter. | |
105 * STOP ends execution of the filter expression and | |
106 * returns the truth value at the top of the stack. | |
107 * OP_LOAD_OCTET, OP_LOAD_SHORT and OP_LOAD_LONG pop | |
108 * an offset value from the stack and load a value of | |
109 * an appropriate size from the packet (octet, short or | |
110 * long). The offset is computed from a base value that | |
111 * may be set via the OP_OFFSET operators. | |
112 * OP_EQ, OP_NE, OP_GT, OP_GE, OP_LT, OP_LE pop two values | |
113 * from the stack and return the result of their comparison. | |
114 * OP_AND, OP_OR, OP_XOR pop two values from the stack and | |
115 * do perform a bitwise operation on them - returning a result | |
116 * to the stack. OP_NOT inverts the bits of the value on the | |
117 * stack. | |
118 * OP_BRFL and OP_BRTR branch to an offset in the code array | |
119 * depending on the value at the top of the stack: true (not 0) | |
120 * or false (0). | |
121 * OP_ADD, OP_SUB, OP_MUL, OP_DIV and OP_REM pop two values | |
122 * from the stack and perform arithmetic. | |
123 * The OP_OFFSET operators change the base from which the | |
124 * OP_LOAD operators compute their offsets. | |
125 * OP_OFFSET_ZERO sets the offset to zero - beginning of packet. | |
126 * OP_OFFSET_LINK sets the base to the first octet after | |
127 * the link (DLC) header. OP_OFFSET_IP, OP_OFFSET_TCP, | |
128 * and OP_OFFSET_UDP do the same for those headers - they | |
129 * set the offset base to the *end* of the header - not the | |
130 * beginning. The OP_OFFSET_RPC operator is a bit unusual. | |
131 * It points the base at the cached RPC header. For the | |
132 * purposes of selection, RPC reply headers look like call | |
133 * headers except for the direction value. | |
134 * OP_OFFSET_ETHERTYPE sets base according to the following | |
135 * algorithm: | |
136 * if the packet is not VLAN tagged, then set base to | |
137 * the ethertype field in the ethernet header | |
138 * else set base to the ethertype field of the VLAN header | |
139 * OP_OFFSET_POP restores the offset base to the value prior | |
140 * to the most recent OP_OFFSET call. | |
141 */ | |
142 enum optype { | |
143 OP_STOP = 0, | |
144 OP_LOAD_OCTET, | |
145 OP_LOAD_SHORT, | |
146 OP_LOAD_LONG, | |
147 OP_LOAD_CONST, | |
148 OP_LOAD_LENGTH, | |
149 OP_EQ, | |
150 OP_NE, | |
151 OP_GT, | |
152 OP_GE, | |
153 OP_LT, | |
154 OP_LE, | |
155 OP_AND, | |
156 OP_OR, | |
157 OP_XOR, | |
158 OP_NOT, | |
159 OP_BRFL, | |
160 OP_BRTR, | |
161 OP_ADD, | |
162 OP_SUB, | |
163 OP_MUL, | |
164 OP_DIV, | |
165 OP_REM, | |
166 OP_OFFSET_POP, | |
167 OP_OFFSET_ZERO, | |
168 OP_OFFSET_LINK, | |
169 OP_OFFSET_IP, | |
170 OP_OFFSET_TCP, | |
171 OP_OFFSET_UDP, | |
172 OP_OFFSET_RPC, | |
173 OP_OFFSET_SLP, | |
174 OP_OFFSET_ETHERTYPE, | |
175 OP_LAST | |
176 }; | |
177 | |
178 static char *opnames[] = { | |
179 "STOP", | |
180 "LOAD_OCTET", | |
181 "LOAD_SHORT", | |
182 "LOAD_LONG", | |
183 "LOAD_CONST", | |
184 "LOAD_LENGTH", | |
185 "EQ", | |
186 "NE", | |
187 "GT", | |
188 "GE", | |
189 "LT", | |
190 "LE", | |
191 "AND", | |
192 "OR", | |
193 "XOR", | |
194 "NOT", | |
195 "BRFL", | |
196 "BRTR", | |
197 "ADD", | |
198 "SUB", | |
199 "MUL", | |
200 "DIV", | |
201 "REM", | |
202 "OFFSET_POP", | |
203 "OFFSET_ZERO", | |
204 "OFFSET_ETHER", | |
205 "OFFSET_IP", | |
206 "OFFSET_TCP", | |
207 "OFFSET_UDP", | |
208 "OFFSET_RPC", | |
209 "OP_OFFSET_SLP", | |
210 "OFFSET_ETHERTYPE", | |
211 "" | |
212 }; | |
213 | |
214 #define MAXOPS 1024 | |
215 #define MAXSS 64 | |
216 static uint_t oplist[MAXOPS]; /* array of operators */ | |
217 static uint_t *curr_op; /* last op generated */ | |
218 | |
219 extern int valid_slp(uchar_t *, int); /* decides if a SLP msg is valid */ | |
220 extern struct hostent *lgetipnodebyname(const char *, int, int, int *); | |
221 | |
222 static void alternation(); | |
223 static uint_t chain(); | |
224 static void codeprint(); | |
225 static void emitop(); | |
226 static void emitval(); | |
227 static void expression(); | |
228 static struct xid_entry *find_rpc(); | |
229 static void optimize(); | |
230 static void ethertype_match(); | |
231 | |
232 /* | |
233 * Get a ushort from a possibly unaligned character buffer. | |
234 * | |
235 * INPUTS: buffer - where the data is. Must be at least | |
236 * sizeof(uint16_t) bytes long. | |
237 * OUPUTS: An unsigned short that contains the data at buffer. | |
238 * No calls to ntohs or htons are done on the data. | |
239 */ | |
240 static uint16_t | |
241 get_u16(uchar_t *buffer) | |
242 { | |
243 uint8_t *bufraw = buffer; | |
244 | |
245 /* | |
246 * ntohs is used only as a cheap way to flip the bits | |
247 * around on a little endian platform. The value will | |
248 * still be in host order or network order, depending on | |
249 * the order it was in when it was passed in. | |
250 */ | |
251 return (ntohs(bufraw[0] << 8 | bufraw[1])); | |
252 } | |
253 | |
254 /* | |
255 * Returns the ULP for an IPv4 or IPv6 packet | |
256 * Assumes that the packet has already been checked to verify | |
257 * that it's either IPv4 or IPv6 | |
258 * | |
259 * XXX Will need to be updated for AH and ESP | |
260 * XXX when IPsec is supported for v6. | |
261 */ | |
262 static uchar_t | |
263 ip_proto_of(uchar_t *ip) | |
264 { | |
265 uchar_t nxt; | |
266 boolean_t not_done = B_TRUE; | |
267 uchar_t *ptr = ip; | |
268 | |
269 switch (IP_VERS(ip)) { | |
270 case IPV4_VERSION: | |
271 return (IP_PROTO_OF(ip)); | |
272 case IPV6_VERSION: | |
273 | |
274 nxt = ip[6]; | |
275 ptr += 40; /* size of ip6 header */ | |
276 do { | |
277 switch (nxt) { | |
278 /* | |
279 * XXX Add IPsec headers here when supported for v6 | |
280 * XXX (the AH will have a different size...) | |
281 */ | |
282 case IPPROTO_HOPOPTS: | |
283 case IPPROTO_ROUTING: | |
284 case IPPROTO_FRAGMENT: | |
285 case IPPROTO_DSTOPTS: | |
286 ptr += (8 * (ptr[1] + 1)); | |
287 nxt = *ptr; | |
288 break; | |
289 | |
290 default: | |
291 not_done = B_FALSE; | |
292 break; | |
293 } | |
294 } while (not_done); | |
295 return (nxt); | |
296 default: | |
297 break; /* shouldn't get here... */ | |
298 } | |
299 return (0); | |
300 } | |
301 | |
302 /* | |
303 * Returns the total IP header length. | |
304 * For v4, this includes any options present. | |
305 * For v6, this is the length of the IPv6 header plus | |
306 * any extension headers present. | |
307 * | |
308 * XXX Will need to be updated for AH and ESP | |
309 * XXX when IPsec is supported for v6. | |
310 */ | |
311 static int | |
312 ip_hdr_len(uchar_t *ip) | |
313 { | |
314 uchar_t nxt; | |
315 int hdr_len; | |
316 boolean_t not_done = B_TRUE; | |
317 int len = 40; /* IPv6 header size */ | |
318 uchar_t *ptr = ip; | |
319 | |
320 switch (IP_VERS(ip)) { | |
321 case IPV4_VERSION: | |
322 return (IP_HDR_LEN(ip)); | |
323 case IPV6_VERSION: | |
324 nxt = ip[6]; | |
325 ptr += len; | |
326 do { | |
327 switch (nxt) { | |
328 /* | |
329 * XXX Add IPsec headers here when supported for v6 | |
330 * XXX (the AH will have a different size...) | |
331 */ | |
332 case IPPROTO_HOPOPTS: | |
333 case IPPROTO_ROUTING: | |
334 case IPPROTO_FRAGMENT: | |
335 case IPPROTO_DSTOPTS: | |
336 hdr_len = (8 * (ptr[1] + 1)); | |
337 len += hdr_len; | |
338 ptr += hdr_len; | |
339 nxt = *ptr; | |
340 break; | |
341 | |
342 default: | |
343 not_done = B_FALSE; | |
344 break; | |
345 } | |
346 } while (not_done); | |
347 return (len); | |
348 default: | |
349 break; | |
350 } | |
351 return (0); /* not IP */ | |
352 } | |
353 | |
354 static void | |
355 codeprint() | |
356 { | |
357 uint_t *op; | |
358 | |
359 printf("User filter:\n"); | |
360 | |
361 for (op = oplist; *op; op++) { | |
362 if (*op <= OP_LAST) | |
363 printf("\t%2d: %s\n", op - oplist, opnames[*op]); | |
364 else | |
365 printf("\t%2d: (%d)\n", op - oplist, *op); | |
366 | |
367 switch (*op) { | |
368 case OP_LOAD_CONST: | |
369 case OP_BRTR: | |
370 case OP_BRFL: | |
371 op++; | |
372 if ((int)*op < 0) | |
373 printf("\t%2d: 0x%08x (%d)\n", | |
374 op - oplist, *op, *op); | |
375 else | |
376 printf("\t%2d: %d (0x%08x)\n", | |
377 op - oplist, *op, *op); | |
378 } | |
379 } | |
380 printf("\t%2d: STOP\n", op - oplist); | |
381 printf("\n"); | |
382 } | |
383 | |
384 | |
385 /* | |
386 * Take a pass through the generated code and optimize | |
387 * branches. A branch true (BRTR) that has another BRTR | |
388 * at its destination can use the address of the destination | |
389 * BRTR. A BRTR that points to a BRFL (branch false) should | |
390 * point to the address following the BRFL. | |
391 * A similar optimization applies to BRFL operators. | |
392 */ | |
393 static void | |
394 optimize(uint_t *oplistp) | |
395 { | |
396 uint_t *op; | |
397 | |
398 for (op = oplistp; *op; op++) { | |
399 switch (*op) { | |
400 case OP_LOAD_CONST: | |
401 op++; | |
402 break; | |
403 case OP_BRTR: | |
404 op++; | |
405 optimize(&oplist[*op]); | |
406 if (oplist[*op] == OP_BRFL) | |
407 *op += 2; | |
408 else if (oplist[*op] == OP_BRTR) | |
409 *op = oplist[*op + 1]; | |
410 break; | |
411 case OP_BRFL: | |
412 op++; | |
413 optimize(&oplist[*op]); | |
414 if (oplist[*op] == OP_BRTR) | |
415 *op += 2; | |
416 else if (oplist[*op] == OP_BRFL) | |
417 *op = oplist[*op + 1]; | |
418 break; | |
419 } | |
420 } | |
421 } | |
422 | |
423 /* | |
424 * RPC packets are tough to filter. | |
425 * While the call packet has all the interesting | |
426 * info: program number, version, procedure etc, | |
427 * the reply packet has none of this information. | |
428 * If we want to do useful filtering based on this | |
429 * information then we have to stash the information | |
430 * from the call packet, and use the XID in the reply | |
431 * to find the stashed info. The stashed info is | |
432 * kept in a circular lifo, assuming that a call packet | |
433 * will be followed quickly by its reply. | |
434 */ | |
435 | |
436 struct xid_entry { | |
437 unsigned x_xid; /* The XID (32 bits) */ | |
438 unsigned x_dir; /* CALL or REPLY */ | |
439 unsigned x_rpcvers; /* Protocol version (2) */ | |
440 unsigned x_prog; /* RPC program number */ | |
441 unsigned x_vers; /* RPC version number */ | |
442 unsigned x_proc; /* RPC procedure number */ | |
443 }; | |
444 static struct xid_entry xe_table[XID_CACHE_SIZE]; | |
445 static struct xid_entry *xe_first = &xe_table[0]; | |
446 static struct xid_entry *xe = &xe_table[0]; | |
447 static struct xid_entry *xe_last = &xe_table[XID_CACHE_SIZE - 1]; | |
448 | |
449 static struct xid_entry * | |
450 find_rpc(struct rpc_msg *rpc) | |
451 { | |
452 struct xid_entry *x; | |
453 | |
454 for (x = xe; x >= xe_first; x--) | |
455 if (x->x_xid == rpc->rm_xid) | |
456 return (x); | |
457 for (x = xe_last; x > xe; x--) | |
458 if (x->x_xid == rpc->rm_xid) | |
459 return (x); | |
460 return (NULL); | |
461 } | |
462 | |
463 static void | |
464 stash_rpc(struct rpc_msg *rpc) | |
465 { | |
466 struct xid_entry *x; | |
467 | |
468 if (find_rpc(rpc)) | |
469 return; | |
470 | |
471 x = xe++; | |
472 if (xe > xe_last) | |
473 xe = xe_first; | |
474 x->x_xid = rpc->rm_xid; | |
475 x->x_dir = htonl(REPLY); | |
476 x->x_prog = rpc->rm_call.cb_prog; | |
477 x->x_vers = rpc->rm_call.cb_vers; | |
478 x->x_proc = rpc->rm_call.cb_proc; | |
479 } | |
480 | |
481 /* | |
482 * SLP can multicast requests, and recieve unicast replies in which | |
483 * neither the source nor destination port is identifiable as a SLP | |
484 * port. Hence, we need to do as RPC does, and keep track of packets we | |
485 * are interested in. For SLP, however, we use ports, not XIDs, and | |
486 * a smaller cache size is more efficient since every incoming packet | |
487 * needs to be checked. | |
488 */ | |
489 | |
490 #define SLP_CACHE_SIZE 64 | |
491 static uint_t slp_table[SLP_CACHE_SIZE]; | |
492 static int slp_index = 0; | |
493 | |
494 /* | |
495 * Returns the index of dport in the table if found, otherwise -1. | |
496 */ | |
497 static int | |
498 find_slp(uint_t dport) { | |
499 int i; | |
500 | |
501 if (!dport) | |
502 return (0); | |
503 | |
504 for (i = slp_index; i >= 0; i--) | |
505 if (slp_table[i] == dport) { | |
506 return (i); | |
507 } | |
508 for (i = SLP_CACHE_SIZE - 1; i > slp_index; i--) | |
509 if (slp_table[i] == dport) { | |
510 return (i); | |
511 } | |
512 return (-1); | |
513 } | |
514 | |
515 static void stash_slp(uint_t sport) { | |
516 if (slp_table[slp_index - 1] == sport) | |
517 /* avoid redundancy due to multicast retransmissions */ | |
518 return; | |
519 | |
520 slp_table[slp_index++] = sport; | |
521 if (slp_index == SLP_CACHE_SIZE) | |
522 slp_index = 0; | |
523 } | |
524 | |
525 /* | |
526 * This routine takes a packet and returns true or false | |
527 * according to whether the filter expression selects it | |
528 * or not. | |
529 * We assume here that offsets for short and long values | |
530 * are even - we may die with an alignment error if the | |
531 * CPU doesn't support odd addresses. Note that long | |
532 * values are loaded as two shorts so that 32 bit word | |
533 * alignment isn't important. | |
534 * | |
535 * IPv6 is a bit stickier to handle than IPv4... | |
536 */ | |
537 | |
538 int | |
539 want_packet(uchar_t *pkt, int len, int origlen) | |
540 { | |
541 uint_t stack[MAXSS]; /* operand stack */ | |
542 uint_t *op; /* current operator */ | |
543 uint_t *sp; /* top of operand stack */ | |
544 uchar_t *base; /* base for offsets into packet */ | |
545 uchar_t *ip; /* addr of IP header, unaligned */ | |
546 uchar_t *tcp; /* addr of TCP header, unaligned */ | |
547 uchar_t *udp; /* addr of UDP header, unaligned */ | |
548 struct rpc_msg rpcmsg; /* addr of RPC header */ | |
549 struct rpc_msg *rpc; | |
550 int newrpc = 0; | |
551 uchar_t *slphdr; /* beginning of SLP header */ | |
552 uint_t slp_sport, slp_dport; | |
553 int off, header_size; | |
554 uchar_t *offstack[MAXSS]; /* offset stack */ | |
555 uchar_t **offp; /* current offset */ | |
556 uchar_t *opkt = NULL; | |
557 uint_t olen; | |
558 uint_t ethertype = 0; | |
559 | |
560 sp = stack; | |
561 *sp = 1; | |
562 base = pkt; | |
563 offp = offstack; | |
564 | |
565 header_size = (*interface->header_len)((char *)pkt); | |
566 | |
567 for (op = oplist; *op; op++) { | |
568 switch ((enum optype) *op) { | |
569 case OP_LOAD_OCTET: | |
570 if ((base + *sp) > (pkt + len)) | |
571 return (0); /* packet too short */ | |
572 | |
573 *sp = *((uchar_t *)(base + *sp)); | |
574 break; | |
575 case OP_LOAD_SHORT: | |
576 off = *sp; | |
577 | |
578 if ((base + off + sizeof (uint16_t) - 1) > (pkt + len)) | |
579 return (0); /* packet too short */ | |
580 | |
581 *sp = ntohs(get_u16((uchar_t *)(base + off))); | |
582 break; | |
583 case OP_LOAD_LONG: | |
584 off = *sp; | |
585 | |
586 if ((base + off + sizeof (uint32_t) - 1) > (pkt + len)) | |
587 return (0); /* packet too short */ | |
588 | |
589 /* | |
590 * Handle 3 possible alignments | |
591 */ | |
592 switch ((((unsigned)base) + off) % sizeof (uint_t)) { | |
593 case 0: | |
594 *sp = *(uint_t *)(base + off); | |
595 break; | |
596 | |
597 case 2: | |
598 *((ushort_t *)(sp)) = | |
599 *((ushort_t *)(base + off)); | |
600 *(((ushort_t *)sp) + 1) = | |
601 *((ushort_t *)(base + off) + 1); | |
602 break; | |
603 | |
604 case 1: | |
605 case 3: | |
606 *((uchar_t *)(sp)) = | |
607 *((uchar_t *)(base + off)); | |
608 *(((uchar_t *)sp) + 1) = | |
609 *((uchar_t *)(base + off) + 1); | |
610 *(((uchar_t *)sp) + 2) = | |
611 *((uchar_t *)(base + off) + 2); | |
612 *(((uchar_t *)sp) + 3) = | |
613 *((uchar_t *)(base + off) + 3); | |
614 break; | |
615 } | |
616 *sp = ntohl(*sp); | |
617 break; | |
618 case OP_LOAD_CONST: | |
619 if (sp >= &stack[MAXSS]) | |
620 return (0); | |
621 *(++sp) = *(++op); | |
622 break; | |
623 case OP_LOAD_LENGTH: | |
624 if (sp >= &stack[MAXSS]) | |
625 return (0); | |
626 *(++sp) = origlen; | |
627 break; | |
628 case OP_EQ: | |
629 if (sp < &stack[1]) | |
630 return (0); | |
631 sp--; | |
632 *sp = *sp == *(sp + 1); | |
633 break; | |
634 case OP_NE: | |
635 if (sp < &stack[1]) | |
636 return (0); | |
637 sp--; | |
638 *sp = *sp != *(sp + 1); | |
639 break; | |
640 case OP_GT: | |
641 if (sp < &stack[1]) | |
642 return (0); | |
643 sp--; | |
644 *sp = *sp > *(sp + 1); | |
645 break; | |
646 case OP_GE: | |
647 if (sp < &stack[1]) | |
648 return (0); | |
649 sp--; | |
650 *sp = *sp >= *(sp + 1); | |
651 break; | |
652 case OP_LT: | |
653 if (sp < &stack[1]) | |
654 return (0); | |
655 sp--; | |
656 *sp = *sp < *(sp + 1); | |
657 break; | |
658 case OP_LE: | |
659 if (sp < &stack[1]) | |
660 return (0); | |
661 sp--; | |
662 *sp = *sp <= *(sp + 1); | |
663 break; | |
664 case OP_AND: | |
665 if (sp < &stack[1]) | |
666 return (0); | |
667 sp--; | |
668 *sp &= *(sp + 1); | |
669 break; | |
670 case OP_OR: | |
671 if (sp < &stack[1]) | |
672 return (0); | |
673 sp--; | |
674 *sp |= *(sp + 1); | |
675 break; | |
676 case OP_XOR: | |
677 if (sp < &stack[1]) | |
678 return (0); | |
679 sp--; | |
680 *sp ^= *(sp + 1); | |
681 break; | |
682 case OP_NOT: | |
683 *sp = !*sp; | |
684 break; | |
685 case OP_BRFL: | |
686 op++; | |
687 if (!*sp) | |
688 op = &oplist[*op] - 1; | |
689 break; | |
690 case OP_BRTR: | |
691 op++; | |
692 if (*sp) | |
693 op = &oplist[*op] - 1; | |
694 break; | |
695 case OP_ADD: | |
696 if (sp < &stack[1]) | |
697 return (0); | |
698 sp--; | |
699 *sp += *(sp + 1); | |
700 break; | |
701 case OP_SUB: | |
702 if (sp < &stack[1]) | |
703 return (0); | |
704 sp--; | |
705 *sp -= *(sp + 1); | |
706 break; | |
707 case OP_MUL: | |
708 if (sp < &stack[1]) | |
709 return (0); | |
710 sp--; | |
711 *sp *= *(sp + 1); | |
712 break; | |
713 case OP_DIV: | |
714 if (sp < &stack[1]) | |
715 return (0); | |
716 sp--; | |
717 *sp /= *(sp + 1); | |
718 break; | |
719 case OP_REM: | |
720 if (sp < &stack[1]) | |
721 return (0); | |
722 sp--; | |
723 *sp %= *(sp + 1); | |
724 break; | |
725 case OP_OFFSET_POP: | |
726 if (offp < &offstack[0]) | |
727 return (0); | |
728 base = *offp--; | |
729 if (opkt != NULL) { | |
730 pkt = opkt; | |
731 len = olen; | |
732 opkt = NULL; | |
733 } | |
734 break; | |
735 case OP_OFFSET_ZERO: | |
736 if (offp >= &offstack[MAXSS]) | |
737 return (0); | |
738 *++offp = base; | |
739 base = pkt; | |
740 break; | |
741 case OP_OFFSET_LINK: | |
742 if (offp >= &offstack[MAXSS]) | |
743 return (0); | |
744 *++offp = base; | |
745 base = pkt + header_size; | |
746 /* | |
747 * If the offset exceeds the packet length, | |
748 * we should not be interested in this packet... | |
749 * Just return 0. | |
750 */ | |
751 if (base > pkt + len) { | |
752 return (0); | |
753 } | |
754 break; | |
755 case OP_OFFSET_IP: | |
756 if (offp >= &offstack[MAXSS]) | |
757 return (0); | |
758 *++offp = base; | |
759 ip = pkt + header_size; | |
760 base = ip + ip_hdr_len(ip); | |
761 if (base == ip) { | |
762 return (0); /* not IP */ | |
763 } | |
764 if (base > pkt + len) { | |
765 return (0); /* bad pkt */ | |
766 } | |
767 break; | |
768 case OP_OFFSET_TCP: | |
769 if (offp >= &offstack[MAXSS]) | |
770 return (0); | |
771 *++offp = base; | |
772 ip = pkt + header_size; | |
773 tcp = ip + ip_hdr_len(ip); | |
774 if (tcp == ip) { | |
775 return (0); /* not IP */ | |
776 } | |
777 base = tcp + TCP_HDR_LEN(tcp); | |
778 if (base > pkt + len) { | |
779 return (0); | |
780 } | |
781 break; | |
782 case OP_OFFSET_UDP: | |
783 if (offp >= &offstack[MAXSS]) | |
784 return (0); | |
785 *++offp = base; | |
786 ip = pkt + header_size; | |
787 udp = ip + ip_hdr_len(ip); | |
788 if (udp == ip) { | |
789 return (0); /* not IP */ | |
790 } | |
791 base = udp + sizeof (struct udphdr); | |
792 if (base > pkt + len) { | |
793 return (0); | |
794 } | |
795 break; | |
796 case OP_OFFSET_RPC: | |
797 if (offp >= &offstack[MAXSS]) | |
798 return (0); | |
799 *++offp = base; | |
800 ip = pkt + header_size; | |
801 rpc = NULL; | |
802 | |
803 if (IP_VERS(ip) != IPV4_VERSION && | |
804 IP_VERS(ip) != IPV6_VERSION) { | |
805 if (sp >= &stack[MAXSS]) | |
806 return (0); | |
807 *(++sp) = 0; | |
808 break; | |
809 } | |
810 | |
811 switch (ip_proto_of(ip)) { | |
812 case IPPROTO_UDP: | |
813 udp = ip + ip_hdr_len(ip); | |
814 rpc = (struct rpc_msg *)(udp + | |
815 sizeof (struct udphdr)); | |
816 break; | |
817 case IPPROTO_TCP: | |
818 tcp = ip + ip_hdr_len(ip); | |
819 /* | |
820 * Need to skip an extra 4 for the xdr_rec | |
821 * field. | |
822 */ | |
823 rpc = (struct rpc_msg *)(tcp + | |
824 TCP_HDR_LEN(tcp) + 4); | |
825 break; | |
826 } | |
827 /* | |
828 * We need to have at least 24 bytes of a RPC | |
829 * packet to look at to determine the validity | |
830 * of it. | |
831 */ | |
832 if (rpc == NULL || (uchar_t *)rpc + 24 > pkt + len) { | |
833 if (sp >= &stack[MAXSS]) | |
834 return (0); | |
835 *(++sp) = 0; | |
836 break; | |
837 } | |
838 /* align */ | |
839 (void) memcpy(&rpcmsg, rpc, 24); | |
840 if (!valid_rpc(&rpcmsg, 24)) { | |
841 if (sp >= &stack[MAXSS]) | |
842 return (0); | |
843 *(++sp) = 0; | |
844 break; | |
845 } | |
846 if (ntohl(rpcmsg.rm_direction) == CALL) { | |
847 base = (uchar_t *)rpc; | |
848 newrpc = 1; | |
849 if (sp >= &stack[MAXSS]) | |
850 return (0); | |
851 *(++sp) = 1; | |
852 } else { | |
853 opkt = pkt; | |
854 olen = len; | |
855 | |
856 pkt = base = (uchar_t *)find_rpc(&rpcmsg); | |
857 len = sizeof (struct xid_entry); | |
858 if (sp >= &stack[MAXSS]) | |
859 return (0); | |
860 *(++sp) = base != NULL; | |
861 } | |
862 break; | |
863 case OP_OFFSET_SLP: | |
864 slphdr = NULL; | |
865 ip = pkt + header_size; | |
866 | |
867 if (IP_VERS(ip) != IPV4_VERSION && | |
868 IP_VERS(ip) != IPV6_VERSION) { | |
869 if (sp >= &stack[MAXSS]) | |
870 return (0); | |
871 *(++sp) = 0; | |
872 break; | |
873 } | |
874 | |
875 switch (ip_proto_of(ip)) { | |
876 struct udphdr udp_h; | |
877 struct tcphdr tcp_h; | |
878 case IPPROTO_UDP: | |
879 udp = ip + ip_hdr_len(ip); | |
880 /* align */ | |
881 memcpy(&udp_h, udp, sizeof (udp_h)); | |
882 slp_sport = ntohs(udp_h.uh_sport); | |
883 slp_dport = ntohs(udp_h.uh_dport); | |
884 slphdr = udp + sizeof (struct udphdr); | |
885 break; | |
886 case IPPROTO_TCP: | |
887 tcp = ip + ip_hdr_len(ip); | |
888 /* align */ | |
889 memcpy(&tcp_h, tcp, sizeof (tcp_h)); | |
890 slp_sport = ntohs(tcp_h.th_sport); | |
891 slp_dport = ntohs(tcp_h.th_dport); | |
892 slphdr = tcp + TCP_HDR_LEN(tcp); | |
893 break; | |
894 } | |
895 if (slphdr == NULL || slphdr > pkt + len) { | |
896 if (sp >= &stack[MAXSS]) | |
897 return (0); | |
898 *(++sp) = 0; | |
899 break; | |
900 } | |
901 if (slp_sport == 427 || slp_dport == 427) { | |
902 if (sp >= &stack[MAXSS]) | |
903 return (0); | |
904 *(++sp) = 1; | |
905 if (slp_sport != 427 && slp_dport == 427) | |
906 stash_slp(slp_sport); | |
907 break; | |
908 } else if (find_slp(slp_dport) != -1) { | |
909 if (valid_slp(slphdr, len)) { | |
910 if (sp >= &stack[MAXSS]) | |
911 return (0); | |
912 *(++sp) = 1; | |
913 break; | |
914 } | |
915 /* else fallthrough to reject */ | |
916 } | |
917 if (sp >= &stack[MAXSS]) | |
918 return (0); | |
919 *(++sp) = 0; | |
920 break; | |
921 case OP_OFFSET_ETHERTYPE: | |
922 /* | |
923 * Set base to the location of the ethertype. | |
924 * If the packet is VLAN tagged, move base | |
925 * to the ethertype field in the VLAN header. | |
926 * Otherwise, set it to the appropriate field | |
927 * for this link type. | |
928 */ | |
929 if (offp >= &offstack[MAXSS]) | |
930 return (0); | |
931 *++offp = base; | |
932 base = pkt + interface->network_type_offset; | |
933 if (base > pkt + len) { | |
934 /* Went too far, drop the packet */ | |
935 return (0); | |
936 } | |
937 | |
938 /* | |
939 * VLAN links are only supported on Ethernet-like | |
940 * links. | |
941 */ | |
942 if (interface->mac_type == DL_ETHER || | |
943 interface->mac_type == DL_CSMACD) { | |
944 if (ntohs(get_u16(base)) == ETHERTYPE_VLAN) { | |
945 /* | |
946 * We need to point to the | |
947 * ethertype field in the VLAN | |
948 * tag, so also move past the | |
949 * ethertype field in the | |
950 * ethernet header. | |
951 */ | |
952 base += (ENCAP_ETHERTYPE_OFF); | |
953 } | |
954 if (base > pkt + len) { | |
955 /* Went too far, drop the packet */ | |
956 return (0); | |
957 } | |
958 } | |
959 break; | |
960 } | |
961 } | |
962 | |
963 if (*sp && newrpc) | |
964 stash_rpc(&rpcmsg); | |
965 | |
966 return (*sp); | |
967 } | |
968 | |
969 static void | |
970 load_const(uint_t constval) | |
971 { | |
972 emitop(OP_LOAD_CONST); | |
973 emitval(constval); | |
974 } | |
975 | |
976 static void | |
977 load_value(int offset, int len) | |
978 { | |
979 if (offset >= 0) | |
980 load_const(offset); | |
981 | |
982 switch (len) { | |
983 case 1: | |
984 emitop(OP_LOAD_OCTET); | |
985 break; | |
986 case 2: | |
987 emitop(OP_LOAD_SHORT); | |
988 break; | |
989 case 4: | |
990 emitop(OP_LOAD_LONG); | |
991 break; | |
992 } | |
993 } | |
994 | |
995 /* | |
996 * Emit code to compare a field in | |
997 * the packet against a constant value. | |
998 */ | |
999 static void | |
1000 compare_value(uint_t offset, uint_t len, uint_t val) | |
1001 { | |
1002 load_const(val); | |
1003 load_value(offset, len); | |
1004 emitop(OP_EQ); | |
1005 } | |
1006 | |
1007 static void | |
1008 compare_addr_v4(uint_t offset, uint_t len, uint_t val) | |
1009 { | |
1010 load_const(ntohl(val)); | |
1011 load_value(offset, len); | |
1012 emitop(OP_EQ); | |
1013 } | |
1014 | |
1015 static void | |
1016 compare_addr_v6(uint_t offset, uint_t len, struct in6_addr val) | |
1017 { | |
1018 int i; | |
1019 uint32_t value; | |
1020 | |
1021 for (i = 0; i < len; i += 4) { | |
1022 value = ntohl(*(uint32_t *)&val.s6_addr[i]); | |
1023 load_const(value); | |
1024 load_value(offset + i, 4); | |
1025 emitop(OP_EQ); | |
1026 if (i != 0) | |
1027 emitop(OP_AND); | |
1028 } | |
1029 } | |
1030 | |
1031 /* | |
1032 * Same as above except do the comparison | |
1033 * after and'ing a mask value. Useful | |
1034 * for comparing IP network numbers | |
1035 */ | |
1036 static void | |
1037 compare_value_mask(uint_t offset, uint_t len, uint_t val, int mask) | |
1038 { | |
1039 load_value(offset, len); | |
1040 load_const(mask); | |
1041 emitop(OP_AND); | |
1042 load_const(val); | |
1043 emitop(OP_EQ); | |
1044 } | |
1045 | |
1046 /* Emit an operator into the code array */ | |
1047 static void | |
1048 emitop(enum optype opcode) | |
1049 { | |
1050 if (curr_op >= &oplist[MAXOPS]) | |
1051 pr_err("expression too long"); | |
1052 *curr_op++ = opcode; | |
1053 } | |
1054 | |
1055 /* | |
1056 * Remove n operators recently emitted into | |
1057 * the code array. Used by alternation(). | |
1058 */ | |
1059 static void | |
1060 unemit(int numops) | |
1061 { | |
1062 curr_op -= numops; | |
1063 } | |
1064 | |
1065 | |
1066 /* | |
1067 * Same as emitop except that we're emitting | |
1068 * a value that's not an operator. | |
1069 */ | |
1070 static void | |
1071 emitval(uint_t val) | |
1072 { | |
1073 if (curr_op >= &oplist[MAXOPS]) | |
1074 pr_err("expression too long"); | |
1075 *curr_op++ = val; | |
1076 } | |
1077 | |
1078 /* | |
1079 * Used to chain forward branches together | |
1080 * for later resolution by resolve_chain(). | |
1081 */ | |
1082 static uint_t | |
1083 chain(int p) | |
1084 { | |
1085 uint_t pos = curr_op - oplist; | |
1086 | |
1087 emitval(p); | |
1088 return (pos); | |
1089 } | |
1090 | |
1091 /* | |
1092 * Proceed backward through the code array | |
1093 * following a chain of forward references. | |
1094 * At each reference install the destination | |
1095 * branch offset. | |
1096 */ | |
1097 static void | |
1098 resolve_chain(uint_t p) | |
1099 { | |
1100 uint_t n; | |
1101 uint_t pos = curr_op - oplist; | |
1102 | |
1103 while (p) { | |
1104 n = oplist[p]; | |
1105 oplist[p] = pos; | |
1106 p = n; | |
1107 } | |
1108 } | |
1109 | |
1110 #define EQ(val) (strcmp(token, val) == 0) | |
1111 | |
1112 char *tkp, *sav_tkp; | |
1113 char *token; | |
1114 enum { EOL, ALPHA, NUMBER, FIELD, ADDR_IP, ADDR_ETHER, SPECIAL, | |
1115 ADDR_IP6, ADDR_AT } tokentype; | |
1116 uint_t tokenval; | |
1117 | |
1118 /* | |
1119 * This is the scanner. Each call returns the next | |
1120 * token in the filter expression. A token is either: | |
1121 * EOL: The end of the line - no more tokens. | |
1122 * ALPHA: A name that begins with a letter and contains | |
1123 * letters or digits, hyphens or underscores. | |
1124 * NUMBER: A number. The value can be represented as | |
1125 * a decimal value (1234) or an octal value | |
1126 * that begins with zero (066) or a hex value | |
1127 * that begins with 0x or 0X (0xff). | |
1128 * FIELD: A name followed by a left square bracket. | |
1129 * ADDR_IP: An IP address. Any sequence of digits | |
1130 * separated by dots e.g. 109.104.40.13 | |
1131 * ADDR_ETHER: An ethernet address. Any sequence of hex | |
1132 * digits separated by colons e.g. 8:0:20:0:76:39 | |
1133 * SPECIAL: A special character e.g. ">" or "(". The scanner | |
1134 * correctly handles digraphs - two special characters | |
1135 * that constitute a single token e.g. "==" or ">=". | |
1136 * ADDR_IP6: An IPv6 address. | |
1137 * | |
1138 * ADDR_AT: An AppleTalk Phase II address. A sequence of two numbers | |
1139 * separated by a dot. | |
1140 * | |
1141 * The current token is maintained in "token" and and its | |
1142 * type in "tokentype". If tokentype is NUMBER then the | |
1143 * value is held in "tokenval". | |
1144 */ | |
1145 | |
1146 static const char *namechars = | |
1147 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-."; | |
1148 static const char *numchars = "0123456789abcdefABCDEFXx:."; | |
1149 | |
1150 void | |
1151 next() | |
1152 { | |
1153 static int savechar; | |
1154 char *p; | |
1155 int size, size1; | |
1156 int base, colons, dots, alphas, double_colon; | |
1157 | |
1158 colons = 0; | |
1159 double_colon = 0; | |
1160 | |
1161 if (*tkp == '\0') { | |
1162 token = tkp; | |
1163 *tkp = savechar; | |
1164 } | |
1165 | |
1166 sav_tkp = tkp; | |
1167 | |
1168 while (isspace(*tkp)) tkp++; | |
1169 token = tkp; | |
1170 if (*token == '\0') { | |
1171 tokentype = EOL; | |
1172 return; | |
1173 } | |
1174 | |
1175 /* A token containing ':' cannot be ALPHA type */ | |
1176 tkp = token + strspn(token, numchars); | |
1177 for (p = token; p < tkp; p++) { | |
1178 if (*p == ':') { | |
1179 colons++; | |
1180 if (*(p+1) == ':') | |
1181 double_colon++; | |
1182 } | |
1183 } | |
1184 | |
1185 tkp = token; | |
1186 if (isalpha(*tkp) && !colons) { | |
1187 tokentype = ALPHA; | |
1188 tkp += strspn(tkp, namechars); | |
1189 if (*tkp == '[') { | |
1190 tokentype = FIELD; | |
1191 *tkp++ = '\0'; | |
1192 } | |
1193 } else | |
1194 | |
1195 /* | |
1196 * RFC1123 states that host names may now start with digits. Need | |
1197 * to change parser to account for this. Also, need to distinguish | |
1198 * between 1.2.3.4 and 1.2.3.a where the first case is an IP address | |
1199 * and the second is a domain name. 333aaa needs to be distinguished | |
1200 * from 0x333aaa. The first is a host name and the second is a number. | |
1201 * | |
1202 * The (colons > 1) conditional differentiates between ethernet | |
1203 * and IPv6 addresses, and an expression of the form base[expr:size], | |
1204 * which can only contain one ':' character. | |
1205 */ | |
1206 if (isdigit(*tkp) || colons > 1) { | |
1207 tkp = token + strspn(token, numchars); | |
1208 dots = alphas = 0; | |
1209 for (p = token; p < tkp; p++) { | |
1210 if (*p == '.') | |
1211 dots++; | |
1212 else if (isalpha(*p)) | |
1213 alphas = 1; | |
1214 } | |
1215 if (colons > 1) { | |
1216 if (colons == 5 && double_colon == 0) { | |
1217 tokentype = ADDR_ETHER; | |
1218 } else { | |
1219 tokentype = ADDR_IP6; | |
1220 } | |
1221 } else if (dots) { | |
1222 size = tkp - token; | |
1223 size1 = strspn(token, "0123456789."); | |
1224 if (dots == 1 && size == size1) { | |
1225 tokentype = ADDR_AT; | |
1226 } else | |
1227 if (dots != 3 || size != size1) { | |
1228 tokentype = ALPHA; | |
1229 if (*tkp != '\0' && !isspace(*tkp)) { | |
1230 tkp += strspn(tkp, namechars); | |
1231 if (*tkp == '[') { | |
1232 tokentype = FIELD; | |
1233 *tkp++ = '\0'; | |
1234 } | |
1235 } | |
1236 } else | |
1237 tokentype = ADDR_IP; | |
1238 } else if (token + strspn(token, namechars) <= tkp) { | |
1239 /* | |
1240 * With the above check, if there are more | |
1241 * characters after the last digit, assume | |
1242 * that it is not a number. | |
1243 */ | |
1244 tokentype = NUMBER; | |
1245 p = tkp; | |
1246 tkp = token; | |
1247 base = 10; | |
1248 if (*tkp == '0') { | |
1249 base = 8; | |
1250 tkp++; | |
1251 if (*tkp == 'x' || *tkp == 'X') | |
1252 base = 16; | |
1253 } | |
1254 if ((base == 10 || base == 8) && alphas) { | |
1255 tokentype = ALPHA; | |
1256 tkp = p; | |
1257 } else if (base == 16) { | |
1258 size = 2 + strspn(token+2, | |
1259 "0123456789abcdefABCDEF"); | |
1260 size1 = p - token; | |
1261 if (size != size1) { | |
1262 tokentype = ALPHA; | |
1263 tkp = p; | |
1264 } else | |
1265 /* | |
1266 * handles the case of 0x so an error message | |
1267 * is not printed. Treats 0x as 0. | |
1268 */ | |
1269 if (size == 2) { | |
1270 tokenval = 0; | |
1271 tkp = token +2; | |
1272 } else { | |
1273 tokenval = strtoul(token, &tkp, base); | |
1274 } | |
1275 } else { | |
1276 tokenval = strtoul(token, &tkp, base); | |
1277 } | |
1278 } else { | |
1279 tokentype = ALPHA; | |
1280 tkp += strspn(tkp, namechars); | |
1281 if (*tkp == '[') { | |
1282 tokentype = FIELD; | |
1283 *tkp++ = '\0'; | |
1284 } | |
1285 } | |
1286 } else { | |
1287 tokentype = SPECIAL; | |
1288 tkp++; | |
1289 if ((*token == '=' && *tkp == '=') || | |
1290 (*token == '>' && *tkp == '=') || | |
1291 (*token == '<' && *tkp == '=') || | |
1292 (*token == '!' && *tkp == '=')) | |
1293 tkp++; | |
1294 } | |
1295 | |
1296 savechar = *tkp; | |
1297 *tkp = '\0'; | |
1298 } | |
1299 | |
1300 static struct match_type { | |
1301 char *m_name; | |
1302 int m_offset; | |
1303 int m_size; | |
1304 int m_value; | |
1305 int m_depend; | |
1306 enum optype m_optype; | |
1307 } match_types[] = { | |
1308 /* | |
1309 * Table initialized assuming Ethernet data link headers. | |
1310 * m_offset is an offset beyond the offset op, which is why | |
1311 * the offset is zero for when snoop needs to check an ethertype. | |
1312 */ | |
1313 "ip", 0, 2, ETHERTYPE_IP, -1, OP_OFFSET_ETHERTYPE, | |
1314 "ip6", 0, 2, ETHERTYPE_IPV6, -1, OP_OFFSET_ETHERTYPE, | |
1315 "arp", 0, 2, ETHERTYPE_ARP, -1, OP_OFFSET_ETHERTYPE, | |
1316 "rarp", 0, 2, ETHERTYPE_REVARP, -1, OP_OFFSET_ETHERTYPE, | |
1317 "pppoed", 0, 2, ETHERTYPE_PPPOED, -1, OP_OFFSET_ETHERTYPE, | |
1318 "pppoes", 0, 2, ETHERTYPE_PPPOES, -1, OP_OFFSET_ETHERTYPE, | |
1319 "tcp", 9, 1, IPPROTO_TCP, 0, OP_OFFSET_LINK, | |
1320 "tcp", 6, 1, IPPROTO_TCP, 1, OP_OFFSET_LINK, | |
1321 "udp", 9, 1, IPPROTO_UDP, 0, OP_OFFSET_LINK, | |
1322 "udp", 6, 1, IPPROTO_UDP, 1, OP_OFFSET_LINK, | |
1323 "icmp", 9, 1, IPPROTO_ICMP, 0, OP_OFFSET_LINK, | |
1324 "icmp6", 6, 1, IPPROTO_ICMPV6, 1, OP_OFFSET_LINK, | |
1325 "ospf", 9, 1, IPPROTO_OSPF, 0, OP_OFFSET_LINK, | |
1326 "ospf", 6, 1, IPPROTO_OSPF, 1, OP_OFFSET_LINK, | |
1327 "ip-in-ip", 9, 1, IPPROTO_ENCAP, 0, OP_OFFSET_LINK, | |
1328 "esp", 9, 1, IPPROTO_ESP, 0, OP_OFFSET_LINK, | |
1329 "esp", 6, 1, IPPROTO_ESP, 1, OP_OFFSET_LINK, | |
1330 "ah", 9, 1, IPPROTO_AH, 0, OP_OFFSET_LINK, | |
1331 "ah", 6, 1, IPPROTO_AH, 1, OP_OFFSET_LINK, | |
1332 "sctp", 9, 1, IPPROTO_SCTP, 0, OP_OFFSET_LINK, | |
1333 "sctp", 6, 1, IPPROTO_SCTP, 1, OP_OFFSET_LINK, | |
1334 0, 0, 0, 0, 0, 0 | |
1335 }; | |
1336 | |
1337 static void | |
1338 generate_check(struct match_type *mtp) | |
1339 { | |
1340 /* | |
1341 * Note: this code assumes the above dependencies are | |
1342 * not cyclic. This *should* always be true. | |
1343 */ | |
1344 if (mtp->m_depend != -1) | |
1345 generate_check(&match_types[mtp->m_depend]); | |
1346 | |
1347 emitop(mtp->m_optype); | |
1348 load_value(mtp->m_offset, mtp->m_size); | |
1349 load_const(mtp->m_value); | |
1350 emitop(OP_OFFSET_POP); | |
1351 | |
1352 emitop(OP_EQ); | |
1353 | |
1354 if (mtp->m_depend != -1) | |
1355 emitop(OP_AND); | |
1356 } | |
1357 | |
1358 /* | |
1359 * Generate code based on the keyword argument. | |
1360 * This word is looked up in the match_types table | |
1361 * and checks a field within the packet for a given | |
1362 * value e.g. ether or ip type field. The match | |
1363 * can also have a dependency on another entry e.g. | |
1364 * "tcp" requires that the packet also be "ip". | |
1365 */ | |
1366 static int | |
1367 comparison(char *s) | |
1368 { | |
1369 unsigned int i, n_checks = 0; | |
1370 | |
1371 for (i = 0; match_types[i].m_name != NULL; i++) { | |
1372 | |
1373 if (strcmp(s, match_types[i].m_name) != 0) | |
1374 continue; | |
1375 | |
1376 n_checks++; | |
1377 generate_check(&match_types[i]); | |
1378 if (n_checks > 1) | |
1379 emitop(OP_OR); | |
1380 } | |
1381 | |
1382 return (n_checks > 0); | |
1383 } | |
1384 | |
1385 enum direction { ANY, TO, FROM }; | |
1386 enum direction dir; | |
1387 | |
1388 /* | |
1389 * Generate code to match an IP address. The address | |
1390 * may be supplied either as a hostname or in dotted format. | |
1391 * For source packets both the IP source address and ARP | |
1392 * src are checked. | |
1393 * Note: we don't check packet type here - whether IP or ARP. | |
1394 * It's possible that we'll do an improper match. | |
1395 */ | |
1396 static void | |
1397 ipaddr_match(enum direction which, char *hostname, int inet_type) | |
1398 { | |
1399 bool_t found_host; | |
1400 int m = 0, n = 0; | |
1401 uint_t *addr4ptr; | |
1402 uint_t addr4; | |
1403 struct in6_addr *addr6ptr; | |
1404 int h_addr_index; | |
1405 struct hostent *hp = NULL; | |
1406 int error_num = 0; | |
1407 boolean_t freehp = B_FALSE; | |
1408 boolean_t first = B_TRUE; | |
1409 | |
1410 /* | |
1411 * The addr4offset and addr6offset variables simplify the code which | |
1412 * generates the address comparison filter. With these two variables, | |
1413 * duplicate code need not exist for the TO and FROM case. | |
1414 * A value of -1 describes the ANY case (TO and FROM). | |
1415 */ | |
1416 int addr4offset; | |
1417 int addr6offset; | |
1418 | |
1419 found_host = 0; | |
1420 | |
1421 if (tokentype == ADDR_IP) { | |
1422 hp = lgetipnodebyname(hostname, AF_INET, | |
1423 0, &error_num); | |
1424 if (hp == NULL) { | |
1425 hp = getipnodebyname(hostname, AF_INET, | |
1426 0, &error_num); | |
1427 freehp = 1; | |
1428 } | |
1429 if (hp == NULL) { | |
1430 if (error_num == TRY_AGAIN) { | |
1431 pr_err("couldn't resolve %s (try again later)", | |
1432 hostname); | |
1433 } else { | |
1434 pr_err("couldn't resolve %s", hostname); | |
1435 } | |
1436 } | |
1437 inet_type = IPV4_ONLY; | |
1438 } else if (tokentype == ADDR_IP6) { | |
1439 hp = lgetipnodebyname(hostname, AF_INET6, | |
1440 0, &error_num); | |
1441 if (hp == NULL) { | |
1442 hp = getipnodebyname(hostname, AF_INET6, | |
1443 0, &error_num); | |
1444 freehp = 1; | |
1445 } | |
1446 if (hp == NULL) { | |
1447 if (error_num == TRY_AGAIN) { | |
1448 pr_err("couldn't resolve %s (try again later)", | |
1449 hostname); | |
1450 } else { | |
1451 pr_err("couldn't resolve %s", hostname); | |
1452 } | |
1453 } | |
1454 inet_type = IPV6_ONLY; | |
1455 } else { | |
1456 /* Some hostname i.e. tokentype is ALPHA */ | |
1457 switch (inet_type) { | |
1458 case IPV4_ONLY: | |
1459 /* Only IPv4 address is needed */ | |
1460 hp = lgetipnodebyname(hostname, AF_INET, | |
1461 0, &error_num); | |
1462 if (hp == NULL) { | |
1463 hp = getipnodebyname(hostname, AF_INET, | |
1464 0, &error_num); | |
1465 freehp = 1; | |
1466 } | |
1467 if (hp != NULL) { | |
1468 found_host = 1; | |
1469 } | |
1470 break; | |
1471 case IPV6_ONLY: | |
1472 /* Only IPv6 address is needed */ | |
1473 hp = lgetipnodebyname(hostname, AF_INET6, | |
1474 0, &error_num); | |
1475 if (hp == NULL) { | |
1476 hp = getipnodebyname(hostname, AF_INET6, | |
1477 0, &error_num); | |
1478 freehp = 1; | |
1479 } | |
1480 if (hp != NULL) { | |
1481 found_host = 1; | |
1482 } | |
1483 break; | |
1484 case IPV4_AND_IPV6: | |
1485 /* Both IPv4 and IPv6 are needed */ | |
1486 hp = lgetipnodebyname(hostname, AF_INET6, | |
1487 AI_ALL | AI_V4MAPPED, &error_num); | |
1488 if (hp == NULL) { | |
1489 hp = getipnodebyname(hostname, AF_INET6, | |
1490 AI_ALL | AI_V4MAPPED, &error_num); | |
1491 freehp = 1; | |
1492 } | |
1493 if (hp != NULL) { | |
1494 found_host = 1; | |
1495 } | |
1496 break; | |
1497 default: | |
1498 found_host = 0; | |
1499 } | |
1500 | |
1501 if (!found_host) { | |
1502 if (error_num == TRY_AGAIN) { | |
1503 pr_err("could not resolve %s (try again later)", | |
1504 hostname); | |
1505 } else { | |
1506 pr_err("could not resolve %s", hostname); | |
1507 } | |
1508 } | |
1509 } | |
1510 | |
1511 switch (which) { | |
1512 case TO: | |
1513 addr4offset = IPV4_DSTADDR_OFFSET; | |
1514 addr6offset = IPV6_DSTADDR_OFFSET; | |
1515 break; | |
1516 case FROM: | |
1517 addr4offset = IPV4_SRCADDR_OFFSET; | |
1518 addr6offset = IPV6_SRCADDR_OFFSET; | |
1519 break; | |
1520 case ANY: | |
1521 addr4offset = -1; | |
1522 addr6offset = -1; | |
1523 break; | |
1524 } | |
1525 | |
1526 /* | |
1527 * The code below generates the filter. | |
1528 */ | |
1529 if (hp != NULL && hp->h_addrtype == AF_INET) { | |
1530 ethertype_match(ETHERTYPE_IP); | |
1531 emitop(OP_BRFL); | |
1532 n = chain(n); | |
1533 emitop(OP_OFFSET_LINK); | |
1534 h_addr_index = 0; | |
1535 addr4ptr = (uint_t *)hp->h_addr_list[h_addr_index]; | |
1536 while (addr4ptr != NULL) { | |
1537 if (addr4offset == -1) { | |
1538 compare_addr_v4(IPV4_SRCADDR_OFFSET, 4, | |
1539 *addr4ptr); | |
1540 emitop(OP_BRTR); | |
1541 m = chain(m); | |
1542 compare_addr_v4(IPV4_DSTADDR_OFFSET, 4, | |
1543 *addr4ptr); | |
1544 } else { | |
1545 compare_addr_v4(addr4offset, 4, *addr4ptr); | |
1546 } | |
1547 addr4ptr = (uint_t *)hp->h_addr_list[++h_addr_index]; | |
1548 if (addr4ptr != NULL) { | |
1549 emitop(OP_BRTR); | |
1550 m = chain(m); | |
1551 } | |
1552 } | |
1553 if (m != 0) { | |
1554 resolve_chain(m); | |
1555 } | |
1556 emitop(OP_OFFSET_POP); | |
1557 resolve_chain(n); | |
1558 } else { | |
1559 /* first pass: IPv4 addresses */ | |
1560 h_addr_index = 0; | |
1561 addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index]; | |
1562 first = B_TRUE; | |
1563 while (addr6ptr != NULL) { | |
1564 if (IN6_IS_ADDR_V4MAPPED(addr6ptr)) { | |
1565 if (first) { | |
1566 ethertype_match(ETHERTYPE_IP); | |
1567 emitop(OP_BRFL); | |
1568 n = chain(n); | |
1569 emitop(OP_OFFSET_LINK); | |
1570 first = B_FALSE; | |
1571 } else { | |
1572 emitop(OP_BRTR); | |
1573 m = chain(m); | |
1574 } | |
1575 IN6_V4MAPPED_TO_INADDR(addr6ptr, | |
1576 (struct in_addr *)&addr4); | |
1577 if (addr4offset == -1) { | |
1578 compare_addr_v4(IPV4_SRCADDR_OFFSET, 4, | |
1579 addr4); | |
1580 emitop(OP_BRTR); | |
1581 m = chain(m); | |
1582 compare_addr_v4(IPV4_DSTADDR_OFFSET, 4, | |
1583 addr4); | |
1584 } else { | |
1585 compare_addr_v4(addr4offset, 4, addr4); | |
1586 } | |
1587 } | |
1588 addr6ptr = (struct in6_addr *) | |
1589 hp->h_addr_list[++h_addr_index]; | |
1590 } | |
1591 /* second pass: IPv6 addresses */ | |
1592 h_addr_index = 0; | |
1593 addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index]; | |
1594 first = B_TRUE; | |
1595 while (addr6ptr != NULL) { | |
1596 if (!IN6_IS_ADDR_V4MAPPED(addr6ptr)) { | |
1597 if (first) { | |
1598 /* | |
1599 * bypass check for IPv6 addresses | |
1600 * when we have an IPv4 packet | |
1601 */ | |
1602 if (n != 0) { | |
1603 emitop(OP_BRTR); | |
1604 m = chain(m); | |
1605 emitop(OP_BRFL); | |
1606 m = chain(m); | |
1607 resolve_chain(n); | |
1608 n = 0; | |
1609 } | |
1610 ethertype_match(ETHERTYPE_IPV6); | |
1611 emitop(OP_BRFL); | |
1612 n = chain(n); | |
1613 emitop(OP_OFFSET_LINK); | |
1614 first = B_FALSE; | |
1615 } else { | |
1616 emitop(OP_BRTR); | |
1617 m = chain(m); | |
1618 } | |
1619 if (addr6offset == -1) { | |
1620 compare_addr_v6(IPV6_SRCADDR_OFFSET, | |
1621 16, *addr6ptr); | |
1622 emitop(OP_BRTR); | |
1623 m = chain(m); | |
1624 compare_addr_v6(IPV6_DSTADDR_OFFSET, | |
1625 16, *addr6ptr); | |
1626 } else { | |
1627 compare_addr_v6(addr6offset, 16, | |
1628 *addr6ptr); | |
1629 } | |
1630 } | |
1631 addr6ptr = (struct in6_addr *) | |
1632 hp->h_addr_list[++h_addr_index]; | |
1633 } | |
1634 if (m != 0) { | |
1635 resolve_chain(m); | |
1636 } | |
1637 emitop(OP_OFFSET_POP); | |
1638 resolve_chain(n); | |
1639 } | |
1640 | |
1641 /* only free struct hostent returned by getipnodebyname() */ | |
1642 if (freehp) { | |
1643 freehostent(hp); | |
1644 } | |
1645 } | |
1646 | |
1647 /* | |
1648 * Generate code to match an AppleTalk address. The address | |
1649 * must be given as two numbers with a dot between | |
1650 * | |
1651 */ | |
1652 static void | |
1653 ataddr_match(enum direction which, char *hostname) | |
1654 { | |
1655 uint_t net; | |
1656 uint_t node; | |
1657 uint_t m, n; | |
1658 | |
1659 sscanf(hostname, "%u.%u", &net, &node); | |
1660 | |
1661 emitop(OP_OFFSET_LINK); | |
1662 switch (which) { | |
1663 case TO: | |
1664 compare_value(AT_DST_NET_OFFSET, 2, net); | |
1665 emitop(OP_BRFL); | |
1666 m = chain(0); | |
1667 compare_value(AT_DST_NODE_OFFSET, 1, node); | |
1668 resolve_chain(m); | |
1669 break; | |
1670 case FROM: | |
1671 compare_value(AT_SRC_NET_OFFSET, 2, net); | |
1672 emitop(OP_BRFL); | |
1673 m = chain(0); | |
1674 compare_value(AT_SRC_NODE_OFFSET, 1, node); | |
1675 resolve_chain(m); | |
1676 break; | |
1677 case ANY: | |
1678 compare_value(AT_DST_NET_OFFSET, 2, net); | |
1679 emitop(OP_BRFL); | |
1680 m = chain(0); | |
1681 compare_value(AT_DST_NODE_OFFSET, 1, node); | |
1682 resolve_chain(m); | |
1683 emitop(OP_BRTR); | |
1684 n = chain(0); | |
1685 compare_value(AT_SRC_NET_OFFSET, 2, net); | |
1686 emitop(OP_BRFL); | |
1687 m = chain(0); | |
1688 compare_value(AT_SRC_NODE_OFFSET, 1, node); | |
1689 resolve_chain(m); | |
1690 resolve_chain(n); | |
1691 break; | |
1692 } | |
1693 emitop(OP_OFFSET_POP); | |
1694 } | |
1695 | |
1696 /* | |
1697 * Compare ethernet addresses. The address may | |
1698 * be provided either as a hostname or as a | |
1699 * 6 octet colon-separated address. | |
1700 */ | |
1701 static void | |
1702 etheraddr_match(enum direction which, char *hostname) | |
1703 { | |
1704 uint_t addr; | |
1705 ushort_t *addrp; | |
1706 int to_offset, from_offset; | |
1707 struct ether_addr e, *ep = NULL; | |
1708 int m; | |
1709 | |
1710 /* | |
1711 * First, check the interface type for whether src/dest address | |
1712 * is determinable; if not, retreat early. | |
1713 */ | |
1714 switch (interface->mac_type) { | |
1715 case DL_ETHER: | |
1716 from_offset = ETHERADDRL; | |
1717 to_offset = 0; | |
1718 break; | |
1719 | |
1720 case DL_IB: | |
1721 /* | |
1722 * If an ethernet address is attempted to be used | |
1723 * on an IPoIB interface, flag error. Link address | |
1724 * based filtering is unsupported on IPoIB, so there | |
1725 * is no ipibaddr_match() or parsing support for IPoIB | |
1726 * 20 byte link addresses. | |
1727 */ | |
1728 pr_err("filter option unsupported on media"); | |
1729 break; | |
1730 | |
1731 case DL_FDDI: | |
1732 from_offset = 7; | |
1733 to_offset = 1; | |
1734 break; | |
1735 | |
1736 default: | |
1737 /* | |
1738 * Where do we find "ether" address for FDDI & TR? | |
1739 * XXX can improve? ~sparker | |
1740 */ | |
1741 load_const(1); | |
1742 return; | |
1743 } | |
1744 | |
1745 if (isxdigit(*hostname)) | |
1746 ep = ether_aton(hostname); | |
1747 if (ep == NULL) { | |
1748 if (ether_hostton(hostname, &e)) | |
1749 if (!arp_for_ether(hostname, &e)) | |
1750 pr_err("cannot obtain ether addr for %s", | |
1751 hostname); | |
1752 ep = &e; | |
1753 } | |
1754 memcpy(&addr, (ushort_t *)ep, 4); | |
1755 addrp = (ushort_t *)ep + 2; | |
1756 | |
1757 emitop(OP_OFFSET_ZERO); | |
1758 switch (which) { | |
1759 case TO: | |
1760 compare_value(to_offset, 4, ntohl(addr)); | |
1761 emitop(OP_BRFL); | |
1762 m = chain(0); | |
1763 compare_value(to_offset + 4, 2, ntohs(*addrp)); | |
1764 resolve_chain(m); | |
1765 break; | |
1766 case FROM: | |
1767 compare_value(from_offset, 4, ntohl(addr)); | |
1768 emitop(OP_BRFL); | |
1769 m = chain(0); | |
1770 compare_value(from_offset + 4, 2, ntohs(*addrp)); | |
1771 resolve_chain(m); | |
1772 break; | |
1773 case ANY: | |
1774 compare_value(to_offset, 4, ntohl(addr)); | |
1775 compare_value(to_offset + 4, 2, ntohs(*addrp)); | |
1776 emitop(OP_AND); | |
1777 emitop(OP_BRTR); | |
1778 m = chain(0); | |
1779 | |
1780 compare_value(from_offset, 4, ntohl(addr)); | |
1781 compare_value(from_offset + 4, 2, ntohs(*addrp)); | |
1782 emitop(OP_AND); | |
1783 resolve_chain(m); | |
1784 break; | |
1785 } | |
1786 emitop(OP_OFFSET_POP); | |
1787 } | |
1788 | |
1789 static void | |
1790 ethertype_match(int val) | |
1791 { | |
1792 int ether_offset = interface->network_type_offset; | |
1793 | |
1794 /* | |
1795 * If the user is interested in ethertype VLAN, | |
1796 * then we need to set the offset to the beginning of the packet. | |
1797 * But if the user is interested in another ethertype, | |
1798 * such as IPv4, then we need to take into consideration | |
1799 * the fact that the packet might be VLAN tagged. | |
1800 */ | |
1801 if (interface->mac_type == DL_ETHER || | |
1802 interface->mac_type == DL_CSMACD) { | |
1803 if (val != ETHERTYPE_VLAN) { | |
1804 /* | |
1805 * OP_OFFSET_ETHERTYPE puts us at the ethertype | |
1806 * field whether or not there is a VLAN tag, | |
1807 * so ether_offset goes to zero if we get here. | |
1808 */ | |
1809 emitop(OP_OFFSET_ETHERTYPE); | |
1810 ether_offset = 0; | |
1811 } else { | |
1812 emitop(OP_OFFSET_ZERO); | |
1813 } | |
1814 } | |
1815 compare_value(ether_offset, 2, val); | |
1816 if (interface->mac_type == DL_ETHER || | |
1817 interface->mac_type == DL_CSMACD) { | |
1818 emitop(OP_OFFSET_POP); | |
1819 } | |
1820 } | |
1821 | |
1822 /* | |
1823 * Match a network address. The host part | |
1824 * is masked out. The network address may | |
1825 * be supplied either as a netname or in | |
1826 * IP dotted format. The mask to be used | |
1827 * for the comparison is assumed from the | |
1828 * address format (see comment below). | |
1829 */ | |
1830 static void | |
1831 netaddr_match(enum direction which, char *netname) | |
1832 { | |
1833 uint_t addr; | |
1834 uint_t mask = 0xff000000; | |
1835 uint_t m; | |
1836 struct netent *np; | |
1837 | |
1838 if (isdigit(*netname)) { | |
1839 addr = inet_network(netname); | |
1840 } else { | |
1841 np = getnetbyname(netname); | |
1842 if (np == NULL) | |
1843 pr_err("net %s not known", netname); | |
1844 addr = np->n_net; | |
1845 } | |
1846 | |
1847 /* | |
1848 * Left justify the address and figure | |
1849 * out a mask based on the supplied address. | |
1850 * Set the mask according to the number of zero | |
1851 * low-order bytes. | |
1852 * Note: this works only for whole octet masks. | |
1853 */ | |
1854 if (addr) { | |
1855 while ((addr & ~mask) != 0) { | |
1856 mask |= (mask >> 8); | |
1857 } | |
1858 } | |
1859 | |
1860 emitop(OP_OFFSET_LINK); | |
1861 switch (which) { | |
1862 case TO: | |
1863 compare_value_mask(16, 4, addr, mask); | |
1864 break; | |
1865 case FROM: | |
1866 compare_value_mask(12, 4, addr, mask); | |
1867 break; | |
1868 case ANY: | |
1869 compare_value_mask(12, 4, addr, mask); | |
1870 emitop(OP_BRTR); | |
1871 m = chain(0); | |
1872 compare_value_mask(16, 4, addr, mask); | |
1873 resolve_chain(m); | |
1874 break; | |
1875 } | |
1876 emitop(OP_OFFSET_POP); | |
1877 } | |
1878 | |
1879 /* | |
1880 * Match either a UDP or TCP port number. | |
1881 * The port number may be provided either as | |
1882 * port name as listed in /etc/services ("nntp") or as | |
1883 * the port number itself (2049). | |
1884 */ | |
1885 static void | |
1886 port_match(enum direction which, char *portname) | |
1887 { | |
1888 struct servent *sp; | |
1889 uint_t m, port; | |
1890 | |
1891 if (isdigit(*portname)) { | |
1892 port = atoi(portname); | |
1893 } else { | |
1894 sp = getservbyname(portname, NULL); | |
1895 if (sp == NULL) | |
1896 pr_err("invalid port number or name: %s", | |
1897 portname); | |
1898 port = ntohs(sp->s_port); | |
1899 } | |
1900 | |
1901 emitop(OP_OFFSET_IP); | |
1902 | |
1903 switch (which) { | |
1904 case TO: | |
1905 compare_value(2, 2, port); | |
1906 break; | |
1907 case FROM: | |
1908 compare_value(0, 2, port); | |
1909 break; | |
1910 case ANY: | |
1911 compare_value(2, 2, port); | |
1912 emitop(OP_BRTR); | |
1913 m = chain(0); | |
1914 compare_value(0, 2, port); | |
1915 resolve_chain(m); | |
1916 break; | |
1917 } | |
1918 emitop(OP_OFFSET_POP); | |
1919 } | |
1920 | |
1921 /* | |
1922 * Generate code to match packets with a specific | |
1923 * RPC program number. If the progname is a name | |
1924 * it is converted to a number via /etc/rpc. | |
1925 * The program version and/or procedure may be provided | |
1926 * as extra qualifiers. | |
1927 */ | |
1928 static void | |
1929 rpc_match_prog(enum direction which, char *progname, int vers, int proc) | |
1930 { | |
1931 struct rpcent *rpc; | |
1932 uint_t prog; | |
1933 uint_t m, n; | |
1934 | |
1935 if (isdigit(*progname)) { | |
1936 prog = atoi(progname); | |
1937 } else { | |
1938 rpc = (struct rpcent *)getrpcbyname(progname); | |
1939 if (rpc == NULL) | |
1940 pr_err("invalid program name: %s", progname); | |
1941 prog = rpc->r_number; | |
1942 } | |
1943 | |
1944 emitop(OP_OFFSET_RPC); | |
1945 emitop(OP_BRFL); | |
1946 n = chain(0); | |
1947 | |
1948 compare_value(12, 4, prog); | |
1949 emitop(OP_BRFL); | |
1950 m = chain(0); | |
1951 if (vers >= 0) { | |
1952 compare_value(16, 4, vers); | |
1953 emitop(OP_BRFL); | |
1954 m = chain(m); | |
1955 } | |
1956 if (proc >= 0) { | |
1957 compare_value(20, 4, proc); | |
1958 emitop(OP_BRFL); | |
1959 m = chain(m); | |
1960 } | |
1961 | |
1962 switch (which) { | |
1963 case TO: | |
1964 compare_value(4, 4, CALL); | |
1965 emitop(OP_BRFL); | |
1966 m = chain(m); | |
1967 break; | |
1968 case FROM: | |
1969 compare_value(4, 4, REPLY); | |
1970 emitop(OP_BRFL); | |
1971 m = chain(m); | |
1972 break; | |
1973 } | |
1974 resolve_chain(m); | |
1975 resolve_chain(n); | |
1976 emitop(OP_OFFSET_POP); | |
1977 } | |
1978 | |
1979 /* | |
1980 * Generate code to parse a field specification | |
1981 * and load the value of the field from the packet | |
1982 * onto the operand stack. | |
1983 * The field offset may be specified relative to the | |
1984 * beginning of the ether header, IP header, UDP header, | |
1985 * or TCP header. An optional size specification may | |
1986 * be provided following a colon. If no size is given | |
1987 * one byte is assumed e.g. | |
1988 * | |
1989 * ether[0] The first byte of the ether header | |
1990 * ip[2:2] The second 16 bit field of the IP header | |
1991 */ | |
1992 static void | |
1993 load_field() | |
1994 { | |
1995 int size = 1; | |
1996 int s; | |
1997 | |
1998 | |
1999 if (EQ("ether")) | |
2000 emitop(OP_OFFSET_ZERO); | |
2001 else if (EQ("ip") || EQ("ip6") || EQ("pppoed") || EQ("pppoes")) | |
2002 emitop(OP_OFFSET_LINK); | |
2003 else if (EQ("udp") || EQ("tcp") || EQ("icmp") || EQ("ip-in-ip") || | |
2004 EQ("ah") || EQ("esp")) | |
2005 emitop(OP_OFFSET_IP); | |
2006 else | |
2007 pr_err("invalid field type"); | |
2008 next(); | |
2009 s = opstack; | |
2010 expression(); | |
2011 if (opstack != s + 1) | |
2012 pr_err("invalid field offset"); | |
2013 opstack--; | |
2014 if (*token == ':') { | |
2015 next(); | |
2016 if (tokentype != NUMBER) | |
2017 pr_err("field size expected"); | |
2018 size = tokenval; | |
2019 if (size != 1 && size != 2 && size != 4) | |
2020 pr_err("field size invalid"); | |
2021 next(); | |
2022 } | |
2023 if (*token != ']') | |
2024 pr_err("right bracket expected"); | |
2025 | |
2026 load_value(-1, size); | |
2027 emitop(OP_OFFSET_POP); | |
2028 } | |
2029 | |
2030 /* | |
2031 * Check that the operand stack | |
2032 * contains n arguments | |
2033 */ | |
2034 static void | |
2035 checkstack(int numargs) | |
2036 { | |
2037 if (opstack != numargs) | |
2038 pr_err("invalid expression at \"%s\".", token); | |
2039 } | |
2040 | |
2041 static void | |
2042 primary() | |
2043 { | |
2044 int m, m2, s; | |
2045 | |
2046 for (;;) { | |
2047 if (tokentype == FIELD) { | |
2048 load_field(); | |
2049 opstack++; | |
2050 next(); | |
2051 break; | |
2052 } | |
2053 | |
2054 if (comparison(token)) { | |
2055 opstack++; | |
2056 next(); | |
2057 break; | |
2058 } | |
2059 | |
2060 if (EQ("not") || EQ("!")) { | |
2061 next(); | |
2062 s = opstack; | |
2063 primary(); | |
2064 checkstack(s + 1); | |
2065 emitop(OP_NOT); | |
2066 break; | |
2067 } | |
2068 | |
2069 if (EQ("(")) { | |
2070 next(); | |
2071 s = opstack; | |
2072 expression(); | |
2073 checkstack(s + 1); | |
2074 if (!EQ(")")) | |
2075 pr_err("right paren expected"); | |
2076 next(); | |
2077 } | |
2078 | |
2079 if (EQ("to") || EQ("dst")) { | |
2080 dir = TO; | |
2081 next(); | |
2082 continue; | |
2083 } | |
2084 | |
2085 if (EQ("from") || EQ("src")) { | |
2086 dir = FROM; | |
2087 next(); | |
2088 continue; | |
2089 } | |
2090 | |
2091 if (EQ("ether")) { | |
2092 eaddr = 1; | |
2093 next(); | |
2094 continue; | |
2095 } | |
2096 | |
2097 if (EQ("proto")) { | |
2098 next(); | |
2099 if (tokentype != NUMBER) | |
2100 pr_err("IP proto type expected"); | |
2101 emitop(OP_OFFSET_LINK); | |
2102 compare_value(IPV4_TYPE_HEADER_OFFSET, 1, tokenval); | |
2103 emitop(OP_OFFSET_POP); | |
2104 opstack++; | |
2105 next(); | |
2106 continue; | |
2107 } | |
2108 | |
2109 if (EQ("broadcast")) { | |
2110 /* | |
2111 * Be tricky: FDDI ether dst address begins at | |
2112 * byte one. Since the address is really six | |
2113 * bytes long, this works for FDDI & ethernet. | |
2114 * XXX - Token ring? | |
2115 */ | |
2116 emitop(OP_OFFSET_ZERO); | |
2117 if (interface->mac_type == DL_IB) | |
2118 pr_err("filter option unsupported on media"); | |
2119 compare_value(1, 4, 0xffffffff); | |
2120 emitop(OP_OFFSET_POP); | |
2121 opstack++; | |
2122 next(); | |
2123 break; | |
2124 } | |
2125 | |
2126 if (EQ("multicast")) { | |
2127 /* XXX Token ring? */ | |
2128 emitop(OP_OFFSET_ZERO); | |
2129 if (interface->mac_type == DL_FDDI) { | |
2130 compare_value_mask(1, 1, 0x01, 0x01); | |
2131 } else if (interface->mac_type == DL_IB) { | |
2132 pr_err("filter option unsupported on media"); | |
2133 } else { | |
2134 compare_value_mask(0, 1, 0x01, 0x01); | |
2135 } | |
2136 emitop(OP_OFFSET_POP); | |
2137 opstack++; | |
2138 next(); | |
2139 break; | |
2140 } | |
2141 | |
2142 if (EQ("decnet")) { | |
2143 /* XXX Token ring? */ | |
2144 if (interface->mac_type == DL_FDDI) { | |
2145 load_value(19, 2); /* ether type */ | |
2146 load_const(0x6000); | |
2147 emitop(OP_GE); | |
2148 emitop(OP_BRFL); | |
2149 m = chain(0); | |
2150 load_value(19, 2); /* ether type */ | |
2151 load_const(0x6009); | |
2152 emitop(OP_LE); | |
2153 resolve_chain(m); | |
2154 } else { | |
2155 emitop(OP_OFFSET_ETHERTYPE); | |
2156 load_value(0, 2); /* ether type */ | |
2157 load_const(0x6000); | |
2158 emitop(OP_GE); | |
2159 emitop(OP_BRFL); | |
2160 m = chain(0); | |
2161 load_value(0, 2); /* ether type */ | |
2162 load_const(0x6009); | |
2163 emitop(OP_LE); | |
2164 resolve_chain(m); | |
2165 emitop(OP_OFFSET_POP); | |
2166 } | |
2167 opstack++; | |
2168 next(); | |
2169 break; | |
2170 } | |
2171 | |
2172 if (EQ("vlan-id")) { | |
2173 next(); | |
2174 if (tokentype != NUMBER) | |
2175 pr_err("vlan id expected"); | |
2176 emitop(OP_OFFSET_ZERO); | |
2177 ethertype_match(ETHERTYPE_VLAN); | |
2178 emitop(OP_BRFL); | |
2179 m = chain(0); | |
2180 compare_value_mask(VLAN_ID_OFFSET, 2, tokenval, | |
2181 VLAN_ID_MASK); | |
2182 resolve_chain(m); | |
2183 emitop(OP_OFFSET_POP); | |
2184 opstack++; | |
2185 next(); | |
2186 break; | |
2187 } | |
2188 | |
2189 if (EQ("apple")) { | |
2190 /* | |
2191 * Appletalk also appears in 802.2 | |
2192 * packets, so check for the ethertypes | |
2193 * at offset 12 and 20 in the MAC header. | |
2194 */ | |
2195 ethertype_match(ETHERTYPE_AT); | |
2196 emitop(OP_BRTR); | |
2197 m = chain(0); | |
2198 ethertype_match(ETHERTYPE_AARP); | |
2199 emitop(OP_BRTR); | |
2200 m = chain(m); | |
2201 compare_value(20, 2, ETHERTYPE_AT); /* 802.2 */ | |
2202 emitop(OP_BRTR); | |
2203 m = chain(m); | |
2204 compare_value(20, 2, ETHERTYPE_AARP); /* 802.2 */ | |
2205 resolve_chain(m); | |
2206 opstack++; | |
2207 next(); | |
2208 break; | |
2209 } | |
2210 | |
2211 if (EQ("vlan")) { | |
2212 ethertype_match(ETHERTYPE_VLAN); | |
2213 compare_value_mask(VLAN_ID_OFFSET, 2, 0, VLAN_ID_MASK); | |
2214 emitop(OP_NOT); | |
2215 emitop(OP_AND); | |
2216 opstack++; | |
2217 next(); | |
2218 break; | |
2219 } | |
2220 | |
2221 if (EQ("bootp") || EQ("dhcp")) { | |
2222 ethertype_match(ETHERTYPE_IP); | |
2223 emitop(OP_BRFL); | |
2224 m = chain(0); | |
2225 emitop(OP_OFFSET_LINK); | |
2226 compare_value(9, 1, IPPROTO_UDP); | |
2227 emitop(OP_OFFSET_POP); | |
2228 emitop(OP_BRFL); | |
2229 m = chain(m); | |
2230 emitop(OP_OFFSET_IP); | |
2231 compare_value(0, 4, | |
2232 (IPPORT_BOOTPS << 16) | IPPORT_BOOTPC); | |
2233 emitop(OP_BRTR); | |
2234 m2 = chain(0); | |
2235 compare_value(0, 4, | |
2236 (IPPORT_BOOTPC << 16) | IPPORT_BOOTPS); | |
2237 resolve_chain(m2); | |
2238 emitop(OP_OFFSET_POP); | |
2239 resolve_chain(m); | |
2240 opstack++; | |
2241 dir = ANY; | |
2242 next(); | |
2243 break; | |
2244 } | |
2245 | |
2246 if (EQ("dhcp6")) { | |
2247 ethertype_match(ETHERTYPE_IPV6); | |
2248 emitop(OP_BRFL); | |
2249 m = chain(0); | |
2250 emitop(OP_OFFSET_LINK); | |
2251 compare_value(6, 1, IPPROTO_UDP); | |
2252 emitop(OP_OFFSET_POP); | |
2253 emitop(OP_BRFL); | |
2254 m = chain(m); | |
2255 emitop(OP_OFFSET_IP); | |
2256 compare_value(2, 2, IPPORT_DHCPV6S); | |
2257 emitop(OP_BRTR); | |
2258 m2 = chain(0); | |
2259 compare_value(2, 2, IPPORT_DHCPV6C); | |
2260 resolve_chain(m2); | |
2261 emitop(OP_OFFSET_POP); | |
2262 resolve_chain(m); | |
2263 opstack++; | |
2264 dir = ANY; | |
2265 next(); | |
2266 break; | |
2267 } | |
2268 | |
2269 if (EQ("ethertype")) { | |
2270 next(); | |
2271 if (tokentype != NUMBER) | |
2272 pr_err("ether type expected"); | |
2273 ethertype_match(tokenval); | |
2274 opstack++; | |
2275 next(); | |
2276 break; | |
2277 } | |
2278 | |
2279 if (EQ("pppoe")) { | |
2280 ethertype_match(ETHERTYPE_PPPOED); | |
2281 ethertype_match(ETHERTYPE_PPPOES); | |
2282 emitop(OP_OR); | |
2283 opstack++; | |
2284 next(); | |
2285 break; | |
2286 } | |
2287 | |
2288 if (EQ("inet")) { | |
2289 next(); | |
2290 if (EQ("host")) | |
2291 next(); | |
2292 if (tokentype != ALPHA && tokentype != ADDR_IP) | |
2293 pr_err("host/IPv4 addr expected after inet"); | |
2294 ipaddr_match(dir, token, IPV4_ONLY); | |
2295 opstack++; | |
2296 next(); | |
2297 break; | |
2298 } | |
2299 | |
2300 if (EQ("inet6")) { | |
2301 next(); | |
2302 if (EQ("host")) | |
2303 next(); | |
2304 if (tokentype != ALPHA && tokentype != ADDR_IP6) | |
2305 pr_err("host/IPv6 addr expected after inet6"); | |
2306 ipaddr_match(dir, token, IPV6_ONLY); | |
2307 opstack++; | |
2308 next(); | |
2309 break; | |
2310 } | |
2311 | |
2312 if (EQ("length")) { | |
2313 emitop(OP_LOAD_LENGTH); | |
2314 opstack++; | |
2315 next(); | |
2316 break; | |
2317 } | |
2318 | |
2319 if (EQ("less")) { | |
2320 next(); | |
2321 if (tokentype != NUMBER) | |
2322 pr_err("packet length expected"); | |
2323 emitop(OP_LOAD_LENGTH); | |
2324 load_const(tokenval); | |
2325 emitop(OP_LT); | |
2326 opstack++; | |
2327 next(); | |
2328 break; | |
2329 } | |
2330 | |
2331 if (EQ("greater")) { | |
2332 next(); | |
2333 if (tokentype != NUMBER) | |
2334 pr_err("packet length expected"); | |
2335 emitop(OP_LOAD_LENGTH); | |
2336 load_const(tokenval); | |
2337 emitop(OP_GT); | |
2338 opstack++; | |
2339 next(); | |
2340 break; | |
2341 } | |
2342 | |
2343 if (EQ("nofrag")) { | |
2344 emitop(OP_OFFSET_LINK); | |
2345 compare_value_mask(6, 2, 0, 0x1fff); | |
2346 emitop(OP_OFFSET_POP); | |
2347 emitop(OP_BRFL); | |
2348 m = chain(0); | |
2349 ethertype_match(ETHERTYPE_IP); | |
2350 resolve_chain(m); | |
2351 opstack++; | |
2352 next(); | |
2353 break; | |
2354 } | |
2355 | |
2356 if (EQ("net") || EQ("dstnet") || EQ("srcnet")) { | |
2357 if (EQ("dstnet")) | |
2358 dir = TO; | |
2359 else if (EQ("srcnet")) | |
2360 dir = FROM; | |
2361 next(); | |
2362 netaddr_match(dir, token); | |
2363 dir = ANY; | |
2364 opstack++; | |
2365 next(); | |
2366 break; | |
2367 } | |
2368 | |
2369 if (EQ("port") || EQ("srcport") || EQ("dstport")) { | |
2370 if (EQ("dstport")) | |
2371 dir = TO; | |
2372 else if (EQ("srcport")) | |
2373 dir = FROM; | |
2374 next(); | |
2375 port_match(dir, token); | |
2376 dir = ANY; | |
2377 opstack++; | |
2378 next(); | |
2379 break; | |
2380 } | |
2381 | |
2382 if (EQ("rpc")) { | |
2383 uint_t vers, proc; | |
2384 char savetoken[32]; | |
2385 | |
2386 vers = proc = -1; | |
2387 next(); | |
2388 (void) strlcpy(savetoken, token, sizeof (savetoken)); | |
2389 next(); | |
2390 if (*token == ',') { | |
2391 next(); | |
2392 if (tokentype != NUMBER) | |
2393 pr_err("version number expected"); | |
2394 vers = tokenval; | |
2395 next(); | |
2396 } | |
2397 if (*token == ',') { | |
2398 next(); | |
2399 if (tokentype != NUMBER) | |
2400 pr_err("proc number expected"); | |
2401 proc = tokenval; | |
2402 next(); | |
2403 } | |
2404 rpc_match_prog(dir, savetoken, vers, proc); | |
2405 dir = ANY; | |
2406 opstack++; | |
2407 break; | |
2408 } | |
2409 | |
2410 if (EQ("slp")) { | |
2411 /* filter out TCP handshakes */ | |
2412 emitop(OP_OFFSET_LINK); | |
2413 compare_value(9, 1, IPPROTO_TCP); | |
2414 emitop(OP_LOAD_CONST); | |
2415 emitval(52); | |
2416 emitop(OP_LOAD_CONST); | |
2417 emitval(2); | |
2418 emitop(OP_LOAD_SHORT); | |
2419 emitop(OP_GE); | |
2420 emitop(OP_AND); /* proto == TCP && len < 52 */ | |
2421 emitop(OP_NOT); | |
2422 emitop(OP_BRFL); /* pkt too short to be a SLP call */ | |
2423 m = chain(0); | |
2424 | |
2425 emitop(OP_OFFSET_POP); | |
2426 emitop(OP_OFFSET_SLP); | |
2427 resolve_chain(m); | |
2428 opstack++; | |
2429 next(); | |
2430 break; | |
2431 } | |
2432 | |
2433 if (EQ("ldap")) { | |
2434 dir = ANY; | |
2435 port_match(dir, "ldap"); | |
2436 opstack++; | |
2437 next(); | |
2438 break; | |
2439 } | |
2440 | |
2441 if (EQ("and") || EQ("or")) { | |
2442 break; | |
2443 } | |
2444 | |
2445 if (EQ("gateway")) { | |
2446 next(); | |
2447 if (eaddr || tokentype != ALPHA) | |
2448 pr_err("hostname required: %s", token); | |
2449 etheraddr_match(dir, token); | |
2450 dir = ANY; | |
2451 emitop(OP_BRFL); | |
2452 m = chain(0); | |
2453 ipaddr_match(dir, token, IPV4_AND_IPV6); | |
2454 emitop(OP_NOT); | |
2455 resolve_chain(m); | |
2456 opstack++; | |
2457 next(); | |
2458 } | |
2459 | |
2460 if (EQ("host") || EQ("between") || | |
2461 tokentype == ALPHA || /* assume its a hostname */ | |
2462 tokentype == ADDR_IP || | |
2463 tokentype == ADDR_IP6 || | |
2464 tokentype == ADDR_AT || | |
2465 tokentype == ADDR_ETHER) { | |
2466 if (EQ("host") || EQ("between")) | |
2467 next(); | |
2468 if (eaddr || tokentype == ADDR_ETHER) { | |
2469 etheraddr_match(dir, token); | |
2470 } else if (tokentype == ALPHA) { | |
2471 ipaddr_match(dir, token, IPV4_AND_IPV6); | |
2472 } else if (tokentype == ADDR_AT) { | |
2473 ataddr_match(dir, token); | |
2474 } else if (tokentype == ADDR_IP) { | |
2475 ipaddr_match(dir, token, IPV4_ONLY); | |
2476 } else { | |
2477 ipaddr_match(dir, token, IPV6_ONLY); | |
2478 } | |
2479 dir = ANY; | |
2480 eaddr = 0; | |
2481 opstack++; | |
2482 next(); | |
2483 break; | |
2484 } | |
2485 | |
2486 if (tokentype == NUMBER) { | |
2487 load_const(tokenval); | |
2488 opstack++; | |
2489 next(); | |
2490 break; | |
2491 } | |
2492 | |
2493 break; /* unknown token */ | |
2494 } | |
2495 } | |
2496 | |
2497 struct optable { | |
2498 char *op_tok; | |
2499 enum optype op_type; | |
2500 }; | |
2501 | |
2502 static struct optable | |
2503 mulops[] = { | |
2504 "*", OP_MUL, | |
2505 "/", OP_DIV, | |
2506 "%", OP_REM, | |
2507 "&", OP_AND, | |
2508 "", OP_STOP, | |
2509 }; | |
2510 | |
2511 static struct optable | |
2512 addops[] = { | |
2513 "+", OP_ADD, | |
2514 "-", OP_SUB, | |
2515 "|", OP_OR, | |
2516 "^", OP_XOR, | |
2517 "", OP_STOP, | |
2518 }; | |
2519 | |
2520 static struct optable | |
2521 compareops[] = { | |
2522 "==", OP_EQ, | |
2523 "=", OP_EQ, | |
2524 "!=", OP_NE, | |
2525 ">", OP_GT, | |
2526 ">=", OP_GE, | |
2527 "<", OP_LT, | |
2528 "<=", OP_LE, | |
2529 "", OP_STOP, | |
2530 }; | |
2531 | |
2532 /* | |
2533 * Using the table, find the operator | |
2534 * that corresponds to the token. | |
2535 * Return 0 if not found. | |
2536 */ | |
2537 static int | |
2538 find_op(char *tok, struct optable *table) | |
2539 { | |
2540 struct optable *op; | |
2541 | |
2542 for (op = table; *op->op_tok; op++) { | |
2543 if (strcmp(tok, op->op_tok) == 0) | |
2544 return (op->op_type); | |
2545 } | |
2546 | |
2547 return (0); | |
2548 } | |
2549 | |
2550 static void | |
2551 expr_mul() | |
2552 { | |
2553 int op; | |
2554 int s = opstack; | |
2555 | |
2556 primary(); | |
2557 while (op = find_op(token, mulops)) { | |
2558 next(); | |
2559 primary(); | |
2560 checkstack(s + 2); | |
2561 emitop(op); | |
2562 opstack--; | |
2563 } | |
2564 } | |
2565 | |
2566 static void | |
2567 expr_add() | |
2568 { | |
2569 int op, s = opstack; | |
2570 | |
2571 expr_mul(); | |
2572 while (op = find_op(token, addops)) { | |
2573 next(); | |
2574 expr_mul(); | |
2575 checkstack(s + 2); | |
2576 emitop(op); | |
2577 opstack--; | |
2578 } | |
2579 } | |
2580 | |
2581 static void | |
2582 expr_compare() | |
2583 { | |
2584 int op, s = opstack; | |
2585 | |
2586 expr_add(); | |
2587 while (op = find_op(token, compareops)) { | |
2588 next(); | |
2589 expr_add(); | |
2590 checkstack(s + 2); | |
2591 emitop(op); | |
2592 opstack--; | |
2593 } | |
2594 } | |
2595 | |
2596 /* | |
2597 * Alternation ("and") is difficult because | |
2598 * an implied "and" is acknowledge between | |
2599 * two adjacent primaries. Just keep calling | |
2600 * the lower-level expression routine until | |
2601 * no value is added to the opstack. | |
2602 */ | |
2603 static void | |
2604 alternation() | |
2605 { | |
2606 int m = 0; | |
2607 int s = opstack; | |
2608 | |
2609 expr_compare(); | |
2610 checkstack(s + 1); | |
2611 for (;;) { | |
2612 if (EQ("and")) | |
2613 next(); | |
2614 emitop(OP_BRFL); | |
2615 m = chain(m); | |
2616 expr_compare(); | |
2617 if (opstack != s + 2) | |
2618 break; | |
2619 opstack--; | |
2620 } | |
2621 unemit(2); | |
2622 resolve_chain(m); | |
2623 } | |
2624 | |
2625 static void | |
2626 expression() | |
2627 { | |
2628 int m = 0; | |
2629 int s = opstack; | |
2630 | |
2631 alternation(); | |
2632 while (EQ("or") || EQ(",")) { | |
2633 emitop(OP_BRTR); | |
2634 m = chain(m); | |
2635 next(); | |
2636 alternation(); | |
2637 checkstack(s + 2); | |
2638 opstack--; | |
2639 } | |
2640 resolve_chain(m); | |
2641 } | |
2642 | |
2643 /* | |
2644 * Take n args from the argv list | |
2645 * and concatenate them into a single string. | |
2646 */ | |
2647 char * | |
2648 concat_args(char **argv, int argc) | |
2649 { | |
2650 int i, len; | |
2651 char *str, *p; | |
2652 | |
2653 /* First add the lengths of all the strings */ | |
2654 len = 0; | |
2655 for (i = 0; i < argc; i++) | |
2656 len += strlen(argv[i]) + 1; | |
2657 | |
2658 /* allocate the big string */ | |
2659 str = (char *)malloc(len); | |
2660 if (str == NULL) | |
2661 pr_err("no mem"); | |
2662 | |
2663 p = str; | |
2664 | |
2665 /* | |
2666 * Concat the strings into the big | |
2667 * string using a space as separator | |
2668 */ | |
2669 for (i = 0; i < argc; i++) { | |
2670 strcpy(p, argv[i]); | |
2671 p += strlen(p); | |
2672 *p++ = ' '; | |
2673 } | |
2674 *--p = '\0'; | |
2675 | |
2676 return (str); | |
2677 } | |
2678 | |
2679 /* | |
2680 * Take the expression in the string "expr" | |
2681 * and compile it into the code array. | |
2682 * Print the generated code if the print | |
2683 * arg is set. | |
2684 */ | |
2685 void | |
2686 compile(char *expr, int print) | |
2687 { | |
2688 expr = strdup(expr); | |
2689 if (expr == NULL) | |
2690 pr_err("no mem"); | |
2691 curr_op = oplist; | |
2692 tkp = expr; | |
2693 dir = ANY; | |
2694 | |
2695 next(); | |
2696 if (tokentype != EOL) | |
2697 expression(); | |
2698 emitop(OP_STOP); | |
2699 if (tokentype != EOL) | |
2700 pr_err("invalid expression"); | |
2701 optimize(oplist); | |
2702 if (print) | |
2703 codeprint(); | |
2704 } | |
2705 | |
2706 /* | |
2707 * Lookup hostname in the arp cache. | |
2708 */ | |
2709 boolean_t | |
2710 arp_for_ether(char *hostname, struct ether_addr *ep) | |
2711 { | |
2712 struct arpreq ar; | |
2713 struct hostent *hp; | |
2714 struct sockaddr_in *sin; | |
2715 int error_num; | |
2716 int s; | |
2717 | |
2718 memset(&ar, 0, sizeof (ar)); | |
2719 sin = (struct sockaddr_in *)&ar.arp_pa; | |
2720 sin->sin_family = AF_INET; | |
2721 hp = getipnodebyname(hostname, AF_INET, 0, &error_num); | |
2722 if (hp == NULL) { | |
2723 return (B_FALSE); | |
2724 } | |
2725 memcpy(&sin->sin_addr, hp->h_addr, sizeof (sin->sin_addr)); | |
2726 s = socket(AF_INET, SOCK_DGRAM, 0); | |
2727 if (s < 0) { | |
2728 return (B_FALSE); | |
2729 } | |
2730 if (ioctl(s, SIOCGARP, &ar) < 0) { | |
2731 close(s); | |
2732 return (B_FALSE); | |
2733 } | |
2734 close(s); | |
2735 memcpy(ep->ether_addr_octet, ar.arp_ha.sa_data, sizeof (*ep)); | |
2736 return (B_TRUE); | |
2737 } |