4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012 Milan Jurik. All rights reserved.
33 #include <sys/types.h>
35 #include <sys/isa_defs.h>
37 #include <sys/socket.h>
40 #include <netinet/in_systm.h>
41 #include <netinet/in.h>
42 #include <netinet/ip.h>
43 #include <netinet/if_ether.h>
44 #include <netinet/tcp.h>
45 #include <netinet/udp.h>
52 #include <sys/pfmod.h>
54 #include "snoop_vlan.h"
57 * This module generates code for the kernel packet filter.
58 * The kernel packet filter is more efficient since it
59 * operates without context switching or moving data into
60 * the capture buffer. On the other hand, it is limited
61 * in its filtering ability i.e. can't cope with variable
62 * length headers, can't compare the packet size, 1 and 4 octet
63 * comparisons are awkward, code space is limited to ENMAXFILTERS
65 * The parser is the same for the user-level packet filter though
66 * more limited in the variety of expressions it can generate
67 * code for. If the pf compiler finds an expression it can't
68 * handle, it tries to set up a split filter in kernel and do the
69 * remaining filtering in userland. If that also fails, it resorts
70 * to userland filter. (See additional comment in pf_compile)
73 extern struct Pf_ext_packetfilt pf
;
77 int eaddr
; /* need ethernet addr */
79 int opstack
; /* operand stack depth */
81 #define EQ(val) (strcmp(token, val) == 0)
84 #define IPV4_AND_IPV6 2
87 int transport_protocol
;
90 * offset is the offset in bytes from the beginning
91 * of the network protocol header to where the transport
97 typedef struct network_table
{
102 static network_table_t ether_network_mapping_table
[] = {
103 { "pup", ETHERTYPE_PUP
},
104 { "ip", ETHERTYPE_IP
},
105 { "arp", ETHERTYPE_ARP
},
106 { "rarp", ETHERTYPE_REVARP
},
107 { "at", ETHERTYPE_AT
},
108 { "aarp", ETHERTYPE_AARP
},
109 { "vlan", ETHERTYPE_VLAN
},
110 { "ip6", ETHERTYPE_IPV6
},
111 { "slow", ETHERTYPE_SLOW
},
112 { "ppoed", ETHERTYPE_PPPOED
},
113 { "ppoes", ETHERTYPE_PPPOES
},
118 static network_table_t ib_network_mapping_table
[] = {
119 { "pup", ETHERTYPE_PUP
},
120 { "ip", ETHERTYPE_IP
},
121 { "arp", ETHERTYPE_ARP
},
122 { "rarp", ETHERTYPE_REVARP
},
123 { "at", ETHERTYPE_AT
},
124 { "aarp", ETHERTYPE_AARP
},
125 { "vlan", ETHERTYPE_VLAN
},
126 { "ip6", ETHERTYPE_IPV6
},
127 { "slow", ETHERTYPE_SLOW
},
128 { "ppoed", ETHERTYPE_PPPOED
},
129 { "ppoes", ETHERTYPE_PPPOES
},
134 static network_table_t ipnet_network_mapping_table
[] = {
135 { "ip", (DL_IPNETINFO_VERSION
<< 8 | AF_INET
) },
136 { "ip6", (DL_IPNETINFO_VERSION
<< 8 | AF_INET6
) },
141 static transport_table_t ether_transport_mapping_table
[] = {
142 {IPPROTO_TCP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
143 {IPPROTO_TCP
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
144 {IPPROTO_UDP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
145 {IPPROTO_UDP
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
146 {IPPROTO_OSPF
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
147 {IPPROTO_OSPF
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
148 {IPPROTO_SCTP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
149 {IPPROTO_SCTP
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
150 {IPPROTO_ICMP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
151 {IPPROTO_ICMPV6
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
152 {IPPROTO_ENCAP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
153 {IPPROTO_ESP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
154 {IPPROTO_ESP
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
155 {IPPROTO_AH
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
156 {IPPROTO_AH
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
157 {-1, 0, 0} /* must be the final entry */
160 static transport_table_t ipnet_transport_mapping_table
[] = {
161 {IPPROTO_TCP
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET
),
162 IPV4_TYPE_HEADER_OFFSET
},
163 {IPPROTO_TCP
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET6
),
164 IPV6_TYPE_HEADER_OFFSET
},
165 {IPPROTO_UDP
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET
),
166 IPV4_TYPE_HEADER_OFFSET
},
167 {IPPROTO_UDP
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET6
),
168 IPV6_TYPE_HEADER_OFFSET
},
169 {IPPROTO_OSPF
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET
),
170 IPV4_TYPE_HEADER_OFFSET
},
171 {IPPROTO_OSPF
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET6
),
172 IPV6_TYPE_HEADER_OFFSET
},
173 {IPPROTO_SCTP
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET
),
174 IPV4_TYPE_HEADER_OFFSET
},
175 {IPPROTO_SCTP
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET6
),
176 IPV6_TYPE_HEADER_OFFSET
},
177 {IPPROTO_ICMP
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET
),
178 IPV4_TYPE_HEADER_OFFSET
},
179 {IPPROTO_ICMPV6
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET6
),
180 IPV6_TYPE_HEADER_OFFSET
},
181 {IPPROTO_ENCAP
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET
),
182 IPV4_TYPE_HEADER_OFFSET
},
183 {IPPROTO_ESP
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET
),
184 IPV4_TYPE_HEADER_OFFSET
},
185 {IPPROTO_ESP
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET6
),
186 IPV6_TYPE_HEADER_OFFSET
},
187 {IPPROTO_AH
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET
),
188 IPV4_TYPE_HEADER_OFFSET
},
189 {IPPROTO_AH
, (DL_IPNETINFO_VERSION
<< 8 | AF_INET6
),
190 IPV6_TYPE_HEADER_OFFSET
},
191 {-1, 0, 0} /* must be the final entry */
194 static transport_table_t ib_transport_mapping_table
[] = {
195 {IPPROTO_TCP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
196 {IPPROTO_TCP
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
197 {IPPROTO_UDP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
198 {IPPROTO_UDP
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
199 {IPPROTO_OSPF
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
200 {IPPROTO_OSPF
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
201 {IPPROTO_SCTP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
202 {IPPROTO_SCTP
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
203 {IPPROTO_ICMP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
204 {IPPROTO_ICMPV6
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
205 {IPPROTO_ENCAP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
206 {IPPROTO_ESP
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
207 {IPPROTO_ESP
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
208 {IPPROTO_AH
, ETHERTYPE_IP
, IPV4_TYPE_HEADER_OFFSET
},
209 {IPPROTO_AH
, ETHERTYPE_IPV6
, IPV6_TYPE_HEADER_OFFSET
},
210 {-1, 0, 0} /* must be the final entry */
213 typedef struct datalink
{
215 void (*dl_match_fn
)(uint_t datatype
);
216 transport_table_t
*dl_trans_map_tbl
;
217 network_table_t
*dl_net_map_tbl
;
218 int dl_link_header_len
;
219 int dl_link_type_offset
;
220 int dl_link_dest_offset
;
221 int dl_link_src_offset
;
222 int dl_link_addr_len
;
227 #define IPV4_SRCADDR_OFFSET (dl.dl_link_header_len + 12)
228 #define IPV4_DSTADDR_OFFSET (dl.dl_link_header_len + 16)
229 #define IPV6_SRCADDR_OFFSET (dl.dl_link_header_len + 8)
230 #define IPV6_DSTADDR_OFFSET (dl.dl_link_header_len + 24)
232 #define IPNET_SRCZONE_OFFSET 16
233 #define IPNET_DSTZONE_OFFSET 20
235 static int inBrace
= 0, inBraceOR
= 0;
236 static int foundOR
= 0;
239 enum { EOL
, ALPHA
, NUMBER
, FIELD
, ADDR_IP
, ADDR_ETHER
, SPECIAL
,
240 ADDR_IP6
} tokentype
;
243 enum direction
{ ANY
, TO
, FROM
};
248 static void pf_expression();
249 static void pf_check_vlan_tag(uint_t offset
);
250 static void pf_clear_offset_register();
251 static void pf_emit_load_offset(uint_t offset
);
252 static void pf_match_ethertype(uint_t ethertype
);
253 static void pf_match_ipnettype(uint_t type
);
254 static void pf_match_ibtype(uint_t type
);
255 static void pf_check_transport_protocol(uint_t transport_protocol
);
256 static void pf_compare_value_mask_generic(int offset
, uint_t len
,
257 uint_t val
, int mask
, uint_t op
);
258 static void pf_matchfn(const char *name
);
261 * This pointer points to the function that last generated
262 * instructions to change the offset register. It's used
263 * for comparisons to see if we need to issue more instructions
264 * to change the register.
266 * It's initialized to pf_clear_offset_register because the offset
267 * register in pfmod is initialized to zero, similar to the state
268 * it would be in after executing the instructions issued by
269 * pf_clear_offset_register.
271 static void *last_offset_operation
= (void*)pf_clear_offset_register
;
277 if (pfp
> &pf
.Pf_Filter
[PF_MAXFILTERS
- 1])
283 pf_codeprint(code
, len
)
288 ushort_t
*plast
= code
+ len
;
292 printf("Kernel Filter:\n");
295 for (pc
= code
; pc
< plast
; pc
++) {
296 printf("\t%3d: ", pc
- code
);
298 op
= *pc
& 0xfc00; /* high 10 bits */
299 action
= *pc
& 0x3ff; /* low 6 bits */
328 case ENF_LOAD_OFFSET
:
329 printf("LOAD_OFFSET ");
342 if (action
>= ENF_PUSHWORD
)
343 printf("PUSHWORD %d ", action
- ENF_PUSHWORD
);
387 if (action
== ENF_PUSHLIT
||
388 action
== ENF_LOAD_OFFSET
||
389 action
== ENF_BRTR
||
390 action
== ENF_BRFL
) {
392 printf("\n\t%3d: %d (0x%04x)", pc
- code
, *pc
, *pc
);
400 * Emit packet filter code to check a
401 * field in the packet for a particular value.
402 * Need different code for each field size.
403 * Since the pf can only compare 16 bit quantities
404 * we have to use masking to compare byte values.
405 * Long word (32 bit) quantities have to be done
406 * as two 16 bit comparisons.
409 pf_compare_value(int offset
, uint_t len
, uint_t val
)
412 * If the property being filtered on is absent in the media
416 pr_err("filter option unsupported on media");
420 pf_emit(ENF_PUSHWORD
+ offset
/ 2);
421 #if defined(_BIG_ENDIAN)
428 pf_emit(ENF_PUSH00FF
| ENF_AND
);
430 pf_emit(ENF_PUSHLIT
| ENF_AND
);
433 pf_emit(ENF_PUSHLIT
| ENF_EQ
);
437 pf_emit(ENF_PUSHFF00
| ENF_AND
);
439 pf_emit(ENF_PUSHLIT
| ENF_AND
);
442 pf_emit(ENF_PUSHLIT
| ENF_EQ
);
448 pf_emit(ENF_PUSHWORD
+ offset
/ 2);
449 pf_emit(ENF_PUSHLIT
| ENF_EQ
);
450 pf_emit((ushort_t
)val
);
454 pf_emit(ENF_PUSHWORD
+ offset
/ 2);
455 pf_emit(ENF_PUSHLIT
| ENF_EQ
);
456 #if defined(_BIG_ENDIAN)
458 #elif defined(_LITTLE_ENDIAN)
459 pf_emit(val
& 0xffff);
461 #error One of _BIG_ENDIAN and _LITTLE_ENDIAN must be defined
463 pf_emit(ENF_PUSHWORD
+ (offset
/ 2) + 1);
464 pf_emit(ENF_PUSHLIT
| ENF_EQ
);
465 #if defined(_BIG_ENDIAN)
466 pf_emit(val
& 0xffff);
476 * same as pf_compare_value, but only for emiting code to
477 * compare ipv6 addresses.
480 pf_compare_value_v6(int offset
, uint_t len
, struct in6_addr val
)
484 for (i
= 0; i
< len
; i
+= 2) {
485 pf_emit(ENF_PUSHWORD
+ offset
/ 2 + i
/ 2);
486 pf_emit(ENF_PUSHLIT
| ENF_EQ
);
487 pf_emit(*(uint16_t *)&val
.s6_addr
[i
]);
495 * Same as above except mask the field value
496 * before doing the comparison. The comparison checks
497 * to make sure the values are equal.
500 pf_compare_value_mask(int offset
, uint_t len
, uint_t val
, int mask
)
502 pf_compare_value_mask_generic(offset
, len
, val
, mask
, ENF_EQ
);
506 * Same as above except the values are compared to see if they are not
510 pf_compare_value_mask_neq(int offset
, uint_t len
, uint_t val
, int mask
)
512 pf_compare_value_mask_generic(offset
, len
, val
, mask
, ENF_NEQ
);
516 * Similar to pf_compare_value.
518 * This is the utility function that does the actual work to compare
519 * two values using a mask. The comparison operation is passed into
523 pf_compare_value_mask_generic(int offset
, uint_t len
, uint_t val
, int mask
,
527 * If the property being filtered on is absent in the media
531 pr_err("filter option unsupported on media");
535 pf_emit(ENF_PUSHWORD
+ offset
/ 2);
536 #if defined(_BIG_ENDIAN)
542 pf_emit(ENF_PUSHLIT
| ENF_AND
);
543 pf_emit(mask
& 0x00ff);
544 pf_emit(ENF_PUSHLIT
| op
);
547 pf_emit(ENF_PUSHLIT
| ENF_AND
);
548 pf_emit((mask
<< 8) & 0xff00);
549 pf_emit(ENF_PUSHLIT
| op
);
555 pf_emit(ENF_PUSHWORD
+ offset
/ 2);
556 pf_emit(ENF_PUSHLIT
| ENF_AND
);
557 pf_emit(htons((ushort_t
)mask
));
558 pf_emit(ENF_PUSHLIT
| op
);
559 pf_emit(htons((ushort_t
)val
));
563 pf_emit(ENF_PUSHWORD
+ offset
/ 2);
564 pf_emit(ENF_PUSHLIT
| ENF_AND
);
565 pf_emit(htons((ushort_t
)((mask
>> 16) & 0xffff)));
566 pf_emit(ENF_PUSHLIT
| op
);
567 pf_emit(htons((ushort_t
)((val
>> 16) & 0xffff)));
569 pf_emit(ENF_PUSHWORD
+ (offset
/ 2) + 1);
570 pf_emit(ENF_PUSHLIT
| ENF_AND
);
571 pf_emit(htons((ushort_t
)(mask
& 0xffff)));
572 pf_emit(ENF_PUSHLIT
| op
);
573 pf_emit(htons((ushort_t
)(val
& 0xffff)));
581 * Like pf_compare_value() but compare on a 32-bit zoneid value.
582 * The argument val passed in is in network byte order.
585 pf_compare_zoneid(int offset
, uint32_t val
)
589 for (i
= 0; i
< sizeof (uint32_t) / 2; i
++) {
590 pf_emit(ENF_PUSHWORD
+ offset
/ 2 + i
);
591 pf_emit(ENF_PUSHLIT
| ENF_EQ
);
592 pf_emit(((uint16_t *)&val
)[i
]);
599 * Generate pf code to match an IPv4 or IPv6 address.
602 pf_ipaddr_match(which
, hostname
, inet_type
)
603 enum direction which
;
610 struct in6_addr
*addr6ptr
;
612 struct hostent
*hp
= NULL
;
614 boolean_t first
= B_TRUE
;
619 * The addr4offset and addr6offset variables simplify the code which
620 * generates the address comparison filter. With these two variables,
621 * duplicate code need not exist for the TO and FROM case.
622 * A value of -1 describes the ANY case (TO and FROM).
629 if (tokentype
== ADDR_IP
) {
630 hp
= getipnodebyname(hostname
, AF_INET
, 0, &error_num
);
632 if (error_num
== TRY_AGAIN
) {
633 pr_err("could not resolve %s (try again later)",
636 pr_err("could not resolve %s", hostname
);
639 inet_type
= IPV4_ONLY
;
640 } else if (tokentype
== ADDR_IP6
) {
641 hp
= getipnodebyname(hostname
, AF_INET6
, 0, &error_num
);
643 if (error_num
== TRY_AGAIN
) {
644 pr_err("could not resolve %s (try again later)",
647 pr_err("could not resolve %s", hostname
);
650 inet_type
= IPV6_ONLY
;
651 } else if (tokentype
== ALPHA
) {
652 /* Some hostname i.e. tokentype is ALPHA */
655 /* Only IPv4 address is needed */
656 hp
= getipnodebyname(hostname
, AF_INET
, 0, &error_num
);
662 /* Only IPv6 address is needed */
663 hp
= getipnodebyname(hostname
, AF_INET6
, 0, &error_num
);
669 /* Both IPv4 and IPv6 are needed */
670 hp
= getipnodebyname(hostname
, AF_INET6
,
671 AI_ALL
| AI_V4MAPPED
, &error_num
);
681 if (error_num
== TRY_AGAIN
) {
682 pr_err("could not resolve %s (try again later)",
685 pr_err("could not resolve %s", hostname
);
689 pr_err("unknown token type: %s", hostname
);
697 addr4offset
= IPV4_DSTADDR_OFFSET
;
698 addr6offset
= IPV6_DSTADDR_OFFSET
;
701 addr4offset
= IPV4_SRCADDR_OFFSET
;
702 addr6offset
= IPV6_SRCADDR_OFFSET
;
710 if (hp
->h_addrtype
== AF_INET
) {
712 if (dl
.dl_type
== DL_ETHER
)
713 pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF
/2);
715 addr4ptr
= (uint_t
*)hp
->h_addr_list
[h_addr_index
];
716 while (addr4ptr
!= NULL
) {
717 if (addr4offset
== -1) {
718 pf_compare_value(IPV4_SRCADDR_OFFSET
, 4,
720 if (h_addr_index
!= 0)
722 pf_compare_value(IPV4_DSTADDR_OFFSET
, 4,
726 pf_compare_value(addr4offset
, 4,
728 if (h_addr_index
!= 0)
731 addr4ptr
= (uint_t
*)hp
->h_addr_list
[++h_addr_index
];
735 /* first pass: IPv4 addresses */
737 addr6ptr
= (struct in6_addr
*)hp
->h_addr_list
[h_addr_index
];
739 while (addr6ptr
!= NULL
) {
740 if (IN6_IS_ADDR_V4MAPPED(addr6ptr
)) {
743 if (dl
.dl_type
== DL_ETHER
) {
745 ENCAP_ETHERTYPE_OFF
/2);
749 IN6_V4MAPPED_TO_INADDR(addr6ptr
,
750 (struct in_addr
*)&addr4
);
751 if (addr4offset
== -1) {
752 pf_compare_value(IPV4_SRCADDR_OFFSET
, 4,
756 pf_compare_value(IPV4_DSTADDR_OFFSET
, 4,
760 pf_compare_value(addr4offset
, 4,
768 addr6ptr
= (struct in6_addr
*)
769 hp
->h_addr_list
[++h_addr_index
];
774 /* second pass: IPv6 addresses */
776 addr6ptr
= (struct in6_addr
*)hp
->h_addr_list
[h_addr_index
];
778 while (addr6ptr
!= NULL
) {
779 if (!IN6_IS_ADDR_V4MAPPED(addr6ptr
)) {
782 if (dl
.dl_type
== DL_ETHER
) {
784 ENCAP_ETHERTYPE_OFF
/2);
788 if (addr6offset
== -1) {
789 pf_compare_value_v6(IPV6_SRCADDR_OFFSET
,
793 pf_compare_value_v6(IPV6_DSTADDR_OFFSET
,
797 pf_compare_value_v6(addr6offset
, 16,
805 addr6ptr
= (struct in6_addr
*)
806 hp
->h_addr_list
[++h_addr_index
];
821 pf_compare_address(int offset
, uint_t len
, uchar_t
*addr
)
825 boolean_t didone
= B_FALSE
;
828 * If the property being filtered on is absent in the media
832 pr_err("filter option unsupported on media");
836 (void) memcpy(&val
, addr
, 4);
837 pf_compare_value(offset
, 4, val
);
841 } else if (len
>= 2) {
842 (void) memcpy(&sval
, addr
, 2);
843 pf_compare_value(offset
, 2, sval
);
848 pf_compare_value(offset
++, 1, *addr
++);
858 * Compare ethernet addresses.
861 pf_etheraddr_match(which
, hostname
)
862 enum direction which
;
865 struct ether_addr e
, *ep
= NULL
;
867 if (isxdigit(*hostname
))
868 ep
= ether_aton(hostname
);
870 if (ether_hostton(hostname
, &e
))
871 if (!arp_for_ether(hostname
, &e
))
872 pr_err("cannot obtain ether addr for %s",
877 pf_clear_offset_register();
881 pf_compare_address(dl
.dl_link_dest_offset
, dl
.dl_link_addr_len
,
885 pf_compare_address(dl
.dl_link_src_offset
, dl
.dl_link_addr_len
,
889 pf_compare_address(dl
.dl_link_dest_offset
, dl
.dl_link_addr_len
,
891 pf_compare_address(dl
.dl_link_src_offset
, dl
.dl_link_addr_len
,
899 * Emit code to compare the network part of
903 pf_netaddr_match(which
, netname
)
904 enum direction which
;
908 uint_t mask
= 0xff000000;
911 if (isdigit(*netname
)) {
912 addr
= inet_network(netname
);
914 np
= getnetbyname(netname
);
916 pr_err("net %s not known", netname
);
921 * Left justify the address and figure
922 * out a mask based on the supplied address.
923 * Set the mask according to the number of zero
925 * Note: this works only for whole octet masks.
928 while ((addr
& ~mask
) != 0) {
933 pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF
/2);
937 pf_compare_value_mask(IPV4_DSTADDR_OFFSET
, 4, addr
, mask
);
940 pf_compare_value_mask(IPV4_SRCADDR_OFFSET
, 4, addr
, mask
);
943 pf_compare_value_mask(IPV4_SRCADDR_OFFSET
, 4, addr
, mask
);
944 pf_compare_value_mask(IPV4_DSTADDR_OFFSET
, 4, addr
, mask
);
951 * Emit code to match on src or destination zoneid.
952 * The zoneid passed in is in network byte order.
955 pf_match_zone(enum direction which
, uint32_t zoneid
)
957 if (dl
.dl_type
!= DL_IPNET
)
958 pr_err("zone filter option unsupported on media");
962 pf_compare_zoneid(IPNET_DSTZONE_OFFSET
, zoneid
);
965 pf_compare_zoneid(IPNET_SRCZONE_OFFSET
, zoneid
);
968 pf_compare_zoneid(IPNET_SRCZONE_OFFSET
, zoneid
);
969 pf_compare_zoneid(IPNET_DSTZONE_OFFSET
, zoneid
);
976 * A helper function to keep the code to emit instructions
977 * to change the offset register in one place.
979 * INPUTS: offset - An value representing an offset in 16-bit
981 * OUTPUTS: If there is enough room in the storage for the
982 * packet filtering program, instructions to load
983 * a constant to the offset register. Otherwise,
987 pf_emit_load_offset(uint_t offset
)
989 pf_emit(ENF_LOAD_OFFSET
| ENF_NOP
);
994 * Clear pfmod's offset register.
997 * OUTPUTS: Instructions to clear the offset register if
998 * there is enough space remaining in the packet
999 * filtering program structure's storage, and
1000 * the last thing done to the offset register was
1001 * not clearing the offset register. Otherwise,
1005 pf_clear_offset_register()
1007 if (last_offset_operation
!= (void*)pf_clear_offset_register
) {
1008 pf_emit_load_offset(0);
1009 last_offset_operation
= (void*)pf_clear_offset_register
;
1014 * This function will issue opcodes to check if a packet
1015 * is VLAN tagged, and if so, update the offset register
1016 * with the appropriate offset.
1018 * Note that if the packet is not VLAN tagged, then the offset
1019 * register will be cleared.
1021 * If the interface type is not an ethernet type, then this
1022 * function returns without doing anything.
1024 * If the last attempt to change the offset register occured because
1025 * of a call to this function that was called with the same offset,
1026 * then we don't issue packet filtering instructions.
1028 * INPUTS: offset - an offset in 16 bit words. The function
1029 * will set the offset register to this
1030 * value if the packet is VLAN tagged.
1031 * OUTPUTS: If the conditions are met, packet filtering instructions.
1034 pf_check_vlan_tag(uint_t offset
)
1036 static uint_t last_offset
= 0;
1038 if ((interface
->mac_type
== DL_ETHER
||
1039 interface
->mac_type
== DL_CSMACD
) &&
1040 (last_offset_operation
!= (void*)pf_check_vlan_tag
||
1041 last_offset
!= offset
)) {
1043 * First thing is to clear the offset register.
1044 * We don't know what state it is in, and if it
1045 * is not zero, then we have no idea what we load
1046 * when we execute ENF_PUSHWORD.
1048 pf_clear_offset_register();
1051 * Check the ethertype.
1053 pf_compare_value(dl
.dl_link_type_offset
, 2,
1054 htons(ETHERTYPE_VLAN
));
1057 * And if it's not VLAN, don't load offset to the offset
1060 pf_emit(ENF_BRFL
| ENF_NOP
);
1064 * Otherwise, load offset to the offset register.
1066 pf_emit_load_offset(offset
);
1069 * Now get rid of the results of the comparison,
1070 * we don't want the results of the comparison to affect
1071 * other logic in the packet filtering program.
1073 pf_emit(ENF_POP
| ENF_NOP
);
1076 * Set the last operation at the end, or any time
1077 * after the call to pf_clear_offset because
1078 * pf_clear_offset uses it.
1080 last_offset_operation
= (void*)pf_check_vlan_tag
;
1081 last_offset
= offset
;
1086 * Utility function used to emit packet filtering code
1087 * to match an ethertype.
1089 * INPUTS: ethertype - The ethertype we want to check for.
1090 * Don't call htons on the ethertype before
1091 * calling this function.
1092 * OUTPUTS: If there is sufficient storage available, packet
1093 * filtering code to check an ethertype. Otherwise,
1097 pf_match_ethertype(uint_t ethertype
)
1100 * If the user wants to filter on ethertype VLAN,
1101 * then clear the offset register so that the offset
1102 * for ENF_PUSHWORD points to the right place in the
1105 * Otherwise, call pf_check_vlan_tag to set the offset
1106 * register such that the contents of the offset register
1107 * plus the argument for ENF_PUSHWORD point to the right
1108 * part of the packet, whether or not the packet is VLAN
1109 * tagged. We call pf_check_vlan_tag with an offset of
1110 * two words because if the packet is VLAN tagged, we have
1111 * to move past the ethertype in the ethernet header, and
1112 * past the lower two octets of the VLAN header to get to
1113 * the ethertype in the VLAN header.
1115 if (ethertype
== ETHERTYPE_VLAN
)
1116 pf_clear_offset_register();
1118 pf_check_vlan_tag(2);
1120 pf_compare_value(dl
.dl_link_type_offset
, 2, htons(ethertype
));
1124 pf_match_ipnettype(uint_t type
)
1126 pf_compare_value(dl
.dl_link_type_offset
, 2, htons(type
));
1130 pf_match_ibtype(uint_t type
)
1132 pf_compare_value(dl
.dl_link_type_offset
, 2, htons(type
));
1136 * This function uses the table above to generate a
1137 * piece of a packet filtering program to check a transport
1140 * INPUTS: tranport_protocol - the transport protocol we're
1142 * OUTPUTS: If there is sufficient storage, then packet filtering
1143 * code to check a transport protocol type. Otherwise,
1147 pf_check_transport_protocol(uint_t transport_protocol
)
1150 uint_t number_of_matches
= 0;
1152 for (i
= 0; dl
.dl_trans_map_tbl
[i
].transport_protocol
!= -1; i
++) {
1153 if (transport_protocol
==
1154 (uint_t
)dl
.dl_trans_map_tbl
[i
].transport_protocol
) {
1155 number_of_matches
++;
1156 dl
.dl_match_fn(dl
.dl_trans_map_tbl
[i
].network_protocol
);
1157 pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF
/2);
1158 pf_compare_value(dl
.dl_trans_map_tbl
[i
].offset
+
1159 dl
.dl_link_header_len
, 1,
1160 transport_protocol
);
1162 if (number_of_matches
> 1) {
1164 * Since we have two or more matches, in
1165 * order to have a correct and complete
1166 * program we need to OR the result of
1167 * each block of comparisons together.
1176 pf_matchfn(const char *proto
)
1180 for (i
= 0; dl
.dl_net_map_tbl
[i
].nmt_val
!= -1; i
++) {
1181 if (strcmp(proto
, dl
.dl_net_map_tbl
[i
].nmt_name
) == 0) {
1182 dl
.dl_match_fn(dl
.dl_net_map_tbl
[i
].nmt_val
);
1192 if (tokentype
== FIELD
)
1210 pf_matchfn("pppoe");
1211 pf_match_ethertype(ETHERTYPE_PPPOES
);
1219 pf_matchfn("pppoed");
1226 pf_matchfn("pppoes");
1241 pf_compare_value_mask_neq(VLAN_ID_OFFSET
, 2,
1249 if (EQ("vlan-id")) {
1251 if (tokentype
!= NUMBER
)
1252 pr_err("VLAN ID expected");
1253 pf_matchfn("vlan-id");
1254 pf_compare_value_mask(VLAN_ID_OFFSET
, 2, tokenval
,
1270 pf_check_transport_protocol(IPPROTO_TCP
);
1277 pf_check_transport_protocol(IPPROTO_UDP
);
1284 pf_check_transport_protocol(IPPROTO_OSPF
);
1292 pf_check_transport_protocol(IPPROTO_SCTP
);
1299 pf_check_transport_protocol(IPPROTO_ICMP
);
1306 pf_check_transport_protocol(IPPROTO_ICMPV6
);
1312 if (EQ("ip-in-ip")) {
1313 pf_check_transport_protocol(IPPROTO_ENCAP
);
1320 pf_check_transport_protocol(IPPROTO_ESP
);
1327 pf_check_transport_protocol(IPPROTO_AH
);
1346 if (EQ("to") || EQ("dst")) {
1352 if (EQ("from") || EQ("src")) {
1368 if (tokentype
!= ALPHA
&& tokentype
!= ADDR_IP
)
1369 pr_err("host/IPv4 addr expected after inet");
1370 pf_ipaddr_match(dir
, token
, IPV4_ONLY
);
1380 if (tokentype
!= ALPHA
&& tokentype
!= ADDR_IP6
)
1381 pr_err("host/IPv6 addr expected after inet6");
1382 pf_ipaddr_match(dir
, token
, IPV6_ONLY
);
1390 if (tokentype
!= NUMBER
)
1391 pr_err("IP proto type expected");
1392 pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF
/2);
1394 IPV4_TYPE_HEADER_OFFSET
+ dl
.dl_link_header_len
, 1,
1401 if (EQ("broadcast")) {
1402 pf_clear_offset_register();
1403 pf_compare_value(dl
.dl_link_dest_offset
, 4, 0xffffffff);
1409 if (EQ("multicast")) {
1410 pf_clear_offset_register();
1411 pf_compare_value_mask(
1412 dl
.dl_link_dest_offset
, 1, 0x01, 0x01);
1418 if (EQ("ethertype")) {
1420 if (tokentype
!= NUMBER
)
1421 pr_err("ether type expected");
1422 pf_match_ethertype(tokenval
);
1428 if (EQ("net") || EQ("dstnet") || EQ("srcnet")) {
1431 else if (EQ("srcnet"))
1434 pf_netaddr_match(dir
, token
);
1443 if (tokentype
!= NUMBER
)
1444 pr_err("zoneid expected after inet");
1445 pf_match_zone(dir
, BE_32((uint32_t)(tokenval
)));
1452 * Give up on anything that's obviously
1455 if (EQ("and") || EQ("or") ||
1456 EQ("not") || EQ("decnet") || EQ("apple") ||
1457 EQ("length") || EQ("less") || EQ("greater") ||
1458 EQ("port") || EQ("srcport") || EQ("dstport") ||
1459 EQ("rpc") || EQ("gateway") || EQ("nofrag") ||
1460 EQ("bootp") || EQ("dhcp") || EQ("dhcp6") ||
1461 EQ("slp") || EQ("ldap")) {
1465 if (EQ("host") || EQ("between") ||
1466 tokentype
== ALPHA
|| /* assume its a hostname */
1467 tokentype
== ADDR_IP
||
1468 tokentype
== ADDR_IP6
||
1469 tokentype
== ADDR_ETHER
) {
1470 if (EQ("host") || EQ("between"))
1472 if (eaddr
|| tokentype
== ADDR_ETHER
) {
1473 pf_etheraddr_match(dir
, token
);
1474 } else if (tokentype
== ALPHA
) {
1475 pf_ipaddr_match(dir
, token
, IPV4_AND_IPV6
);
1476 } else if (tokentype
== ADDR_IP
) {
1477 pf_ipaddr_match(dir
, token
, IPV4_ONLY
);
1479 pf_ipaddr_match(dir
, token
, IPV6_ONLY
);
1488 break; /* unknown token */
1502 if (opstack
!= s
+ 2)
1513 while (EQ("or") || EQ(",")) {
1526 * Attempt to compile the expression
1527 * in the string "e". If we can generate
1528 * pf code for it then return 1 - otherwise
1529 * return 0 and leave it up to the user-level
1533 pf_compile(e
, print
)
1538 char *sav_str
, *ptr
, *sav_ptr
;
1539 int inBr
= 0, aheadOR
= 0;
1546 pfp
= &pf
.Pf_Filter
[0];
1552 * Set media specific packet offsets that this code uses.
1554 if (interface
->mac_type
== DL_ETHER
) {
1555 dl
.dl_type
= DL_ETHER
;
1556 dl
.dl_match_fn
= pf_match_ethertype
;
1557 dl
.dl_trans_map_tbl
= ether_transport_mapping_table
;
1558 dl
.dl_net_map_tbl
= ether_network_mapping_table
;
1559 dl
.dl_link_header_len
= 14;
1560 dl
.dl_link_type_offset
= 12;
1561 dl
.dl_link_dest_offset
= 0;
1562 dl
.dl_link_src_offset
= 6;
1563 dl
.dl_link_addr_len
= 6;
1566 if (interface
->mac_type
== DL_IB
) {
1568 dl
.dl_link_header_len
= 4;
1569 dl
.dl_link_type_offset
= 0;
1570 dl
.dl_link_dest_offset
= dl
.dl_link_src_offset
= -1;
1571 dl
.dl_link_addr_len
= 20;
1572 dl
.dl_match_fn
= pf_match_ibtype
;
1573 dl
.dl_trans_map_tbl
= ib_transport_mapping_table
;
1574 dl
.dl_net_map_tbl
= ib_network_mapping_table
;
1577 if (interface
->mac_type
== DL_IPNET
) {
1578 dl
.dl_type
= DL_IPNET
;
1579 dl
.dl_link_header_len
= 24;
1580 dl
.dl_link_type_offset
= 0;
1581 dl
.dl_link_dest_offset
= dl
.dl_link_src_offset
= -1;
1582 dl
.dl_link_addr_len
= -1;
1583 dl
.dl_match_fn
= pf_match_ipnettype
;
1584 dl
.dl_trans_map_tbl
= ipnet_transport_mapping_table
;
1585 dl
.dl_net_map_tbl
= ipnet_network_mapping_table
;
1591 if (tokentype
!= EOL
) {
1593 * The idea here is to do as much filtering as possible in
1594 * the kernel. So even if we find a token we don't understand,
1595 * we try to see if we can still set up a portion of the filter
1596 * in the kernel and use the userland filter to filter the
1597 * remaining stuff. Obviously, if our filter expression is of
1598 * type A AND B, we can filter A in kernel and then apply B
1599 * to the packets that got through. The same is not true for
1600 * a filter of type A OR B. We can't apply A first and then B
1601 * on the packets filtered through A.
1603 * (We need to keep track of the fact when we find an OR,
1604 * and the fact that we are inside brackets when we find OR.
1605 * The variable 'foundOR' tells us if there was an OR behind,
1606 * 'inBraceOR' tells us if we found an OR before we could find
1607 * the end brace i.e. ')', and variable 'aheadOR' checks if
1608 * there is an OR in the expression ahead. if either of these
1609 * cases become true, we can't split the filtering)
1612 if (foundOR
|| inBraceOR
) {
1613 /* FORGET IN KERNEL FILTERING */
1617 /* CHECK IF NO OR AHEAD */
1618 sav_ptr
= (char *)((uintptr_t)sav_str
+
1619 (uintptr_t)sav_tkp
-
1622 while (*ptr
!= '\0') {
1632 if ((*(ptr
+ 1) == 'R' ||
1633 *(ptr
+ 1) == 'r') && !inBr
)
1644 /* NO OR AHEAD, SPLIT UP THE FILTERING */
1645 pf
.Pf_FilterLen
= pfp
- &pf
.Pf_Filter
[0];
1648 pf_codeprint(&pf
.Pf_Filter
[0],
1651 compile(sav_ptr
, print
);
1658 pf
.Pf_FilterLen
= pfp
- &pf
.Pf_Filter
[0];
1659 pf
.Pf_Priority
= 5; /* unimportant, so long as > 2 */
1661 pf_codeprint(&pf
.Pf_Filter
[0], pf
.Pf_FilterLen
);