xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pf.c (revision 3431:9f2d277dcffa)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52760Sdg199075  * Common Development and Distribution License (the "License").
62760Sdg199075  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*3431Scarlsonj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SunOS	*/
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <stdio.h>
292760Sdg199075 #include <stddef.h>
300Sstevel@tonic-gate #include <ctype.h>
310Sstevel@tonic-gate #include <string.h>
320Sstevel@tonic-gate #include <fcntl.h>
330Sstevel@tonic-gate #include <string.h>
340Sstevel@tonic-gate #include <sys/types.h>
350Sstevel@tonic-gate #include <sys/time.h>
360Sstevel@tonic-gate #include <sys/isa_defs.h>
370Sstevel@tonic-gate 
380Sstevel@tonic-gate #include <sys/socket.h>
390Sstevel@tonic-gate #include <sys/sockio.h>
400Sstevel@tonic-gate #include <sys/dlpi.h>
412760Sdg199075 #include <sys/vlan.h>
420Sstevel@tonic-gate #include <net/if.h>
430Sstevel@tonic-gate #include <netinet/in_systm.h>
440Sstevel@tonic-gate #include <netinet/in.h>
450Sstevel@tonic-gate #include <netinet/ip.h>
460Sstevel@tonic-gate #include <netinet/if_ether.h>
470Sstevel@tonic-gate #include <netinet/tcp.h>
480Sstevel@tonic-gate #include <netinet/udp.h>
490Sstevel@tonic-gate #include <netdb.h>
500Sstevel@tonic-gate #include <rpc/rpc.h>
510Sstevel@tonic-gate #include <setjmp.h>
520Sstevel@tonic-gate 
530Sstevel@tonic-gate #include <sys/pfmod.h>
540Sstevel@tonic-gate #include "snoop.h"
552760Sdg199075 #include "snoop_vlan.h"
560Sstevel@tonic-gate 
570Sstevel@tonic-gate /*
580Sstevel@tonic-gate  * This module generates code for the kernel packet filter.
590Sstevel@tonic-gate  * The kernel packet filter is more efficient since it
600Sstevel@tonic-gate  * operates without context switching or moving data into
610Sstevel@tonic-gate  * the capture buffer.  On the other hand, it is limited
620Sstevel@tonic-gate  * in its filtering ability i.e. can't cope with variable
630Sstevel@tonic-gate  * length headers, can't compare the packet size, 1 and 4 octet
640Sstevel@tonic-gate  * comparisons are awkward, code space is limited to ENMAXFILTERS
650Sstevel@tonic-gate  * halfwords, etc.
660Sstevel@tonic-gate  * The parser is the same for the user-level packet filter though
670Sstevel@tonic-gate  * more limited in the variety of expressions it can generate
680Sstevel@tonic-gate  * code for.  If the pf compiler finds an expression it can't
690Sstevel@tonic-gate  * handle, it tries to set up a split filter in kernel and do the
700Sstevel@tonic-gate  * remaining filtering in userland. If that also fails, it resorts
710Sstevel@tonic-gate  * to userland filter. (See additional comment in pf_compile)
720Sstevel@tonic-gate  */
730Sstevel@tonic-gate 
740Sstevel@tonic-gate extern struct Pf_ext_packetfilt pf;
750Sstevel@tonic-gate static ushort_t *pfp;
760Sstevel@tonic-gate jmp_buf env;
770Sstevel@tonic-gate 
780Sstevel@tonic-gate int eaddr;	/* need ethernet addr */
790Sstevel@tonic-gate 
800Sstevel@tonic-gate int opstack;	/* operand stack depth */
810Sstevel@tonic-gate 
820Sstevel@tonic-gate #define	EQ(val)		(strcmp(token, val) == 0)
830Sstevel@tonic-gate #define	IPV4_ONLY	0
840Sstevel@tonic-gate #define	IPV6_ONLY	1
850Sstevel@tonic-gate #define	IPV4_AND_IPV6	2
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /*
880Sstevel@tonic-gate  * The following constants represent the offsets in bytes from the beginning
890Sstevel@tonic-gate  * of the packet of the link and IP(v6) layer source/destination/type fields,
900Sstevel@tonic-gate  * initialized for Ethernet. Media specific code can set any unavailable
910Sstevel@tonic-gate  * link layer property's offset to -1 to indicate that the property's value
920Sstevel@tonic-gate  * is not available from the frame.
930Sstevel@tonic-gate  */
940Sstevel@tonic-gate static int link_header_len = 14, link_type_offset = 12;
950Sstevel@tonic-gate static int link_dest_offset = 0, link_src_offset = 6;
960Sstevel@tonic-gate static int link_addr_len = 6;
970Sstevel@tonic-gate 
980Sstevel@tonic-gate #define	IPV4_SRCADDR_OFFSET	(link_header_len + 12)
990Sstevel@tonic-gate #define	IPV4_DSTADDR_OFFSET	(link_header_len + 16)
1000Sstevel@tonic-gate #define	IPV6_SRCADDR_OFFSET	(link_header_len + 8)
1010Sstevel@tonic-gate #define	IPV6_DSTADDR_OFFSET	(link_header_len + 24)
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate static int inBrace = 0, inBraceOR = 0;
1040Sstevel@tonic-gate static int foundOR = 0;
1050Sstevel@tonic-gate char *tkp, *sav_tkp;
1060Sstevel@tonic-gate char *token;
1070Sstevel@tonic-gate enum { EOL, ALPHA, NUMBER, FIELD, ADDR_IP, ADDR_ETHER, SPECIAL,
1080Sstevel@tonic-gate 	ADDR_IP6 } tokentype;
1090Sstevel@tonic-gate uint_t tokenval;
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate enum direction { ANY, TO, FROM };
1120Sstevel@tonic-gate enum direction dir;
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate extern void next();
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate static void pf_expression();
1172760Sdg199075 static void pf_check_vlan_tag(uint_t offset);
1182760Sdg199075 static void pf_clear_offset_register();
1192760Sdg199075 static void pf_emit_load_offset(uint_t offset);
1202760Sdg199075 static void pf_match_ethertype(uint_t ethertype);
1212760Sdg199075 static void pf_check_transport_protocol(uint_t transport_protocol);
1222760Sdg199075 static void pf_compare_value_mask_generic(int offset, uint_t len,
1232760Sdg199075     uint_t val, int mask, uint_t op);
1242760Sdg199075 
1252760Sdg199075 /*
1262760Sdg199075  * This pointer points to the function that last generated
1272760Sdg199075  * instructions to change the offset register.  It's used
1282760Sdg199075  * for comparisons to see if we need to issue more instructions
1292760Sdg199075  * to change the register.
1302760Sdg199075  *
1312760Sdg199075  * It's initialized to pf_clear_offset_register because the offset
1322760Sdg199075  * register in pfmod is initialized to zero, similar to the state
1332760Sdg199075  * it would be in after executing the instructions issued by
1342760Sdg199075  * pf_clear_offset_register.
1352760Sdg199075  */
1362760Sdg199075 static void *last_offset_operation = (void*)pf_clear_offset_register;
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate static void
1390Sstevel@tonic-gate pf_emit(x)
1400Sstevel@tonic-gate 	ushort_t x;
1410Sstevel@tonic-gate {
1420Sstevel@tonic-gate 	if (pfp > &pf.Pf_Filter[PF_MAXFILTERS - 1])
1430Sstevel@tonic-gate 		longjmp(env, 1);
1440Sstevel@tonic-gate 	*pfp++ = x;
1450Sstevel@tonic-gate }
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate static void
1480Sstevel@tonic-gate pf_codeprint(code, len)
1490Sstevel@tonic-gate 	ushort_t *code;
1500Sstevel@tonic-gate 	int len;
1510Sstevel@tonic-gate {
1520Sstevel@tonic-gate 	ushort_t *pc;
1530Sstevel@tonic-gate 	ushort_t *plast = code + len;
1540Sstevel@tonic-gate 	int op, action;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	if (len > 0) {
1570Sstevel@tonic-gate 		printf("Kernel Filter:\n");
1580Sstevel@tonic-gate 	}
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 	for (pc = code; pc < plast; pc++) {
1610Sstevel@tonic-gate 		printf("\t%3d: ", pc - code);
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 		op = *pc & 0xfc00;	/* high 10 bits */
1640Sstevel@tonic-gate 		action = *pc & 0x3ff;	/* low   6 bits */
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 		switch (action) {
1670Sstevel@tonic-gate 		case ENF_PUSHLIT:
1680Sstevel@tonic-gate 			printf("PUSHLIT ");
1690Sstevel@tonic-gate 			break;
1700Sstevel@tonic-gate 		case ENF_PUSHZERO:
1710Sstevel@tonic-gate 			printf("PUSHZERO ");
1720Sstevel@tonic-gate 			break;
1730Sstevel@tonic-gate #ifdef ENF_PUSHONE
1740Sstevel@tonic-gate 		case ENF_PUSHONE:
1750Sstevel@tonic-gate 			printf("PUSHONE ");
1760Sstevel@tonic-gate 			break;
1770Sstevel@tonic-gate #endif
1780Sstevel@tonic-gate #ifdef ENF_PUSHFFFF
1790Sstevel@tonic-gate 		case ENF_PUSHFFFF:
1800Sstevel@tonic-gate 			printf("PUSHFFFF ");
1810Sstevel@tonic-gate 			break;
1820Sstevel@tonic-gate #endif
1830Sstevel@tonic-gate #ifdef ENF_PUSHFF00
1840Sstevel@tonic-gate 		case ENF_PUSHFF00:
1850Sstevel@tonic-gate 			printf("PUSHFF00 ");
1860Sstevel@tonic-gate 			break;
1870Sstevel@tonic-gate #endif
1880Sstevel@tonic-gate #ifdef ENF_PUSH00FF
1890Sstevel@tonic-gate 		case ENF_PUSH00FF:
1900Sstevel@tonic-gate 			printf("PUSH00FF ");
1910Sstevel@tonic-gate 			break;
1920Sstevel@tonic-gate #endif
1932760Sdg199075 		case ENF_LOAD_OFFSET:
1942760Sdg199075 			printf("LOAD_OFFSET ");
1952760Sdg199075 			break;
1962760Sdg199075 		case ENF_BRTR:
1972760Sdg199075 			printf("BRTR ");
1982760Sdg199075 			break;
1992760Sdg199075 		case ENF_BRFL:
2002760Sdg199075 			printf("BRFL ");
2012760Sdg199075 			break;
2022760Sdg199075 		case ENF_POP:
2032760Sdg199075 			printf("POP ");
2042760Sdg199075 			break;
2050Sstevel@tonic-gate 		}
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 		if (action >= ENF_PUSHWORD)
2080Sstevel@tonic-gate 			printf("PUSHWORD %d ", action - ENF_PUSHWORD);
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 		switch (op) {
2110Sstevel@tonic-gate 		case ENF_EQ:
2120Sstevel@tonic-gate 			printf("EQ ");
2130Sstevel@tonic-gate 			break;
2140Sstevel@tonic-gate 		case ENF_LT:
2150Sstevel@tonic-gate 			printf("LT ");
2160Sstevel@tonic-gate 			break;
2170Sstevel@tonic-gate 		case ENF_LE:
2180Sstevel@tonic-gate 			printf("LE ");
2190Sstevel@tonic-gate 			break;
2200Sstevel@tonic-gate 		case ENF_GT:
2210Sstevel@tonic-gate 			printf("GT ");
2220Sstevel@tonic-gate 			break;
2230Sstevel@tonic-gate 		case ENF_GE:
2240Sstevel@tonic-gate 			printf("GE ");
2250Sstevel@tonic-gate 			break;
2260Sstevel@tonic-gate 		case ENF_AND:
2270Sstevel@tonic-gate 			printf("AND ");
2280Sstevel@tonic-gate 			break;
2290Sstevel@tonic-gate 		case ENF_OR:
2300Sstevel@tonic-gate 			printf("OR ");
2310Sstevel@tonic-gate 			break;
2320Sstevel@tonic-gate 		case ENF_XOR:
2330Sstevel@tonic-gate 			printf("XOR ");
2340Sstevel@tonic-gate 			break;
2350Sstevel@tonic-gate 		case ENF_COR:
2360Sstevel@tonic-gate 			printf("COR ");
2370Sstevel@tonic-gate 			break;
2380Sstevel@tonic-gate 		case ENF_CAND:
2390Sstevel@tonic-gate 			printf("CAND ");
2400Sstevel@tonic-gate 			break;
2410Sstevel@tonic-gate 		case ENF_CNOR:
2420Sstevel@tonic-gate 			printf("CNOR ");
2430Sstevel@tonic-gate 			break;
2440Sstevel@tonic-gate 		case ENF_CNAND:
2450Sstevel@tonic-gate 			printf("CNAND ");
2460Sstevel@tonic-gate 			break;
2470Sstevel@tonic-gate 		case ENF_NEQ:
2480Sstevel@tonic-gate 			printf("NEQ ");
2490Sstevel@tonic-gate 			break;
2500Sstevel@tonic-gate 		}
2510Sstevel@tonic-gate 
2522760Sdg199075 		if (action == ENF_PUSHLIT ||
2532760Sdg199075 		    action == ENF_LOAD_OFFSET ||
2542760Sdg199075 		    action == ENF_BRTR ||
2552760Sdg199075 		    action == ENF_BRFL) {
2560Sstevel@tonic-gate 			pc++;
2570Sstevel@tonic-gate 			printf("\n\t%3d:   %d (0x%04x)", pc - code, *pc, *pc);
2580Sstevel@tonic-gate 		}
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 		printf("\n");
2610Sstevel@tonic-gate 	}
2620Sstevel@tonic-gate }
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate /*
2650Sstevel@tonic-gate  * Emit packet filter code to check a
2660Sstevel@tonic-gate  * field in the packet for a particular value.
2670Sstevel@tonic-gate  * Need different code for each field size.
2680Sstevel@tonic-gate  * Since the pf can only compare 16 bit quantities
2690Sstevel@tonic-gate  * we have to use masking to compare byte values.
2700Sstevel@tonic-gate  * Long word (32 bit) quantities have to be done
2710Sstevel@tonic-gate  * as two 16 bit comparisons.
2720Sstevel@tonic-gate  */
2730Sstevel@tonic-gate static void
2740Sstevel@tonic-gate pf_compare_value(int offset, uint_t len, uint_t val)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate 	/*
2770Sstevel@tonic-gate 	 * If the property being filtered on is absent in the media
2780Sstevel@tonic-gate 	 * packet, error out.
2790Sstevel@tonic-gate 	 */
2800Sstevel@tonic-gate 	if (offset == -1)
2810Sstevel@tonic-gate 		pr_err("filter option unsupported on media");
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	switch (len) {
2840Sstevel@tonic-gate 	case 1:
2850Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
2860Sstevel@tonic-gate #if defined(_BIG_ENDIAN)
2870Sstevel@tonic-gate 		if (offset % 2)
2880Sstevel@tonic-gate #else
2890Sstevel@tonic-gate 		if (!(offset % 2))
2900Sstevel@tonic-gate #endif
2910Sstevel@tonic-gate 		{
2920Sstevel@tonic-gate #ifdef ENF_PUSH00FF
2930Sstevel@tonic-gate 			pf_emit(ENF_PUSH00FF | ENF_AND);
2940Sstevel@tonic-gate #else
2950Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_AND);
2960Sstevel@tonic-gate 			pf_emit(0x00FF);
2970Sstevel@tonic-gate #endif
2980Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_EQ);
2990Sstevel@tonic-gate 			pf_emit(val);
3000Sstevel@tonic-gate 		} else {
3010Sstevel@tonic-gate #ifdef ENF_PUSHFF00
3020Sstevel@tonic-gate 			pf_emit(ENF_PUSHFF00 | ENF_AND);
3030Sstevel@tonic-gate #else
3040Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_AND);
3050Sstevel@tonic-gate 			pf_emit(0xFF00);
3060Sstevel@tonic-gate #endif
3070Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_EQ);
3080Sstevel@tonic-gate 			pf_emit(val << 8);
3090Sstevel@tonic-gate 		}
3100Sstevel@tonic-gate 		break;
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	case 2:
3130Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
3140Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_EQ);
3150Sstevel@tonic-gate 		pf_emit((ushort_t)val);
3160Sstevel@tonic-gate 		break;
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	case 4:
3190Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
3200Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_EQ);
3210Sstevel@tonic-gate #if defined(_BIG_ENDIAN)
3220Sstevel@tonic-gate 		pf_emit(val >> 16);
3230Sstevel@tonic-gate #elif defined(_LITTLE_ENDIAN)
3240Sstevel@tonic-gate 		pf_emit(val & 0xffff);
3250Sstevel@tonic-gate #else
3260Sstevel@tonic-gate #error One of _BIG_ENDIAN and _LITTLE_ENDIAN must be defined
3270Sstevel@tonic-gate #endif
3280Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + (offset / 2) + 1);
3290Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_EQ);
3300Sstevel@tonic-gate #if defined(_BIG_ENDIAN)
3310Sstevel@tonic-gate 		pf_emit(val & 0xffff);
3320Sstevel@tonic-gate #else
3330Sstevel@tonic-gate 		pf_emit(val >> 16);
3340Sstevel@tonic-gate #endif
3350Sstevel@tonic-gate 		pf_emit(ENF_AND);
3360Sstevel@tonic-gate 		break;
3370Sstevel@tonic-gate 	}
3380Sstevel@tonic-gate }
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate /*
3410Sstevel@tonic-gate  * same as pf_compare_value, but only for emiting code to
3420Sstevel@tonic-gate  * compare ipv6 addresses.
3430Sstevel@tonic-gate  */
3440Sstevel@tonic-gate static void
3450Sstevel@tonic-gate pf_compare_value_v6(int offset, uint_t len, struct in6_addr val)
3460Sstevel@tonic-gate {
3470Sstevel@tonic-gate 	int i;
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	for (i = 0; i < len; i += 2) {
3500Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2 + i / 2);
3510Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_EQ);
3520Sstevel@tonic-gate 		pf_emit(*(uint16_t *)&val.s6_addr[i]);
3530Sstevel@tonic-gate 		if (i != 0)
3540Sstevel@tonic-gate 			pf_emit(ENF_AND);
3550Sstevel@tonic-gate 	}
3560Sstevel@tonic-gate }
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate /*
3600Sstevel@tonic-gate  * Same as above except mask the field value
3612760Sdg199075  * before doing the comparison.  The comparison checks
3622760Sdg199075  * to make sure the values are equal.
3630Sstevel@tonic-gate  */
3640Sstevel@tonic-gate static void
3650Sstevel@tonic-gate pf_compare_value_mask(int offset, uint_t len, uint_t val, int mask)
3660Sstevel@tonic-gate {
3672760Sdg199075 	pf_compare_value_mask_generic(offset, len, val, mask, ENF_EQ);
3682760Sdg199075 }
3692760Sdg199075 
3702760Sdg199075 /*
3712760Sdg199075  * Same as above except the values are compared to see if they are not
3722760Sdg199075  * equal.
3732760Sdg199075  */
3742760Sdg199075 static void
3752760Sdg199075 pf_compare_value_mask_neq(int offset, uint_t len, uint_t val, int mask)
3762760Sdg199075 {
3772760Sdg199075 	pf_compare_value_mask_generic(offset, len, val, mask, ENF_NEQ);
3782760Sdg199075 }
3792760Sdg199075 
3802760Sdg199075 /*
3812760Sdg199075  * Similar to pf_compare_value.
3822760Sdg199075  *
3832760Sdg199075  * This is the utility function that does the actual work to compare
3842760Sdg199075  * two values using a mask.  The comparison operation is passed into
3852760Sdg199075  * the function.
3862760Sdg199075  */
3872760Sdg199075 static void
3882760Sdg199075 pf_compare_value_mask_generic(int offset, uint_t len, uint_t val, int mask,
3892760Sdg199075     uint_t op)
3902760Sdg199075 {
3910Sstevel@tonic-gate 	/*
3920Sstevel@tonic-gate 	 * If the property being filtered on is absent in the media
3930Sstevel@tonic-gate 	 * packet, error out.
3940Sstevel@tonic-gate 	 */
3950Sstevel@tonic-gate 	if (offset == -1)
3960Sstevel@tonic-gate 		pr_err("filter option unsupported on media");
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 	switch (len) {
3990Sstevel@tonic-gate 	case 1:
4000Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
4010Sstevel@tonic-gate #if defined(_BIG_ENDIAN)
4020Sstevel@tonic-gate 		if (offset % 2)
4030Sstevel@tonic-gate #else
4040Sstevel@tonic-gate 		if (!offset % 2)
4050Sstevel@tonic-gate #endif
4060Sstevel@tonic-gate 		{
4070Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_AND);
4080Sstevel@tonic-gate 			pf_emit(mask & 0x00ff);
4092760Sdg199075 			pf_emit(ENF_PUSHLIT | op);
4100Sstevel@tonic-gate 			pf_emit(val);
4110Sstevel@tonic-gate 		} else {
4120Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_AND);
4130Sstevel@tonic-gate 			pf_emit((mask << 8) & 0xff00);
4142760Sdg199075 			pf_emit(ENF_PUSHLIT | op);
4150Sstevel@tonic-gate 			pf_emit(val << 8);
4160Sstevel@tonic-gate 		}
4170Sstevel@tonic-gate 		break;
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	case 2:
4200Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
4210Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_AND);
4220Sstevel@tonic-gate 		pf_emit(htons((ushort_t)mask));
4232760Sdg199075 		pf_emit(ENF_PUSHLIT | op);
4240Sstevel@tonic-gate 		pf_emit(htons((ushort_t)val));
4250Sstevel@tonic-gate 		break;
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	case 4:
4280Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
4290Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_AND);
4300Sstevel@tonic-gate 		pf_emit(htons((ushort_t)((mask >> 16) & 0xffff)));
4312760Sdg199075 		pf_emit(ENF_PUSHLIT | op);
4320Sstevel@tonic-gate 		pf_emit(htons((ushort_t)((val >> 16) & 0xffff)));
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + (offset / 2) + 1);
4350Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_AND);
4360Sstevel@tonic-gate 		pf_emit(htons((ushort_t)(mask & 0xffff)));
4372760Sdg199075 		pf_emit(ENF_PUSHLIT | op);
4380Sstevel@tonic-gate 		pf_emit(htons((ushort_t)(val & 0xffff)));
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate 		pf_emit(ENF_AND);
4410Sstevel@tonic-gate 		break;
4420Sstevel@tonic-gate 	}
4430Sstevel@tonic-gate }
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate /*
4460Sstevel@tonic-gate  * Generate pf code to match an IPv4 or IPv6 address.
4470Sstevel@tonic-gate  */
4480Sstevel@tonic-gate static void
4490Sstevel@tonic-gate pf_ipaddr_match(which, hostname, inet_type)
4500Sstevel@tonic-gate 	enum direction which;
4510Sstevel@tonic-gate 	char *hostname;
4520Sstevel@tonic-gate 	int inet_type;
4530Sstevel@tonic-gate {
4540Sstevel@tonic-gate 	bool_t found_host;
4550Sstevel@tonic-gate 	uint_t *addr4ptr;
4560Sstevel@tonic-gate 	uint_t addr4;
4570Sstevel@tonic-gate 	struct in6_addr *addr6ptr;
4580Sstevel@tonic-gate 	int h_addr_index;
4590Sstevel@tonic-gate 	struct hostent *hp = NULL;
4600Sstevel@tonic-gate 	int error_num = 0;
4610Sstevel@tonic-gate 	boolean_t first = B_TRUE;
4620Sstevel@tonic-gate 	int pass = 0;
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	/*
4650Sstevel@tonic-gate 	 * The addr4offset and addr6offset variables simplify the code which
4660Sstevel@tonic-gate 	 * generates the address comparison filter.  With these two variables,
4670Sstevel@tonic-gate 	 * duplicate code need not exist for the TO and FROM case.
4680Sstevel@tonic-gate 	 * A value of -1 describes the ANY case (TO and FROM).
4690Sstevel@tonic-gate 	 */
4700Sstevel@tonic-gate 	int addr4offset;
4710Sstevel@tonic-gate 	int addr6offset;
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	found_host = 0;
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	if (tokentype == ADDR_IP) {
4760Sstevel@tonic-gate 		hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
4770Sstevel@tonic-gate 		if (hp == NULL) {
4780Sstevel@tonic-gate 			if (error_num == TRY_AGAIN) {
4790Sstevel@tonic-gate 				pr_err("could not resolve %s (try again later)",
4800Sstevel@tonic-gate 				    hostname);
4810Sstevel@tonic-gate 			} else {
4820Sstevel@tonic-gate 				pr_err("could not resolve %s", hostname);
4830Sstevel@tonic-gate 			}
4840Sstevel@tonic-gate 		}
4850Sstevel@tonic-gate 		inet_type = IPV4_ONLY;
4860Sstevel@tonic-gate 	} else if (tokentype == ADDR_IP6) {
4870Sstevel@tonic-gate 		hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
4880Sstevel@tonic-gate 		if (hp == NULL) {
4890Sstevel@tonic-gate 			if (error_num == TRY_AGAIN) {
4900Sstevel@tonic-gate 				pr_err("could not resolve %s (try again later)",
4910Sstevel@tonic-gate 				    hostname);
4920Sstevel@tonic-gate 			} else {
4930Sstevel@tonic-gate 				pr_err("could not resolve %s", hostname);
4940Sstevel@tonic-gate 			}
4950Sstevel@tonic-gate 		}
4960Sstevel@tonic-gate 		inet_type = IPV6_ONLY;
4970Sstevel@tonic-gate 	} else if (tokentype == ALPHA) {
4980Sstevel@tonic-gate 		/* Some hostname i.e. tokentype is ALPHA */
4990Sstevel@tonic-gate 		switch (inet_type) {
5000Sstevel@tonic-gate 		case IPV4_ONLY:
5010Sstevel@tonic-gate 			/* Only IPv4 address is needed */
5020Sstevel@tonic-gate 			hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
5030Sstevel@tonic-gate 			if (hp != NULL) {
5040Sstevel@tonic-gate 				found_host = 1;
5050Sstevel@tonic-gate 			}
5060Sstevel@tonic-gate 			break;
5070Sstevel@tonic-gate 		case IPV6_ONLY:
5080Sstevel@tonic-gate 			/* Only IPv6 address is needed */
5090Sstevel@tonic-gate 			hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
5100Sstevel@tonic-gate 			if (hp != NULL) {
5110Sstevel@tonic-gate 				found_host = 1;
5120Sstevel@tonic-gate 			}
5130Sstevel@tonic-gate 			break;
5140Sstevel@tonic-gate 		case IPV4_AND_IPV6:
5150Sstevel@tonic-gate 			/* Both IPv4 and IPv6 are needed */
5160Sstevel@tonic-gate 			hp = getipnodebyname(hostname, AF_INET6,
5170Sstevel@tonic-gate 			    AI_ALL | AI_V4MAPPED, &error_num);
5180Sstevel@tonic-gate 			if (hp != NULL) {
5190Sstevel@tonic-gate 				found_host = 1;
5200Sstevel@tonic-gate 			}
5210Sstevel@tonic-gate 			break;
5220Sstevel@tonic-gate 		default:
5230Sstevel@tonic-gate 			found_host = 0;
5240Sstevel@tonic-gate 		}
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 		if (!found_host) {
5270Sstevel@tonic-gate 			if (error_num == TRY_AGAIN) {
5280Sstevel@tonic-gate 				pr_err("could not resolve %s (try again later)",
5290Sstevel@tonic-gate 				    hostname);
5300Sstevel@tonic-gate 			} else {
5310Sstevel@tonic-gate 				pr_err("could not resolve %s", hostname);
5320Sstevel@tonic-gate 			}
5330Sstevel@tonic-gate 		}
5340Sstevel@tonic-gate 	} else {
5350Sstevel@tonic-gate 		pr_err("unknown token type: %s", hostname);
5360Sstevel@tonic-gate 	}
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	switch (which) {
5390Sstevel@tonic-gate 	case TO:
5400Sstevel@tonic-gate 		addr4offset = IPV4_DSTADDR_OFFSET;
5410Sstevel@tonic-gate 		addr6offset = IPV6_DSTADDR_OFFSET;
5420Sstevel@tonic-gate 		break;
5430Sstevel@tonic-gate 	case FROM:
5440Sstevel@tonic-gate 		addr4offset = IPV4_SRCADDR_OFFSET;
5450Sstevel@tonic-gate 		addr6offset = IPV6_SRCADDR_OFFSET;
5460Sstevel@tonic-gate 		break;
5470Sstevel@tonic-gate 	case ANY:
5480Sstevel@tonic-gate 		addr4offset = -1;
5490Sstevel@tonic-gate 		addr6offset = -1;
5500Sstevel@tonic-gate 		break;
5510Sstevel@tonic-gate 	}
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 	if (hp != NULL && hp->h_addrtype == AF_INET) {
5542760Sdg199075 		pf_match_ethertype(ETHERTYPE_IP);
5552760Sdg199075 		pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
5560Sstevel@tonic-gate 		h_addr_index = 0;
5570Sstevel@tonic-gate 		addr4ptr = (uint_t *)hp->h_addr_list[h_addr_index];
5580Sstevel@tonic-gate 		while (addr4ptr != NULL) {
5590Sstevel@tonic-gate 			if (addr4offset == -1) {
5600Sstevel@tonic-gate 				pf_compare_value(IPV4_SRCADDR_OFFSET, 4,
5610Sstevel@tonic-gate 				    *addr4ptr);
5620Sstevel@tonic-gate 				if (h_addr_index != 0)
5630Sstevel@tonic-gate 					pf_emit(ENF_OR);
5640Sstevel@tonic-gate 				pf_compare_value(IPV4_DSTADDR_OFFSET, 4,
5650Sstevel@tonic-gate 				    *addr4ptr);
5660Sstevel@tonic-gate 				pf_emit(ENF_OR);
5670Sstevel@tonic-gate 			} else {
5680Sstevel@tonic-gate 				pf_compare_value(addr4offset, 4,
5690Sstevel@tonic-gate 				    *addr4ptr);
5700Sstevel@tonic-gate 				if (h_addr_index != 0)
5710Sstevel@tonic-gate 					pf_emit(ENF_OR);
5720Sstevel@tonic-gate 			}
5730Sstevel@tonic-gate 			addr4ptr = (uint_t *)hp->h_addr_list[++h_addr_index];
5740Sstevel@tonic-gate 		}
5750Sstevel@tonic-gate 		pf_emit(ENF_AND);
5760Sstevel@tonic-gate 	} else {
5770Sstevel@tonic-gate 		/* first pass: IPv4 addresses */
5780Sstevel@tonic-gate 		h_addr_index = 0;
5790Sstevel@tonic-gate 		addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
5800Sstevel@tonic-gate 		first = B_TRUE;
5810Sstevel@tonic-gate 		while (addr6ptr != NULL) {
5820Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
5830Sstevel@tonic-gate 				if (first) {
5842760Sdg199075 					pf_match_ethertype(ETHERTYPE_IP);
5852760Sdg199075 					pf_check_vlan_tag(
5862760Sdg199075 					    ENCAP_ETHERTYPE_OFF/2);
5870Sstevel@tonic-gate 					pass++;
5880Sstevel@tonic-gate 				}
5890Sstevel@tonic-gate 				IN6_V4MAPPED_TO_INADDR(addr6ptr,
5900Sstevel@tonic-gate 				    (struct in_addr *)&addr4);
5910Sstevel@tonic-gate 				if (addr4offset == -1) {
5920Sstevel@tonic-gate 					pf_compare_value(IPV4_SRCADDR_OFFSET, 4,
5930Sstevel@tonic-gate 					    addr4);
5940Sstevel@tonic-gate 					if (!first)
5950Sstevel@tonic-gate 						pf_emit(ENF_OR);
5960Sstevel@tonic-gate 					pf_compare_value(IPV4_DSTADDR_OFFSET, 4,
5970Sstevel@tonic-gate 					    addr4);
5980Sstevel@tonic-gate 					pf_emit(ENF_OR);
5990Sstevel@tonic-gate 				} else {
6000Sstevel@tonic-gate 					pf_compare_value(addr4offset, 4,
6010Sstevel@tonic-gate 					    addr4);
6020Sstevel@tonic-gate 					if (!first)
6030Sstevel@tonic-gate 						pf_emit(ENF_OR);
6040Sstevel@tonic-gate 				}
6050Sstevel@tonic-gate 				if (first)
6060Sstevel@tonic-gate 					first = B_FALSE;
6070Sstevel@tonic-gate 			}
6080Sstevel@tonic-gate 			addr6ptr = (struct in6_addr *)
6090Sstevel@tonic-gate 				hp->h_addr_list[++h_addr_index];
6100Sstevel@tonic-gate 		}
6110Sstevel@tonic-gate 		if (!first) {
6120Sstevel@tonic-gate 			pf_emit(ENF_AND);
6130Sstevel@tonic-gate 		}
6140Sstevel@tonic-gate 		/* second pass: IPv6 addresses */
6150Sstevel@tonic-gate 		h_addr_index = 0;
6160Sstevel@tonic-gate 		addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
6170Sstevel@tonic-gate 		first = B_TRUE;
6180Sstevel@tonic-gate 		while (addr6ptr != NULL) {
6190Sstevel@tonic-gate 			if (!IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
6200Sstevel@tonic-gate 				if (first) {
6212760Sdg199075 					pf_match_ethertype(ETHERTYPE_IPV6);
6222760Sdg199075 					pf_check_vlan_tag(
6232760Sdg199075 					    ENCAP_ETHERTYPE_OFF/2);
6240Sstevel@tonic-gate 					pass++;
6250Sstevel@tonic-gate 				}
6260Sstevel@tonic-gate 				if (addr6offset == -1) {
6270Sstevel@tonic-gate 					pf_compare_value_v6(IPV6_SRCADDR_OFFSET,
6280Sstevel@tonic-gate 					    16, *addr6ptr);
6290Sstevel@tonic-gate 					if (!first)
6300Sstevel@tonic-gate 						pf_emit(ENF_OR);
6310Sstevel@tonic-gate 					pf_compare_value_v6(IPV6_DSTADDR_OFFSET,
6320Sstevel@tonic-gate 					    16, *addr6ptr);
6330Sstevel@tonic-gate 					pf_emit(ENF_OR);
6340Sstevel@tonic-gate 				} else {
6350Sstevel@tonic-gate 					pf_compare_value_v6(addr6offset, 16,
6360Sstevel@tonic-gate 					    *addr6ptr);
6370Sstevel@tonic-gate 					if (!first)
6380Sstevel@tonic-gate 						pf_emit(ENF_OR);
6390Sstevel@tonic-gate 				}
6400Sstevel@tonic-gate 				if (first)
6410Sstevel@tonic-gate 					first = B_FALSE;
6420Sstevel@tonic-gate 			}
6430Sstevel@tonic-gate 			addr6ptr = (struct in6_addr *)
6440Sstevel@tonic-gate 				hp->h_addr_list[++h_addr_index];
6450Sstevel@tonic-gate 		}
6460Sstevel@tonic-gate 		if (!first) {
6470Sstevel@tonic-gate 			pf_emit(ENF_AND);
6480Sstevel@tonic-gate 		}
6490Sstevel@tonic-gate 		if (pass == 2) {
6500Sstevel@tonic-gate 			pf_emit(ENF_OR);
6510Sstevel@tonic-gate 		}
6520Sstevel@tonic-gate 	}
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 	if (hp != NULL) {
6550Sstevel@tonic-gate 		freehostent(hp);
6560Sstevel@tonic-gate 	}
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate static void
6610Sstevel@tonic-gate pf_compare_address(int offset, uint_t len, uchar_t *addr)
6620Sstevel@tonic-gate {
6630Sstevel@tonic-gate 	uint32_t val;
6640Sstevel@tonic-gate 	uint16_t sval;
6650Sstevel@tonic-gate 	boolean_t didone = B_FALSE;
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate 	/*
6680Sstevel@tonic-gate 	 * If the property being filtered on is absent in the media
6690Sstevel@tonic-gate 	 * packet, error out.
6700Sstevel@tonic-gate 	 */
6710Sstevel@tonic-gate 	if (offset == -1)
6720Sstevel@tonic-gate 		pr_err("filter option unsupported on media");
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 	while (len > 0) {
6750Sstevel@tonic-gate 		if (len >= 4) {
6760Sstevel@tonic-gate 			(void) memcpy(&val, addr, 4);
6770Sstevel@tonic-gate 			pf_compare_value(offset, 4, val);
6780Sstevel@tonic-gate 			addr += 4;
6790Sstevel@tonic-gate 			offset += 4;
6800Sstevel@tonic-gate 			len -= 4;
6810Sstevel@tonic-gate 		} else if (len >= 2) {
6820Sstevel@tonic-gate 			(void) memcpy(&sval, addr, 2);
6830Sstevel@tonic-gate 			pf_compare_value(offset, 2, sval);
6840Sstevel@tonic-gate 			addr += 2;
6850Sstevel@tonic-gate 			offset += 2;
6860Sstevel@tonic-gate 			len -= 2;
6870Sstevel@tonic-gate 		} else {
6880Sstevel@tonic-gate 			pf_compare_value(offset++, 1, *addr++);
6890Sstevel@tonic-gate 			len--;
6900Sstevel@tonic-gate 		}
6910Sstevel@tonic-gate 		if (didone)
6920Sstevel@tonic-gate 			pf_emit(ENF_AND);
6930Sstevel@tonic-gate 		didone = B_TRUE;
6940Sstevel@tonic-gate 	}
6950Sstevel@tonic-gate }
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate /*
6980Sstevel@tonic-gate  * Compare ethernet addresses.
6990Sstevel@tonic-gate  */
7000Sstevel@tonic-gate static void
7010Sstevel@tonic-gate pf_etheraddr_match(which, hostname)
7020Sstevel@tonic-gate 	enum direction which;
7030Sstevel@tonic-gate 	char *hostname;
7040Sstevel@tonic-gate {
7050Sstevel@tonic-gate 	struct ether_addr e, *ep = NULL;
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	if (isxdigit(*hostname))
7080Sstevel@tonic-gate 		ep = ether_aton(hostname);
7090Sstevel@tonic-gate 	if (ep == NULL) {
7100Sstevel@tonic-gate 		if (ether_hostton(hostname, &e))
7110Sstevel@tonic-gate 			if (!arp_for_ether(hostname, &e))
7120Sstevel@tonic-gate 				pr_err("cannot obtain ether addr for %s",
7130Sstevel@tonic-gate 					hostname);
7140Sstevel@tonic-gate 		ep = &e;
7150Sstevel@tonic-gate 	}
7160Sstevel@tonic-gate 
7172760Sdg199075 	pf_clear_offset_register();
7182760Sdg199075 
7190Sstevel@tonic-gate 	switch (which) {
7200Sstevel@tonic-gate 	case TO:
7210Sstevel@tonic-gate 		pf_compare_address(link_dest_offset, link_addr_len,
7220Sstevel@tonic-gate 		    (uchar_t *)ep);
7230Sstevel@tonic-gate 		break;
7240Sstevel@tonic-gate 	case FROM:
7250Sstevel@tonic-gate 		pf_compare_address(link_src_offset, link_addr_len,
7260Sstevel@tonic-gate 		    (uchar_t *)ep);
7270Sstevel@tonic-gate 		break;
7280Sstevel@tonic-gate 	case ANY:
7290Sstevel@tonic-gate 		pf_compare_address(link_dest_offset, link_addr_len,
7300Sstevel@tonic-gate 		    (uchar_t *)ep);
7310Sstevel@tonic-gate 		pf_compare_address(link_src_offset, link_addr_len,
7320Sstevel@tonic-gate 		    (uchar_t *)ep);
7330Sstevel@tonic-gate 		pf_emit(ENF_OR);
7340Sstevel@tonic-gate 		break;
7350Sstevel@tonic-gate 	}
7360Sstevel@tonic-gate }
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate /*
7390Sstevel@tonic-gate  * Emit code to compare the network part of
7400Sstevel@tonic-gate  * an IP address.
7410Sstevel@tonic-gate  */
7420Sstevel@tonic-gate static void
7430Sstevel@tonic-gate pf_netaddr_match(which, netname)
7440Sstevel@tonic-gate 	enum direction which;
7450Sstevel@tonic-gate 	char *netname;
7460Sstevel@tonic-gate {
7470Sstevel@tonic-gate 	uint_t addr;
7480Sstevel@tonic-gate 	uint_t mask = 0xff000000;
7490Sstevel@tonic-gate 	struct netent *np;
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 	if (isdigit(*netname)) {
7520Sstevel@tonic-gate 		addr = inet_network(netname);
7530Sstevel@tonic-gate 	} else {
7540Sstevel@tonic-gate 		np = getnetbyname(netname);
7550Sstevel@tonic-gate 		if (np == NULL)
7560Sstevel@tonic-gate 			pr_err("net %s not known", netname);
7570Sstevel@tonic-gate 		addr = np->n_net;
7580Sstevel@tonic-gate 	}
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 	/*
7610Sstevel@tonic-gate 	 * Left justify the address and figure
7620Sstevel@tonic-gate 	 * out a mask based on the supplied address.
7630Sstevel@tonic-gate 	 * Set the mask according to the number of zero
7640Sstevel@tonic-gate 	 * low-order bytes.
7650Sstevel@tonic-gate 	 * Note: this works only for whole octet masks.
7660Sstevel@tonic-gate 	 */
7670Sstevel@tonic-gate 	if (addr) {
7680Sstevel@tonic-gate 		while ((addr & ~mask) != 0) {
7690Sstevel@tonic-gate 			mask |= (mask >> 8);
7700Sstevel@tonic-gate 		}
7710Sstevel@tonic-gate 	}
7720Sstevel@tonic-gate 
7732760Sdg199075 	pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
7742760Sdg199075 
7750Sstevel@tonic-gate 	switch (which) {
7760Sstevel@tonic-gate 	case TO:
7770Sstevel@tonic-gate 		pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask);
7780Sstevel@tonic-gate 		break;
7790Sstevel@tonic-gate 	case FROM:
7800Sstevel@tonic-gate 		pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask);
7810Sstevel@tonic-gate 		break;
7820Sstevel@tonic-gate 	case ANY:
7830Sstevel@tonic-gate 		pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask);
7840Sstevel@tonic-gate 		pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask);
7850Sstevel@tonic-gate 		pf_emit(ENF_OR);
7860Sstevel@tonic-gate 		break;
7870Sstevel@tonic-gate 	}
7880Sstevel@tonic-gate }
7890Sstevel@tonic-gate 
7902760Sdg199075 /*
7912760Sdg199075  * A helper function to keep the code to emit instructions
7922760Sdg199075  * to change the offset register in one place.
7932760Sdg199075  *
7942760Sdg199075  * INPUTS: offset - An value representing an offset in 16-bit
7952760Sdg199075  *                  words.
7962760Sdg199075  * OUTPUTS:  If there is enough room in the storage for the
7972760Sdg199075  *           packet filtering program, instructions to load
7982760Sdg199075  *           a constant to the offset register.  Otherwise,
7992760Sdg199075  *           nothing.
8002760Sdg199075  */
8012760Sdg199075 static void
8022760Sdg199075 pf_emit_load_offset(uint_t offset)
8032760Sdg199075 {
8042760Sdg199075 	pf_emit(ENF_LOAD_OFFSET | ENF_NOP);
8052760Sdg199075 	pf_emit(offset);
8062760Sdg199075 }
8072760Sdg199075 
8082760Sdg199075 /*
8092760Sdg199075  * Clear pfmod's offset register.
8102760Sdg199075  *
8112760Sdg199075  * INPUTS:  none
8122760Sdg199075  * OUTPUTS:  Instructions to clear the offset register if
8132760Sdg199075  *           there is enough space remaining in the packet
8142760Sdg199075  *           filtering program structure's storage, and
8152760Sdg199075  *           the last thing done to the offset register was
8162760Sdg199075  *           not clearing the offset register.  Otherwise,
8172760Sdg199075  *           nothing.
8182760Sdg199075  */
8192760Sdg199075 static void
8202760Sdg199075 pf_clear_offset_register()
8212760Sdg199075 {
8222760Sdg199075 	if (last_offset_operation != (void*)pf_clear_offset_register) {
8232760Sdg199075 		pf_emit_load_offset(0);
8242760Sdg199075 		last_offset_operation = (void*)pf_clear_offset_register;
8252760Sdg199075 	}
8262760Sdg199075 }
8272760Sdg199075 
8282760Sdg199075 /*
8292760Sdg199075  * This function will issue opcodes to check if a packet
8302760Sdg199075  * is VLAN tagged, and if so, update the offset register
8312760Sdg199075  * with the appropriate offset.
8322760Sdg199075  *
8332760Sdg199075  * Note that if the packet is not VLAN tagged, then the offset
8342760Sdg199075  * register will be cleared.
8352760Sdg199075  *
8362760Sdg199075  * If the interface type is not an ethernet type, then this
8372760Sdg199075  * function returns without doing anything.
8382760Sdg199075  *
8392760Sdg199075  * If the last attempt to change the offset register occured because
8402760Sdg199075  * of a call to this function that was called with the same offset,
8412760Sdg199075  * then we don't issue packet filtering instructions.
8422760Sdg199075  *
8432760Sdg199075  * INPUTS:  offset - an offset in 16 bit words.  The function
8442760Sdg199075  *                   will set the offset register to this
8452760Sdg199075  *                   value if the packet is VLAN tagged.
8462760Sdg199075  * OUTPUTS:  If the conditions are met, packet filtering instructions.
8472760Sdg199075  */
8482760Sdg199075 static void
8492760Sdg199075 pf_check_vlan_tag(uint_t offset)
8502760Sdg199075 {
8512760Sdg199075 	static uint_t last_offset = 0;
8522760Sdg199075 
8532760Sdg199075 	if ((interface->mac_type == DL_ETHER ||
8542760Sdg199075 	    interface->mac_type == DL_CSMACD) &&
8552760Sdg199075 	    (last_offset_operation != (void*)pf_check_vlan_tag ||
8562760Sdg199075 	    last_offset != offset)) {
8572760Sdg199075 		/*
8582760Sdg199075 		 * First thing is to clear the offset register.
8592760Sdg199075 		 * We don't know what state it is in, and if it
8602760Sdg199075 		 * is not zero, then we have no idea what we load
8612760Sdg199075 		 * when we execute ENF_PUSHWORD.
8622760Sdg199075 		 */
8632760Sdg199075 		pf_clear_offset_register();
8642760Sdg199075 
8652760Sdg199075 		/*
8662760Sdg199075 		 * Check the ethertype.
8672760Sdg199075 		 */
8682760Sdg199075 		pf_compare_value(link_type_offset, 2, htons(ETHERTYPE_VLAN));
8692760Sdg199075 
8702760Sdg199075 		/*
8712760Sdg199075 		 * And if it's not VLAN, don't load offset to the offset
8722760Sdg199075 		 * register.
8732760Sdg199075 		 */
8742760Sdg199075 		pf_emit(ENF_BRFL | ENF_NOP);
8752760Sdg199075 		pf_emit(3);
8762760Sdg199075 
8772760Sdg199075 		/*
8782760Sdg199075 		 * Otherwise, load offset to the offset register.
8792760Sdg199075 		 */
8802760Sdg199075 		pf_emit_load_offset(offset);
8812760Sdg199075 
8822760Sdg199075 		/*
8832760Sdg199075 		 * Now get rid of the results of the comparison,
8842760Sdg199075 		 * we don't want the results of the comparison to affect
8852760Sdg199075 		 * other logic in the packet filtering program.
8862760Sdg199075 		 */
8872760Sdg199075 		pf_emit(ENF_POP | ENF_NOP);
8882760Sdg199075 
8892760Sdg199075 		/*
8902760Sdg199075 		 * Set the last operation at the end, or any time
8912760Sdg199075 		 * after the call to pf_clear_offset because
8922760Sdg199075 		 * pf_clear_offset uses it.
8932760Sdg199075 		 */
8942760Sdg199075 		last_offset_operation = (void*)pf_check_vlan_tag;
8952760Sdg199075 		last_offset = offset;
8962760Sdg199075 	}
8972760Sdg199075 }
8982760Sdg199075 
8992760Sdg199075 /*
9002760Sdg199075  * Utility function used to emit packet filtering code
9012760Sdg199075  * to match an ethertype.
9022760Sdg199075  *
9032760Sdg199075  * INPUTS:  ethertype - The ethertype we want to check for.
9042760Sdg199075  *                      Don't call htons on the ethertype before
9052760Sdg199075  *                      calling this function.
9062760Sdg199075  * OUTPUTS:  If there is sufficient storage available, packet
9072760Sdg199075  *           filtering code to check an ethertype.  Otherwise,
9082760Sdg199075  *           nothing.
9092760Sdg199075  */
9102760Sdg199075 static void
9112760Sdg199075 pf_match_ethertype(uint_t ethertype)
9122760Sdg199075 {
9132760Sdg199075 	/*
9142760Sdg199075 	 * If the user wants to filter on ethertype VLAN,
9152760Sdg199075 	 * then clear the offset register so that the offset
9162760Sdg199075 	 * for ENF_PUSHWORD points to the right place in the
9172760Sdg199075 	 * packet.
9182760Sdg199075 	 *
9192760Sdg199075 	 * Otherwise, call pf_check_vlan_tag to set the offset
9202760Sdg199075 	 * register such that the contents of the offset register
9212760Sdg199075 	 * plus the argument for ENF_PUSHWORD point to the right
9222760Sdg199075 	 * part of the packet, whether or not the packet is VLAN
9232760Sdg199075 	 * tagged.  We call pf_check_vlan_tag with an offset of
9242760Sdg199075 	 * two words because if the packet is VLAN tagged, we have
9252760Sdg199075 	 * to move past the ethertype in the ethernet header, and
9262760Sdg199075 	 * past the lower two octets of the VLAN header to get to
9272760Sdg199075 	 * the ethertype in the VLAN header.
9282760Sdg199075 	 */
9292760Sdg199075 	if (ethertype == ETHERTYPE_VLAN)
9302760Sdg199075 		pf_clear_offset_register();
9312760Sdg199075 	else
9322760Sdg199075 		pf_check_vlan_tag(2);
9332760Sdg199075 
9342760Sdg199075 	pf_compare_value(link_type_offset, 2, htons(ethertype));
9352760Sdg199075 }
9362760Sdg199075 
9372760Sdg199075 typedef struct {
9382760Sdg199075 	int	transport_protocol;
9392760Sdg199075 	int	network_protocol;
9402760Sdg199075 	/*
9412760Sdg199075 	 * offset is the offset in bytes from the beginning
9422760Sdg199075 	 * of the network protocol header to where the transport
9432760Sdg199075 	 * protocol type is.
9442760Sdg199075 	 */
9452760Sdg199075 	int	offset;
9462760Sdg199075 } transport_protocol_table_t;
9472760Sdg199075 
9482760Sdg199075 static transport_protocol_table_t mapping_table[] = {
9492760Sdg199075 	{IPPROTO_TCP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
9502760Sdg199075 	{IPPROTO_TCP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
9512760Sdg199075 	{IPPROTO_UDP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
9522760Sdg199075 	{IPPROTO_UDP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
9532760Sdg199075 	{IPPROTO_OSPF, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
9542760Sdg199075 	{IPPROTO_OSPF, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
9552760Sdg199075 	{IPPROTO_SCTP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
9562760Sdg199075 	{IPPROTO_SCTP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
9572760Sdg199075 	{IPPROTO_ICMP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
9582760Sdg199075 	{IPPROTO_ICMPV6, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
9592760Sdg199075 	{IPPROTO_ENCAP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
9602760Sdg199075 	{IPPROTO_ESP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
9612760Sdg199075 	{IPPROTO_ESP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
9622760Sdg199075 	{IPPROTO_AH, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
9632760Sdg199075 	{IPPROTO_AH, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
9642760Sdg199075 	{-1, 0, 0}	/* must be the final entry */
9652760Sdg199075 };
9662760Sdg199075 
9672760Sdg199075 /*
9682760Sdg199075  * This function uses the table above to generate a
9692760Sdg199075  * piece of a packet filtering program to check a transport
9702760Sdg199075  * protocol type.
9712760Sdg199075  *
9722760Sdg199075  * INPUTS:  tranport_protocol - the transport protocol we're
9732760Sdg199075  *                              interested in.
9742760Sdg199075  * OUTPUTS:  If there is sufficient storage, then packet filtering
9752760Sdg199075  *           code to check a transport protocol type.  Otherwise,
9762760Sdg199075  *           nothing.
9772760Sdg199075  */
9782760Sdg199075 static void
9792760Sdg199075 pf_check_transport_protocol(uint_t transport_protocol)
9802760Sdg199075 {
9812760Sdg199075 	int i = 0;
9822760Sdg199075 	uint_t number_of_matches = 0;
9832760Sdg199075 
9842760Sdg199075 	for (i = 0; mapping_table[i].transport_protocol != -1; i++) {
9852760Sdg199075 		if (transport_protocol ==
9862760Sdg199075 		    (uint_t)mapping_table[i].transport_protocol) {
9872760Sdg199075 			number_of_matches++;
9882760Sdg199075 			pf_match_ethertype(mapping_table[i].network_protocol);
9892760Sdg199075 			pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
9902760Sdg199075 			pf_compare_value(
9912760Sdg199075 				mapping_table[i].offset + link_header_len, 1,
9922760Sdg199075 				transport_protocol);
9932760Sdg199075 			pf_emit(ENF_AND);
9942760Sdg199075 			if (number_of_matches > 1) {
9952760Sdg199075 				/*
9962760Sdg199075 				 * Since we have two or more matches, in
9972760Sdg199075 				 * order to have a correct and complete
9982760Sdg199075 				 * program we need to OR the result of
9992760Sdg199075 				 * each block of comparisons together.
10002760Sdg199075 				 */
10012760Sdg199075 				pf_emit(ENF_OR);
10022760Sdg199075 			}
10032760Sdg199075 		}
10042760Sdg199075 	}
10052760Sdg199075 }
10062760Sdg199075 
10070Sstevel@tonic-gate static void
10080Sstevel@tonic-gate pf_primary()
10090Sstevel@tonic-gate {
10100Sstevel@tonic-gate 	for (;;) {
10110Sstevel@tonic-gate 		if (tokentype == FIELD)
10120Sstevel@tonic-gate 			break;
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 		if (EQ("ip")) {
10152760Sdg199075 			pf_match_ethertype(ETHERTYPE_IP);
10160Sstevel@tonic-gate 			opstack++;
10170Sstevel@tonic-gate 			next();
10180Sstevel@tonic-gate 			break;
10190Sstevel@tonic-gate 		}
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 		if (EQ("ip6")) {
10222760Sdg199075 			pf_match_ethertype(ETHERTYPE_IPV6);
10230Sstevel@tonic-gate 			opstack++;
10240Sstevel@tonic-gate 			next();
10250Sstevel@tonic-gate 			break;
10260Sstevel@tonic-gate 		}
10270Sstevel@tonic-gate 
10280Sstevel@tonic-gate 		if (EQ("pppoe")) {
10292760Sdg199075 			pf_match_ethertype(ETHERTYPE_PPPOED);
10302760Sdg199075 			pf_match_ethertype(ETHERTYPE_PPPOES);
10310Sstevel@tonic-gate 			pf_emit(ENF_OR);
10320Sstevel@tonic-gate 			opstack++;
10330Sstevel@tonic-gate 			next();
10340Sstevel@tonic-gate 			break;
10350Sstevel@tonic-gate 		}
10360Sstevel@tonic-gate 
10370Sstevel@tonic-gate 		if (EQ("pppoed")) {
10382760Sdg199075 			pf_match_ethertype(ETHERTYPE_PPPOED);
10390Sstevel@tonic-gate 			opstack++;
10400Sstevel@tonic-gate 			next();
10410Sstevel@tonic-gate 			break;
10420Sstevel@tonic-gate 		}
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate 		if (EQ("pppoes")) {
10452760Sdg199075 			pf_match_ethertype(ETHERTYPE_PPPOES);
10460Sstevel@tonic-gate 			opstack++;
10470Sstevel@tonic-gate 			next();
10480Sstevel@tonic-gate 			break;
10490Sstevel@tonic-gate 		}
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate 		if (EQ("arp")) {
10522760Sdg199075 			pf_match_ethertype(ETHERTYPE_ARP);
10532760Sdg199075 			opstack++;
10542760Sdg199075 			next();
10552760Sdg199075 			break;
10562760Sdg199075 		}
10572760Sdg199075 
10582760Sdg199075 		if (EQ("vlan")) {
10592760Sdg199075 			pf_match_ethertype(ETHERTYPE_VLAN);
10602760Sdg199075 			pf_compare_value_mask_neq(VLAN_ID_OFFSET, 2,
10612760Sdg199075 			    0, VLAN_ID_MASK);
10622760Sdg199075 			pf_emit(ENF_AND);
10632760Sdg199075 			opstack++;
10642760Sdg199075 			next();
10652760Sdg199075 			break;
10662760Sdg199075 		}
10672760Sdg199075 
10682760Sdg199075 		if (EQ("vlan-id")) {
10692760Sdg199075 			next();
10702760Sdg199075 			if (tokentype != NUMBER)
10712760Sdg199075 				pr_err("VLAN ID expected");
10722760Sdg199075 			pf_match_ethertype(ETHERTYPE_VLAN);
10732760Sdg199075 			pf_compare_value_mask(VLAN_ID_OFFSET, 2, tokenval,
10742760Sdg199075 			    VLAN_ID_MASK);
10752760Sdg199075 			pf_emit(ENF_AND);
10760Sstevel@tonic-gate 			opstack++;
10770Sstevel@tonic-gate 			next();
10780Sstevel@tonic-gate 			break;
10790Sstevel@tonic-gate 		}
10800Sstevel@tonic-gate 
10810Sstevel@tonic-gate 		if (EQ("rarp")) {
10822760Sdg199075 			pf_match_ethertype(ETHERTYPE_REVARP);
10830Sstevel@tonic-gate 			opstack++;
10840Sstevel@tonic-gate 			next();
10850Sstevel@tonic-gate 			break;
10860Sstevel@tonic-gate 		}
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate 		if (EQ("tcp")) {
10892760Sdg199075 			pf_check_transport_protocol(IPPROTO_TCP);
10900Sstevel@tonic-gate 			opstack++;
10910Sstevel@tonic-gate 			next();
10920Sstevel@tonic-gate 			break;
10930Sstevel@tonic-gate 		}
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 		if (EQ("udp")) {
10962760Sdg199075 			pf_check_transport_protocol(IPPROTO_UDP);
10970Sstevel@tonic-gate 			opstack++;
10980Sstevel@tonic-gate 			next();
10990Sstevel@tonic-gate 			break;
11000Sstevel@tonic-gate 		}
11010Sstevel@tonic-gate 
11020Sstevel@tonic-gate 		if (EQ("ospf")) {
11032760Sdg199075 			pf_check_transport_protocol(IPPROTO_OSPF);
11040Sstevel@tonic-gate 			opstack++;
11050Sstevel@tonic-gate 			next();
11060Sstevel@tonic-gate 			break;
11070Sstevel@tonic-gate 		}
11080Sstevel@tonic-gate 
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate 		if (EQ("sctp")) {
11112760Sdg199075 			pf_check_transport_protocol(IPPROTO_SCTP);
11120Sstevel@tonic-gate 			opstack++;
11130Sstevel@tonic-gate 			next();
11140Sstevel@tonic-gate 			break;
11150Sstevel@tonic-gate 		}
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 		if (EQ("icmp")) {
11182760Sdg199075 			pf_check_transport_protocol(IPPROTO_ICMP);
11190Sstevel@tonic-gate 			opstack++;
11200Sstevel@tonic-gate 			next();
11210Sstevel@tonic-gate 			break;
11220Sstevel@tonic-gate 		}
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 		if (EQ("icmp6")) {
11252760Sdg199075 			pf_check_transport_protocol(IPPROTO_ICMPV6);
11260Sstevel@tonic-gate 			opstack++;
11270Sstevel@tonic-gate 			next();
11280Sstevel@tonic-gate 			break;
11290Sstevel@tonic-gate 		}
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 		if (EQ("ip-in-ip")) {
11322760Sdg199075 			pf_check_transport_protocol(IPPROTO_ENCAP);
11330Sstevel@tonic-gate 			opstack++;
11340Sstevel@tonic-gate 			next();
11350Sstevel@tonic-gate 			break;
11360Sstevel@tonic-gate 		}
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 		if (EQ("esp")) {
11392760Sdg199075 			pf_check_transport_protocol(IPPROTO_ESP);
11400Sstevel@tonic-gate 			opstack++;
11410Sstevel@tonic-gate 			next();
11420Sstevel@tonic-gate 			break;
11430Sstevel@tonic-gate 		}
11440Sstevel@tonic-gate 
11450Sstevel@tonic-gate 		if (EQ("ah")) {
11462760Sdg199075 			pf_check_transport_protocol(IPPROTO_AH);
11470Sstevel@tonic-gate 			opstack++;
11480Sstevel@tonic-gate 			next();
11490Sstevel@tonic-gate 			break;
11500Sstevel@tonic-gate 		}
11510Sstevel@tonic-gate 
11520Sstevel@tonic-gate 		if (EQ("(")) {
11530Sstevel@tonic-gate 			inBrace++;
11540Sstevel@tonic-gate 			next();
11550Sstevel@tonic-gate 			pf_expression();
11560Sstevel@tonic-gate 			if (EQ(")")) {
11570Sstevel@tonic-gate 				if (inBrace)
11580Sstevel@tonic-gate 					inBraceOR--;
11590Sstevel@tonic-gate 				inBrace--;
11600Sstevel@tonic-gate 				next();
11610Sstevel@tonic-gate 			}
11620Sstevel@tonic-gate 			break;
11630Sstevel@tonic-gate 		}
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate 		if (EQ("to") || EQ("dst")) {
11660Sstevel@tonic-gate 			dir = TO;
11670Sstevel@tonic-gate 			next();
11680Sstevel@tonic-gate 			continue;
11690Sstevel@tonic-gate 		}
11700Sstevel@tonic-gate 
11710Sstevel@tonic-gate 		if (EQ("from") || EQ("src")) {
11720Sstevel@tonic-gate 			dir = FROM;
11730Sstevel@tonic-gate 			next();
11740Sstevel@tonic-gate 			continue;
11750Sstevel@tonic-gate 		}
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 		if (EQ("ether")) {
11780Sstevel@tonic-gate 			eaddr = 1;
11790Sstevel@tonic-gate 			next();
11800Sstevel@tonic-gate 			continue;
11810Sstevel@tonic-gate 		}
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate 		if (EQ("inet")) {
11840Sstevel@tonic-gate 			next();
11850Sstevel@tonic-gate 			if (EQ("host"))
11860Sstevel@tonic-gate 				next();
11870Sstevel@tonic-gate 			if (tokentype != ALPHA && tokentype != ADDR_IP)
11880Sstevel@tonic-gate 				pr_err("host/IPv4 addr expected after inet");
11890Sstevel@tonic-gate 			pf_ipaddr_match(dir, token, IPV4_ONLY);
11900Sstevel@tonic-gate 			opstack++;
11910Sstevel@tonic-gate 			next();
11920Sstevel@tonic-gate 			break;
11930Sstevel@tonic-gate 		}
11940Sstevel@tonic-gate 
11950Sstevel@tonic-gate 		if (EQ("inet6")) {
11960Sstevel@tonic-gate 			next();
11970Sstevel@tonic-gate 			if (EQ("host"))
11980Sstevel@tonic-gate 				next();
11990Sstevel@tonic-gate 			if (tokentype != ALPHA && tokentype != ADDR_IP6)
12000Sstevel@tonic-gate 				pr_err("host/IPv6 addr expected after inet6");
12010Sstevel@tonic-gate 			pf_ipaddr_match(dir, token, IPV6_ONLY);
12020Sstevel@tonic-gate 			opstack++;
12030Sstevel@tonic-gate 			next();
12040Sstevel@tonic-gate 			break;
12050Sstevel@tonic-gate 		}
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 		if (EQ("proto")) {
12080Sstevel@tonic-gate 			next();
12090Sstevel@tonic-gate 			if (tokentype != NUMBER)
12100Sstevel@tonic-gate 				pr_err("IP proto type expected");
12112760Sdg199075 			pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
12123220Sdg199075 			pf_compare_value(
12133220Sdg199075 			    IPV4_TYPE_HEADER_OFFSET + link_header_len, 1,
12143220Sdg199075 			    tokenval);
12150Sstevel@tonic-gate 			opstack++;
12160Sstevel@tonic-gate 			next();
12170Sstevel@tonic-gate 			break;
12180Sstevel@tonic-gate 		}
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 		if (EQ("broadcast")) {
12212760Sdg199075 			pf_clear_offset_register();
12220Sstevel@tonic-gate 			pf_compare_value(link_dest_offset, 4, 0xffffffff);
12230Sstevel@tonic-gate 			opstack++;
12240Sstevel@tonic-gate 			next();
12250Sstevel@tonic-gate 			break;
12260Sstevel@tonic-gate 		}
12270Sstevel@tonic-gate 
12280Sstevel@tonic-gate 		if (EQ("multicast")) {
12292760Sdg199075 			pf_clear_offset_register();
12300Sstevel@tonic-gate 			pf_compare_value_mask(link_dest_offset, 1, 0x01, 0x01);
12310Sstevel@tonic-gate 			opstack++;
12320Sstevel@tonic-gate 			next();
12330Sstevel@tonic-gate 			break;
12340Sstevel@tonic-gate 		}
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate 		if (EQ("ethertype")) {
12370Sstevel@tonic-gate 			next();
12380Sstevel@tonic-gate 			if (tokentype != NUMBER)
12390Sstevel@tonic-gate 				pr_err("ether type expected");
12402760Sdg199075 			pf_match_ethertype(tokenval);
12410Sstevel@tonic-gate 			opstack++;
12420Sstevel@tonic-gate 			next();
12430Sstevel@tonic-gate 			break;
12440Sstevel@tonic-gate 		}
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 		if (EQ("net") || EQ("dstnet") || EQ("srcnet")) {
12470Sstevel@tonic-gate 			if (EQ("dstnet"))
12480Sstevel@tonic-gate 				dir = TO;
12490Sstevel@tonic-gate 			else if (EQ("srcnet"))
12500Sstevel@tonic-gate 				dir = FROM;
12510Sstevel@tonic-gate 			next();
12520Sstevel@tonic-gate 			pf_netaddr_match(dir, token);
12530Sstevel@tonic-gate 			dir = ANY;
12540Sstevel@tonic-gate 			opstack++;
12550Sstevel@tonic-gate 			next();
12560Sstevel@tonic-gate 			break;
12570Sstevel@tonic-gate 		}
12580Sstevel@tonic-gate 
12590Sstevel@tonic-gate 		/*
12600Sstevel@tonic-gate 		 * Give up on anything that's obviously
12610Sstevel@tonic-gate 		 * not a primary.
12620Sstevel@tonic-gate 		 */
12630Sstevel@tonic-gate 		if (EQ("and") || EQ("or") ||
12640Sstevel@tonic-gate 		    EQ("not") || EQ("decnet") || EQ("apple") ||
12650Sstevel@tonic-gate 		    EQ("length") || EQ("less") || EQ("greater") ||
12660Sstevel@tonic-gate 		    EQ("port") || EQ("srcport") || EQ("dstport") ||
12670Sstevel@tonic-gate 		    EQ("rpc") || EQ("gateway") || EQ("nofrag") ||
1268*3431Scarlsonj 		    EQ("bootp") || EQ("dhcp") || EQ("dhcp6") ||
1269*3431Scarlsonj 		    EQ("slp") || EQ("ldap")) {
12700Sstevel@tonic-gate 			break;
12710Sstevel@tonic-gate 		}
12720Sstevel@tonic-gate 
12730Sstevel@tonic-gate 		if (EQ("host") || EQ("between") ||
12740Sstevel@tonic-gate 		    tokentype == ALPHA || /* assume its a hostname */
12750Sstevel@tonic-gate 		    tokentype == ADDR_IP ||
12760Sstevel@tonic-gate 		    tokentype == ADDR_IP6 ||
12770Sstevel@tonic-gate 		    tokentype == ADDR_ETHER) {
12780Sstevel@tonic-gate 			if (EQ("host") || EQ("between"))
12790Sstevel@tonic-gate 				next();
12800Sstevel@tonic-gate 			if (eaddr || tokentype == ADDR_ETHER) {
12810Sstevel@tonic-gate 				pf_etheraddr_match(dir, token);
12820Sstevel@tonic-gate 			} else if (tokentype == ALPHA) {
12830Sstevel@tonic-gate 				pf_ipaddr_match(dir, token, IPV4_AND_IPV6);
12840Sstevel@tonic-gate 			} else if (tokentype == ADDR_IP) {
12850Sstevel@tonic-gate 				pf_ipaddr_match(dir, token, IPV4_ONLY);
12860Sstevel@tonic-gate 			} else {
12870Sstevel@tonic-gate 				pf_ipaddr_match(dir, token, IPV6_ONLY);
12880Sstevel@tonic-gate 			}
12890Sstevel@tonic-gate 			dir = ANY;
12900Sstevel@tonic-gate 			eaddr = 0;
12910Sstevel@tonic-gate 			opstack++;
12920Sstevel@tonic-gate 			next();
12930Sstevel@tonic-gate 			break;
12940Sstevel@tonic-gate 		}
12950Sstevel@tonic-gate 
12960Sstevel@tonic-gate 		break;	/* unknown token */
12970Sstevel@tonic-gate 	}
12980Sstevel@tonic-gate }
12990Sstevel@tonic-gate 
13000Sstevel@tonic-gate static void
13010Sstevel@tonic-gate pf_alternation()
13020Sstevel@tonic-gate {
13030Sstevel@tonic-gate 	int s = opstack;
13040Sstevel@tonic-gate 
13050Sstevel@tonic-gate 	pf_primary();
13060Sstevel@tonic-gate 	for (;;) {
13070Sstevel@tonic-gate 		if (EQ("and"))
13080Sstevel@tonic-gate 			next();
13090Sstevel@tonic-gate 		pf_primary();
13100Sstevel@tonic-gate 		if (opstack != s + 2)
13110Sstevel@tonic-gate 			break;
13120Sstevel@tonic-gate 		pf_emit(ENF_AND);
13130Sstevel@tonic-gate 		opstack--;
13140Sstevel@tonic-gate 	}
13150Sstevel@tonic-gate }
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate static void
13180Sstevel@tonic-gate pf_expression()
13190Sstevel@tonic-gate {
13200Sstevel@tonic-gate 	pf_alternation();
13210Sstevel@tonic-gate 	while (EQ("or") || EQ(",")) {
13220Sstevel@tonic-gate 		if (inBrace)
13230Sstevel@tonic-gate 			inBraceOR++;
13240Sstevel@tonic-gate 		else
13250Sstevel@tonic-gate 			foundOR++;
13260Sstevel@tonic-gate 		next();
13270Sstevel@tonic-gate 		pf_alternation();
13280Sstevel@tonic-gate 		pf_emit(ENF_OR);
13290Sstevel@tonic-gate 		opstack--;
13300Sstevel@tonic-gate 	}
13310Sstevel@tonic-gate }
13320Sstevel@tonic-gate 
13330Sstevel@tonic-gate /*
13340Sstevel@tonic-gate  * Attempt to compile the expression
13350Sstevel@tonic-gate  * in the string "e".  If we can generate
13360Sstevel@tonic-gate  * pf code for it then return 1 - otherwise
13370Sstevel@tonic-gate  * return 0 and leave it up to the user-level
13380Sstevel@tonic-gate  * filter.
13390Sstevel@tonic-gate  */
13400Sstevel@tonic-gate int
13410Sstevel@tonic-gate pf_compile(e, print)
13420Sstevel@tonic-gate 	char *e;
13430Sstevel@tonic-gate 	int print;
13440Sstevel@tonic-gate {
13450Sstevel@tonic-gate 	char *argstr;
13460Sstevel@tonic-gate 	char *sav_str, *ptr, *sav_ptr;
13470Sstevel@tonic-gate 	int inBr = 0, aheadOR = 0;
13480Sstevel@tonic-gate 
13490Sstevel@tonic-gate 	argstr = strdup(e);
13500Sstevel@tonic-gate 	sav_str = e;
13510Sstevel@tonic-gate 	tkp = argstr;
13520Sstevel@tonic-gate 	dir = ANY;
13530Sstevel@tonic-gate 
13540Sstevel@tonic-gate 	pfp = &pf.Pf_Filter[0];
13550Sstevel@tonic-gate 	if (setjmp(env)) {
13560Sstevel@tonic-gate 		return (0);
13570Sstevel@tonic-gate 	}
13580Sstevel@tonic-gate 
13590Sstevel@tonic-gate 	/*
13600Sstevel@tonic-gate 	 * Set media specific packet offsets that this code uses.
13610Sstevel@tonic-gate 	 */
13620Sstevel@tonic-gate 	if (interface->mac_type == DL_IB) {
13630Sstevel@tonic-gate 		link_header_len = 4;
13640Sstevel@tonic-gate 		link_type_offset = 0;
13650Sstevel@tonic-gate 		link_dest_offset = link_src_offset = -1;
13660Sstevel@tonic-gate 		link_addr_len = 20;
13670Sstevel@tonic-gate 	}
13680Sstevel@tonic-gate 
13690Sstevel@tonic-gate 	next();
13700Sstevel@tonic-gate 	pf_expression();
13710Sstevel@tonic-gate 
13720Sstevel@tonic-gate 	if (tokentype != EOL) {
13730Sstevel@tonic-gate 		/*
13740Sstevel@tonic-gate 		 * The idea here is to do as much filtering as possible in
13750Sstevel@tonic-gate 		 * the kernel. So even if we find a token we don't understand,
13760Sstevel@tonic-gate 		 * we try to see if we can still set up a portion of the filter
13770Sstevel@tonic-gate 		 * in the kernel and use the userland filter to filter the
13780Sstevel@tonic-gate 		 * remaining stuff. Obviously, if our filter expression is of
13790Sstevel@tonic-gate 		 * type A AND B, we can filter A in kernel and then apply B
13800Sstevel@tonic-gate 		 * to the packets that got through. The same is not true for
13810Sstevel@tonic-gate 		 * a filter of type A OR B. We can't apply A first and then B
13820Sstevel@tonic-gate 		 * on the packets filtered through A.
13830Sstevel@tonic-gate 		 *
13840Sstevel@tonic-gate 		 * (We need to keep track of the fact when we find an OR,
13850Sstevel@tonic-gate 		 * and the fact that we are inside brackets when we find OR.
13860Sstevel@tonic-gate 		 * The variable 'foundOR' tells us if there was an OR behind,
13870Sstevel@tonic-gate 		 * 'inBraceOR' tells us if we found an OR before we could find
13880Sstevel@tonic-gate 		 * the end brace i.e. ')', and variable 'aheadOR' checks if
13890Sstevel@tonic-gate 		 * there is an OR in the expression ahead. if either of these
13900Sstevel@tonic-gate 		 * cases become true, we can't split the filtering)
13910Sstevel@tonic-gate 		 */
13920Sstevel@tonic-gate 
13930Sstevel@tonic-gate 		if (foundOR || inBraceOR) {
13940Sstevel@tonic-gate 			/* FORGET IN KERNEL FILTERING */
13950Sstevel@tonic-gate 			return (0);
13960Sstevel@tonic-gate 		} else {
13970Sstevel@tonic-gate 
13980Sstevel@tonic-gate 			/* CHECK IF NO OR AHEAD */
13990Sstevel@tonic-gate 			sav_ptr = (char *)((uintptr_t)sav_str +
14000Sstevel@tonic-gate 						(uintptr_t)sav_tkp -
14010Sstevel@tonic-gate 						(uintptr_t)argstr);
14020Sstevel@tonic-gate 			ptr = sav_ptr;
14030Sstevel@tonic-gate 			while (*ptr != '\0') {
14040Sstevel@tonic-gate 				switch (*ptr) {
14050Sstevel@tonic-gate 				case '(':
14060Sstevel@tonic-gate 					inBr++;
14070Sstevel@tonic-gate 					break;
14080Sstevel@tonic-gate 				case ')':
14090Sstevel@tonic-gate 					inBr--;
14100Sstevel@tonic-gate 					break;
14110Sstevel@tonic-gate 				case 'o':
14120Sstevel@tonic-gate 				case 'O':
14130Sstevel@tonic-gate 					if ((*(ptr + 1) == 'R' ||
14140Sstevel@tonic-gate 						*(ptr + 1) == 'r') && !inBr)
14150Sstevel@tonic-gate 						aheadOR = 1;
14160Sstevel@tonic-gate 					break;
14170Sstevel@tonic-gate 				case ',':
14180Sstevel@tonic-gate 					if (!inBr)
14190Sstevel@tonic-gate 						aheadOR = 1;
14200Sstevel@tonic-gate 					break;
14210Sstevel@tonic-gate 				}
14220Sstevel@tonic-gate 				ptr++;
14230Sstevel@tonic-gate 			}
14240Sstevel@tonic-gate 			if (!aheadOR) {
14250Sstevel@tonic-gate 				/* NO OR AHEAD, SPLIT UP THE FILTERING */
14260Sstevel@tonic-gate 				pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0];
14270Sstevel@tonic-gate 				pf.Pf_Priority = 5;
14280Sstevel@tonic-gate 				if (print) {
14290Sstevel@tonic-gate 					pf_codeprint(&pf.Pf_Filter[0],
14300Sstevel@tonic-gate 							pf.Pf_FilterLen);
14310Sstevel@tonic-gate 				}
14320Sstevel@tonic-gate 				compile(sav_ptr, print);
14330Sstevel@tonic-gate 				return (2);
14340Sstevel@tonic-gate 			} else
14350Sstevel@tonic-gate 				return (0);
14360Sstevel@tonic-gate 		}
14370Sstevel@tonic-gate 	}
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 	pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0];
14400Sstevel@tonic-gate 	pf.Pf_Priority = 5;	/* unimportant, so long as > 2 */
14410Sstevel@tonic-gate 	if (print) {
14420Sstevel@tonic-gate 		pf_codeprint(&pf.Pf_Filter[0], pf.Pf_FilterLen);
14430Sstevel@tonic-gate 	}
14440Sstevel@tonic-gate 	return (1);
14450Sstevel@tonic-gate }
1446