xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pf.c (revision 8023:faf256d5c16c)
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 /*
226631Sss150715  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <stdio.h>
272760Sdg199075 #include <stddef.h>
280Sstevel@tonic-gate #include <ctype.h>
290Sstevel@tonic-gate #include <string.h>
300Sstevel@tonic-gate #include <fcntl.h>
310Sstevel@tonic-gate #include <string.h>
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/time.h>
340Sstevel@tonic-gate #include <sys/isa_defs.h>
350Sstevel@tonic-gate 
360Sstevel@tonic-gate #include <sys/socket.h>
372760Sdg199075 #include <sys/vlan.h>
380Sstevel@tonic-gate #include <net/if.h>
390Sstevel@tonic-gate #include <netinet/in_systm.h>
400Sstevel@tonic-gate #include <netinet/in.h>
410Sstevel@tonic-gate #include <netinet/ip.h>
420Sstevel@tonic-gate #include <netinet/if_ether.h>
430Sstevel@tonic-gate #include <netinet/tcp.h>
440Sstevel@tonic-gate #include <netinet/udp.h>
45*8023SPhil.Kirk@Sun.COM #include <inet/ip.h>
46*8023SPhil.Kirk@Sun.COM #include <inet/ip6.h>
470Sstevel@tonic-gate #include <netdb.h>
480Sstevel@tonic-gate #include <rpc/rpc.h>
490Sstevel@tonic-gate #include <setjmp.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate #include <sys/pfmod.h>
520Sstevel@tonic-gate #include "snoop.h"
532760Sdg199075 #include "snoop_vlan.h"
540Sstevel@tonic-gate 
550Sstevel@tonic-gate /*
560Sstevel@tonic-gate  * This module generates code for the kernel packet filter.
570Sstevel@tonic-gate  * The kernel packet filter is more efficient since it
580Sstevel@tonic-gate  * operates without context switching or moving data into
590Sstevel@tonic-gate  * the capture buffer.  On the other hand, it is limited
600Sstevel@tonic-gate  * in its filtering ability i.e. can't cope with variable
610Sstevel@tonic-gate  * length headers, can't compare the packet size, 1 and 4 octet
620Sstevel@tonic-gate  * comparisons are awkward, code space is limited to ENMAXFILTERS
630Sstevel@tonic-gate  * halfwords, etc.
640Sstevel@tonic-gate  * The parser is the same for the user-level packet filter though
650Sstevel@tonic-gate  * more limited in the variety of expressions it can generate
660Sstevel@tonic-gate  * code for.  If the pf compiler finds an expression it can't
670Sstevel@tonic-gate  * handle, it tries to set up a split filter in kernel and do the
680Sstevel@tonic-gate  * remaining filtering in userland. If that also fails, it resorts
690Sstevel@tonic-gate  * to userland filter. (See additional comment in pf_compile)
700Sstevel@tonic-gate  */
710Sstevel@tonic-gate 
720Sstevel@tonic-gate extern struct Pf_ext_packetfilt pf;
730Sstevel@tonic-gate static ushort_t *pfp;
740Sstevel@tonic-gate jmp_buf env;
750Sstevel@tonic-gate 
760Sstevel@tonic-gate int eaddr;	/* need ethernet addr */
770Sstevel@tonic-gate 
780Sstevel@tonic-gate int opstack;	/* operand stack depth */
790Sstevel@tonic-gate 
800Sstevel@tonic-gate #define	EQ(val)		(strcmp(token, val) == 0)
810Sstevel@tonic-gate #define	IPV4_ONLY	0
820Sstevel@tonic-gate #define	IPV6_ONLY	1
830Sstevel@tonic-gate #define	IPV4_AND_IPV6	2
840Sstevel@tonic-gate 
85*8023SPhil.Kirk@Sun.COM typedef struct {
86*8023SPhil.Kirk@Sun.COM 	int	transport_protocol;
87*8023SPhil.Kirk@Sun.COM 	int	network_protocol;
88*8023SPhil.Kirk@Sun.COM 	/*
89*8023SPhil.Kirk@Sun.COM 	 * offset is the offset in bytes from the beginning
90*8023SPhil.Kirk@Sun.COM 	 * of the network protocol header to where the transport
91*8023SPhil.Kirk@Sun.COM 	 * protocol type is.
92*8023SPhil.Kirk@Sun.COM 	 */
93*8023SPhil.Kirk@Sun.COM 	int	offset;
94*8023SPhil.Kirk@Sun.COM } transport_protocol_table_t;
95*8023SPhil.Kirk@Sun.COM 
96*8023SPhil.Kirk@Sun.COM typedef struct network_table {
97*8023SPhil.Kirk@Sun.COM 	char *nmt_name;
98*8023SPhil.Kirk@Sun.COM 	int nmt_val;
99*8023SPhil.Kirk@Sun.COM } network_table_t;
100*8023SPhil.Kirk@Sun.COM 
101*8023SPhil.Kirk@Sun.COM static network_table_t ether_network_mapping_table[] = {
102*8023SPhil.Kirk@Sun.COM 	{ "pup", ETHERTYPE_PUP },
103*8023SPhil.Kirk@Sun.COM 	{ "ip", ETHERTYPE_IP },
104*8023SPhil.Kirk@Sun.COM 	{ "arp", ETHERTYPE_ARP },
105*8023SPhil.Kirk@Sun.COM 	{ "revarp", ETHERTYPE_REVARP },
106*8023SPhil.Kirk@Sun.COM 	{ "at", ETHERTYPE_AT },
107*8023SPhil.Kirk@Sun.COM 	{ "aarp", ETHERTYPE_AARP },
108*8023SPhil.Kirk@Sun.COM 	{ "vlan", ETHERTYPE_VLAN },
109*8023SPhil.Kirk@Sun.COM 	{ "ip6", ETHERTYPE_IPV6 },
110*8023SPhil.Kirk@Sun.COM 	{ "slow", ETHERTYPE_SLOW },
111*8023SPhil.Kirk@Sun.COM 	{ "ppoed", ETHERTYPE_PPPOED },
112*8023SPhil.Kirk@Sun.COM 	{ "ppoes", ETHERTYPE_PPPOES },
113*8023SPhil.Kirk@Sun.COM 	{ "NULL", -1 }
114*8023SPhil.Kirk@Sun.COM 
115*8023SPhil.Kirk@Sun.COM };
116*8023SPhil.Kirk@Sun.COM 
117*8023SPhil.Kirk@Sun.COM static network_table_t ib_network_mapping_table[] = {
118*8023SPhil.Kirk@Sun.COM 	{ "pup", ETHERTYPE_PUP },
119*8023SPhil.Kirk@Sun.COM 	{ "ip", ETHERTYPE_IP },
120*8023SPhil.Kirk@Sun.COM 	{ "arp", ETHERTYPE_ARP },
121*8023SPhil.Kirk@Sun.COM 	{ "revarp", ETHERTYPE_REVARP },
122*8023SPhil.Kirk@Sun.COM 	{ "at", ETHERTYPE_AT },
123*8023SPhil.Kirk@Sun.COM 	{ "aarp", ETHERTYPE_AARP },
124*8023SPhil.Kirk@Sun.COM 	{ "vlan", ETHERTYPE_VLAN },
125*8023SPhil.Kirk@Sun.COM 	{ "ip6", ETHERTYPE_IPV6 },
126*8023SPhil.Kirk@Sun.COM 	{ "slow", ETHERTYPE_SLOW },
127*8023SPhil.Kirk@Sun.COM 	{ "ppoed", ETHERTYPE_PPPOED },
128*8023SPhil.Kirk@Sun.COM 	{ "ppoes", ETHERTYPE_PPPOES },
129*8023SPhil.Kirk@Sun.COM 	{ "NULL", -1 }
130*8023SPhil.Kirk@Sun.COM 
131*8023SPhil.Kirk@Sun.COM };
132*8023SPhil.Kirk@Sun.COM 
133*8023SPhil.Kirk@Sun.COM static network_table_t ipnet_network_mapping_table[] = {
134*8023SPhil.Kirk@Sun.COM 	{ "ip", (DL_IPNETINFO_VERSION << 8 | IPV4_VERSION) },
135*8023SPhil.Kirk@Sun.COM 	{ "ip6", (DL_IPNETINFO_VERSION << 8 | IPV6_VERSION) },
136*8023SPhil.Kirk@Sun.COM 	{ "NULL", -1 }
137*8023SPhil.Kirk@Sun.COM 
138*8023SPhil.Kirk@Sun.COM };
139*8023SPhil.Kirk@Sun.COM 
140*8023SPhil.Kirk@Sun.COM static transport_protocol_table_t ether_transport_mapping_table[] = {
141*8023SPhil.Kirk@Sun.COM 	{IPPROTO_TCP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
142*8023SPhil.Kirk@Sun.COM 	{IPPROTO_TCP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
143*8023SPhil.Kirk@Sun.COM 	{IPPROTO_UDP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
144*8023SPhil.Kirk@Sun.COM 	{IPPROTO_UDP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
145*8023SPhil.Kirk@Sun.COM 	{IPPROTO_OSPF, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
146*8023SPhil.Kirk@Sun.COM 	{IPPROTO_OSPF, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
147*8023SPhil.Kirk@Sun.COM 	{IPPROTO_SCTP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
148*8023SPhil.Kirk@Sun.COM 	{IPPROTO_SCTP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
149*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ICMP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
150*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ICMPV6, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
151*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ENCAP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
152*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ESP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
153*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ESP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
154*8023SPhil.Kirk@Sun.COM 	{IPPROTO_AH, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
155*8023SPhil.Kirk@Sun.COM 	{IPPROTO_AH, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
156*8023SPhil.Kirk@Sun.COM 	{-1, 0, 0}	/* must be the final entry */
157*8023SPhil.Kirk@Sun.COM };
1580Sstevel@tonic-gate 
159*8023SPhil.Kirk@Sun.COM static transport_protocol_table_t ipnet_transport_mapping_table[] = {
160*8023SPhil.Kirk@Sun.COM 	{IPPROTO_TCP, (DL_IPNETINFO_VERSION << 8 | IPV4_VERSION),
161*8023SPhil.Kirk@Sun.COM 	    IPV4_TYPE_HEADER_OFFSET},
162*8023SPhil.Kirk@Sun.COM 	{IPPROTO_TCP, (DL_IPNETINFO_VERSION << 8 | IPV6_VERSION),
163*8023SPhil.Kirk@Sun.COM 	    IPV6_TYPE_HEADER_OFFSET},
164*8023SPhil.Kirk@Sun.COM 	{IPPROTO_UDP, (DL_IPNETINFO_VERSION << 8 | IPV4_VERSION),
165*8023SPhil.Kirk@Sun.COM 	    IPV4_TYPE_HEADER_OFFSET},
166*8023SPhil.Kirk@Sun.COM 	{IPPROTO_UDP, (DL_IPNETINFO_VERSION << 8 | IPV6_VERSION),
167*8023SPhil.Kirk@Sun.COM 	    IPV6_TYPE_HEADER_OFFSET},
168*8023SPhil.Kirk@Sun.COM 	{IPPROTO_OSPF, (DL_IPNETINFO_VERSION << 8 | IPV4_VERSION),
169*8023SPhil.Kirk@Sun.COM 	    IPV4_TYPE_HEADER_OFFSET},
170*8023SPhil.Kirk@Sun.COM 	{IPPROTO_OSPF, (DL_IPNETINFO_VERSION << 8 | IPV6_VERSION),
171*8023SPhil.Kirk@Sun.COM 	    IPV6_TYPE_HEADER_OFFSET},
172*8023SPhil.Kirk@Sun.COM 	{IPPROTO_SCTP, (DL_IPNETINFO_VERSION << 8 | IPV4_VERSION),
173*8023SPhil.Kirk@Sun.COM 	    IPV4_TYPE_HEADER_OFFSET},
174*8023SPhil.Kirk@Sun.COM 	{IPPROTO_SCTP, (DL_IPNETINFO_VERSION << 8 | IPV6_VERSION),
175*8023SPhil.Kirk@Sun.COM 	    IPV6_TYPE_HEADER_OFFSET},
176*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ICMP, (DL_IPNETINFO_VERSION << 8 | IPV4_VERSION),
177*8023SPhil.Kirk@Sun.COM 	    IPV4_TYPE_HEADER_OFFSET},
178*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ICMPV6, (DL_IPNETINFO_VERSION << 8 | IPV6_VERSION),
179*8023SPhil.Kirk@Sun.COM 	    IPV6_TYPE_HEADER_OFFSET},
180*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ENCAP, (DL_IPNETINFO_VERSION << 8 | IPV4_VERSION),
181*8023SPhil.Kirk@Sun.COM 	    IPV4_TYPE_HEADER_OFFSET},
182*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ESP, (DL_IPNETINFO_VERSION << 8 | IPV4_VERSION),
183*8023SPhil.Kirk@Sun.COM 	    IPV4_TYPE_HEADER_OFFSET},
184*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ESP, (DL_IPNETINFO_VERSION << 8 | IPV6_VERSION),
185*8023SPhil.Kirk@Sun.COM 	    IPV6_TYPE_HEADER_OFFSET},
186*8023SPhil.Kirk@Sun.COM 	{IPPROTO_AH, (DL_IPNETINFO_VERSION << 8 | IPV4_VERSION),
187*8023SPhil.Kirk@Sun.COM 	    IPV4_TYPE_HEADER_OFFSET},
188*8023SPhil.Kirk@Sun.COM 	{IPPROTO_AH, (DL_IPNETINFO_VERSION << 8 | IPV6_VERSION),
189*8023SPhil.Kirk@Sun.COM 	    IPV6_TYPE_HEADER_OFFSET},
190*8023SPhil.Kirk@Sun.COM 	{-1, 0, 0}	/* must be the final entry */
191*8023SPhil.Kirk@Sun.COM };
192*8023SPhil.Kirk@Sun.COM 
193*8023SPhil.Kirk@Sun.COM static transport_protocol_table_t ib_transport_mapping_table[] = {
194*8023SPhil.Kirk@Sun.COM 	{IPPROTO_TCP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
195*8023SPhil.Kirk@Sun.COM 	{IPPROTO_TCP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
196*8023SPhil.Kirk@Sun.COM 	{IPPROTO_UDP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
197*8023SPhil.Kirk@Sun.COM 	{IPPROTO_UDP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
198*8023SPhil.Kirk@Sun.COM 	{IPPROTO_OSPF, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
199*8023SPhil.Kirk@Sun.COM 	{IPPROTO_OSPF, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
200*8023SPhil.Kirk@Sun.COM 	{IPPROTO_SCTP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
201*8023SPhil.Kirk@Sun.COM 	{IPPROTO_SCTP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
202*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ICMP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
203*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ICMPV6, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
204*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ENCAP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
205*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ESP, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
206*8023SPhil.Kirk@Sun.COM 	{IPPROTO_ESP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
207*8023SPhil.Kirk@Sun.COM 	{IPPROTO_AH, ETHERTYPE_IP,   IPV4_TYPE_HEADER_OFFSET},
208*8023SPhil.Kirk@Sun.COM 	{IPPROTO_AH, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
209*8023SPhil.Kirk@Sun.COM 	{-1, 0, 0}	/* must be the final entry */
210*8023SPhil.Kirk@Sun.COM };
211*8023SPhil.Kirk@Sun.COM 
212*8023SPhil.Kirk@Sun.COM typedef struct datalink {
213*8023SPhil.Kirk@Sun.COM 	uint_t	dl_type;
214*8023SPhil.Kirk@Sun.COM 	void	(*dl_match_fn)(uint_t datatype);
215*8023SPhil.Kirk@Sun.COM 	transport_protocol_table_t *dl_transport_mapping_table;
216*8023SPhil.Kirk@Sun.COM 	network_table_t *dl_net_map_tbl;
217*8023SPhil.Kirk@Sun.COM 	int dl_link_header_len;
218*8023SPhil.Kirk@Sun.COM 	int dl_link_type_offset;
219*8023SPhil.Kirk@Sun.COM 	int dl_link_dest_offset;
220*8023SPhil.Kirk@Sun.COM 	int dl_link_src_offset;
221*8023SPhil.Kirk@Sun.COM 	int dl_link_addr_len;
222*8023SPhil.Kirk@Sun.COM } datalink_t;
223*8023SPhil.Kirk@Sun.COM 
224*8023SPhil.Kirk@Sun.COM datalink_t	dl;
225*8023SPhil.Kirk@Sun.COM 
226*8023SPhil.Kirk@Sun.COM #define	IPV4_SRCADDR_OFFSET	(dl.dl_link_header_len + 12)
227*8023SPhil.Kirk@Sun.COM #define	IPV4_DSTADDR_OFFSET	(dl.dl_link_header_len + 16)
228*8023SPhil.Kirk@Sun.COM #define	IPV6_SRCADDR_OFFSET	(dl.dl_link_header_len + 8)
229*8023SPhil.Kirk@Sun.COM #define	IPV6_DSTADDR_OFFSET	(dl.dl_link_header_len + 24)
230*8023SPhil.Kirk@Sun.COM 
231*8023SPhil.Kirk@Sun.COM #define	IPNET_SRCZONE_OFFSET 8
232*8023SPhil.Kirk@Sun.COM #define	IPNET_DSTZONE_OFFSET 16
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate static int inBrace = 0, inBraceOR = 0;
2350Sstevel@tonic-gate static int foundOR = 0;
2360Sstevel@tonic-gate char *tkp, *sav_tkp;
2370Sstevel@tonic-gate char *token;
2380Sstevel@tonic-gate enum { EOL, ALPHA, NUMBER, FIELD, ADDR_IP, ADDR_ETHER, SPECIAL,
2390Sstevel@tonic-gate 	ADDR_IP6 } tokentype;
2400Sstevel@tonic-gate uint_t tokenval;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate enum direction { ANY, TO, FROM };
2430Sstevel@tonic-gate enum direction dir;
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate extern void next();
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate static void pf_expression();
2482760Sdg199075 static void pf_check_vlan_tag(uint_t offset);
2492760Sdg199075 static void pf_clear_offset_register();
2502760Sdg199075 static void pf_emit_load_offset(uint_t offset);
2512760Sdg199075 static void pf_match_ethertype(uint_t ethertype);
252*8023SPhil.Kirk@Sun.COM static void pf_match_ipnettype(uint_t type);
253*8023SPhil.Kirk@Sun.COM static void pf_match_ibtype(uint_t type);
2542760Sdg199075 static void pf_check_transport_protocol(uint_t transport_protocol);
2552760Sdg199075 static void pf_compare_value_mask_generic(int offset, uint_t len,
2562760Sdg199075     uint_t val, int mask, uint_t op);
2572760Sdg199075 
2582760Sdg199075 /*
2592760Sdg199075  * This pointer points to the function that last generated
2602760Sdg199075  * instructions to change the offset register.  It's used
2612760Sdg199075  * for comparisons to see if we need to issue more instructions
2622760Sdg199075  * to change the register.
2632760Sdg199075  *
2642760Sdg199075  * It's initialized to pf_clear_offset_register because the offset
2652760Sdg199075  * register in pfmod is initialized to zero, similar to the state
2662760Sdg199075  * it would be in after executing the instructions issued by
2672760Sdg199075  * pf_clear_offset_register.
2682760Sdg199075  */
2692760Sdg199075 static void *last_offset_operation = (void*)pf_clear_offset_register;
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate static void
2720Sstevel@tonic-gate pf_emit(x)
2730Sstevel@tonic-gate 	ushort_t x;
2740Sstevel@tonic-gate {
2750Sstevel@tonic-gate 	if (pfp > &pf.Pf_Filter[PF_MAXFILTERS - 1])
2760Sstevel@tonic-gate 		longjmp(env, 1);
2770Sstevel@tonic-gate 	*pfp++ = x;
2780Sstevel@tonic-gate }
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate static void
2810Sstevel@tonic-gate pf_codeprint(code, len)
2820Sstevel@tonic-gate 	ushort_t *code;
2830Sstevel@tonic-gate 	int len;
2840Sstevel@tonic-gate {
2850Sstevel@tonic-gate 	ushort_t *pc;
2860Sstevel@tonic-gate 	ushort_t *plast = code + len;
2870Sstevel@tonic-gate 	int op, action;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	if (len > 0) {
2900Sstevel@tonic-gate 		printf("Kernel Filter:\n");
2910Sstevel@tonic-gate 	}
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	for (pc = code; pc < plast; pc++) {
2940Sstevel@tonic-gate 		printf("\t%3d: ", pc - code);
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 		op = *pc & 0xfc00;	/* high 10 bits */
2970Sstevel@tonic-gate 		action = *pc & 0x3ff;	/* low   6 bits */
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 		switch (action) {
3000Sstevel@tonic-gate 		case ENF_PUSHLIT:
3010Sstevel@tonic-gate 			printf("PUSHLIT ");
3020Sstevel@tonic-gate 			break;
3030Sstevel@tonic-gate 		case ENF_PUSHZERO:
3040Sstevel@tonic-gate 			printf("PUSHZERO ");
3050Sstevel@tonic-gate 			break;
3060Sstevel@tonic-gate #ifdef ENF_PUSHONE
3070Sstevel@tonic-gate 		case ENF_PUSHONE:
3080Sstevel@tonic-gate 			printf("PUSHONE ");
3090Sstevel@tonic-gate 			break;
3100Sstevel@tonic-gate #endif
3110Sstevel@tonic-gate #ifdef ENF_PUSHFFFF
3120Sstevel@tonic-gate 		case ENF_PUSHFFFF:
3130Sstevel@tonic-gate 			printf("PUSHFFFF ");
3140Sstevel@tonic-gate 			break;
3150Sstevel@tonic-gate #endif
3160Sstevel@tonic-gate #ifdef ENF_PUSHFF00
3170Sstevel@tonic-gate 		case ENF_PUSHFF00:
3180Sstevel@tonic-gate 			printf("PUSHFF00 ");
3190Sstevel@tonic-gate 			break;
3200Sstevel@tonic-gate #endif
3210Sstevel@tonic-gate #ifdef ENF_PUSH00FF
3220Sstevel@tonic-gate 		case ENF_PUSH00FF:
3230Sstevel@tonic-gate 			printf("PUSH00FF ");
3240Sstevel@tonic-gate 			break;
3250Sstevel@tonic-gate #endif
3262760Sdg199075 		case ENF_LOAD_OFFSET:
3272760Sdg199075 			printf("LOAD_OFFSET ");
3282760Sdg199075 			break;
3292760Sdg199075 		case ENF_BRTR:
3302760Sdg199075 			printf("BRTR ");
3312760Sdg199075 			break;
3322760Sdg199075 		case ENF_BRFL:
3332760Sdg199075 			printf("BRFL ");
3342760Sdg199075 			break;
3352760Sdg199075 		case ENF_POP:
3362760Sdg199075 			printf("POP ");
3372760Sdg199075 			break;
3380Sstevel@tonic-gate 		}
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 		if (action >= ENF_PUSHWORD)
3410Sstevel@tonic-gate 			printf("PUSHWORD %d ", action - ENF_PUSHWORD);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 		switch (op) {
3440Sstevel@tonic-gate 		case ENF_EQ:
3450Sstevel@tonic-gate 			printf("EQ ");
3460Sstevel@tonic-gate 			break;
3470Sstevel@tonic-gate 		case ENF_LT:
3480Sstevel@tonic-gate 			printf("LT ");
3490Sstevel@tonic-gate 			break;
3500Sstevel@tonic-gate 		case ENF_LE:
3510Sstevel@tonic-gate 			printf("LE ");
3520Sstevel@tonic-gate 			break;
3530Sstevel@tonic-gate 		case ENF_GT:
3540Sstevel@tonic-gate 			printf("GT ");
3550Sstevel@tonic-gate 			break;
3560Sstevel@tonic-gate 		case ENF_GE:
3570Sstevel@tonic-gate 			printf("GE ");
3580Sstevel@tonic-gate 			break;
3590Sstevel@tonic-gate 		case ENF_AND:
3600Sstevel@tonic-gate 			printf("AND ");
3610Sstevel@tonic-gate 			break;
3620Sstevel@tonic-gate 		case ENF_OR:
3630Sstevel@tonic-gate 			printf("OR ");
3640Sstevel@tonic-gate 			break;
3650Sstevel@tonic-gate 		case ENF_XOR:
3660Sstevel@tonic-gate 			printf("XOR ");
3670Sstevel@tonic-gate 			break;
3680Sstevel@tonic-gate 		case ENF_COR:
3690Sstevel@tonic-gate 			printf("COR ");
3700Sstevel@tonic-gate 			break;
3710Sstevel@tonic-gate 		case ENF_CAND:
3720Sstevel@tonic-gate 			printf("CAND ");
3730Sstevel@tonic-gate 			break;
3740Sstevel@tonic-gate 		case ENF_CNOR:
3750Sstevel@tonic-gate 			printf("CNOR ");
3760Sstevel@tonic-gate 			break;
3770Sstevel@tonic-gate 		case ENF_CNAND:
3780Sstevel@tonic-gate 			printf("CNAND ");
3790Sstevel@tonic-gate 			break;
3800Sstevel@tonic-gate 		case ENF_NEQ:
3810Sstevel@tonic-gate 			printf("NEQ ");
3820Sstevel@tonic-gate 			break;
3830Sstevel@tonic-gate 		}
3840Sstevel@tonic-gate 
3852760Sdg199075 		if (action == ENF_PUSHLIT ||
3862760Sdg199075 		    action == ENF_LOAD_OFFSET ||
3872760Sdg199075 		    action == ENF_BRTR ||
3882760Sdg199075 		    action == ENF_BRFL) {
3890Sstevel@tonic-gate 			pc++;
3900Sstevel@tonic-gate 			printf("\n\t%3d:   %d (0x%04x)", pc - code, *pc, *pc);
3910Sstevel@tonic-gate 		}
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 		printf("\n");
3940Sstevel@tonic-gate 	}
3950Sstevel@tonic-gate }
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate /*
3980Sstevel@tonic-gate  * Emit packet filter code to check a
3990Sstevel@tonic-gate  * field in the packet for a particular value.
4000Sstevel@tonic-gate  * Need different code for each field size.
4010Sstevel@tonic-gate  * Since the pf can only compare 16 bit quantities
4020Sstevel@tonic-gate  * we have to use masking to compare byte values.
4030Sstevel@tonic-gate  * Long word (32 bit) quantities have to be done
4040Sstevel@tonic-gate  * as two 16 bit comparisons.
4050Sstevel@tonic-gate  */
4060Sstevel@tonic-gate static void
4070Sstevel@tonic-gate pf_compare_value(int offset, uint_t len, uint_t val)
4080Sstevel@tonic-gate {
4090Sstevel@tonic-gate 	/*
4100Sstevel@tonic-gate 	 * If the property being filtered on is absent in the media
4110Sstevel@tonic-gate 	 * packet, error out.
4120Sstevel@tonic-gate 	 */
4130Sstevel@tonic-gate 	if (offset == -1)
4140Sstevel@tonic-gate 		pr_err("filter option unsupported on media");
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	switch (len) {
4170Sstevel@tonic-gate 	case 1:
4180Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
4190Sstevel@tonic-gate #if defined(_BIG_ENDIAN)
4200Sstevel@tonic-gate 		if (offset % 2)
4210Sstevel@tonic-gate #else
4220Sstevel@tonic-gate 		if (!(offset % 2))
4230Sstevel@tonic-gate #endif
4240Sstevel@tonic-gate 		{
4250Sstevel@tonic-gate #ifdef ENF_PUSH00FF
4260Sstevel@tonic-gate 			pf_emit(ENF_PUSH00FF | ENF_AND);
4270Sstevel@tonic-gate #else
4280Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_AND);
4290Sstevel@tonic-gate 			pf_emit(0x00FF);
4300Sstevel@tonic-gate #endif
4310Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_EQ);
4320Sstevel@tonic-gate 			pf_emit(val);
4330Sstevel@tonic-gate 		} else {
4340Sstevel@tonic-gate #ifdef ENF_PUSHFF00
4350Sstevel@tonic-gate 			pf_emit(ENF_PUSHFF00 | ENF_AND);
4360Sstevel@tonic-gate #else
4370Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_AND);
4380Sstevel@tonic-gate 			pf_emit(0xFF00);
4390Sstevel@tonic-gate #endif
4400Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_EQ);
4410Sstevel@tonic-gate 			pf_emit(val << 8);
4420Sstevel@tonic-gate 		}
4430Sstevel@tonic-gate 		break;
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	case 2:
4460Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
4470Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_EQ);
4480Sstevel@tonic-gate 		pf_emit((ushort_t)val);
4490Sstevel@tonic-gate 		break;
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 	case 4:
4520Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
4530Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_EQ);
4540Sstevel@tonic-gate #if defined(_BIG_ENDIAN)
4550Sstevel@tonic-gate 		pf_emit(val >> 16);
4560Sstevel@tonic-gate #elif defined(_LITTLE_ENDIAN)
4570Sstevel@tonic-gate 		pf_emit(val & 0xffff);
4580Sstevel@tonic-gate #else
4590Sstevel@tonic-gate #error One of _BIG_ENDIAN and _LITTLE_ENDIAN must be defined
4600Sstevel@tonic-gate #endif
4610Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + (offset / 2) + 1);
4620Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_EQ);
4630Sstevel@tonic-gate #if defined(_BIG_ENDIAN)
4640Sstevel@tonic-gate 		pf_emit(val & 0xffff);
4650Sstevel@tonic-gate #else
4660Sstevel@tonic-gate 		pf_emit(val >> 16);
4670Sstevel@tonic-gate #endif
4680Sstevel@tonic-gate 		pf_emit(ENF_AND);
4690Sstevel@tonic-gate 		break;
4700Sstevel@tonic-gate 	}
4710Sstevel@tonic-gate }
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate /*
4740Sstevel@tonic-gate  * same as pf_compare_value, but only for emiting code to
4750Sstevel@tonic-gate  * compare ipv6 addresses.
4760Sstevel@tonic-gate  */
4770Sstevel@tonic-gate static void
4780Sstevel@tonic-gate pf_compare_value_v6(int offset, uint_t len, struct in6_addr val)
4790Sstevel@tonic-gate {
4800Sstevel@tonic-gate 	int i;
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	for (i = 0; i < len; i += 2) {
4830Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2 + i / 2);
4840Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_EQ);
4850Sstevel@tonic-gate 		pf_emit(*(uint16_t *)&val.s6_addr[i]);
4860Sstevel@tonic-gate 		if (i != 0)
4870Sstevel@tonic-gate 			pf_emit(ENF_AND);
4880Sstevel@tonic-gate 	}
4890Sstevel@tonic-gate }
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate /*
4930Sstevel@tonic-gate  * Same as above except mask the field value
4942760Sdg199075  * before doing the comparison.  The comparison checks
4952760Sdg199075  * to make sure the values are equal.
4960Sstevel@tonic-gate  */
4970Sstevel@tonic-gate static void
4980Sstevel@tonic-gate pf_compare_value_mask(int offset, uint_t len, uint_t val, int mask)
4990Sstevel@tonic-gate {
5002760Sdg199075 	pf_compare_value_mask_generic(offset, len, val, mask, ENF_EQ);
5012760Sdg199075 }
5022760Sdg199075 
5032760Sdg199075 /*
5042760Sdg199075  * Same as above except the values are compared to see if they are not
5052760Sdg199075  * equal.
5062760Sdg199075  */
5072760Sdg199075 static void
5082760Sdg199075 pf_compare_value_mask_neq(int offset, uint_t len, uint_t val, int mask)
5092760Sdg199075 {
5102760Sdg199075 	pf_compare_value_mask_generic(offset, len, val, mask, ENF_NEQ);
5112760Sdg199075 }
5122760Sdg199075 
5132760Sdg199075 /*
5142760Sdg199075  * Similar to pf_compare_value.
5152760Sdg199075  *
5162760Sdg199075  * This is the utility function that does the actual work to compare
5172760Sdg199075  * two values using a mask.  The comparison operation is passed into
5182760Sdg199075  * the function.
5192760Sdg199075  */
5202760Sdg199075 static void
5212760Sdg199075 pf_compare_value_mask_generic(int offset, uint_t len, uint_t val, int mask,
5222760Sdg199075     uint_t op)
5232760Sdg199075 {
5240Sstevel@tonic-gate 	/*
5250Sstevel@tonic-gate 	 * If the property being filtered on is absent in the media
5260Sstevel@tonic-gate 	 * packet, error out.
5270Sstevel@tonic-gate 	 */
5280Sstevel@tonic-gate 	if (offset == -1)
5290Sstevel@tonic-gate 		pr_err("filter option unsupported on media");
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	switch (len) {
5320Sstevel@tonic-gate 	case 1:
5330Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
5340Sstevel@tonic-gate #if defined(_BIG_ENDIAN)
5350Sstevel@tonic-gate 		if (offset % 2)
5360Sstevel@tonic-gate #else
5370Sstevel@tonic-gate 		if (!offset % 2)
5380Sstevel@tonic-gate #endif
5390Sstevel@tonic-gate 		{
5400Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_AND);
5410Sstevel@tonic-gate 			pf_emit(mask & 0x00ff);
5422760Sdg199075 			pf_emit(ENF_PUSHLIT | op);
5430Sstevel@tonic-gate 			pf_emit(val);
5440Sstevel@tonic-gate 		} else {
5450Sstevel@tonic-gate 			pf_emit(ENF_PUSHLIT | ENF_AND);
5460Sstevel@tonic-gate 			pf_emit((mask << 8) & 0xff00);
5472760Sdg199075 			pf_emit(ENF_PUSHLIT | op);
5480Sstevel@tonic-gate 			pf_emit(val << 8);
5490Sstevel@tonic-gate 		}
5500Sstevel@tonic-gate 		break;
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	case 2:
5530Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
5540Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_AND);
5550Sstevel@tonic-gate 		pf_emit(htons((ushort_t)mask));
5562760Sdg199075 		pf_emit(ENF_PUSHLIT | op);
5570Sstevel@tonic-gate 		pf_emit(htons((ushort_t)val));
5580Sstevel@tonic-gate 		break;
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	case 4:
5610Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + offset / 2);
5620Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_AND);
5630Sstevel@tonic-gate 		pf_emit(htons((ushort_t)((mask >> 16) & 0xffff)));
5642760Sdg199075 		pf_emit(ENF_PUSHLIT | op);
5650Sstevel@tonic-gate 		pf_emit(htons((ushort_t)((val >> 16) & 0xffff)));
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 		pf_emit(ENF_PUSHWORD + (offset / 2) + 1);
5680Sstevel@tonic-gate 		pf_emit(ENF_PUSHLIT | ENF_AND);
5690Sstevel@tonic-gate 		pf_emit(htons((ushort_t)(mask & 0xffff)));
5702760Sdg199075 		pf_emit(ENF_PUSHLIT | op);
5710Sstevel@tonic-gate 		pf_emit(htons((ushort_t)(val & 0xffff)));
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 		pf_emit(ENF_AND);
5740Sstevel@tonic-gate 		break;
5750Sstevel@tonic-gate 	}
5760Sstevel@tonic-gate }
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate /*
579*8023SPhil.Kirk@Sun.COM  * Like pf_compare_value() but compare on a 64-bit zoneid value.
580*8023SPhil.Kirk@Sun.COM  * The argument val passed in is in network byte order.
581*8023SPhil.Kirk@Sun.COM  */
582*8023SPhil.Kirk@Sun.COM static void
583*8023SPhil.Kirk@Sun.COM pf_compare_zoneid(int offset, uint64_t val)
584*8023SPhil.Kirk@Sun.COM {
585*8023SPhil.Kirk@Sun.COM 	int i;
586*8023SPhil.Kirk@Sun.COM 
587*8023SPhil.Kirk@Sun.COM 	for (i = 0; i < sizeof (uint64_t) / 2; i ++) {
588*8023SPhil.Kirk@Sun.COM 		pf_emit(ENF_PUSHWORD + offset / 2 + i);
589*8023SPhil.Kirk@Sun.COM 		pf_emit(ENF_PUSHLIT | ENF_EQ);
590*8023SPhil.Kirk@Sun.COM 		pf_emit(((uint16_t *)&val)[i]);
591*8023SPhil.Kirk@Sun.COM 		if (i != 0)
592*8023SPhil.Kirk@Sun.COM 			pf_emit(ENF_AND);
593*8023SPhil.Kirk@Sun.COM 	}
594*8023SPhil.Kirk@Sun.COM }
595*8023SPhil.Kirk@Sun.COM 
596*8023SPhil.Kirk@Sun.COM /*
5970Sstevel@tonic-gate  * Generate pf code to match an IPv4 or IPv6 address.
5980Sstevel@tonic-gate  */
5990Sstevel@tonic-gate static void
6000Sstevel@tonic-gate pf_ipaddr_match(which, hostname, inet_type)
6010Sstevel@tonic-gate 	enum direction which;
6020Sstevel@tonic-gate 	char *hostname;
6030Sstevel@tonic-gate 	int inet_type;
6040Sstevel@tonic-gate {
6050Sstevel@tonic-gate 	bool_t found_host;
6060Sstevel@tonic-gate 	uint_t *addr4ptr;
6070Sstevel@tonic-gate 	uint_t addr4;
6080Sstevel@tonic-gate 	struct in6_addr *addr6ptr;
6090Sstevel@tonic-gate 	int h_addr_index;
6100Sstevel@tonic-gate 	struct hostent *hp = NULL;
6110Sstevel@tonic-gate 	int error_num = 0;
6120Sstevel@tonic-gate 	boolean_t first = B_TRUE;
6130Sstevel@tonic-gate 	int pass = 0;
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	/*
6160Sstevel@tonic-gate 	 * The addr4offset and addr6offset variables simplify the code which
6170Sstevel@tonic-gate 	 * generates the address comparison filter.  With these two variables,
6180Sstevel@tonic-gate 	 * duplicate code need not exist for the TO and FROM case.
6190Sstevel@tonic-gate 	 * A value of -1 describes the ANY case (TO and FROM).
6200Sstevel@tonic-gate 	 */
6210Sstevel@tonic-gate 	int addr4offset;
6220Sstevel@tonic-gate 	int addr6offset;
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	found_host = 0;
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	if (tokentype == ADDR_IP) {
6270Sstevel@tonic-gate 		hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
6280Sstevel@tonic-gate 		if (hp == NULL) {
6290Sstevel@tonic-gate 			if (error_num == TRY_AGAIN) {
6300Sstevel@tonic-gate 				pr_err("could not resolve %s (try again later)",
6310Sstevel@tonic-gate 				    hostname);
6320Sstevel@tonic-gate 			} else {
6330Sstevel@tonic-gate 				pr_err("could not resolve %s", hostname);
6340Sstevel@tonic-gate 			}
6350Sstevel@tonic-gate 		}
6360Sstevel@tonic-gate 		inet_type = IPV4_ONLY;
6370Sstevel@tonic-gate 	} else if (tokentype == ADDR_IP6) {
6380Sstevel@tonic-gate 		hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
6390Sstevel@tonic-gate 		if (hp == NULL) {
6400Sstevel@tonic-gate 			if (error_num == TRY_AGAIN) {
6410Sstevel@tonic-gate 				pr_err("could not resolve %s (try again later)",
6420Sstevel@tonic-gate 				    hostname);
6430Sstevel@tonic-gate 			} else {
6440Sstevel@tonic-gate 				pr_err("could not resolve %s", hostname);
6450Sstevel@tonic-gate 			}
6460Sstevel@tonic-gate 		}
6470Sstevel@tonic-gate 		inet_type = IPV6_ONLY;
6480Sstevel@tonic-gate 	} else if (tokentype == ALPHA) {
6490Sstevel@tonic-gate 		/* Some hostname i.e. tokentype is ALPHA */
6500Sstevel@tonic-gate 		switch (inet_type) {
6510Sstevel@tonic-gate 		case IPV4_ONLY:
6520Sstevel@tonic-gate 			/* Only IPv4 address is needed */
6530Sstevel@tonic-gate 			hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
6540Sstevel@tonic-gate 			if (hp != NULL) {
6550Sstevel@tonic-gate 				found_host = 1;
6560Sstevel@tonic-gate 			}
6570Sstevel@tonic-gate 			break;
6580Sstevel@tonic-gate 		case IPV6_ONLY:
6590Sstevel@tonic-gate 			/* Only IPv6 address is needed */
6600Sstevel@tonic-gate 			hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
6610Sstevel@tonic-gate 			if (hp != NULL) {
6620Sstevel@tonic-gate 				found_host = 1;
6630Sstevel@tonic-gate 			}
6640Sstevel@tonic-gate 			break;
6650Sstevel@tonic-gate 		case IPV4_AND_IPV6:
6660Sstevel@tonic-gate 			/* Both IPv4 and IPv6 are needed */
6670Sstevel@tonic-gate 			hp = getipnodebyname(hostname, AF_INET6,
6680Sstevel@tonic-gate 			    AI_ALL | AI_V4MAPPED, &error_num);
6690Sstevel@tonic-gate 			if (hp != NULL) {
6700Sstevel@tonic-gate 				found_host = 1;
6710Sstevel@tonic-gate 			}
6720Sstevel@tonic-gate 			break;
6730Sstevel@tonic-gate 		default:
6740Sstevel@tonic-gate 			found_host = 0;
6750Sstevel@tonic-gate 		}
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 		if (!found_host) {
6780Sstevel@tonic-gate 			if (error_num == TRY_AGAIN) {
6790Sstevel@tonic-gate 				pr_err("could not resolve %s (try again later)",
6800Sstevel@tonic-gate 				    hostname);
6810Sstevel@tonic-gate 			} else {
6820Sstevel@tonic-gate 				pr_err("could not resolve %s", hostname);
6830Sstevel@tonic-gate 			}
6840Sstevel@tonic-gate 		}
6850Sstevel@tonic-gate 	} else {
6860Sstevel@tonic-gate 		pr_err("unknown token type: %s", hostname);
6870Sstevel@tonic-gate 	}
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate 	switch (which) {
6900Sstevel@tonic-gate 	case TO:
6910Sstevel@tonic-gate 		addr4offset = IPV4_DSTADDR_OFFSET;
6920Sstevel@tonic-gate 		addr6offset = IPV6_DSTADDR_OFFSET;
6930Sstevel@tonic-gate 		break;
6940Sstevel@tonic-gate 	case FROM:
6950Sstevel@tonic-gate 		addr4offset = IPV4_SRCADDR_OFFSET;
6960Sstevel@tonic-gate 		addr6offset = IPV6_SRCADDR_OFFSET;
6970Sstevel@tonic-gate 		break;
6980Sstevel@tonic-gate 	case ANY:
6990Sstevel@tonic-gate 		addr4offset = -1;
7000Sstevel@tonic-gate 		addr6offset = -1;
7010Sstevel@tonic-gate 		break;
7020Sstevel@tonic-gate 	}
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 	if (hp != NULL && hp->h_addrtype == AF_INET) {
705*8023SPhil.Kirk@Sun.COM 		for (; dl.dl_net_map_tbl->nmt_val != -1;
706*8023SPhil.Kirk@Sun.COM 		    dl.dl_net_map_tbl++) {
707*8023SPhil.Kirk@Sun.COM 			if (strcmp("ip",
708*8023SPhil.Kirk@Sun.COM 				dl.dl_net_map_tbl->nmt_name) == 0) {
709*8023SPhil.Kirk@Sun.COM 				dl.dl_match_fn(
710*8023SPhil.Kirk@Sun.COM 					dl.dl_net_map_tbl->nmt_val);
711*8023SPhil.Kirk@Sun.COM 			}
712*8023SPhil.Kirk@Sun.COM 		}
713*8023SPhil.Kirk@Sun.COM 		if (dl.dl_type == DL_ETHER)
714*8023SPhil.Kirk@Sun.COM 			pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
7150Sstevel@tonic-gate 		h_addr_index = 0;
7160Sstevel@tonic-gate 		addr4ptr = (uint_t *)hp->h_addr_list[h_addr_index];
7170Sstevel@tonic-gate 		while (addr4ptr != NULL) {
7180Sstevel@tonic-gate 			if (addr4offset == -1) {
7190Sstevel@tonic-gate 				pf_compare_value(IPV4_SRCADDR_OFFSET, 4,
7200Sstevel@tonic-gate 				    *addr4ptr);
7210Sstevel@tonic-gate 				if (h_addr_index != 0)
7220Sstevel@tonic-gate 					pf_emit(ENF_OR);
7230Sstevel@tonic-gate 				pf_compare_value(IPV4_DSTADDR_OFFSET, 4,
7240Sstevel@tonic-gate 				    *addr4ptr);
7250Sstevel@tonic-gate 				pf_emit(ENF_OR);
7260Sstevel@tonic-gate 			} else {
7270Sstevel@tonic-gate 				pf_compare_value(addr4offset, 4,
7280Sstevel@tonic-gate 				    *addr4ptr);
7290Sstevel@tonic-gate 				if (h_addr_index != 0)
7300Sstevel@tonic-gate 					pf_emit(ENF_OR);
7310Sstevel@tonic-gate 			}
7320Sstevel@tonic-gate 			addr4ptr = (uint_t *)hp->h_addr_list[++h_addr_index];
7330Sstevel@tonic-gate 		}
7340Sstevel@tonic-gate 		pf_emit(ENF_AND);
7350Sstevel@tonic-gate 	} else {
7360Sstevel@tonic-gate 		/* first pass: IPv4 addresses */
7370Sstevel@tonic-gate 		h_addr_index = 0;
7380Sstevel@tonic-gate 		addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
7390Sstevel@tonic-gate 		first = B_TRUE;
7400Sstevel@tonic-gate 		while (addr6ptr != NULL) {
7410Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
7420Sstevel@tonic-gate 				if (first) {
743*8023SPhil.Kirk@Sun.COM 					for (; dl.dl_net_map_tbl->nmt_val != -1;
744*8023SPhil.Kirk@Sun.COM 					    dl.dl_net_map_tbl++) {
745*8023SPhil.Kirk@Sun.COM 						if (strcmp("ip",
746*8023SPhil.Kirk@Sun.COM 							dl.dl_net_map_tbl->
747*8023SPhil.Kirk@Sun.COM 							nmt_name) == 0) {
748*8023SPhil.Kirk@Sun.COM 							dl.dl_match_fn(
749*8023SPhil.Kirk@Sun.COM 								dl.
750*8023SPhil.Kirk@Sun.COM 								dl_net_map_tbl->
751*8023SPhil.Kirk@Sun.COM 								nmt_val);
752*8023SPhil.Kirk@Sun.COM 						}
753*8023SPhil.Kirk@Sun.COM 					}
754*8023SPhil.Kirk@Sun.COM 					if (dl.dl_type == DL_ETHER) {
755*8023SPhil.Kirk@Sun.COM 						pf_check_vlan_tag(
756*8023SPhil.Kirk@Sun.COM 							ENCAP_ETHERTYPE_OFF/2);
757*8023SPhil.Kirk@Sun.COM 					}
7580Sstevel@tonic-gate 					pass++;
7590Sstevel@tonic-gate 				}
7600Sstevel@tonic-gate 				IN6_V4MAPPED_TO_INADDR(addr6ptr,
7610Sstevel@tonic-gate 				    (struct in_addr *)&addr4);
7620Sstevel@tonic-gate 				if (addr4offset == -1) {
7630Sstevel@tonic-gate 					pf_compare_value(IPV4_SRCADDR_OFFSET, 4,
7640Sstevel@tonic-gate 					    addr4);
7650Sstevel@tonic-gate 					if (!first)
7660Sstevel@tonic-gate 						pf_emit(ENF_OR);
7670Sstevel@tonic-gate 					pf_compare_value(IPV4_DSTADDR_OFFSET, 4,
7680Sstevel@tonic-gate 					    addr4);
7690Sstevel@tonic-gate 					pf_emit(ENF_OR);
7700Sstevel@tonic-gate 				} else {
7710Sstevel@tonic-gate 					pf_compare_value(addr4offset, 4,
7720Sstevel@tonic-gate 					    addr4);
7730Sstevel@tonic-gate 					if (!first)
7740Sstevel@tonic-gate 						pf_emit(ENF_OR);
7750Sstevel@tonic-gate 				}
7760Sstevel@tonic-gate 				if (first)
7770Sstevel@tonic-gate 					first = B_FALSE;
7780Sstevel@tonic-gate 			}
7790Sstevel@tonic-gate 			addr6ptr = (struct in6_addr *)
7800Sstevel@tonic-gate 				hp->h_addr_list[++h_addr_index];
7810Sstevel@tonic-gate 		}
7820Sstevel@tonic-gate 		if (!first) {
7830Sstevel@tonic-gate 			pf_emit(ENF_AND);
7840Sstevel@tonic-gate 		}
7850Sstevel@tonic-gate 		/* second pass: IPv6 addresses */
7860Sstevel@tonic-gate 		h_addr_index = 0;
7870Sstevel@tonic-gate 		addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
7880Sstevel@tonic-gate 		first = B_TRUE;
7890Sstevel@tonic-gate 		while (addr6ptr != NULL) {
7900Sstevel@tonic-gate 			if (!IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
7910Sstevel@tonic-gate 				if (first) {
792*8023SPhil.Kirk@Sun.COM 					for (; dl.dl_net_map_tbl->nmt_val != -1;
793*8023SPhil.Kirk@Sun.COM 					    dl.dl_net_map_tbl++) {
794*8023SPhil.Kirk@Sun.COM 						if (strcmp("ip6",
795*8023SPhil.Kirk@Sun.COM 							dl.dl_net_map_tbl->
796*8023SPhil.Kirk@Sun.COM 							nmt_name) == 0) {
797*8023SPhil.Kirk@Sun.COM 							dl.dl_match_fn(
798*8023SPhil.Kirk@Sun.COM 								dl.
799*8023SPhil.Kirk@Sun.COM 								dl_net_map_tbl->
800*8023SPhil.Kirk@Sun.COM 								nmt_val);
801*8023SPhil.Kirk@Sun.COM 						}
802*8023SPhil.Kirk@Sun.COM 					}
803*8023SPhil.Kirk@Sun.COM 					if (dl.dl_type == DL_ETHER) {
804*8023SPhil.Kirk@Sun.COM 						pf_check_vlan_tag(
805*8023SPhil.Kirk@Sun.COM 							ENCAP_ETHERTYPE_OFF/2);
806*8023SPhil.Kirk@Sun.COM 					}
8070Sstevel@tonic-gate 					pass++;
8080Sstevel@tonic-gate 				}
8090Sstevel@tonic-gate 				if (addr6offset == -1) {
8100Sstevel@tonic-gate 					pf_compare_value_v6(IPV6_SRCADDR_OFFSET,
8110Sstevel@tonic-gate 					    16, *addr6ptr);
8120Sstevel@tonic-gate 					if (!first)
8130Sstevel@tonic-gate 						pf_emit(ENF_OR);
8140Sstevel@tonic-gate 					pf_compare_value_v6(IPV6_DSTADDR_OFFSET,
8150Sstevel@tonic-gate 					    16, *addr6ptr);
8160Sstevel@tonic-gate 					pf_emit(ENF_OR);
8170Sstevel@tonic-gate 				} else {
8180Sstevel@tonic-gate 					pf_compare_value_v6(addr6offset, 16,
8190Sstevel@tonic-gate 					    *addr6ptr);
8200Sstevel@tonic-gate 					if (!first)
8210Sstevel@tonic-gate 						pf_emit(ENF_OR);
8220Sstevel@tonic-gate 				}
8230Sstevel@tonic-gate 				if (first)
8240Sstevel@tonic-gate 					first = B_FALSE;
8250Sstevel@tonic-gate 			}
8260Sstevel@tonic-gate 			addr6ptr = (struct in6_addr *)
8270Sstevel@tonic-gate 				hp->h_addr_list[++h_addr_index];
8280Sstevel@tonic-gate 		}
8290Sstevel@tonic-gate 		if (!first) {
8300Sstevel@tonic-gate 			pf_emit(ENF_AND);
8310Sstevel@tonic-gate 		}
8320Sstevel@tonic-gate 		if (pass == 2) {
8330Sstevel@tonic-gate 			pf_emit(ENF_OR);
8340Sstevel@tonic-gate 		}
8350Sstevel@tonic-gate 	}
8360Sstevel@tonic-gate 
8370Sstevel@tonic-gate 	if (hp != NULL) {
8380Sstevel@tonic-gate 		freehostent(hp);
8390Sstevel@tonic-gate 	}
8400Sstevel@tonic-gate }
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate static void
8440Sstevel@tonic-gate pf_compare_address(int offset, uint_t len, uchar_t *addr)
8450Sstevel@tonic-gate {
8460Sstevel@tonic-gate 	uint32_t val;
8470Sstevel@tonic-gate 	uint16_t sval;
8480Sstevel@tonic-gate 	boolean_t didone = B_FALSE;
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate 	/*
8510Sstevel@tonic-gate 	 * If the property being filtered on is absent in the media
8520Sstevel@tonic-gate 	 * packet, error out.
8530Sstevel@tonic-gate 	 */
8540Sstevel@tonic-gate 	if (offset == -1)
8550Sstevel@tonic-gate 		pr_err("filter option unsupported on media");
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	while (len > 0) {
8580Sstevel@tonic-gate 		if (len >= 4) {
8590Sstevel@tonic-gate 			(void) memcpy(&val, addr, 4);
8600Sstevel@tonic-gate 			pf_compare_value(offset, 4, val);
8610Sstevel@tonic-gate 			addr += 4;
8620Sstevel@tonic-gate 			offset += 4;
8630Sstevel@tonic-gate 			len -= 4;
8640Sstevel@tonic-gate 		} else if (len >= 2) {
8650Sstevel@tonic-gate 			(void) memcpy(&sval, addr, 2);
8660Sstevel@tonic-gate 			pf_compare_value(offset, 2, sval);
8670Sstevel@tonic-gate 			addr += 2;
8680Sstevel@tonic-gate 			offset += 2;
8690Sstevel@tonic-gate 			len -= 2;
8700Sstevel@tonic-gate 		} else {
8710Sstevel@tonic-gate 			pf_compare_value(offset++, 1, *addr++);
8720Sstevel@tonic-gate 			len--;
8730Sstevel@tonic-gate 		}
8740Sstevel@tonic-gate 		if (didone)
8750Sstevel@tonic-gate 			pf_emit(ENF_AND);
8760Sstevel@tonic-gate 		didone = B_TRUE;
8770Sstevel@tonic-gate 	}
8780Sstevel@tonic-gate }
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate /*
8810Sstevel@tonic-gate  * Compare ethernet addresses.
8820Sstevel@tonic-gate  */
8830Sstevel@tonic-gate static void
8840Sstevel@tonic-gate pf_etheraddr_match(which, hostname)
8850Sstevel@tonic-gate 	enum direction which;
8860Sstevel@tonic-gate 	char *hostname;
8870Sstevel@tonic-gate {
8880Sstevel@tonic-gate 	struct ether_addr e, *ep = NULL;
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate 	if (isxdigit(*hostname))
8910Sstevel@tonic-gate 		ep = ether_aton(hostname);
8920Sstevel@tonic-gate 	if (ep == NULL) {
8930Sstevel@tonic-gate 		if (ether_hostton(hostname, &e))
8940Sstevel@tonic-gate 			if (!arp_for_ether(hostname, &e))
8950Sstevel@tonic-gate 				pr_err("cannot obtain ether addr for %s",
8960Sstevel@tonic-gate 					hostname);
8970Sstevel@tonic-gate 		ep = &e;
8980Sstevel@tonic-gate 	}
8990Sstevel@tonic-gate 
9002760Sdg199075 	pf_clear_offset_register();
9012760Sdg199075 
9020Sstevel@tonic-gate 	switch (which) {
9030Sstevel@tonic-gate 	case TO:
904*8023SPhil.Kirk@Sun.COM 		pf_compare_address(dl.dl_link_dest_offset, dl.dl_link_addr_len,
9050Sstevel@tonic-gate 		    (uchar_t *)ep);
9060Sstevel@tonic-gate 		break;
9070Sstevel@tonic-gate 	case FROM:
908*8023SPhil.Kirk@Sun.COM 		pf_compare_address(dl.dl_link_src_offset, dl.dl_link_addr_len,
9090Sstevel@tonic-gate 		    (uchar_t *)ep);
9100Sstevel@tonic-gate 		break;
9110Sstevel@tonic-gate 	case ANY:
912*8023SPhil.Kirk@Sun.COM 		pf_compare_address(dl.dl_link_dest_offset, dl.dl_link_addr_len,
9130Sstevel@tonic-gate 		    (uchar_t *)ep);
914*8023SPhil.Kirk@Sun.COM 		pf_compare_address(dl.dl_link_src_offset, dl.dl_link_addr_len,
9150Sstevel@tonic-gate 		    (uchar_t *)ep);
9160Sstevel@tonic-gate 		pf_emit(ENF_OR);
9170Sstevel@tonic-gate 		break;
9180Sstevel@tonic-gate 	}
9190Sstevel@tonic-gate }
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate /*
9220Sstevel@tonic-gate  * Emit code to compare the network part of
9230Sstevel@tonic-gate  * an IP address.
9240Sstevel@tonic-gate  */
9250Sstevel@tonic-gate static void
9260Sstevel@tonic-gate pf_netaddr_match(which, netname)
9270Sstevel@tonic-gate 	enum direction which;
9280Sstevel@tonic-gate 	char *netname;
9290Sstevel@tonic-gate {
9300Sstevel@tonic-gate 	uint_t addr;
9310Sstevel@tonic-gate 	uint_t mask = 0xff000000;
9320Sstevel@tonic-gate 	struct netent *np;
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 	if (isdigit(*netname)) {
9350Sstevel@tonic-gate 		addr = inet_network(netname);
9360Sstevel@tonic-gate 	} else {
9370Sstevel@tonic-gate 		np = getnetbyname(netname);
9380Sstevel@tonic-gate 		if (np == NULL)
9390Sstevel@tonic-gate 			pr_err("net %s not known", netname);
9400Sstevel@tonic-gate 		addr = np->n_net;
9410Sstevel@tonic-gate 	}
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate 	/*
9440Sstevel@tonic-gate 	 * Left justify the address and figure
9450Sstevel@tonic-gate 	 * out a mask based on the supplied address.
9460Sstevel@tonic-gate 	 * Set the mask according to the number of zero
9470Sstevel@tonic-gate 	 * low-order bytes.
9480Sstevel@tonic-gate 	 * Note: this works only for whole octet masks.
9490Sstevel@tonic-gate 	 */
9500Sstevel@tonic-gate 	if (addr) {
9510Sstevel@tonic-gate 		while ((addr & ~mask) != 0) {
9520Sstevel@tonic-gate 			mask |= (mask >> 8);
9530Sstevel@tonic-gate 		}
9540Sstevel@tonic-gate 	}
9550Sstevel@tonic-gate 
9562760Sdg199075 	pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
9572760Sdg199075 
9580Sstevel@tonic-gate 	switch (which) {
9590Sstevel@tonic-gate 	case TO:
9600Sstevel@tonic-gate 		pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask);
9610Sstevel@tonic-gate 		break;
9620Sstevel@tonic-gate 	case FROM:
9630Sstevel@tonic-gate 		pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask);
9640Sstevel@tonic-gate 		break;
9650Sstevel@tonic-gate 	case ANY:
9660Sstevel@tonic-gate 		pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask);
9670Sstevel@tonic-gate 		pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask);
9680Sstevel@tonic-gate 		pf_emit(ENF_OR);
9690Sstevel@tonic-gate 		break;
9700Sstevel@tonic-gate 	}
9710Sstevel@tonic-gate }
9720Sstevel@tonic-gate 
9732760Sdg199075 /*
974*8023SPhil.Kirk@Sun.COM  * Emit code to match on src or destination zoneid.
975*8023SPhil.Kirk@Sun.COM  * The zoneid passed in is in network byte order.
976*8023SPhil.Kirk@Sun.COM  */
977*8023SPhil.Kirk@Sun.COM static void
978*8023SPhil.Kirk@Sun.COM pf_match_zone(enum direction which, uint64_t zoneid)
979*8023SPhil.Kirk@Sun.COM {
980*8023SPhil.Kirk@Sun.COM 	if (dl.dl_type != DL_IPNET)
981*8023SPhil.Kirk@Sun.COM 		pr_err("zone filter option unsupported on media");
982*8023SPhil.Kirk@Sun.COM 
983*8023SPhil.Kirk@Sun.COM 	switch (which) {
984*8023SPhil.Kirk@Sun.COM 	case TO:
985*8023SPhil.Kirk@Sun.COM 		pf_compare_zoneid(IPNET_DSTZONE_OFFSET, zoneid);
986*8023SPhil.Kirk@Sun.COM 		break;
987*8023SPhil.Kirk@Sun.COM 	case FROM:
988*8023SPhil.Kirk@Sun.COM 		pf_compare_zoneid(IPNET_SRCZONE_OFFSET, zoneid);
989*8023SPhil.Kirk@Sun.COM 		break;
990*8023SPhil.Kirk@Sun.COM 	case ANY:
991*8023SPhil.Kirk@Sun.COM 		pf_compare_zoneid(IPNET_SRCZONE_OFFSET, zoneid);
992*8023SPhil.Kirk@Sun.COM 		pf_compare_zoneid(IPNET_DSTZONE_OFFSET, zoneid);
993*8023SPhil.Kirk@Sun.COM 		pf_emit(ENF_OR);
994*8023SPhil.Kirk@Sun.COM 		break;
995*8023SPhil.Kirk@Sun.COM 	}
996*8023SPhil.Kirk@Sun.COM }
997*8023SPhil.Kirk@Sun.COM 
998*8023SPhil.Kirk@Sun.COM /*
9992760Sdg199075  * A helper function to keep the code to emit instructions
10002760Sdg199075  * to change the offset register in one place.
10012760Sdg199075  *
10022760Sdg199075  * INPUTS: offset - An value representing an offset in 16-bit
10032760Sdg199075  *                  words.
10042760Sdg199075  * OUTPUTS:  If there is enough room in the storage for the
10052760Sdg199075  *           packet filtering program, instructions to load
10062760Sdg199075  *           a constant to the offset register.  Otherwise,
10072760Sdg199075  *           nothing.
10082760Sdg199075  */
10092760Sdg199075 static void
10102760Sdg199075 pf_emit_load_offset(uint_t offset)
10112760Sdg199075 {
10122760Sdg199075 	pf_emit(ENF_LOAD_OFFSET | ENF_NOP);
10132760Sdg199075 	pf_emit(offset);
10142760Sdg199075 }
10152760Sdg199075 
10162760Sdg199075 /*
10172760Sdg199075  * Clear pfmod's offset register.
10182760Sdg199075  *
10192760Sdg199075  * INPUTS:  none
10202760Sdg199075  * OUTPUTS:  Instructions to clear the offset register if
10212760Sdg199075  *           there is enough space remaining in the packet
10222760Sdg199075  *           filtering program structure's storage, and
10232760Sdg199075  *           the last thing done to the offset register was
10242760Sdg199075  *           not clearing the offset register.  Otherwise,
10252760Sdg199075  *           nothing.
10262760Sdg199075  */
10272760Sdg199075 static void
10282760Sdg199075 pf_clear_offset_register()
10292760Sdg199075 {
10302760Sdg199075 	if (last_offset_operation != (void*)pf_clear_offset_register) {
10312760Sdg199075 		pf_emit_load_offset(0);
10322760Sdg199075 		last_offset_operation = (void*)pf_clear_offset_register;
10332760Sdg199075 	}
10342760Sdg199075 }
10352760Sdg199075 
10362760Sdg199075 /*
10372760Sdg199075  * This function will issue opcodes to check if a packet
10382760Sdg199075  * is VLAN tagged, and if so, update the offset register
10392760Sdg199075  * with the appropriate offset.
10402760Sdg199075  *
10412760Sdg199075  * Note that if the packet is not VLAN tagged, then the offset
10422760Sdg199075  * register will be cleared.
10432760Sdg199075  *
10442760Sdg199075  * If the interface type is not an ethernet type, then this
10452760Sdg199075  * function returns without doing anything.
10462760Sdg199075  *
10472760Sdg199075  * If the last attempt to change the offset register occured because
10482760Sdg199075  * of a call to this function that was called with the same offset,
10492760Sdg199075  * then we don't issue packet filtering instructions.
10502760Sdg199075  *
10512760Sdg199075  * INPUTS:  offset - an offset in 16 bit words.  The function
10522760Sdg199075  *                   will set the offset register to this
10532760Sdg199075  *                   value if the packet is VLAN tagged.
10542760Sdg199075  * OUTPUTS:  If the conditions are met, packet filtering instructions.
10552760Sdg199075  */
10562760Sdg199075 static void
10572760Sdg199075 pf_check_vlan_tag(uint_t offset)
10582760Sdg199075 {
10592760Sdg199075 	static uint_t last_offset = 0;
10602760Sdg199075 
10612760Sdg199075 	if ((interface->mac_type == DL_ETHER ||
10622760Sdg199075 	    interface->mac_type == DL_CSMACD) &&
10632760Sdg199075 	    (last_offset_operation != (void*)pf_check_vlan_tag ||
10642760Sdg199075 	    last_offset != offset)) {
10652760Sdg199075 		/*
10662760Sdg199075 		 * First thing is to clear the offset register.
10672760Sdg199075 		 * We don't know what state it is in, and if it
10682760Sdg199075 		 * is not zero, then we have no idea what we load
10692760Sdg199075 		 * when we execute ENF_PUSHWORD.
10702760Sdg199075 		 */
10712760Sdg199075 		pf_clear_offset_register();
10722760Sdg199075 
10732760Sdg199075 		/*
10742760Sdg199075 		 * Check the ethertype.
10752760Sdg199075 		 */
1076*8023SPhil.Kirk@Sun.COM 		pf_compare_value(dl.dl_link_type_offset, 2,
1077*8023SPhil.Kirk@Sun.COM 		    htons(ETHERTYPE_VLAN));
10782760Sdg199075 
10792760Sdg199075 		/*
10802760Sdg199075 		 * And if it's not VLAN, don't load offset to the offset
10812760Sdg199075 		 * register.
10822760Sdg199075 		 */
10832760Sdg199075 		pf_emit(ENF_BRFL | ENF_NOP);
10842760Sdg199075 		pf_emit(3);
10852760Sdg199075 
10862760Sdg199075 		/*
10872760Sdg199075 		 * Otherwise, load offset to the offset register.
10882760Sdg199075 		 */
10892760Sdg199075 		pf_emit_load_offset(offset);
10902760Sdg199075 
10912760Sdg199075 		/*
10922760Sdg199075 		 * Now get rid of the results of the comparison,
10932760Sdg199075 		 * we don't want the results of the comparison to affect
10942760Sdg199075 		 * other logic in the packet filtering program.
10952760Sdg199075 		 */
10962760Sdg199075 		pf_emit(ENF_POP | ENF_NOP);
10972760Sdg199075 
10982760Sdg199075 		/*
10992760Sdg199075 		 * Set the last operation at the end, or any time
11002760Sdg199075 		 * after the call to pf_clear_offset because
11012760Sdg199075 		 * pf_clear_offset uses it.
11022760Sdg199075 		 */
11032760Sdg199075 		last_offset_operation = (void*)pf_check_vlan_tag;
11042760Sdg199075 		last_offset = offset;
11052760Sdg199075 	}
11062760Sdg199075 }
11072760Sdg199075 
11082760Sdg199075 /*
11092760Sdg199075  * Utility function used to emit packet filtering code
11102760Sdg199075  * to match an ethertype.
11112760Sdg199075  *
11122760Sdg199075  * INPUTS:  ethertype - The ethertype we want to check for.
11132760Sdg199075  *                      Don't call htons on the ethertype before
11142760Sdg199075  *                      calling this function.
11152760Sdg199075  * OUTPUTS:  If there is sufficient storage available, packet
11162760Sdg199075  *           filtering code to check an ethertype.  Otherwise,
11172760Sdg199075  *           nothing.
11182760Sdg199075  */
11192760Sdg199075 static void
11202760Sdg199075 pf_match_ethertype(uint_t ethertype)
11212760Sdg199075 {
11222760Sdg199075 	/*
11232760Sdg199075 	 * If the user wants to filter on ethertype VLAN,
11242760Sdg199075 	 * then clear the offset register so that the offset
11252760Sdg199075 	 * for ENF_PUSHWORD points to the right place in the
11262760Sdg199075 	 * packet.
11272760Sdg199075 	 *
11282760Sdg199075 	 * Otherwise, call pf_check_vlan_tag to set the offset
11292760Sdg199075 	 * register such that the contents of the offset register
11302760Sdg199075 	 * plus the argument for ENF_PUSHWORD point to the right
11312760Sdg199075 	 * part of the packet, whether or not the packet is VLAN
11322760Sdg199075 	 * tagged.  We call pf_check_vlan_tag with an offset of
11332760Sdg199075 	 * two words because if the packet is VLAN tagged, we have
11342760Sdg199075 	 * to move past the ethertype in the ethernet header, and
11352760Sdg199075 	 * past the lower two octets of the VLAN header to get to
11362760Sdg199075 	 * the ethertype in the VLAN header.
11372760Sdg199075 	 */
11382760Sdg199075 	if (ethertype == ETHERTYPE_VLAN)
11392760Sdg199075 		pf_clear_offset_register();
11402760Sdg199075 	else
11412760Sdg199075 		pf_check_vlan_tag(2);
11422760Sdg199075 
1143*8023SPhil.Kirk@Sun.COM 	pf_compare_value(dl.dl_link_type_offset, 2, htons(ethertype));
11442760Sdg199075 }
11452760Sdg199075 
1146*8023SPhil.Kirk@Sun.COM static void
1147*8023SPhil.Kirk@Sun.COM pf_match_ipnettype(uint_t type)
1148*8023SPhil.Kirk@Sun.COM {
1149*8023SPhil.Kirk@Sun.COM 	pf_compare_value(dl.dl_link_type_offset, 2, htons(type));
1150*8023SPhil.Kirk@Sun.COM }
11512760Sdg199075 
1152*8023SPhil.Kirk@Sun.COM static void
1153*8023SPhil.Kirk@Sun.COM pf_match_ibtype(uint_t type)
1154*8023SPhil.Kirk@Sun.COM {
1155*8023SPhil.Kirk@Sun.COM 	pf_compare_value(dl.dl_link_type_offset, 2, htons(type));
1156*8023SPhil.Kirk@Sun.COM }
11572760Sdg199075 
11582760Sdg199075 /*
11592760Sdg199075  * This function uses the table above to generate a
11602760Sdg199075  * piece of a packet filtering program to check a transport
11612760Sdg199075  * protocol type.
11622760Sdg199075  *
11632760Sdg199075  * INPUTS:  tranport_protocol - the transport protocol we're
11642760Sdg199075  *                              interested in.
11652760Sdg199075  * OUTPUTS:  If there is sufficient storage, then packet filtering
11662760Sdg199075  *           code to check a transport protocol type.  Otherwise,
11672760Sdg199075  *           nothing.
11682760Sdg199075  */
11692760Sdg199075 static void
11702760Sdg199075 pf_check_transport_protocol(uint_t transport_protocol)
11712760Sdg199075 {
11722760Sdg199075 	int i = 0;
11732760Sdg199075 	uint_t number_of_matches = 0;
11742760Sdg199075 
1175*8023SPhil.Kirk@Sun.COM 	for (; dl.dl_transport_mapping_table->transport_protocol != -1;
1176*8023SPhil.Kirk@Sun.COM 	    dl.dl_transport_mapping_table++) {
11772760Sdg199075 		if (transport_protocol ==
1178*8023SPhil.Kirk@Sun.COM 		    (uint_t)dl.dl_transport_mapping_table->transport_protocol) {
11792760Sdg199075 			number_of_matches++;
1180*8023SPhil.Kirk@Sun.COM 			dl.dl_match_fn(dl.dl_transport_mapping_table->
1181*8023SPhil.Kirk@Sun.COM 			    network_protocol);
11822760Sdg199075 			pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
11832760Sdg199075 			pf_compare_value(
1184*8023SPhil.Kirk@Sun.COM 			    dl.dl_transport_mapping_table->offset +
1185*8023SPhil.Kirk@Sun.COM 			    dl.dl_link_header_len, 1,
11866631Sss150715 			    transport_protocol);
11872760Sdg199075 			pf_emit(ENF_AND);
11882760Sdg199075 			if (number_of_matches > 1) {
11892760Sdg199075 				/*
11902760Sdg199075 				 * Since we have two or more matches, in
11912760Sdg199075 				 * order to have a correct and complete
11922760Sdg199075 				 * program we need to OR the result of
11932760Sdg199075 				 * each block of comparisons together.
11942760Sdg199075 				 */
11952760Sdg199075 				pf_emit(ENF_OR);
11962760Sdg199075 			}
11972760Sdg199075 		}
11982760Sdg199075 	}
11992760Sdg199075 }
12002760Sdg199075 
12010Sstevel@tonic-gate static void
1202*8023SPhil.Kirk@Sun.COM pf_matchfn(char *proto)
1203*8023SPhil.Kirk@Sun.COM {
1204*8023SPhil.Kirk@Sun.COM 	for (; dl.dl_net_map_tbl->nmt_val != -1; dl.dl_net_map_tbl++) {
1205*8023SPhil.Kirk@Sun.COM 		if (strcmp(proto, dl.dl_net_map_tbl->nmt_name) == 0)
1206*8023SPhil.Kirk@Sun.COM 			dl.dl_match_fn(dl.dl_net_map_tbl->nmt_val);
1207*8023SPhil.Kirk@Sun.COM 	}
1208*8023SPhil.Kirk@Sun.COM }
1209*8023SPhil.Kirk@Sun.COM 
1210*8023SPhil.Kirk@Sun.COM static void
12110Sstevel@tonic-gate pf_primary()
12120Sstevel@tonic-gate {
12130Sstevel@tonic-gate 	for (;;) {
12140Sstevel@tonic-gate 		if (tokentype == FIELD)
12150Sstevel@tonic-gate 			break;
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 		if (EQ("ip")) {
1218*8023SPhil.Kirk@Sun.COM 			pf_matchfn("ip");
12190Sstevel@tonic-gate 			opstack++;
12200Sstevel@tonic-gate 			next();
12210Sstevel@tonic-gate 			break;
12220Sstevel@tonic-gate 		}
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate 		if (EQ("ip6")) {
1225*8023SPhil.Kirk@Sun.COM 			pf_matchfn("ip6");
12260Sstevel@tonic-gate 			opstack++;
12270Sstevel@tonic-gate 			next();
12280Sstevel@tonic-gate 			break;
12290Sstevel@tonic-gate 		}
12300Sstevel@tonic-gate 
12310Sstevel@tonic-gate 		if (EQ("pppoe")) {
1232*8023SPhil.Kirk@Sun.COM 			pf_matchfn("pppoe");
12332760Sdg199075 			pf_match_ethertype(ETHERTYPE_PPPOES);
12340Sstevel@tonic-gate 			pf_emit(ENF_OR);
12350Sstevel@tonic-gate 			opstack++;
12360Sstevel@tonic-gate 			next();
12370Sstevel@tonic-gate 			break;
12380Sstevel@tonic-gate 		}
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 		if (EQ("pppoed")) {
1241*8023SPhil.Kirk@Sun.COM 			pf_matchfn("pppoed");
12420Sstevel@tonic-gate 			opstack++;
12430Sstevel@tonic-gate 			next();
12440Sstevel@tonic-gate 			break;
12450Sstevel@tonic-gate 		}
12460Sstevel@tonic-gate 
12470Sstevel@tonic-gate 		if (EQ("pppoes")) {
1248*8023SPhil.Kirk@Sun.COM 			pf_matchfn("pppoes");
12490Sstevel@tonic-gate 			opstack++;
12500Sstevel@tonic-gate 			next();
12510Sstevel@tonic-gate 			break;
12520Sstevel@tonic-gate 		}
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 		if (EQ("arp")) {
1255*8023SPhil.Kirk@Sun.COM 			pf_matchfn("arp");
12562760Sdg199075 			opstack++;
12572760Sdg199075 			next();
12582760Sdg199075 			break;
12592760Sdg199075 		}
12602760Sdg199075 
12612760Sdg199075 		if (EQ("vlan")) {
1262*8023SPhil.Kirk@Sun.COM 			pf_matchfn("vlan");
12632760Sdg199075 			pf_compare_value_mask_neq(VLAN_ID_OFFSET, 2,
12642760Sdg199075 			    0, VLAN_ID_MASK);
12652760Sdg199075 			pf_emit(ENF_AND);
12662760Sdg199075 			opstack++;
12672760Sdg199075 			next();
12682760Sdg199075 			break;
12692760Sdg199075 		}
12702760Sdg199075 
12712760Sdg199075 		if (EQ("vlan-id")) {
12722760Sdg199075 			next();
12732760Sdg199075 			if (tokentype != NUMBER)
12742760Sdg199075 				pr_err("VLAN ID expected");
1275*8023SPhil.Kirk@Sun.COM 			pf_matchfn("vlan-id");
12762760Sdg199075 			pf_compare_value_mask(VLAN_ID_OFFSET, 2, tokenval,
12772760Sdg199075 			    VLAN_ID_MASK);
12782760Sdg199075 			pf_emit(ENF_AND);
12790Sstevel@tonic-gate 			opstack++;
12800Sstevel@tonic-gate 			next();
12810Sstevel@tonic-gate 			break;
12820Sstevel@tonic-gate 		}
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate 		if (EQ("rarp")) {
1285*8023SPhil.Kirk@Sun.COM 			pf_matchfn("rarp");
12860Sstevel@tonic-gate 			opstack++;
12870Sstevel@tonic-gate 			next();
12880Sstevel@tonic-gate 			break;
12890Sstevel@tonic-gate 		}
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 		if (EQ("tcp")) {
12922760Sdg199075 			pf_check_transport_protocol(IPPROTO_TCP);
12930Sstevel@tonic-gate 			opstack++;
12940Sstevel@tonic-gate 			next();
12950Sstevel@tonic-gate 			break;
12960Sstevel@tonic-gate 		}
12970Sstevel@tonic-gate 
12980Sstevel@tonic-gate 		if (EQ("udp")) {
12992760Sdg199075 			pf_check_transport_protocol(IPPROTO_UDP);
13000Sstevel@tonic-gate 			opstack++;
13010Sstevel@tonic-gate 			next();
13020Sstevel@tonic-gate 			break;
13030Sstevel@tonic-gate 		}
13040Sstevel@tonic-gate 
13050Sstevel@tonic-gate 		if (EQ("ospf")) {
13062760Sdg199075 			pf_check_transport_protocol(IPPROTO_OSPF);
13070Sstevel@tonic-gate 			opstack++;
13080Sstevel@tonic-gate 			next();
13090Sstevel@tonic-gate 			break;
13100Sstevel@tonic-gate 		}
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate 		if (EQ("sctp")) {
13142760Sdg199075 			pf_check_transport_protocol(IPPROTO_SCTP);
13150Sstevel@tonic-gate 			opstack++;
13160Sstevel@tonic-gate 			next();
13170Sstevel@tonic-gate 			break;
13180Sstevel@tonic-gate 		}
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 		if (EQ("icmp")) {
13212760Sdg199075 			pf_check_transport_protocol(IPPROTO_ICMP);
13220Sstevel@tonic-gate 			opstack++;
13230Sstevel@tonic-gate 			next();
13240Sstevel@tonic-gate 			break;
13250Sstevel@tonic-gate 		}
13260Sstevel@tonic-gate 
13270Sstevel@tonic-gate 		if (EQ("icmp6")) {
13282760Sdg199075 			pf_check_transport_protocol(IPPROTO_ICMPV6);
13290Sstevel@tonic-gate 			opstack++;
13300Sstevel@tonic-gate 			next();
13310Sstevel@tonic-gate 			break;
13320Sstevel@tonic-gate 		}
13330Sstevel@tonic-gate 
13340Sstevel@tonic-gate 		if (EQ("ip-in-ip")) {
13352760Sdg199075 			pf_check_transport_protocol(IPPROTO_ENCAP);
13360Sstevel@tonic-gate 			opstack++;
13370Sstevel@tonic-gate 			next();
13380Sstevel@tonic-gate 			break;
13390Sstevel@tonic-gate 		}
13400Sstevel@tonic-gate 
13410Sstevel@tonic-gate 		if (EQ("esp")) {
13422760Sdg199075 			pf_check_transport_protocol(IPPROTO_ESP);
13430Sstevel@tonic-gate 			opstack++;
13440Sstevel@tonic-gate 			next();
13450Sstevel@tonic-gate 			break;
13460Sstevel@tonic-gate 		}
13470Sstevel@tonic-gate 
13480Sstevel@tonic-gate 		if (EQ("ah")) {
13492760Sdg199075 			pf_check_transport_protocol(IPPROTO_AH);
13500Sstevel@tonic-gate 			opstack++;
13510Sstevel@tonic-gate 			next();
13520Sstevel@tonic-gate 			break;
13530Sstevel@tonic-gate 		}
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 		if (EQ("(")) {
13560Sstevel@tonic-gate 			inBrace++;
13570Sstevel@tonic-gate 			next();
13580Sstevel@tonic-gate 			pf_expression();
13590Sstevel@tonic-gate 			if (EQ(")")) {
13600Sstevel@tonic-gate 				if (inBrace)
13610Sstevel@tonic-gate 					inBraceOR--;
13620Sstevel@tonic-gate 				inBrace--;
13630Sstevel@tonic-gate 				next();
13640Sstevel@tonic-gate 			}
13650Sstevel@tonic-gate 			break;
13660Sstevel@tonic-gate 		}
13670Sstevel@tonic-gate 
13680Sstevel@tonic-gate 		if (EQ("to") || EQ("dst")) {
13690Sstevel@tonic-gate 			dir = TO;
13700Sstevel@tonic-gate 			next();
13710Sstevel@tonic-gate 			continue;
13720Sstevel@tonic-gate 		}
13730Sstevel@tonic-gate 
13740Sstevel@tonic-gate 		if (EQ("from") || EQ("src")) {
13750Sstevel@tonic-gate 			dir = FROM;
13760Sstevel@tonic-gate 			next();
13770Sstevel@tonic-gate 			continue;
13780Sstevel@tonic-gate 		}
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate 		if (EQ("ether")) {
13810Sstevel@tonic-gate 			eaddr = 1;
13820Sstevel@tonic-gate 			next();
13830Sstevel@tonic-gate 			continue;
13840Sstevel@tonic-gate 		}
13850Sstevel@tonic-gate 
13860Sstevel@tonic-gate 		if (EQ("inet")) {
13870Sstevel@tonic-gate 			next();
13880Sstevel@tonic-gate 			if (EQ("host"))
13890Sstevel@tonic-gate 				next();
13900Sstevel@tonic-gate 			if (tokentype != ALPHA && tokentype != ADDR_IP)
13910Sstevel@tonic-gate 				pr_err("host/IPv4 addr expected after inet");
13920Sstevel@tonic-gate 			pf_ipaddr_match(dir, token, IPV4_ONLY);
13930Sstevel@tonic-gate 			opstack++;
13940Sstevel@tonic-gate 			next();
13950Sstevel@tonic-gate 			break;
13960Sstevel@tonic-gate 		}
13970Sstevel@tonic-gate 
13980Sstevel@tonic-gate 		if (EQ("inet6")) {
13990Sstevel@tonic-gate 			next();
14000Sstevel@tonic-gate 			if (EQ("host"))
14010Sstevel@tonic-gate 				next();
14020Sstevel@tonic-gate 			if (tokentype != ALPHA && tokentype != ADDR_IP6)
14030Sstevel@tonic-gate 				pr_err("host/IPv6 addr expected after inet6");
14040Sstevel@tonic-gate 			pf_ipaddr_match(dir, token, IPV6_ONLY);
14050Sstevel@tonic-gate 			opstack++;
14060Sstevel@tonic-gate 			next();
14070Sstevel@tonic-gate 			break;
14080Sstevel@tonic-gate 		}
14090Sstevel@tonic-gate 
14100Sstevel@tonic-gate 		if (EQ("proto")) {
14110Sstevel@tonic-gate 			next();
14120Sstevel@tonic-gate 			if (tokentype != NUMBER)
14130Sstevel@tonic-gate 				pr_err("IP proto type expected");
14142760Sdg199075 			pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
14153220Sdg199075 			pf_compare_value(
1416*8023SPhil.Kirk@Sun.COM 			    IPV4_TYPE_HEADER_OFFSET + dl.dl_link_header_len, 1,
14173220Sdg199075 			    tokenval);
14180Sstevel@tonic-gate 			opstack++;
14190Sstevel@tonic-gate 			next();
14200Sstevel@tonic-gate 			break;
14210Sstevel@tonic-gate 		}
14220Sstevel@tonic-gate 
14230Sstevel@tonic-gate 		if (EQ("broadcast")) {
14242760Sdg199075 			pf_clear_offset_register();
1425*8023SPhil.Kirk@Sun.COM 			pf_compare_value(dl.dl_link_dest_offset, 4, 0xffffffff);
14260Sstevel@tonic-gate 			opstack++;
14270Sstevel@tonic-gate 			next();
14280Sstevel@tonic-gate 			break;
14290Sstevel@tonic-gate 		}
14300Sstevel@tonic-gate 
14310Sstevel@tonic-gate 		if (EQ("multicast")) {
14322760Sdg199075 			pf_clear_offset_register();
1433*8023SPhil.Kirk@Sun.COM 			pf_compare_value_mask(
1434*8023SPhil.Kirk@Sun.COM 			    dl.dl_link_dest_offset, 1, 0x01, 0x01);
14350Sstevel@tonic-gate 			opstack++;
14360Sstevel@tonic-gate 			next();
14370Sstevel@tonic-gate 			break;
14380Sstevel@tonic-gate 		}
14390Sstevel@tonic-gate 
14400Sstevel@tonic-gate 		if (EQ("ethertype")) {
14410Sstevel@tonic-gate 			next();
14420Sstevel@tonic-gate 			if (tokentype != NUMBER)
14430Sstevel@tonic-gate 				pr_err("ether type expected");
14442760Sdg199075 			pf_match_ethertype(tokenval);
14450Sstevel@tonic-gate 			opstack++;
14460Sstevel@tonic-gate 			next();
14470Sstevel@tonic-gate 			break;
14480Sstevel@tonic-gate 		}
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 		if (EQ("net") || EQ("dstnet") || EQ("srcnet")) {
14510Sstevel@tonic-gate 			if (EQ("dstnet"))
14520Sstevel@tonic-gate 				dir = TO;
14530Sstevel@tonic-gate 			else if (EQ("srcnet"))
14540Sstevel@tonic-gate 				dir = FROM;
14550Sstevel@tonic-gate 			next();
14560Sstevel@tonic-gate 			pf_netaddr_match(dir, token);
14570Sstevel@tonic-gate 			dir = ANY;
14580Sstevel@tonic-gate 			opstack++;
14590Sstevel@tonic-gate 			next();
14600Sstevel@tonic-gate 			break;
14610Sstevel@tonic-gate 		}
14620Sstevel@tonic-gate 
1463*8023SPhil.Kirk@Sun.COM 		if (EQ("zone")) {
1464*8023SPhil.Kirk@Sun.COM 			next();
1465*8023SPhil.Kirk@Sun.COM 			if (tokentype != NUMBER)
1466*8023SPhil.Kirk@Sun.COM 				pr_err("zoneid expected after inet");
1467*8023SPhil.Kirk@Sun.COM 			pf_match_zone(dir, BE_64((uint64_t)(tokenval)));
1468*8023SPhil.Kirk@Sun.COM 			opstack++;
1469*8023SPhil.Kirk@Sun.COM 			next();
1470*8023SPhil.Kirk@Sun.COM 			break;
1471*8023SPhil.Kirk@Sun.COM 		}
1472*8023SPhil.Kirk@Sun.COM 
14730Sstevel@tonic-gate 		/*
14740Sstevel@tonic-gate 		 * Give up on anything that's obviously
14750Sstevel@tonic-gate 		 * not a primary.
14760Sstevel@tonic-gate 		 */
14770Sstevel@tonic-gate 		if (EQ("and") || EQ("or") ||
14780Sstevel@tonic-gate 		    EQ("not") || EQ("decnet") || EQ("apple") ||
14790Sstevel@tonic-gate 		    EQ("length") || EQ("less") || EQ("greater") ||
14800Sstevel@tonic-gate 		    EQ("port") || EQ("srcport") || EQ("dstport") ||
14810Sstevel@tonic-gate 		    EQ("rpc") || EQ("gateway") || EQ("nofrag") ||
14823431Scarlsonj 		    EQ("bootp") || EQ("dhcp") || EQ("dhcp6") ||
14833431Scarlsonj 		    EQ("slp") || EQ("ldap")) {
14840Sstevel@tonic-gate 			break;
14850Sstevel@tonic-gate 		}
14860Sstevel@tonic-gate 
14870Sstevel@tonic-gate 		if (EQ("host") || EQ("between") ||
14880Sstevel@tonic-gate 		    tokentype == ALPHA || /* assume its a hostname */
14890Sstevel@tonic-gate 		    tokentype == ADDR_IP ||
14900Sstevel@tonic-gate 		    tokentype == ADDR_IP6 ||
14910Sstevel@tonic-gate 		    tokentype == ADDR_ETHER) {
14920Sstevel@tonic-gate 			if (EQ("host") || EQ("between"))
14930Sstevel@tonic-gate 				next();
14940Sstevel@tonic-gate 			if (eaddr || tokentype == ADDR_ETHER) {
14950Sstevel@tonic-gate 				pf_etheraddr_match(dir, token);
14960Sstevel@tonic-gate 			} else if (tokentype == ALPHA) {
14970Sstevel@tonic-gate 				pf_ipaddr_match(dir, token, IPV4_AND_IPV6);
14980Sstevel@tonic-gate 			} else if (tokentype == ADDR_IP) {
14990Sstevel@tonic-gate 				pf_ipaddr_match(dir, token, IPV4_ONLY);
15000Sstevel@tonic-gate 			} else {
15010Sstevel@tonic-gate 				pf_ipaddr_match(dir, token, IPV6_ONLY);
15020Sstevel@tonic-gate 			}
15030Sstevel@tonic-gate 			dir = ANY;
15040Sstevel@tonic-gate 			eaddr = 0;
15050Sstevel@tonic-gate 			opstack++;
15060Sstevel@tonic-gate 			next();
15070Sstevel@tonic-gate 			break;
15080Sstevel@tonic-gate 		}
15090Sstevel@tonic-gate 
15100Sstevel@tonic-gate 		break;	/* unknown token */
15110Sstevel@tonic-gate 	}
15120Sstevel@tonic-gate }
15130Sstevel@tonic-gate 
15140Sstevel@tonic-gate static void
15150Sstevel@tonic-gate pf_alternation()
15160Sstevel@tonic-gate {
15170Sstevel@tonic-gate 	int s = opstack;
15180Sstevel@tonic-gate 
15190Sstevel@tonic-gate 	pf_primary();
15200Sstevel@tonic-gate 	for (;;) {
15210Sstevel@tonic-gate 		if (EQ("and"))
15220Sstevel@tonic-gate 			next();
15230Sstevel@tonic-gate 		pf_primary();
15240Sstevel@tonic-gate 		if (opstack != s + 2)
15250Sstevel@tonic-gate 			break;
15260Sstevel@tonic-gate 		pf_emit(ENF_AND);
15270Sstevel@tonic-gate 		opstack--;
15280Sstevel@tonic-gate 	}
15290Sstevel@tonic-gate }
15300Sstevel@tonic-gate 
15310Sstevel@tonic-gate static void
15320Sstevel@tonic-gate pf_expression()
15330Sstevel@tonic-gate {
15340Sstevel@tonic-gate 	pf_alternation();
15350Sstevel@tonic-gate 	while (EQ("or") || EQ(",")) {
15360Sstevel@tonic-gate 		if (inBrace)
15370Sstevel@tonic-gate 			inBraceOR++;
15380Sstevel@tonic-gate 		else
15390Sstevel@tonic-gate 			foundOR++;
15400Sstevel@tonic-gate 		next();
15410Sstevel@tonic-gate 		pf_alternation();
15420Sstevel@tonic-gate 		pf_emit(ENF_OR);
15430Sstevel@tonic-gate 		opstack--;
15440Sstevel@tonic-gate 	}
15450Sstevel@tonic-gate }
15460Sstevel@tonic-gate 
15470Sstevel@tonic-gate /*
15480Sstevel@tonic-gate  * Attempt to compile the expression
15490Sstevel@tonic-gate  * in the string "e".  If we can generate
15500Sstevel@tonic-gate  * pf code for it then return 1 - otherwise
15510Sstevel@tonic-gate  * return 0 and leave it up to the user-level
15520Sstevel@tonic-gate  * filter.
15530Sstevel@tonic-gate  */
15540Sstevel@tonic-gate int
15550Sstevel@tonic-gate pf_compile(e, print)
15560Sstevel@tonic-gate 	char *e;
15570Sstevel@tonic-gate 	int print;
15580Sstevel@tonic-gate {
15590Sstevel@tonic-gate 	char *argstr;
15600Sstevel@tonic-gate 	char *sav_str, *ptr, *sav_ptr;
15610Sstevel@tonic-gate 	int inBr = 0, aheadOR = 0;
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate 	argstr = strdup(e);
15640Sstevel@tonic-gate 	sav_str = e;
15650Sstevel@tonic-gate 	tkp = argstr;
15660Sstevel@tonic-gate 	dir = ANY;
15670Sstevel@tonic-gate 
15680Sstevel@tonic-gate 	pfp = &pf.Pf_Filter[0];
15690Sstevel@tonic-gate 	if (setjmp(env)) {
15700Sstevel@tonic-gate 		return (0);
15710Sstevel@tonic-gate 	}
15720Sstevel@tonic-gate 
15730Sstevel@tonic-gate 	/*
15740Sstevel@tonic-gate 	 * Set media specific packet offsets that this code uses.
15750Sstevel@tonic-gate 	 */
1576*8023SPhil.Kirk@Sun.COM 	if (interface->mac_type == DL_ETHER) {
1577*8023SPhil.Kirk@Sun.COM 		dl.dl_type = DL_ETHER;
1578*8023SPhil.Kirk@Sun.COM 		dl.dl_match_fn = pf_match_ethertype;
1579*8023SPhil.Kirk@Sun.COM 		dl.dl_transport_mapping_table =
1580*8023SPhil.Kirk@Sun.COM 		    &ether_transport_mapping_table[0];
1581*8023SPhil.Kirk@Sun.COM 		dl.dl_net_map_tbl =
1582*8023SPhil.Kirk@Sun.COM 		    &ether_network_mapping_table[0];
1583*8023SPhil.Kirk@Sun.COM 		dl.dl_link_header_len = 14;
1584*8023SPhil.Kirk@Sun.COM 		dl.dl_link_type_offset = 12;
1585*8023SPhil.Kirk@Sun.COM 		dl.dl_link_dest_offset = 0;
1586*8023SPhil.Kirk@Sun.COM 		dl.dl_link_src_offset = 6;
1587*8023SPhil.Kirk@Sun.COM 		dl.dl_link_addr_len = 6;
1588*8023SPhil.Kirk@Sun.COM 	}
1589*8023SPhil.Kirk@Sun.COM 
15900Sstevel@tonic-gate 	if (interface->mac_type == DL_IB) {
1591*8023SPhil.Kirk@Sun.COM 		dl.dl_type = DL_IB;
1592*8023SPhil.Kirk@Sun.COM 		dl.dl_link_header_len = 4;
1593*8023SPhil.Kirk@Sun.COM 		dl.dl_link_type_offset = 0;
1594*8023SPhil.Kirk@Sun.COM 		dl.dl_link_dest_offset = dl.dl_link_src_offset = -1;
1595*8023SPhil.Kirk@Sun.COM 		dl.dl_link_addr_len = 20;
1596*8023SPhil.Kirk@Sun.COM 		dl.dl_match_fn = pf_match_ibtype;
1597*8023SPhil.Kirk@Sun.COM 		dl.dl_transport_mapping_table =
1598*8023SPhil.Kirk@Sun.COM 		    &ib_transport_mapping_table[0];
1599*8023SPhil.Kirk@Sun.COM 		dl.dl_net_map_tbl =
1600*8023SPhil.Kirk@Sun.COM 		    &ib_network_mapping_table[0];
1601*8023SPhil.Kirk@Sun.COM 	}
1602*8023SPhil.Kirk@Sun.COM 
1603*8023SPhil.Kirk@Sun.COM 	if (interface->mac_type == DL_IPNET) {
1604*8023SPhil.Kirk@Sun.COM 		dl.dl_type = DL_IPNET;
1605*8023SPhil.Kirk@Sun.COM 		dl.dl_link_header_len = 24;
1606*8023SPhil.Kirk@Sun.COM 		dl.dl_link_type_offset = 0;
1607*8023SPhil.Kirk@Sun.COM 		dl.dl_link_dest_offset = dl.dl_link_src_offset = -1;
1608*8023SPhil.Kirk@Sun.COM 		dl.dl_link_addr_len = -1;
1609*8023SPhil.Kirk@Sun.COM 		dl.dl_match_fn = pf_match_ipnettype;
1610*8023SPhil.Kirk@Sun.COM 		dl.dl_transport_mapping_table =
1611*8023SPhil.Kirk@Sun.COM 		    &ipnet_transport_mapping_table[0];
1612*8023SPhil.Kirk@Sun.COM 		dl.dl_net_map_tbl =
1613*8023SPhil.Kirk@Sun.COM 		    &ipnet_network_mapping_table[0];
16140Sstevel@tonic-gate 	}
16150Sstevel@tonic-gate 
16160Sstevel@tonic-gate 	next();
16170Sstevel@tonic-gate 	pf_expression();
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 	if (tokentype != EOL) {
16200Sstevel@tonic-gate 		/*
16210Sstevel@tonic-gate 		 * The idea here is to do as much filtering as possible in
16220Sstevel@tonic-gate 		 * the kernel. So even if we find a token we don't understand,
16230Sstevel@tonic-gate 		 * we try to see if we can still set up a portion of the filter
16240Sstevel@tonic-gate 		 * in the kernel and use the userland filter to filter the
16250Sstevel@tonic-gate 		 * remaining stuff. Obviously, if our filter expression is of
16260Sstevel@tonic-gate 		 * type A AND B, we can filter A in kernel and then apply B
16270Sstevel@tonic-gate 		 * to the packets that got through. The same is not true for
16280Sstevel@tonic-gate 		 * a filter of type A OR B. We can't apply A first and then B
16290Sstevel@tonic-gate 		 * on the packets filtered through A.
16300Sstevel@tonic-gate 		 *
16310Sstevel@tonic-gate 		 * (We need to keep track of the fact when we find an OR,
16320Sstevel@tonic-gate 		 * and the fact that we are inside brackets when we find OR.
16330Sstevel@tonic-gate 		 * The variable 'foundOR' tells us if there was an OR behind,
16340Sstevel@tonic-gate 		 * 'inBraceOR' tells us if we found an OR before we could find
16350Sstevel@tonic-gate 		 * the end brace i.e. ')', and variable 'aheadOR' checks if
16360Sstevel@tonic-gate 		 * there is an OR in the expression ahead. if either of these
16370Sstevel@tonic-gate 		 * cases become true, we can't split the filtering)
16380Sstevel@tonic-gate 		 */
16390Sstevel@tonic-gate 
16400Sstevel@tonic-gate 		if (foundOR || inBraceOR) {
16410Sstevel@tonic-gate 			/* FORGET IN KERNEL FILTERING */
16420Sstevel@tonic-gate 			return (0);
16430Sstevel@tonic-gate 		} else {
16440Sstevel@tonic-gate 
16450Sstevel@tonic-gate 			/* CHECK IF NO OR AHEAD */
16460Sstevel@tonic-gate 			sav_ptr = (char *)((uintptr_t)sav_str +
16470Sstevel@tonic-gate 						(uintptr_t)sav_tkp -
16480Sstevel@tonic-gate 						(uintptr_t)argstr);
16490Sstevel@tonic-gate 			ptr = sav_ptr;
16500Sstevel@tonic-gate 			while (*ptr != '\0') {
16510Sstevel@tonic-gate 				switch (*ptr) {
16520Sstevel@tonic-gate 				case '(':
16530Sstevel@tonic-gate 					inBr++;
16540Sstevel@tonic-gate 					break;
16550Sstevel@tonic-gate 				case ')':
16560Sstevel@tonic-gate 					inBr--;
16570Sstevel@tonic-gate 					break;
16580Sstevel@tonic-gate 				case 'o':
16590Sstevel@tonic-gate 				case 'O':
16600Sstevel@tonic-gate 					if ((*(ptr + 1) == 'R' ||
16610Sstevel@tonic-gate 						*(ptr + 1) == 'r') && !inBr)
16620Sstevel@tonic-gate 						aheadOR = 1;
16630Sstevel@tonic-gate 					break;
16640Sstevel@tonic-gate 				case ',':
16650Sstevel@tonic-gate 					if (!inBr)
16660Sstevel@tonic-gate 						aheadOR = 1;
16670Sstevel@tonic-gate 					break;
16680Sstevel@tonic-gate 				}
16690Sstevel@tonic-gate 				ptr++;
16700Sstevel@tonic-gate 			}
16710Sstevel@tonic-gate 			if (!aheadOR) {
16720Sstevel@tonic-gate 				/* NO OR AHEAD, SPLIT UP THE FILTERING */
16730Sstevel@tonic-gate 				pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0];
16740Sstevel@tonic-gate 				pf.Pf_Priority = 5;
16750Sstevel@tonic-gate 				if (print) {
16760Sstevel@tonic-gate 					pf_codeprint(&pf.Pf_Filter[0],
16770Sstevel@tonic-gate 							pf.Pf_FilterLen);
16780Sstevel@tonic-gate 				}
16790Sstevel@tonic-gate 				compile(sav_ptr, print);
16800Sstevel@tonic-gate 				return (2);
16810Sstevel@tonic-gate 			} else
16820Sstevel@tonic-gate 				return (0);
16830Sstevel@tonic-gate 		}
16840Sstevel@tonic-gate 	}
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate 	pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0];
16870Sstevel@tonic-gate 	pf.Pf_Priority = 5;	/* unimportant, so long as > 2 */
16880Sstevel@tonic-gate 	if (print) {
16890Sstevel@tonic-gate 		pf_codeprint(&pf.Pf_Filter[0], pf.Pf_FilterLen);
16900Sstevel@tonic-gate 	}
16910Sstevel@tonic-gate 	return (1);
16920Sstevel@tonic-gate }
1693