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 5*2760Sdg199075 * Common Development and Distribution License (the "License"). 6*2760Sdg199075 * 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*2760Sdg199075 * Copyright 2006 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> 29*2760Sdg199075 #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> 41*2760Sdg199075 #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" 55*2760Sdg199075 #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) 102*2760Sdg199075 #define IPV4_TYPE_HEADER_OFFSET (9) 103*2760Sdg199075 #define IPV6_TYPE_HEADER_OFFSET (6) 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate static int inBrace = 0, inBraceOR = 0; 1060Sstevel@tonic-gate static int foundOR = 0; 1070Sstevel@tonic-gate char *tkp, *sav_tkp; 1080Sstevel@tonic-gate char *token; 1090Sstevel@tonic-gate enum { EOL, ALPHA, NUMBER, FIELD, ADDR_IP, ADDR_ETHER, SPECIAL, 1100Sstevel@tonic-gate ADDR_IP6 } tokentype; 1110Sstevel@tonic-gate uint_t tokenval; 1120Sstevel@tonic-gate 1130Sstevel@tonic-gate enum direction { ANY, TO, FROM }; 1140Sstevel@tonic-gate enum direction dir; 1150Sstevel@tonic-gate 1160Sstevel@tonic-gate extern void next(); 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate static void pf_expression(); 119*2760Sdg199075 static void pf_check_vlan_tag(uint_t offset); 120*2760Sdg199075 static void pf_clear_offset_register(); 121*2760Sdg199075 static void pf_emit_load_offset(uint_t offset); 122*2760Sdg199075 static void pf_match_ethertype(uint_t ethertype); 123*2760Sdg199075 static void pf_check_transport_protocol(uint_t transport_protocol); 124*2760Sdg199075 static void pf_compare_value_mask_generic(int offset, uint_t len, 125*2760Sdg199075 uint_t val, int mask, uint_t op); 126*2760Sdg199075 127*2760Sdg199075 /* 128*2760Sdg199075 * This pointer points to the function that last generated 129*2760Sdg199075 * instructions to change the offset register. It's used 130*2760Sdg199075 * for comparisons to see if we need to issue more instructions 131*2760Sdg199075 * to change the register. 132*2760Sdg199075 * 133*2760Sdg199075 * It's initialized to pf_clear_offset_register because the offset 134*2760Sdg199075 * register in pfmod is initialized to zero, similar to the state 135*2760Sdg199075 * it would be in after executing the instructions issued by 136*2760Sdg199075 * pf_clear_offset_register. 137*2760Sdg199075 */ 138*2760Sdg199075 static void *last_offset_operation = (void*)pf_clear_offset_register; 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate static void 1410Sstevel@tonic-gate pf_emit(x) 1420Sstevel@tonic-gate ushort_t x; 1430Sstevel@tonic-gate { 1440Sstevel@tonic-gate if (pfp > &pf.Pf_Filter[PF_MAXFILTERS - 1]) 1450Sstevel@tonic-gate longjmp(env, 1); 1460Sstevel@tonic-gate *pfp++ = x; 1470Sstevel@tonic-gate } 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate static void 1500Sstevel@tonic-gate pf_codeprint(code, len) 1510Sstevel@tonic-gate ushort_t *code; 1520Sstevel@tonic-gate int len; 1530Sstevel@tonic-gate { 1540Sstevel@tonic-gate ushort_t *pc; 1550Sstevel@tonic-gate ushort_t *plast = code + len; 1560Sstevel@tonic-gate int op, action; 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate if (len > 0) { 1590Sstevel@tonic-gate printf("Kernel Filter:\n"); 1600Sstevel@tonic-gate } 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate for (pc = code; pc < plast; pc++) { 1630Sstevel@tonic-gate printf("\t%3d: ", pc - code); 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate op = *pc & 0xfc00; /* high 10 bits */ 1660Sstevel@tonic-gate action = *pc & 0x3ff; /* low 6 bits */ 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate switch (action) { 1690Sstevel@tonic-gate case ENF_PUSHLIT: 1700Sstevel@tonic-gate printf("PUSHLIT "); 1710Sstevel@tonic-gate break; 1720Sstevel@tonic-gate case ENF_PUSHZERO: 1730Sstevel@tonic-gate printf("PUSHZERO "); 1740Sstevel@tonic-gate break; 1750Sstevel@tonic-gate #ifdef ENF_PUSHONE 1760Sstevel@tonic-gate case ENF_PUSHONE: 1770Sstevel@tonic-gate printf("PUSHONE "); 1780Sstevel@tonic-gate break; 1790Sstevel@tonic-gate #endif 1800Sstevel@tonic-gate #ifdef ENF_PUSHFFFF 1810Sstevel@tonic-gate case ENF_PUSHFFFF: 1820Sstevel@tonic-gate printf("PUSHFFFF "); 1830Sstevel@tonic-gate break; 1840Sstevel@tonic-gate #endif 1850Sstevel@tonic-gate #ifdef ENF_PUSHFF00 1860Sstevel@tonic-gate case ENF_PUSHFF00: 1870Sstevel@tonic-gate printf("PUSHFF00 "); 1880Sstevel@tonic-gate break; 1890Sstevel@tonic-gate #endif 1900Sstevel@tonic-gate #ifdef ENF_PUSH00FF 1910Sstevel@tonic-gate case ENF_PUSH00FF: 1920Sstevel@tonic-gate printf("PUSH00FF "); 1930Sstevel@tonic-gate break; 1940Sstevel@tonic-gate #endif 195*2760Sdg199075 case ENF_LOAD_OFFSET: 196*2760Sdg199075 printf("LOAD_OFFSET "); 197*2760Sdg199075 break; 198*2760Sdg199075 case ENF_BRTR: 199*2760Sdg199075 printf("BRTR "); 200*2760Sdg199075 break; 201*2760Sdg199075 case ENF_BRFL: 202*2760Sdg199075 printf("BRFL "); 203*2760Sdg199075 break; 204*2760Sdg199075 case ENF_POP: 205*2760Sdg199075 printf("POP "); 206*2760Sdg199075 break; 2070Sstevel@tonic-gate } 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate if (action >= ENF_PUSHWORD) 2100Sstevel@tonic-gate printf("PUSHWORD %d ", action - ENF_PUSHWORD); 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate switch (op) { 2130Sstevel@tonic-gate case ENF_EQ: 2140Sstevel@tonic-gate printf("EQ "); 2150Sstevel@tonic-gate break; 2160Sstevel@tonic-gate case ENF_LT: 2170Sstevel@tonic-gate printf("LT "); 2180Sstevel@tonic-gate break; 2190Sstevel@tonic-gate case ENF_LE: 2200Sstevel@tonic-gate printf("LE "); 2210Sstevel@tonic-gate break; 2220Sstevel@tonic-gate case ENF_GT: 2230Sstevel@tonic-gate printf("GT "); 2240Sstevel@tonic-gate break; 2250Sstevel@tonic-gate case ENF_GE: 2260Sstevel@tonic-gate printf("GE "); 2270Sstevel@tonic-gate break; 2280Sstevel@tonic-gate case ENF_AND: 2290Sstevel@tonic-gate printf("AND "); 2300Sstevel@tonic-gate break; 2310Sstevel@tonic-gate case ENF_OR: 2320Sstevel@tonic-gate printf("OR "); 2330Sstevel@tonic-gate break; 2340Sstevel@tonic-gate case ENF_XOR: 2350Sstevel@tonic-gate printf("XOR "); 2360Sstevel@tonic-gate break; 2370Sstevel@tonic-gate case ENF_COR: 2380Sstevel@tonic-gate printf("COR "); 2390Sstevel@tonic-gate break; 2400Sstevel@tonic-gate case ENF_CAND: 2410Sstevel@tonic-gate printf("CAND "); 2420Sstevel@tonic-gate break; 2430Sstevel@tonic-gate case ENF_CNOR: 2440Sstevel@tonic-gate printf("CNOR "); 2450Sstevel@tonic-gate break; 2460Sstevel@tonic-gate case ENF_CNAND: 2470Sstevel@tonic-gate printf("CNAND "); 2480Sstevel@tonic-gate break; 2490Sstevel@tonic-gate case ENF_NEQ: 2500Sstevel@tonic-gate printf("NEQ "); 2510Sstevel@tonic-gate break; 2520Sstevel@tonic-gate } 2530Sstevel@tonic-gate 254*2760Sdg199075 if (action == ENF_PUSHLIT || 255*2760Sdg199075 action == ENF_LOAD_OFFSET || 256*2760Sdg199075 action == ENF_BRTR || 257*2760Sdg199075 action == ENF_BRFL) { 2580Sstevel@tonic-gate pc++; 2590Sstevel@tonic-gate printf("\n\t%3d: %d (0x%04x)", pc - code, *pc, *pc); 2600Sstevel@tonic-gate } 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate printf("\n"); 2630Sstevel@tonic-gate } 2640Sstevel@tonic-gate } 2650Sstevel@tonic-gate 2660Sstevel@tonic-gate /* 2670Sstevel@tonic-gate * Emit packet filter code to check a 2680Sstevel@tonic-gate * field in the packet for a particular value. 2690Sstevel@tonic-gate * Need different code for each field size. 2700Sstevel@tonic-gate * Since the pf can only compare 16 bit quantities 2710Sstevel@tonic-gate * we have to use masking to compare byte values. 2720Sstevel@tonic-gate * Long word (32 bit) quantities have to be done 2730Sstevel@tonic-gate * as two 16 bit comparisons. 2740Sstevel@tonic-gate */ 2750Sstevel@tonic-gate static void 2760Sstevel@tonic-gate pf_compare_value(int offset, uint_t len, uint_t val) 2770Sstevel@tonic-gate { 2780Sstevel@tonic-gate /* 2790Sstevel@tonic-gate * If the property being filtered on is absent in the media 2800Sstevel@tonic-gate * packet, error out. 2810Sstevel@tonic-gate */ 2820Sstevel@tonic-gate if (offset == -1) 2830Sstevel@tonic-gate pr_err("filter option unsupported on media"); 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate switch (len) { 2860Sstevel@tonic-gate case 1: 2870Sstevel@tonic-gate pf_emit(ENF_PUSHWORD + offset / 2); 2880Sstevel@tonic-gate #if defined(_BIG_ENDIAN) 2890Sstevel@tonic-gate if (offset % 2) 2900Sstevel@tonic-gate #else 2910Sstevel@tonic-gate if (!(offset % 2)) 2920Sstevel@tonic-gate #endif 2930Sstevel@tonic-gate { 2940Sstevel@tonic-gate #ifdef ENF_PUSH00FF 2950Sstevel@tonic-gate pf_emit(ENF_PUSH00FF | ENF_AND); 2960Sstevel@tonic-gate #else 2970Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_AND); 2980Sstevel@tonic-gate pf_emit(0x00FF); 2990Sstevel@tonic-gate #endif 3000Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_EQ); 3010Sstevel@tonic-gate pf_emit(val); 3020Sstevel@tonic-gate } else { 3030Sstevel@tonic-gate #ifdef ENF_PUSHFF00 3040Sstevel@tonic-gate pf_emit(ENF_PUSHFF00 | ENF_AND); 3050Sstevel@tonic-gate #else 3060Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_AND); 3070Sstevel@tonic-gate pf_emit(0xFF00); 3080Sstevel@tonic-gate #endif 3090Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_EQ); 3100Sstevel@tonic-gate pf_emit(val << 8); 3110Sstevel@tonic-gate } 3120Sstevel@tonic-gate break; 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate case 2: 3150Sstevel@tonic-gate pf_emit(ENF_PUSHWORD + offset / 2); 3160Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_EQ); 3170Sstevel@tonic-gate pf_emit((ushort_t)val); 3180Sstevel@tonic-gate break; 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate case 4: 3210Sstevel@tonic-gate pf_emit(ENF_PUSHWORD + offset / 2); 3220Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_EQ); 3230Sstevel@tonic-gate #if defined(_BIG_ENDIAN) 3240Sstevel@tonic-gate pf_emit(val >> 16); 3250Sstevel@tonic-gate #elif defined(_LITTLE_ENDIAN) 3260Sstevel@tonic-gate pf_emit(val & 0xffff); 3270Sstevel@tonic-gate #else 3280Sstevel@tonic-gate #error One of _BIG_ENDIAN and _LITTLE_ENDIAN must be defined 3290Sstevel@tonic-gate #endif 3300Sstevel@tonic-gate pf_emit(ENF_PUSHWORD + (offset / 2) + 1); 3310Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_EQ); 3320Sstevel@tonic-gate #if defined(_BIG_ENDIAN) 3330Sstevel@tonic-gate pf_emit(val & 0xffff); 3340Sstevel@tonic-gate #else 3350Sstevel@tonic-gate pf_emit(val >> 16); 3360Sstevel@tonic-gate #endif 3370Sstevel@tonic-gate pf_emit(ENF_AND); 3380Sstevel@tonic-gate break; 3390Sstevel@tonic-gate } 3400Sstevel@tonic-gate } 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate /* 3430Sstevel@tonic-gate * same as pf_compare_value, but only for emiting code to 3440Sstevel@tonic-gate * compare ipv6 addresses. 3450Sstevel@tonic-gate */ 3460Sstevel@tonic-gate static void 3470Sstevel@tonic-gate pf_compare_value_v6(int offset, uint_t len, struct in6_addr val) 3480Sstevel@tonic-gate { 3490Sstevel@tonic-gate int i; 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate for (i = 0; i < len; i += 2) { 3520Sstevel@tonic-gate pf_emit(ENF_PUSHWORD + offset / 2 + i / 2); 3530Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_EQ); 3540Sstevel@tonic-gate pf_emit(*(uint16_t *)&val.s6_addr[i]); 3550Sstevel@tonic-gate if (i != 0) 3560Sstevel@tonic-gate pf_emit(ENF_AND); 3570Sstevel@tonic-gate } 3580Sstevel@tonic-gate } 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate /* 3620Sstevel@tonic-gate * Same as above except mask the field value 363*2760Sdg199075 * before doing the comparison. The comparison checks 364*2760Sdg199075 * to make sure the values are equal. 3650Sstevel@tonic-gate */ 3660Sstevel@tonic-gate static void 3670Sstevel@tonic-gate pf_compare_value_mask(int offset, uint_t len, uint_t val, int mask) 3680Sstevel@tonic-gate { 369*2760Sdg199075 pf_compare_value_mask_generic(offset, len, val, mask, ENF_EQ); 370*2760Sdg199075 } 371*2760Sdg199075 372*2760Sdg199075 /* 373*2760Sdg199075 * Same as above except the values are compared to see if they are not 374*2760Sdg199075 * equal. 375*2760Sdg199075 */ 376*2760Sdg199075 static void 377*2760Sdg199075 pf_compare_value_mask_neq(int offset, uint_t len, uint_t val, int mask) 378*2760Sdg199075 { 379*2760Sdg199075 pf_compare_value_mask_generic(offset, len, val, mask, ENF_NEQ); 380*2760Sdg199075 } 381*2760Sdg199075 382*2760Sdg199075 /* 383*2760Sdg199075 * Similar to pf_compare_value. 384*2760Sdg199075 * 385*2760Sdg199075 * This is the utility function that does the actual work to compare 386*2760Sdg199075 * two values using a mask. The comparison operation is passed into 387*2760Sdg199075 * the function. 388*2760Sdg199075 */ 389*2760Sdg199075 static void 390*2760Sdg199075 pf_compare_value_mask_generic(int offset, uint_t len, uint_t val, int mask, 391*2760Sdg199075 uint_t op) 392*2760Sdg199075 { 3930Sstevel@tonic-gate /* 3940Sstevel@tonic-gate * If the property being filtered on is absent in the media 3950Sstevel@tonic-gate * packet, error out. 3960Sstevel@tonic-gate */ 3970Sstevel@tonic-gate if (offset == -1) 3980Sstevel@tonic-gate pr_err("filter option unsupported on media"); 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate switch (len) { 4010Sstevel@tonic-gate case 1: 4020Sstevel@tonic-gate pf_emit(ENF_PUSHWORD + offset / 2); 4030Sstevel@tonic-gate #if defined(_BIG_ENDIAN) 4040Sstevel@tonic-gate if (offset % 2) 4050Sstevel@tonic-gate #else 4060Sstevel@tonic-gate if (!offset % 2) 4070Sstevel@tonic-gate #endif 4080Sstevel@tonic-gate { 4090Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_AND); 4100Sstevel@tonic-gate pf_emit(mask & 0x00ff); 411*2760Sdg199075 pf_emit(ENF_PUSHLIT | op); 4120Sstevel@tonic-gate pf_emit(val); 4130Sstevel@tonic-gate } else { 4140Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_AND); 4150Sstevel@tonic-gate pf_emit((mask << 8) & 0xff00); 416*2760Sdg199075 pf_emit(ENF_PUSHLIT | op); 4170Sstevel@tonic-gate pf_emit(val << 8); 4180Sstevel@tonic-gate } 4190Sstevel@tonic-gate break; 4200Sstevel@tonic-gate 4210Sstevel@tonic-gate case 2: 4220Sstevel@tonic-gate pf_emit(ENF_PUSHWORD + offset / 2); 4230Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_AND); 4240Sstevel@tonic-gate pf_emit(htons((ushort_t)mask)); 425*2760Sdg199075 pf_emit(ENF_PUSHLIT | op); 4260Sstevel@tonic-gate pf_emit(htons((ushort_t)val)); 4270Sstevel@tonic-gate break; 4280Sstevel@tonic-gate 4290Sstevel@tonic-gate case 4: 4300Sstevel@tonic-gate pf_emit(ENF_PUSHWORD + offset / 2); 4310Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_AND); 4320Sstevel@tonic-gate pf_emit(htons((ushort_t)((mask >> 16) & 0xffff))); 433*2760Sdg199075 pf_emit(ENF_PUSHLIT | op); 4340Sstevel@tonic-gate pf_emit(htons((ushort_t)((val >> 16) & 0xffff))); 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate pf_emit(ENF_PUSHWORD + (offset / 2) + 1); 4370Sstevel@tonic-gate pf_emit(ENF_PUSHLIT | ENF_AND); 4380Sstevel@tonic-gate pf_emit(htons((ushort_t)(mask & 0xffff))); 439*2760Sdg199075 pf_emit(ENF_PUSHLIT | op); 4400Sstevel@tonic-gate pf_emit(htons((ushort_t)(val & 0xffff))); 4410Sstevel@tonic-gate 4420Sstevel@tonic-gate pf_emit(ENF_AND); 4430Sstevel@tonic-gate break; 4440Sstevel@tonic-gate } 4450Sstevel@tonic-gate } 4460Sstevel@tonic-gate 4470Sstevel@tonic-gate /* 4480Sstevel@tonic-gate * Generate pf code to match an IPv4 or IPv6 address. 4490Sstevel@tonic-gate */ 4500Sstevel@tonic-gate static void 4510Sstevel@tonic-gate pf_ipaddr_match(which, hostname, inet_type) 4520Sstevel@tonic-gate enum direction which; 4530Sstevel@tonic-gate char *hostname; 4540Sstevel@tonic-gate int inet_type; 4550Sstevel@tonic-gate { 4560Sstevel@tonic-gate bool_t found_host; 4570Sstevel@tonic-gate uint_t *addr4ptr; 4580Sstevel@tonic-gate uint_t addr4; 4590Sstevel@tonic-gate struct in6_addr *addr6ptr; 4600Sstevel@tonic-gate int h_addr_index; 4610Sstevel@tonic-gate struct hostent *hp = NULL; 4620Sstevel@tonic-gate int error_num = 0; 4630Sstevel@tonic-gate boolean_t first = B_TRUE; 4640Sstevel@tonic-gate int pass = 0; 4650Sstevel@tonic-gate 4660Sstevel@tonic-gate /* 4670Sstevel@tonic-gate * The addr4offset and addr6offset variables simplify the code which 4680Sstevel@tonic-gate * generates the address comparison filter. With these two variables, 4690Sstevel@tonic-gate * duplicate code need not exist for the TO and FROM case. 4700Sstevel@tonic-gate * A value of -1 describes the ANY case (TO and FROM). 4710Sstevel@tonic-gate */ 4720Sstevel@tonic-gate int addr4offset; 4730Sstevel@tonic-gate int addr6offset; 4740Sstevel@tonic-gate 4750Sstevel@tonic-gate found_host = 0; 4760Sstevel@tonic-gate 4770Sstevel@tonic-gate if (tokentype == ADDR_IP) { 4780Sstevel@tonic-gate hp = getipnodebyname(hostname, AF_INET, 0, &error_num); 4790Sstevel@tonic-gate if (hp == NULL) { 4800Sstevel@tonic-gate if (error_num == TRY_AGAIN) { 4810Sstevel@tonic-gate pr_err("could not resolve %s (try again later)", 4820Sstevel@tonic-gate hostname); 4830Sstevel@tonic-gate } else { 4840Sstevel@tonic-gate pr_err("could not resolve %s", hostname); 4850Sstevel@tonic-gate } 4860Sstevel@tonic-gate } 4870Sstevel@tonic-gate inet_type = IPV4_ONLY; 4880Sstevel@tonic-gate } else if (tokentype == ADDR_IP6) { 4890Sstevel@tonic-gate hp = getipnodebyname(hostname, AF_INET6, 0, &error_num); 4900Sstevel@tonic-gate if (hp == NULL) { 4910Sstevel@tonic-gate if (error_num == TRY_AGAIN) { 4920Sstevel@tonic-gate pr_err("could not resolve %s (try again later)", 4930Sstevel@tonic-gate hostname); 4940Sstevel@tonic-gate } else { 4950Sstevel@tonic-gate pr_err("could not resolve %s", hostname); 4960Sstevel@tonic-gate } 4970Sstevel@tonic-gate } 4980Sstevel@tonic-gate inet_type = IPV6_ONLY; 4990Sstevel@tonic-gate } else if (tokentype == ALPHA) { 5000Sstevel@tonic-gate /* Some hostname i.e. tokentype is ALPHA */ 5010Sstevel@tonic-gate switch (inet_type) { 5020Sstevel@tonic-gate case IPV4_ONLY: 5030Sstevel@tonic-gate /* Only IPv4 address is needed */ 5040Sstevel@tonic-gate hp = getipnodebyname(hostname, AF_INET, 0, &error_num); 5050Sstevel@tonic-gate if (hp != NULL) { 5060Sstevel@tonic-gate found_host = 1; 5070Sstevel@tonic-gate } 5080Sstevel@tonic-gate break; 5090Sstevel@tonic-gate case IPV6_ONLY: 5100Sstevel@tonic-gate /* Only IPv6 address is needed */ 5110Sstevel@tonic-gate hp = getipnodebyname(hostname, AF_INET6, 0, &error_num); 5120Sstevel@tonic-gate if (hp != NULL) { 5130Sstevel@tonic-gate found_host = 1; 5140Sstevel@tonic-gate } 5150Sstevel@tonic-gate break; 5160Sstevel@tonic-gate case IPV4_AND_IPV6: 5170Sstevel@tonic-gate /* Both IPv4 and IPv6 are needed */ 5180Sstevel@tonic-gate hp = getipnodebyname(hostname, AF_INET6, 5190Sstevel@tonic-gate AI_ALL | AI_V4MAPPED, &error_num); 5200Sstevel@tonic-gate if (hp != NULL) { 5210Sstevel@tonic-gate found_host = 1; 5220Sstevel@tonic-gate } 5230Sstevel@tonic-gate break; 5240Sstevel@tonic-gate default: 5250Sstevel@tonic-gate found_host = 0; 5260Sstevel@tonic-gate } 5270Sstevel@tonic-gate 5280Sstevel@tonic-gate if (!found_host) { 5290Sstevel@tonic-gate if (error_num == TRY_AGAIN) { 5300Sstevel@tonic-gate pr_err("could not resolve %s (try again later)", 5310Sstevel@tonic-gate hostname); 5320Sstevel@tonic-gate } else { 5330Sstevel@tonic-gate pr_err("could not resolve %s", hostname); 5340Sstevel@tonic-gate } 5350Sstevel@tonic-gate } 5360Sstevel@tonic-gate } else { 5370Sstevel@tonic-gate pr_err("unknown token type: %s", hostname); 5380Sstevel@tonic-gate } 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate switch (which) { 5410Sstevel@tonic-gate case TO: 5420Sstevel@tonic-gate addr4offset = IPV4_DSTADDR_OFFSET; 5430Sstevel@tonic-gate addr6offset = IPV6_DSTADDR_OFFSET; 5440Sstevel@tonic-gate break; 5450Sstevel@tonic-gate case FROM: 5460Sstevel@tonic-gate addr4offset = IPV4_SRCADDR_OFFSET; 5470Sstevel@tonic-gate addr6offset = IPV6_SRCADDR_OFFSET; 5480Sstevel@tonic-gate break; 5490Sstevel@tonic-gate case ANY: 5500Sstevel@tonic-gate addr4offset = -1; 5510Sstevel@tonic-gate addr6offset = -1; 5520Sstevel@tonic-gate break; 5530Sstevel@tonic-gate } 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate if (hp != NULL && hp->h_addrtype == AF_INET) { 556*2760Sdg199075 pf_match_ethertype(ETHERTYPE_IP); 557*2760Sdg199075 pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2); 5580Sstevel@tonic-gate h_addr_index = 0; 5590Sstevel@tonic-gate addr4ptr = (uint_t *)hp->h_addr_list[h_addr_index]; 5600Sstevel@tonic-gate while (addr4ptr != NULL) { 5610Sstevel@tonic-gate if (addr4offset == -1) { 5620Sstevel@tonic-gate pf_compare_value(IPV4_SRCADDR_OFFSET, 4, 5630Sstevel@tonic-gate *addr4ptr); 5640Sstevel@tonic-gate if (h_addr_index != 0) 5650Sstevel@tonic-gate pf_emit(ENF_OR); 5660Sstevel@tonic-gate pf_compare_value(IPV4_DSTADDR_OFFSET, 4, 5670Sstevel@tonic-gate *addr4ptr); 5680Sstevel@tonic-gate pf_emit(ENF_OR); 5690Sstevel@tonic-gate } else { 5700Sstevel@tonic-gate pf_compare_value(addr4offset, 4, 5710Sstevel@tonic-gate *addr4ptr); 5720Sstevel@tonic-gate if (h_addr_index != 0) 5730Sstevel@tonic-gate pf_emit(ENF_OR); 5740Sstevel@tonic-gate } 5750Sstevel@tonic-gate addr4ptr = (uint_t *)hp->h_addr_list[++h_addr_index]; 5760Sstevel@tonic-gate } 5770Sstevel@tonic-gate pf_emit(ENF_AND); 5780Sstevel@tonic-gate } else { 5790Sstevel@tonic-gate /* first pass: IPv4 addresses */ 5800Sstevel@tonic-gate h_addr_index = 0; 5810Sstevel@tonic-gate addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index]; 5820Sstevel@tonic-gate first = B_TRUE; 5830Sstevel@tonic-gate while (addr6ptr != NULL) { 5840Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(addr6ptr)) { 5850Sstevel@tonic-gate if (first) { 586*2760Sdg199075 pf_match_ethertype(ETHERTYPE_IP); 587*2760Sdg199075 pf_check_vlan_tag( 588*2760Sdg199075 ENCAP_ETHERTYPE_OFF/2); 5890Sstevel@tonic-gate pass++; 5900Sstevel@tonic-gate } 5910Sstevel@tonic-gate IN6_V4MAPPED_TO_INADDR(addr6ptr, 5920Sstevel@tonic-gate (struct in_addr *)&addr4); 5930Sstevel@tonic-gate if (addr4offset == -1) { 5940Sstevel@tonic-gate pf_compare_value(IPV4_SRCADDR_OFFSET, 4, 5950Sstevel@tonic-gate addr4); 5960Sstevel@tonic-gate if (!first) 5970Sstevel@tonic-gate pf_emit(ENF_OR); 5980Sstevel@tonic-gate pf_compare_value(IPV4_DSTADDR_OFFSET, 4, 5990Sstevel@tonic-gate addr4); 6000Sstevel@tonic-gate pf_emit(ENF_OR); 6010Sstevel@tonic-gate } else { 6020Sstevel@tonic-gate pf_compare_value(addr4offset, 4, 6030Sstevel@tonic-gate addr4); 6040Sstevel@tonic-gate if (!first) 6050Sstevel@tonic-gate pf_emit(ENF_OR); 6060Sstevel@tonic-gate } 6070Sstevel@tonic-gate if (first) 6080Sstevel@tonic-gate first = B_FALSE; 6090Sstevel@tonic-gate } 6100Sstevel@tonic-gate addr6ptr = (struct in6_addr *) 6110Sstevel@tonic-gate hp->h_addr_list[++h_addr_index]; 6120Sstevel@tonic-gate } 6130Sstevel@tonic-gate if (!first) { 6140Sstevel@tonic-gate pf_emit(ENF_AND); 6150Sstevel@tonic-gate } 6160Sstevel@tonic-gate /* second pass: IPv6 addresses */ 6170Sstevel@tonic-gate h_addr_index = 0; 6180Sstevel@tonic-gate addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index]; 6190Sstevel@tonic-gate first = B_TRUE; 6200Sstevel@tonic-gate while (addr6ptr != NULL) { 6210Sstevel@tonic-gate if (!IN6_IS_ADDR_V4MAPPED(addr6ptr)) { 6220Sstevel@tonic-gate if (first) { 623*2760Sdg199075 pf_match_ethertype(ETHERTYPE_IPV6); 624*2760Sdg199075 pf_check_vlan_tag( 625*2760Sdg199075 ENCAP_ETHERTYPE_OFF/2); 6260Sstevel@tonic-gate pass++; 6270Sstevel@tonic-gate } 6280Sstevel@tonic-gate if (addr6offset == -1) { 6290Sstevel@tonic-gate pf_compare_value_v6(IPV6_SRCADDR_OFFSET, 6300Sstevel@tonic-gate 16, *addr6ptr); 6310Sstevel@tonic-gate if (!first) 6320Sstevel@tonic-gate pf_emit(ENF_OR); 6330Sstevel@tonic-gate pf_compare_value_v6(IPV6_DSTADDR_OFFSET, 6340Sstevel@tonic-gate 16, *addr6ptr); 6350Sstevel@tonic-gate pf_emit(ENF_OR); 6360Sstevel@tonic-gate } else { 6370Sstevel@tonic-gate pf_compare_value_v6(addr6offset, 16, 6380Sstevel@tonic-gate *addr6ptr); 6390Sstevel@tonic-gate if (!first) 6400Sstevel@tonic-gate pf_emit(ENF_OR); 6410Sstevel@tonic-gate } 6420Sstevel@tonic-gate if (first) 6430Sstevel@tonic-gate first = B_FALSE; 6440Sstevel@tonic-gate } 6450Sstevel@tonic-gate addr6ptr = (struct in6_addr *) 6460Sstevel@tonic-gate hp->h_addr_list[++h_addr_index]; 6470Sstevel@tonic-gate } 6480Sstevel@tonic-gate if (!first) { 6490Sstevel@tonic-gate pf_emit(ENF_AND); 6500Sstevel@tonic-gate } 6510Sstevel@tonic-gate if (pass == 2) { 6520Sstevel@tonic-gate pf_emit(ENF_OR); 6530Sstevel@tonic-gate } 6540Sstevel@tonic-gate } 6550Sstevel@tonic-gate 6560Sstevel@tonic-gate if (hp != NULL) { 6570Sstevel@tonic-gate freehostent(hp); 6580Sstevel@tonic-gate } 6590Sstevel@tonic-gate } 6600Sstevel@tonic-gate 6610Sstevel@tonic-gate 6620Sstevel@tonic-gate static void 6630Sstevel@tonic-gate pf_compare_address(int offset, uint_t len, uchar_t *addr) 6640Sstevel@tonic-gate { 6650Sstevel@tonic-gate uint32_t val; 6660Sstevel@tonic-gate uint16_t sval; 6670Sstevel@tonic-gate boolean_t didone = B_FALSE; 6680Sstevel@tonic-gate 6690Sstevel@tonic-gate /* 6700Sstevel@tonic-gate * If the property being filtered on is absent in the media 6710Sstevel@tonic-gate * packet, error out. 6720Sstevel@tonic-gate */ 6730Sstevel@tonic-gate if (offset == -1) 6740Sstevel@tonic-gate pr_err("filter option unsupported on media"); 6750Sstevel@tonic-gate 6760Sstevel@tonic-gate while (len > 0) { 6770Sstevel@tonic-gate if (len >= 4) { 6780Sstevel@tonic-gate (void) memcpy(&val, addr, 4); 6790Sstevel@tonic-gate pf_compare_value(offset, 4, val); 6800Sstevel@tonic-gate addr += 4; 6810Sstevel@tonic-gate offset += 4; 6820Sstevel@tonic-gate len -= 4; 6830Sstevel@tonic-gate } else if (len >= 2) { 6840Sstevel@tonic-gate (void) memcpy(&sval, addr, 2); 6850Sstevel@tonic-gate pf_compare_value(offset, 2, sval); 6860Sstevel@tonic-gate addr += 2; 6870Sstevel@tonic-gate offset += 2; 6880Sstevel@tonic-gate len -= 2; 6890Sstevel@tonic-gate } else { 6900Sstevel@tonic-gate pf_compare_value(offset++, 1, *addr++); 6910Sstevel@tonic-gate len--; 6920Sstevel@tonic-gate } 6930Sstevel@tonic-gate if (didone) 6940Sstevel@tonic-gate pf_emit(ENF_AND); 6950Sstevel@tonic-gate didone = B_TRUE; 6960Sstevel@tonic-gate } 6970Sstevel@tonic-gate } 6980Sstevel@tonic-gate 6990Sstevel@tonic-gate /* 7000Sstevel@tonic-gate * Compare ethernet addresses. 7010Sstevel@tonic-gate */ 7020Sstevel@tonic-gate static void 7030Sstevel@tonic-gate pf_etheraddr_match(which, hostname) 7040Sstevel@tonic-gate enum direction which; 7050Sstevel@tonic-gate char *hostname; 7060Sstevel@tonic-gate { 7070Sstevel@tonic-gate struct ether_addr e, *ep = NULL; 7080Sstevel@tonic-gate 7090Sstevel@tonic-gate if (isxdigit(*hostname)) 7100Sstevel@tonic-gate ep = ether_aton(hostname); 7110Sstevel@tonic-gate if (ep == NULL) { 7120Sstevel@tonic-gate if (ether_hostton(hostname, &e)) 7130Sstevel@tonic-gate if (!arp_for_ether(hostname, &e)) 7140Sstevel@tonic-gate pr_err("cannot obtain ether addr for %s", 7150Sstevel@tonic-gate hostname); 7160Sstevel@tonic-gate ep = &e; 7170Sstevel@tonic-gate } 7180Sstevel@tonic-gate 719*2760Sdg199075 pf_clear_offset_register(); 720*2760Sdg199075 7210Sstevel@tonic-gate switch (which) { 7220Sstevel@tonic-gate case TO: 7230Sstevel@tonic-gate pf_compare_address(link_dest_offset, link_addr_len, 7240Sstevel@tonic-gate (uchar_t *)ep); 7250Sstevel@tonic-gate break; 7260Sstevel@tonic-gate case FROM: 7270Sstevel@tonic-gate pf_compare_address(link_src_offset, link_addr_len, 7280Sstevel@tonic-gate (uchar_t *)ep); 7290Sstevel@tonic-gate break; 7300Sstevel@tonic-gate case ANY: 7310Sstevel@tonic-gate pf_compare_address(link_dest_offset, link_addr_len, 7320Sstevel@tonic-gate (uchar_t *)ep); 7330Sstevel@tonic-gate pf_compare_address(link_src_offset, link_addr_len, 7340Sstevel@tonic-gate (uchar_t *)ep); 7350Sstevel@tonic-gate pf_emit(ENF_OR); 7360Sstevel@tonic-gate break; 7370Sstevel@tonic-gate } 7380Sstevel@tonic-gate } 7390Sstevel@tonic-gate 7400Sstevel@tonic-gate /* 7410Sstevel@tonic-gate * Emit code to compare the network part of 7420Sstevel@tonic-gate * an IP address. 7430Sstevel@tonic-gate */ 7440Sstevel@tonic-gate static void 7450Sstevel@tonic-gate pf_netaddr_match(which, netname) 7460Sstevel@tonic-gate enum direction which; 7470Sstevel@tonic-gate char *netname; 7480Sstevel@tonic-gate { 7490Sstevel@tonic-gate uint_t addr; 7500Sstevel@tonic-gate uint_t mask = 0xff000000; 7510Sstevel@tonic-gate struct netent *np; 7520Sstevel@tonic-gate 7530Sstevel@tonic-gate if (isdigit(*netname)) { 7540Sstevel@tonic-gate addr = inet_network(netname); 7550Sstevel@tonic-gate } else { 7560Sstevel@tonic-gate np = getnetbyname(netname); 7570Sstevel@tonic-gate if (np == NULL) 7580Sstevel@tonic-gate pr_err("net %s not known", netname); 7590Sstevel@tonic-gate addr = np->n_net; 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate /* 7630Sstevel@tonic-gate * Left justify the address and figure 7640Sstevel@tonic-gate * out a mask based on the supplied address. 7650Sstevel@tonic-gate * Set the mask according to the number of zero 7660Sstevel@tonic-gate * low-order bytes. 7670Sstevel@tonic-gate * Note: this works only for whole octet masks. 7680Sstevel@tonic-gate */ 7690Sstevel@tonic-gate if (addr) { 7700Sstevel@tonic-gate while ((addr & ~mask) != 0) { 7710Sstevel@tonic-gate mask |= (mask >> 8); 7720Sstevel@tonic-gate } 7730Sstevel@tonic-gate } 7740Sstevel@tonic-gate 775*2760Sdg199075 pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2); 776*2760Sdg199075 7770Sstevel@tonic-gate switch (which) { 7780Sstevel@tonic-gate case TO: 7790Sstevel@tonic-gate pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask); 7800Sstevel@tonic-gate break; 7810Sstevel@tonic-gate case FROM: 7820Sstevel@tonic-gate pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask); 7830Sstevel@tonic-gate break; 7840Sstevel@tonic-gate case ANY: 7850Sstevel@tonic-gate pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask); 7860Sstevel@tonic-gate pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask); 7870Sstevel@tonic-gate pf_emit(ENF_OR); 7880Sstevel@tonic-gate break; 7890Sstevel@tonic-gate } 7900Sstevel@tonic-gate } 7910Sstevel@tonic-gate 792*2760Sdg199075 /* 793*2760Sdg199075 * A helper function to keep the code to emit instructions 794*2760Sdg199075 * to change the offset register in one place. 795*2760Sdg199075 * 796*2760Sdg199075 * INPUTS: offset - An value representing an offset in 16-bit 797*2760Sdg199075 * words. 798*2760Sdg199075 * OUTPUTS: If there is enough room in the storage for the 799*2760Sdg199075 * packet filtering program, instructions to load 800*2760Sdg199075 * a constant to the offset register. Otherwise, 801*2760Sdg199075 * nothing. 802*2760Sdg199075 */ 803*2760Sdg199075 static void 804*2760Sdg199075 pf_emit_load_offset(uint_t offset) 805*2760Sdg199075 { 806*2760Sdg199075 pf_emit(ENF_LOAD_OFFSET | ENF_NOP); 807*2760Sdg199075 pf_emit(offset); 808*2760Sdg199075 } 809*2760Sdg199075 810*2760Sdg199075 /* 811*2760Sdg199075 * Clear pfmod's offset register. 812*2760Sdg199075 * 813*2760Sdg199075 * INPUTS: none 814*2760Sdg199075 * OUTPUTS: Instructions to clear the offset register if 815*2760Sdg199075 * there is enough space remaining in the packet 816*2760Sdg199075 * filtering program structure's storage, and 817*2760Sdg199075 * the last thing done to the offset register was 818*2760Sdg199075 * not clearing the offset register. Otherwise, 819*2760Sdg199075 * nothing. 820*2760Sdg199075 */ 821*2760Sdg199075 static void 822*2760Sdg199075 pf_clear_offset_register() 823*2760Sdg199075 { 824*2760Sdg199075 if (last_offset_operation != (void*)pf_clear_offset_register) { 825*2760Sdg199075 pf_emit_load_offset(0); 826*2760Sdg199075 last_offset_operation = (void*)pf_clear_offset_register; 827*2760Sdg199075 } 828*2760Sdg199075 } 829*2760Sdg199075 830*2760Sdg199075 /* 831*2760Sdg199075 * This function will issue opcodes to check if a packet 832*2760Sdg199075 * is VLAN tagged, and if so, update the offset register 833*2760Sdg199075 * with the appropriate offset. 834*2760Sdg199075 * 835*2760Sdg199075 * Note that if the packet is not VLAN tagged, then the offset 836*2760Sdg199075 * register will be cleared. 837*2760Sdg199075 * 838*2760Sdg199075 * If the interface type is not an ethernet type, then this 839*2760Sdg199075 * function returns without doing anything. 840*2760Sdg199075 * 841*2760Sdg199075 * If the last attempt to change the offset register occured because 842*2760Sdg199075 * of a call to this function that was called with the same offset, 843*2760Sdg199075 * then we don't issue packet filtering instructions. 844*2760Sdg199075 * 845*2760Sdg199075 * INPUTS: offset - an offset in 16 bit words. The function 846*2760Sdg199075 * will set the offset register to this 847*2760Sdg199075 * value if the packet is VLAN tagged. 848*2760Sdg199075 * OUTPUTS: If the conditions are met, packet filtering instructions. 849*2760Sdg199075 */ 850*2760Sdg199075 static void 851*2760Sdg199075 pf_check_vlan_tag(uint_t offset) 852*2760Sdg199075 { 853*2760Sdg199075 static uint_t last_offset = 0; 854*2760Sdg199075 855*2760Sdg199075 if ((interface->mac_type == DL_ETHER || 856*2760Sdg199075 interface->mac_type == DL_CSMACD) && 857*2760Sdg199075 (last_offset_operation != (void*)pf_check_vlan_tag || 858*2760Sdg199075 last_offset != offset)) { 859*2760Sdg199075 /* 860*2760Sdg199075 * First thing is to clear the offset register. 861*2760Sdg199075 * We don't know what state it is in, and if it 862*2760Sdg199075 * is not zero, then we have no idea what we load 863*2760Sdg199075 * when we execute ENF_PUSHWORD. 864*2760Sdg199075 */ 865*2760Sdg199075 pf_clear_offset_register(); 866*2760Sdg199075 867*2760Sdg199075 /* 868*2760Sdg199075 * Check the ethertype. 869*2760Sdg199075 */ 870*2760Sdg199075 pf_compare_value(link_type_offset, 2, htons(ETHERTYPE_VLAN)); 871*2760Sdg199075 872*2760Sdg199075 /* 873*2760Sdg199075 * And if it's not VLAN, don't load offset to the offset 874*2760Sdg199075 * register. 875*2760Sdg199075 */ 876*2760Sdg199075 pf_emit(ENF_BRFL | ENF_NOP); 877*2760Sdg199075 pf_emit(3); 878*2760Sdg199075 879*2760Sdg199075 /* 880*2760Sdg199075 * Otherwise, load offset to the offset register. 881*2760Sdg199075 */ 882*2760Sdg199075 pf_emit_load_offset(offset); 883*2760Sdg199075 884*2760Sdg199075 /* 885*2760Sdg199075 * Now get rid of the results of the comparison, 886*2760Sdg199075 * we don't want the results of the comparison to affect 887*2760Sdg199075 * other logic in the packet filtering program. 888*2760Sdg199075 */ 889*2760Sdg199075 pf_emit(ENF_POP | ENF_NOP); 890*2760Sdg199075 891*2760Sdg199075 /* 892*2760Sdg199075 * Set the last operation at the end, or any time 893*2760Sdg199075 * after the call to pf_clear_offset because 894*2760Sdg199075 * pf_clear_offset uses it. 895*2760Sdg199075 */ 896*2760Sdg199075 last_offset_operation = (void*)pf_check_vlan_tag; 897*2760Sdg199075 last_offset = offset; 898*2760Sdg199075 } 899*2760Sdg199075 } 900*2760Sdg199075 901*2760Sdg199075 /* 902*2760Sdg199075 * Utility function used to emit packet filtering code 903*2760Sdg199075 * to match an ethertype. 904*2760Sdg199075 * 905*2760Sdg199075 * INPUTS: ethertype - The ethertype we want to check for. 906*2760Sdg199075 * Don't call htons on the ethertype before 907*2760Sdg199075 * calling this function. 908*2760Sdg199075 * OUTPUTS: If there is sufficient storage available, packet 909*2760Sdg199075 * filtering code to check an ethertype. Otherwise, 910*2760Sdg199075 * nothing. 911*2760Sdg199075 */ 912*2760Sdg199075 static void 913*2760Sdg199075 pf_match_ethertype(uint_t ethertype) 914*2760Sdg199075 { 915*2760Sdg199075 /* 916*2760Sdg199075 * If the user wants to filter on ethertype VLAN, 917*2760Sdg199075 * then clear the offset register so that the offset 918*2760Sdg199075 * for ENF_PUSHWORD points to the right place in the 919*2760Sdg199075 * packet. 920*2760Sdg199075 * 921*2760Sdg199075 * Otherwise, call pf_check_vlan_tag to set the offset 922*2760Sdg199075 * register such that the contents of the offset register 923*2760Sdg199075 * plus the argument for ENF_PUSHWORD point to the right 924*2760Sdg199075 * part of the packet, whether or not the packet is VLAN 925*2760Sdg199075 * tagged. We call pf_check_vlan_tag with an offset of 926*2760Sdg199075 * two words because if the packet is VLAN tagged, we have 927*2760Sdg199075 * to move past the ethertype in the ethernet header, and 928*2760Sdg199075 * past the lower two octets of the VLAN header to get to 929*2760Sdg199075 * the ethertype in the VLAN header. 930*2760Sdg199075 */ 931*2760Sdg199075 if (ethertype == ETHERTYPE_VLAN) 932*2760Sdg199075 pf_clear_offset_register(); 933*2760Sdg199075 else 934*2760Sdg199075 pf_check_vlan_tag(2); 935*2760Sdg199075 936*2760Sdg199075 pf_compare_value(link_type_offset, 2, htons(ethertype)); 937*2760Sdg199075 } 938*2760Sdg199075 939*2760Sdg199075 typedef struct { 940*2760Sdg199075 int transport_protocol; 941*2760Sdg199075 int network_protocol; 942*2760Sdg199075 /* 943*2760Sdg199075 * offset is the offset in bytes from the beginning 944*2760Sdg199075 * of the network protocol header to where the transport 945*2760Sdg199075 * protocol type is. 946*2760Sdg199075 */ 947*2760Sdg199075 int offset; 948*2760Sdg199075 } transport_protocol_table_t; 949*2760Sdg199075 950*2760Sdg199075 static transport_protocol_table_t mapping_table[] = { 951*2760Sdg199075 {IPPROTO_TCP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET}, 952*2760Sdg199075 {IPPROTO_TCP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET}, 953*2760Sdg199075 {IPPROTO_UDP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET}, 954*2760Sdg199075 {IPPROTO_UDP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET}, 955*2760Sdg199075 {IPPROTO_OSPF, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET}, 956*2760Sdg199075 {IPPROTO_OSPF, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET}, 957*2760Sdg199075 {IPPROTO_SCTP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET}, 958*2760Sdg199075 {IPPROTO_SCTP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET}, 959*2760Sdg199075 {IPPROTO_ICMP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET}, 960*2760Sdg199075 {IPPROTO_ICMPV6, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET}, 961*2760Sdg199075 {IPPROTO_ENCAP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET}, 962*2760Sdg199075 {IPPROTO_ESP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET}, 963*2760Sdg199075 {IPPROTO_ESP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET}, 964*2760Sdg199075 {IPPROTO_AH, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET}, 965*2760Sdg199075 {IPPROTO_AH, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET}, 966*2760Sdg199075 {-1, 0, 0} /* must be the final entry */ 967*2760Sdg199075 }; 968*2760Sdg199075 969*2760Sdg199075 /* 970*2760Sdg199075 * This function uses the table above to generate a 971*2760Sdg199075 * piece of a packet filtering program to check a transport 972*2760Sdg199075 * protocol type. 973*2760Sdg199075 * 974*2760Sdg199075 * INPUTS: tranport_protocol - the transport protocol we're 975*2760Sdg199075 * interested in. 976*2760Sdg199075 * OUTPUTS: If there is sufficient storage, then packet filtering 977*2760Sdg199075 * code to check a transport protocol type. Otherwise, 978*2760Sdg199075 * nothing. 979*2760Sdg199075 */ 980*2760Sdg199075 static void 981*2760Sdg199075 pf_check_transport_protocol(uint_t transport_protocol) 982*2760Sdg199075 { 983*2760Sdg199075 int i = 0; 984*2760Sdg199075 uint_t number_of_matches = 0; 985*2760Sdg199075 986*2760Sdg199075 for (i = 0; mapping_table[i].transport_protocol != -1; i++) { 987*2760Sdg199075 if (transport_protocol == 988*2760Sdg199075 (uint_t)mapping_table[i].transport_protocol) { 989*2760Sdg199075 number_of_matches++; 990*2760Sdg199075 pf_match_ethertype(mapping_table[i].network_protocol); 991*2760Sdg199075 pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2); 992*2760Sdg199075 pf_compare_value( 993*2760Sdg199075 mapping_table[i].offset + link_header_len, 1, 994*2760Sdg199075 transport_protocol); 995*2760Sdg199075 pf_emit(ENF_AND); 996*2760Sdg199075 if (number_of_matches > 1) { 997*2760Sdg199075 /* 998*2760Sdg199075 * Since we have two or more matches, in 999*2760Sdg199075 * order to have a correct and complete 1000*2760Sdg199075 * program we need to OR the result of 1001*2760Sdg199075 * each block of comparisons together. 1002*2760Sdg199075 */ 1003*2760Sdg199075 pf_emit(ENF_OR); 1004*2760Sdg199075 } 1005*2760Sdg199075 } 1006*2760Sdg199075 } 1007*2760Sdg199075 } 1008*2760Sdg199075 10090Sstevel@tonic-gate static void 10100Sstevel@tonic-gate pf_primary() 10110Sstevel@tonic-gate { 10120Sstevel@tonic-gate for (;;) { 10130Sstevel@tonic-gate if (tokentype == FIELD) 10140Sstevel@tonic-gate break; 10150Sstevel@tonic-gate 10160Sstevel@tonic-gate if (EQ("ip")) { 1017*2760Sdg199075 pf_match_ethertype(ETHERTYPE_IP); 10180Sstevel@tonic-gate opstack++; 10190Sstevel@tonic-gate next(); 10200Sstevel@tonic-gate break; 10210Sstevel@tonic-gate } 10220Sstevel@tonic-gate 10230Sstevel@tonic-gate if (EQ("ip6")) { 1024*2760Sdg199075 pf_match_ethertype(ETHERTYPE_IPV6); 10250Sstevel@tonic-gate opstack++; 10260Sstevel@tonic-gate next(); 10270Sstevel@tonic-gate break; 10280Sstevel@tonic-gate } 10290Sstevel@tonic-gate 10300Sstevel@tonic-gate if (EQ("pppoe")) { 1031*2760Sdg199075 pf_match_ethertype(ETHERTYPE_PPPOED); 1032*2760Sdg199075 pf_match_ethertype(ETHERTYPE_PPPOES); 10330Sstevel@tonic-gate pf_emit(ENF_OR); 10340Sstevel@tonic-gate opstack++; 10350Sstevel@tonic-gate next(); 10360Sstevel@tonic-gate break; 10370Sstevel@tonic-gate } 10380Sstevel@tonic-gate 10390Sstevel@tonic-gate if (EQ("pppoed")) { 1040*2760Sdg199075 pf_match_ethertype(ETHERTYPE_PPPOED); 10410Sstevel@tonic-gate opstack++; 10420Sstevel@tonic-gate next(); 10430Sstevel@tonic-gate break; 10440Sstevel@tonic-gate } 10450Sstevel@tonic-gate 10460Sstevel@tonic-gate if (EQ("pppoes")) { 1047*2760Sdg199075 pf_match_ethertype(ETHERTYPE_PPPOES); 10480Sstevel@tonic-gate opstack++; 10490Sstevel@tonic-gate next(); 10500Sstevel@tonic-gate break; 10510Sstevel@tonic-gate } 10520Sstevel@tonic-gate 10530Sstevel@tonic-gate if (EQ("arp")) { 1054*2760Sdg199075 pf_match_ethertype(ETHERTYPE_ARP); 1055*2760Sdg199075 opstack++; 1056*2760Sdg199075 next(); 1057*2760Sdg199075 break; 1058*2760Sdg199075 } 1059*2760Sdg199075 1060*2760Sdg199075 if (EQ("vlan")) { 1061*2760Sdg199075 pf_match_ethertype(ETHERTYPE_VLAN); 1062*2760Sdg199075 pf_compare_value_mask_neq(VLAN_ID_OFFSET, 2, 1063*2760Sdg199075 0, VLAN_ID_MASK); 1064*2760Sdg199075 pf_emit(ENF_AND); 1065*2760Sdg199075 opstack++; 1066*2760Sdg199075 next(); 1067*2760Sdg199075 break; 1068*2760Sdg199075 } 1069*2760Sdg199075 1070*2760Sdg199075 if (EQ("vlan-id")) { 1071*2760Sdg199075 next(); 1072*2760Sdg199075 if (tokentype != NUMBER) 1073*2760Sdg199075 pr_err("VLAN ID expected"); 1074*2760Sdg199075 pf_match_ethertype(ETHERTYPE_VLAN); 1075*2760Sdg199075 pf_compare_value_mask(VLAN_ID_OFFSET, 2, tokenval, 1076*2760Sdg199075 VLAN_ID_MASK); 1077*2760Sdg199075 pf_emit(ENF_AND); 10780Sstevel@tonic-gate opstack++; 10790Sstevel@tonic-gate next(); 10800Sstevel@tonic-gate break; 10810Sstevel@tonic-gate } 10820Sstevel@tonic-gate 10830Sstevel@tonic-gate if (EQ("rarp")) { 1084*2760Sdg199075 pf_match_ethertype(ETHERTYPE_REVARP); 10850Sstevel@tonic-gate opstack++; 10860Sstevel@tonic-gate next(); 10870Sstevel@tonic-gate break; 10880Sstevel@tonic-gate } 10890Sstevel@tonic-gate 10900Sstevel@tonic-gate if (EQ("tcp")) { 1091*2760Sdg199075 pf_check_transport_protocol(IPPROTO_TCP); 10920Sstevel@tonic-gate opstack++; 10930Sstevel@tonic-gate next(); 10940Sstevel@tonic-gate break; 10950Sstevel@tonic-gate } 10960Sstevel@tonic-gate 10970Sstevel@tonic-gate if (EQ("udp")) { 1098*2760Sdg199075 pf_check_transport_protocol(IPPROTO_UDP); 10990Sstevel@tonic-gate opstack++; 11000Sstevel@tonic-gate next(); 11010Sstevel@tonic-gate break; 11020Sstevel@tonic-gate } 11030Sstevel@tonic-gate 11040Sstevel@tonic-gate if (EQ("ospf")) { 1105*2760Sdg199075 pf_check_transport_protocol(IPPROTO_OSPF); 11060Sstevel@tonic-gate opstack++; 11070Sstevel@tonic-gate next(); 11080Sstevel@tonic-gate break; 11090Sstevel@tonic-gate } 11100Sstevel@tonic-gate 11110Sstevel@tonic-gate 11120Sstevel@tonic-gate if (EQ("sctp")) { 1113*2760Sdg199075 pf_check_transport_protocol(IPPROTO_SCTP); 11140Sstevel@tonic-gate opstack++; 11150Sstevel@tonic-gate next(); 11160Sstevel@tonic-gate break; 11170Sstevel@tonic-gate } 11180Sstevel@tonic-gate 11190Sstevel@tonic-gate if (EQ("icmp")) { 1120*2760Sdg199075 pf_check_transport_protocol(IPPROTO_ICMP); 11210Sstevel@tonic-gate opstack++; 11220Sstevel@tonic-gate next(); 11230Sstevel@tonic-gate break; 11240Sstevel@tonic-gate } 11250Sstevel@tonic-gate 11260Sstevel@tonic-gate if (EQ("icmp6")) { 1127*2760Sdg199075 pf_check_transport_protocol(IPPROTO_ICMPV6); 11280Sstevel@tonic-gate opstack++; 11290Sstevel@tonic-gate next(); 11300Sstevel@tonic-gate break; 11310Sstevel@tonic-gate } 11320Sstevel@tonic-gate 11330Sstevel@tonic-gate if (EQ("ip-in-ip")) { 1134*2760Sdg199075 pf_check_transport_protocol(IPPROTO_ENCAP); 11350Sstevel@tonic-gate opstack++; 11360Sstevel@tonic-gate next(); 11370Sstevel@tonic-gate break; 11380Sstevel@tonic-gate } 11390Sstevel@tonic-gate 11400Sstevel@tonic-gate if (EQ("esp")) { 1141*2760Sdg199075 pf_check_transport_protocol(IPPROTO_ESP); 11420Sstevel@tonic-gate opstack++; 11430Sstevel@tonic-gate next(); 11440Sstevel@tonic-gate break; 11450Sstevel@tonic-gate } 11460Sstevel@tonic-gate 11470Sstevel@tonic-gate if (EQ("ah")) { 1148*2760Sdg199075 pf_check_transport_protocol(IPPROTO_AH); 11490Sstevel@tonic-gate opstack++; 11500Sstevel@tonic-gate next(); 11510Sstevel@tonic-gate break; 11520Sstevel@tonic-gate } 11530Sstevel@tonic-gate 11540Sstevel@tonic-gate if (EQ("(")) { 11550Sstevel@tonic-gate inBrace++; 11560Sstevel@tonic-gate next(); 11570Sstevel@tonic-gate pf_expression(); 11580Sstevel@tonic-gate if (EQ(")")) { 11590Sstevel@tonic-gate if (inBrace) 11600Sstevel@tonic-gate inBraceOR--; 11610Sstevel@tonic-gate inBrace--; 11620Sstevel@tonic-gate next(); 11630Sstevel@tonic-gate } 11640Sstevel@tonic-gate break; 11650Sstevel@tonic-gate } 11660Sstevel@tonic-gate 11670Sstevel@tonic-gate if (EQ("to") || EQ("dst")) { 11680Sstevel@tonic-gate dir = TO; 11690Sstevel@tonic-gate next(); 11700Sstevel@tonic-gate continue; 11710Sstevel@tonic-gate } 11720Sstevel@tonic-gate 11730Sstevel@tonic-gate if (EQ("from") || EQ("src")) { 11740Sstevel@tonic-gate dir = FROM; 11750Sstevel@tonic-gate next(); 11760Sstevel@tonic-gate continue; 11770Sstevel@tonic-gate } 11780Sstevel@tonic-gate 11790Sstevel@tonic-gate if (EQ("ether")) { 11800Sstevel@tonic-gate eaddr = 1; 11810Sstevel@tonic-gate next(); 11820Sstevel@tonic-gate continue; 11830Sstevel@tonic-gate } 11840Sstevel@tonic-gate 11850Sstevel@tonic-gate if (EQ("inet")) { 11860Sstevel@tonic-gate next(); 11870Sstevel@tonic-gate if (EQ("host")) 11880Sstevel@tonic-gate next(); 11890Sstevel@tonic-gate if (tokentype != ALPHA && tokentype != ADDR_IP) 11900Sstevel@tonic-gate pr_err("host/IPv4 addr expected after inet"); 11910Sstevel@tonic-gate pf_ipaddr_match(dir, token, IPV4_ONLY); 11920Sstevel@tonic-gate opstack++; 11930Sstevel@tonic-gate next(); 11940Sstevel@tonic-gate break; 11950Sstevel@tonic-gate } 11960Sstevel@tonic-gate 11970Sstevel@tonic-gate if (EQ("inet6")) { 11980Sstevel@tonic-gate next(); 11990Sstevel@tonic-gate if (EQ("host")) 12000Sstevel@tonic-gate next(); 12010Sstevel@tonic-gate if (tokentype != ALPHA && tokentype != ADDR_IP6) 12020Sstevel@tonic-gate pr_err("host/IPv6 addr expected after inet6"); 12030Sstevel@tonic-gate pf_ipaddr_match(dir, token, IPV6_ONLY); 12040Sstevel@tonic-gate opstack++; 12050Sstevel@tonic-gate next(); 12060Sstevel@tonic-gate break; 12070Sstevel@tonic-gate } 12080Sstevel@tonic-gate 12090Sstevel@tonic-gate if (EQ("proto")) { 12100Sstevel@tonic-gate next(); 12110Sstevel@tonic-gate if (tokentype != NUMBER) 12120Sstevel@tonic-gate pr_err("IP proto type expected"); 1213*2760Sdg199075 pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2); 1214*2760Sdg199075 pf_compare_value(IPV4_TYPE_HEADER_OFFSET, 1, 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")) { 1221*2760Sdg199075 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")) { 1229*2760Sdg199075 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"); 1240*2760Sdg199075 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") || 12680Sstevel@tonic-gate EQ("bootp") || EQ("dhcp") || EQ("slp") || EQ("ldap")) { 12690Sstevel@tonic-gate break; 12700Sstevel@tonic-gate } 12710Sstevel@tonic-gate 12720Sstevel@tonic-gate if (EQ("host") || EQ("between") || 12730Sstevel@tonic-gate tokentype == ALPHA || /* assume its a hostname */ 12740Sstevel@tonic-gate tokentype == ADDR_IP || 12750Sstevel@tonic-gate tokentype == ADDR_IP6 || 12760Sstevel@tonic-gate tokentype == ADDR_ETHER) { 12770Sstevel@tonic-gate if (EQ("host") || EQ("between")) 12780Sstevel@tonic-gate next(); 12790Sstevel@tonic-gate if (eaddr || tokentype == ADDR_ETHER) { 12800Sstevel@tonic-gate pf_etheraddr_match(dir, token); 12810Sstevel@tonic-gate } else if (tokentype == ALPHA) { 12820Sstevel@tonic-gate pf_ipaddr_match(dir, token, IPV4_AND_IPV6); 12830Sstevel@tonic-gate } else if (tokentype == ADDR_IP) { 12840Sstevel@tonic-gate pf_ipaddr_match(dir, token, IPV4_ONLY); 12850Sstevel@tonic-gate } else { 12860Sstevel@tonic-gate pf_ipaddr_match(dir, token, IPV6_ONLY); 12870Sstevel@tonic-gate } 12880Sstevel@tonic-gate dir = ANY; 12890Sstevel@tonic-gate eaddr = 0; 12900Sstevel@tonic-gate opstack++; 12910Sstevel@tonic-gate next(); 12920Sstevel@tonic-gate break; 12930Sstevel@tonic-gate } 12940Sstevel@tonic-gate 12950Sstevel@tonic-gate break; /* unknown token */ 12960Sstevel@tonic-gate } 12970Sstevel@tonic-gate } 12980Sstevel@tonic-gate 12990Sstevel@tonic-gate static void 13000Sstevel@tonic-gate pf_alternation() 13010Sstevel@tonic-gate { 13020Sstevel@tonic-gate int s = opstack; 13030Sstevel@tonic-gate 13040Sstevel@tonic-gate pf_primary(); 13050Sstevel@tonic-gate for (;;) { 13060Sstevel@tonic-gate if (EQ("and")) 13070Sstevel@tonic-gate next(); 13080Sstevel@tonic-gate pf_primary(); 13090Sstevel@tonic-gate if (opstack != s + 2) 13100Sstevel@tonic-gate break; 13110Sstevel@tonic-gate pf_emit(ENF_AND); 13120Sstevel@tonic-gate opstack--; 13130Sstevel@tonic-gate } 13140Sstevel@tonic-gate } 13150Sstevel@tonic-gate 13160Sstevel@tonic-gate static void 13170Sstevel@tonic-gate pf_expression() 13180Sstevel@tonic-gate { 13190Sstevel@tonic-gate pf_alternation(); 13200Sstevel@tonic-gate while (EQ("or") || EQ(",")) { 13210Sstevel@tonic-gate if (inBrace) 13220Sstevel@tonic-gate inBraceOR++; 13230Sstevel@tonic-gate else 13240Sstevel@tonic-gate foundOR++; 13250Sstevel@tonic-gate next(); 13260Sstevel@tonic-gate pf_alternation(); 13270Sstevel@tonic-gate pf_emit(ENF_OR); 13280Sstevel@tonic-gate opstack--; 13290Sstevel@tonic-gate } 13300Sstevel@tonic-gate } 13310Sstevel@tonic-gate 13320Sstevel@tonic-gate /* 13330Sstevel@tonic-gate * Attempt to compile the expression 13340Sstevel@tonic-gate * in the string "e". If we can generate 13350Sstevel@tonic-gate * pf code for it then return 1 - otherwise 13360Sstevel@tonic-gate * return 0 and leave it up to the user-level 13370Sstevel@tonic-gate * filter. 13380Sstevel@tonic-gate */ 13390Sstevel@tonic-gate int 13400Sstevel@tonic-gate pf_compile(e, print) 13410Sstevel@tonic-gate char *e; 13420Sstevel@tonic-gate int print; 13430Sstevel@tonic-gate { 13440Sstevel@tonic-gate char *argstr; 13450Sstevel@tonic-gate char *sav_str, *ptr, *sav_ptr; 13460Sstevel@tonic-gate int inBr = 0, aheadOR = 0; 13470Sstevel@tonic-gate 13480Sstevel@tonic-gate argstr = strdup(e); 13490Sstevel@tonic-gate sav_str = e; 13500Sstevel@tonic-gate tkp = argstr; 13510Sstevel@tonic-gate dir = ANY; 13520Sstevel@tonic-gate 13530Sstevel@tonic-gate pfp = &pf.Pf_Filter[0]; 13540Sstevel@tonic-gate if (setjmp(env)) { 13550Sstevel@tonic-gate return (0); 13560Sstevel@tonic-gate } 13570Sstevel@tonic-gate 13580Sstevel@tonic-gate /* 13590Sstevel@tonic-gate * Set media specific packet offsets that this code uses. 13600Sstevel@tonic-gate */ 13610Sstevel@tonic-gate if (interface->mac_type == DL_IB) { 13620Sstevel@tonic-gate link_header_len = 4; 13630Sstevel@tonic-gate link_type_offset = 0; 13640Sstevel@tonic-gate link_dest_offset = link_src_offset = -1; 13650Sstevel@tonic-gate link_addr_len = 20; 13660Sstevel@tonic-gate } 13670Sstevel@tonic-gate 13680Sstevel@tonic-gate next(); 13690Sstevel@tonic-gate pf_expression(); 13700Sstevel@tonic-gate 13710Sstevel@tonic-gate if (tokentype != EOL) { 13720Sstevel@tonic-gate /* 13730Sstevel@tonic-gate * The idea here is to do as much filtering as possible in 13740Sstevel@tonic-gate * the kernel. So even if we find a token we don't understand, 13750Sstevel@tonic-gate * we try to see if we can still set up a portion of the filter 13760Sstevel@tonic-gate * in the kernel and use the userland filter to filter the 13770Sstevel@tonic-gate * remaining stuff. Obviously, if our filter expression is of 13780Sstevel@tonic-gate * type A AND B, we can filter A in kernel and then apply B 13790Sstevel@tonic-gate * to the packets that got through. The same is not true for 13800Sstevel@tonic-gate * a filter of type A OR B. We can't apply A first and then B 13810Sstevel@tonic-gate * on the packets filtered through A. 13820Sstevel@tonic-gate * 13830Sstevel@tonic-gate * (We need to keep track of the fact when we find an OR, 13840Sstevel@tonic-gate * and the fact that we are inside brackets when we find OR. 13850Sstevel@tonic-gate * The variable 'foundOR' tells us if there was an OR behind, 13860Sstevel@tonic-gate * 'inBraceOR' tells us if we found an OR before we could find 13870Sstevel@tonic-gate * the end brace i.e. ')', and variable 'aheadOR' checks if 13880Sstevel@tonic-gate * there is an OR in the expression ahead. if either of these 13890Sstevel@tonic-gate * cases become true, we can't split the filtering) 13900Sstevel@tonic-gate */ 13910Sstevel@tonic-gate 13920Sstevel@tonic-gate if (foundOR || inBraceOR) { 13930Sstevel@tonic-gate /* FORGET IN KERNEL FILTERING */ 13940Sstevel@tonic-gate return (0); 13950Sstevel@tonic-gate } else { 13960Sstevel@tonic-gate 13970Sstevel@tonic-gate /* CHECK IF NO OR AHEAD */ 13980Sstevel@tonic-gate sav_ptr = (char *)((uintptr_t)sav_str + 13990Sstevel@tonic-gate (uintptr_t)sav_tkp - 14000Sstevel@tonic-gate (uintptr_t)argstr); 14010Sstevel@tonic-gate ptr = sav_ptr; 14020Sstevel@tonic-gate while (*ptr != '\0') { 14030Sstevel@tonic-gate switch (*ptr) { 14040Sstevel@tonic-gate case '(': 14050Sstevel@tonic-gate inBr++; 14060Sstevel@tonic-gate break; 14070Sstevel@tonic-gate case ')': 14080Sstevel@tonic-gate inBr--; 14090Sstevel@tonic-gate break; 14100Sstevel@tonic-gate case 'o': 14110Sstevel@tonic-gate case 'O': 14120Sstevel@tonic-gate if ((*(ptr + 1) == 'R' || 14130Sstevel@tonic-gate *(ptr + 1) == 'r') && !inBr) 14140Sstevel@tonic-gate aheadOR = 1; 14150Sstevel@tonic-gate break; 14160Sstevel@tonic-gate case ',': 14170Sstevel@tonic-gate if (!inBr) 14180Sstevel@tonic-gate aheadOR = 1; 14190Sstevel@tonic-gate break; 14200Sstevel@tonic-gate } 14210Sstevel@tonic-gate ptr++; 14220Sstevel@tonic-gate } 14230Sstevel@tonic-gate if (!aheadOR) { 14240Sstevel@tonic-gate /* NO OR AHEAD, SPLIT UP THE FILTERING */ 14250Sstevel@tonic-gate pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0]; 14260Sstevel@tonic-gate pf.Pf_Priority = 5; 14270Sstevel@tonic-gate if (print) { 14280Sstevel@tonic-gate pf_codeprint(&pf.Pf_Filter[0], 14290Sstevel@tonic-gate pf.Pf_FilterLen); 14300Sstevel@tonic-gate } 14310Sstevel@tonic-gate compile(sav_ptr, print); 14320Sstevel@tonic-gate return (2); 14330Sstevel@tonic-gate } else 14340Sstevel@tonic-gate return (0); 14350Sstevel@tonic-gate } 14360Sstevel@tonic-gate } 14370Sstevel@tonic-gate 14380Sstevel@tonic-gate pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0]; 14390Sstevel@tonic-gate pf.Pf_Priority = 5; /* unimportant, so long as > 2 */ 14400Sstevel@tonic-gate if (print) { 14410Sstevel@tonic-gate pf_codeprint(&pf.Pf_Filter[0], pf.Pf_FilterLen); 14420Sstevel@tonic-gate } 14430Sstevel@tonic-gate return (1); 14440Sstevel@tonic-gate } 1445