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