]>
Commit | Line | Data |
---|---|---|
91447636 | 1 | /* |
316670eb | 2 | * Copyright (c) 2003-2012 Apple Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0a7de745 | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
0a7de745 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
2d21ac55 A |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
0a7de745 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
91447636 A |
27 | */ |
28 | ||
29 | /* $FreeBSD: src/sys/netinet6/ip6_fw.c,v 1.2.2.9 2002/04/28 05:40:27 suz Exp $ */ | |
30 | /* $KAME: ip6_fw.c,v 1.21 2001/01/24 01:25:32 itojun Exp $ */ | |
31 | ||
32 | /* | |
33 | * Copyright (C) 1998, 1999, 2000 and 2001 WIDE Project. | |
34 | * All rights reserved. | |
35 | * | |
36 | * Redistribution and use in source and binary forms, with or without | |
37 | * modification, are permitted provided that the following conditions | |
38 | * are met: | |
39 | * 1. Redistributions of source code must retain the above copyright | |
40 | * notice, this list of conditions and the following disclaimer. | |
41 | * 2. Redistributions in binary form must reproduce the above copyright | |
42 | * notice, this list of conditions and the following disclaimer in the | |
43 | * documentation and/or other materials provided with the distribution. | |
44 | * 3. Neither the name of the project nor the names of its contributors | |
45 | * may be used to endorse or promote products derived from this software | |
46 | * without specific prior written permission. | |
47 | * | |
48 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
58 | * SUCH DAMAGE. | |
59 | */ | |
60 | ||
61 | /* | |
62 | * Copyright (c) 1993 Daniel Boulet | |
63 | * Copyright (c) 1994 Ugen J.S.Antsilevich | |
64 | * Copyright (c) 1996 Alex Nash | |
65 | * | |
66 | * Redistribution and use in source forms, with and without modification, | |
67 | * are permitted provided that this entire comment appears intact. | |
68 | * | |
69 | * Redistribution in binary form may occur without any restrictions. | |
70 | * Obviously, it would be nice if you gave credit where credit is due | |
71 | * but requiring it would be too onerous. | |
72 | * | |
73 | * This software is provided ``AS IS'' without any warranties of any kind. | |
74 | */ | |
75 | ||
76 | /* | |
77 | * Implement IPv6 packet firewall | |
78 | */ | |
79 | ||
80 | ||
81 | #ifdef IP6DIVERT | |
82 | #error "NOT SUPPORTED IPV6 DIVERT" | |
83 | #endif | |
84 | #ifdef IP6FW_DIVERT_RESTART | |
85 | #error "NOT SUPPORTED IPV6 DIVERT" | |
86 | #endif | |
87 | ||
88 | #include <string.h> | |
91447636 A |
89 | #include <sys/param.h> |
90 | #include <sys/systm.h> | |
91 | #include <sys/malloc.h> | |
92 | #include <sys/mbuf.h> | |
93 | #include <sys/queue.h> | |
94 | #include <sys/kernel.h> | |
95 | #include <sys/socket.h> | |
96 | #include <sys/socketvar.h> | |
97 | #include <sys/syslog.h> | |
98 | #include <sys/lock.h> | |
99 | #include <sys/time.h> | |
2d21ac55 A |
100 | #include <sys/kern_event.h> |
101 | ||
91447636 A |
102 | #include <net/if.h> |
103 | #include <net/route.h> | |
104 | #include <netinet/in_systm.h> | |
105 | #include <netinet/in.h> | |
106 | #include <netinet/ip.h> | |
107 | ||
108 | #include <netinet/ip6.h> | |
109 | #include <netinet6/ip6_var.h> | |
110 | #include <netinet6/in6_var.h> | |
111 | #include <netinet/icmp6.h> | |
112 | ||
113 | #include <netinet/in_pcb.h> | |
114 | ||
115 | #include <netinet6/ip6_fw.h> | |
116 | #include <netinet/ip_var.h> | |
117 | #include <netinet/tcp.h> | |
118 | #include <netinet/tcp_seq.h> | |
119 | #include <netinet/tcp_timer.h> | |
120 | #include <netinet/tcp_var.h> | |
121 | #include <netinet/udp.h> | |
122 | ||
123 | #include <sys/sysctl.h> | |
124 | ||
125 | #include <net/net_osdep.h> | |
126 | ||
127 | MALLOC_DEFINE(M_IP6FW, "Ip6Fw/Ip6Acct", "Ip6Fw/Ip6Acct chain's"); | |
128 | ||
2d21ac55 | 129 | static int fw6_debug = 0; |
91447636 A |
130 | #ifdef IPV6FIREWALL_VERBOSE |
131 | static int fw6_verbose = 1; | |
132 | #else | |
133 | static int fw6_verbose = 0; | |
134 | #endif | |
135 | #ifdef IPV6FIREWALL_VERBOSE_LIMIT | |
136 | static int fw6_verbose_limit = IPV6FIREWALL_VERBOSE_LIMIT; | |
137 | #else | |
138 | static int fw6_verbose_limit = 0; | |
139 | #endif | |
140 | ||
0a7de745 | 141 | LIST_HEAD(ip6_fw_head, ip6_fw_chain) ip6_fw_chain; |
91447636 | 142 | |
2d21ac55 A |
143 | static void ip6fw_kev_post_msg(u_int32_t ); |
144 | ||
91447636 | 145 | #ifdef SYSCTL_NODE |
2d21ac55 A |
146 | static int ip6fw_sysctl SYSCTL_HANDLER_ARGS; |
147 | ||
91447636 | 148 | SYSCTL_DECL(_net_inet6_ip6); |
0a7de745 A |
149 | SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, fw, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "Firewall"); |
150 | SYSCTL_PROC(_net_inet6_ip6_fw, OID_AUTO, enable, | |
151 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, | |
152 | &ip6_fw_enable, 0, ip6fw_sysctl, "I", "Enable ip6fw"); | |
6d2010ae A |
153 | SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED, &fw6_debug, 0, ""); |
154 | SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose, CTLFLAG_RW | CTLFLAG_LOCKED, &fw6_verbose, 0, ""); | |
155 | SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose_limit, CTLFLAG_RW | CTLFLAG_LOCKED, &fw6_verbose_limit, 0, ""); | |
2d21ac55 A |
156 | |
157 | static int | |
158 | ip6fw_sysctl SYSCTL_HANDLER_ARGS | |
159 | { | |
160 | #pragma unused(arg1, arg2) | |
161 | int error; | |
0a7de745 | 162 | |
2d21ac55 | 163 | error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); |
0a7de745 A |
164 | if (error || !req->newptr) { |
165 | return error; | |
166 | } | |
167 | ||
2d21ac55 | 168 | ip6fw_kev_post_msg(KEV_IP6FW_ENABLE); |
0a7de745 | 169 | |
2d21ac55 A |
170 | return error; |
171 | } | |
172 | ||
91447636 A |
173 | #endif |
174 | ||
0a7de745 A |
175 | #define dprintf(a) do { \ |
176 | if (fw6_debug) \ | |
177 | printf a; \ | |
178 | } while (0) | |
91447636 A |
179 | #define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0 |
180 | ||
0a7de745 A |
181 | static int add_entry6 __P((struct ip6_fw_head *chainptr, struct ip6_fw *frwl)); |
182 | static int del_entry6 __P((struct ip6_fw_head *chainptr, u_short number)); | |
183 | static int zero_entry6 __P((struct ip6_fw *frwl)); | |
91447636 | 184 | static struct ip6_fw *check_ip6fw_struct __P((struct ip6_fw *m)); |
0a7de745 A |
185 | static int ip6opts_match __P((struct ip6_hdr **ip6, struct ip6_fw *f, |
186 | struct mbuf **m, | |
187 | int *off, int *nxt, u_short *offset)); | |
188 | static int port_match6 __P((u_short *portptr, int nports, u_short port, | |
189 | int range_flag)); | |
190 | static int tcp6flg_match __P((struct tcphdr *tcp6, struct ip6_fw *f)); | |
191 | static int icmp6type_match __P((struct icmp6_hdr * icmp, struct ip6_fw * f)); | |
192 | static void ip6fw_report __P((struct ip6_fw *f, struct ip6_hdr *ip6, | |
193 | struct ifnet *rif, struct ifnet *oif, int off, int nxt)); | |
194 | ||
195 | static int ip6_fw_chk __P((struct ip6_hdr **pip6, | |
196 | struct ifnet *oif, u_int16_t *cookie, struct mbuf **m)); | |
197 | static int ip6_fw_ctl __P((struct sockopt *)); | |
b0d623f7 A |
198 | static void cp_to_user_64( struct ip6_fw_64 *userrule_64, struct ip6_fw *rule); |
199 | static void cp_from_user_64( struct ip6_fw_64 *userrule_64, struct ip6_fw *rule); | |
200 | static void cp_to_user_32( struct ip6_fw_32 *userrule_32, struct ip6_fw *rule); | |
201 | static void cp_from_user_32( struct ip6_fw_32 *userrule_32, struct ip6_fw *rule); | |
91447636 A |
202 | |
203 | static char err_prefix[] = "ip6_fw_ctl:"; | |
91447636 A |
204 | |
205 | /* | |
206 | * Returns 1 if the port is matched by the vector, 0 otherwise | |
207 | */ | |
208 | static | |
209 | __inline int | |
210 | port_match6(u_short *portptr, int nports, u_short port, int range_flag) | |
211 | { | |
0a7de745 | 212 | if (!nports) { |
91447636 | 213 | return 1; |
0a7de745 | 214 | } |
91447636 A |
215 | if (range_flag) { |
216 | if (portptr[0] <= port && port <= portptr[1]) { | |
217 | return 1; | |
218 | } | |
219 | nports -= 2; | |
220 | portptr += 2; | |
221 | } | |
222 | while (nports-- > 0) { | |
223 | if (*portptr++ == port) { | |
224 | return 1; | |
225 | } | |
226 | } | |
227 | return 0; | |
228 | } | |
229 | ||
230 | static int | |
231 | tcp6flg_match(struct tcphdr *tcp6, struct ip6_fw *f) | |
232 | { | |
0a7de745 A |
233 | u_char flg_set, flg_clr; |
234 | ||
91447636 A |
235 | /* |
236 | * If an established connection is required, reject packets that | |
237 | * have only SYN of RST|ACK|SYN set. Otherwise, fall through to | |
238 | * other flag requirements. | |
239 | */ | |
240 | if ((f->fw_ipflg & IPV6_FW_IF_TCPEST) && | |
241 | ((tcp6->th_flags & (IPV6_FW_TCPF_RST | IPV6_FW_TCPF_ACK | | |
0a7de745 | 242 | IPV6_FW_TCPF_SYN)) == IPV6_FW_TCPF_SYN)) { |
91447636 | 243 | return 0; |
0a7de745 | 244 | } |
91447636 A |
245 | |
246 | flg_set = tcp6->th_flags & f->fw_tcpf; | |
247 | flg_clr = tcp6->th_flags & f->fw_tcpnf; | |
248 | ||
0a7de745 | 249 | if (flg_set != f->fw_tcpf) { |
91447636 | 250 | return 0; |
0a7de745 A |
251 | } |
252 | if (flg_clr) { | |
91447636 | 253 | return 0; |
0a7de745 | 254 | } |
91447636 A |
255 | |
256 | return 1; | |
257 | } | |
258 | ||
259 | static int | |
260 | icmp6type_match(struct icmp6_hdr *icmp6, struct ip6_fw *f) | |
261 | { | |
262 | int type; | |
263 | ||
0a7de745 A |
264 | if (!(f->fw_flg & IPV6_FW_F_ICMPBIT)) { |
265 | return 1; | |
266 | } | |
91447636 A |
267 | |
268 | type = icmp6->icmp6_type; | |
269 | ||
270 | /* check for matching type in the bitmap */ | |
271 | if (type < IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8 && | |
0a7de745 A |
272 | (f->fw_icmp6types[type / (sizeof(unsigned) * 8)] & |
273 | (1U << (type % (8 * sizeof(unsigned)))))) { | |
274 | return 1; | |
275 | } | |
91447636 | 276 | |
0a7de745 | 277 | return 0; /* no match */ |
91447636 A |
278 | } |
279 | ||
280 | static int | |
281 | is_icmp6_query(struct ip6_hdr *ip6, int off) | |
282 | { | |
283 | const struct icmp6_hdr *icmp6; | |
284 | int icmp6_type; | |
285 | ||
286 | icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); | |
287 | icmp6_type = icmp6->icmp6_type; | |
288 | ||
289 | if (icmp6_type == ICMP6_ECHO_REQUEST || | |
290 | icmp6_type == ICMP6_MEMBERSHIP_QUERY || | |
291 | icmp6_type == ICMP6_WRUREQUEST || | |
292 | icmp6_type == ICMP6_FQDN_QUERY || | |
0a7de745 A |
293 | icmp6_type == ICMP6_NI_QUERY) { |
294 | return 1; | |
295 | } | |
91447636 | 296 | |
0a7de745 | 297 | return 0; |
91447636 A |
298 | } |
299 | ||
300 | static int | |
301 | ip6opts_match(struct ip6_hdr **pip6, struct ip6_fw *f, struct mbuf **m, | |
0a7de745 | 302 | int *off, int *nxt, u_short *offset) |
91447636 A |
303 | { |
304 | int len; | |
305 | struct ip6_hdr *ip6 = *pip6; | |
306 | struct ip6_ext *ip6e; | |
0a7de745 | 307 | u_char opts, nopts, nopts_sve; |
91447636 A |
308 | |
309 | opts = f->fw_ip6opt; | |
310 | nopts = nopts_sve = f->fw_ip6nopt; | |
311 | ||
312 | *nxt = ip6->ip6_nxt; | |
313 | *off = sizeof(struct ip6_hdr); | |
314 | len = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr); | |
315 | while (*off < len) { | |
316 | ip6e = (struct ip6_ext *)((caddr_t) ip6 + *off); | |
0a7de745 A |
317 | if ((*m)->m_len < *off + sizeof(*ip6e)) { |
318 | goto opts_check; /* XXX */ | |
319 | } | |
320 | switch (*nxt) { | |
91447636 A |
321 | case IPPROTO_FRAGMENT: |
322 | if ((*m)->m_len >= *off + sizeof(struct ip6_frag)) { | |
323 | struct ip6_frag *ip6f; | |
324 | ||
325 | ip6f = (struct ip6_frag *) ((caddr_t)ip6 + *off); | |
326 | *offset = ip6f->ip6f_offlg & IP6F_OFF_MASK; | |
327 | } | |
328 | opts &= ~IPV6_FW_IP6OPT_FRAG; | |
329 | nopts &= ~IPV6_FW_IP6OPT_FRAG; | |
330 | *off += sizeof(struct ip6_frag); | |
331 | break; | |
332 | case IPPROTO_AH: | |
333 | opts &= ~IPV6_FW_IP6OPT_AH; | |
334 | nopts &= ~IPV6_FW_IP6OPT_AH; | |
335 | *off += (ip6e->ip6e_len + 2) << 2; | |
336 | break; | |
337 | default: | |
338 | switch (*nxt) { | |
339 | case IPPROTO_HOPOPTS: | |
340 | opts &= ~IPV6_FW_IP6OPT_HOPOPT; | |
341 | nopts &= ~IPV6_FW_IP6OPT_HOPOPT; | |
342 | break; | |
343 | case IPPROTO_ROUTING: | |
344 | opts &= ~IPV6_FW_IP6OPT_ROUTE; | |
345 | nopts &= ~IPV6_FW_IP6OPT_ROUTE; | |
346 | break; | |
347 | case IPPROTO_ESP: | |
348 | opts &= ~IPV6_FW_IP6OPT_ESP; | |
349 | nopts &= ~IPV6_FW_IP6OPT_ESP; | |
350 | break; | |
351 | case IPPROTO_NONE: | |
352 | opts &= ~IPV6_FW_IP6OPT_NONXT; | |
353 | nopts &= ~IPV6_FW_IP6OPT_NONXT; | |
354 | goto opts_check; | |
355 | break; | |
356 | case IPPROTO_DSTOPTS: | |
357 | opts &= ~IPV6_FW_IP6OPT_OPTS; | |
358 | nopts &= ~IPV6_FW_IP6OPT_OPTS; | |
359 | break; | |
360 | default: | |
361 | goto opts_check; | |
362 | break; | |
363 | } | |
364 | *off += (ip6e->ip6e_len + 1) << 3; | |
365 | break; | |
366 | } | |
367 | *nxt = ip6e->ip6e_nxt; | |
91447636 | 368 | } |
0a7de745 A |
369 | opts_check: |
370 | if (f->fw_ip6opt == f->fw_ip6nopt) { /* XXX */ | |
91447636 | 371 | return 1; |
0a7de745 | 372 | } |
91447636 | 373 | |
0a7de745 | 374 | if (opts == 0 && nopts == nopts_sve) { |
91447636 | 375 | return 1; |
0a7de745 | 376 | } else { |
91447636 | 377 | return 0; |
0a7de745 | 378 | } |
91447636 A |
379 | } |
380 | ||
381 | static | |
382 | __inline int | |
383 | iface_match(struct ifnet *ifp, union ip6_fw_if *ifu, int byname) | |
384 | { | |
385 | /* Check by name or by IP address */ | |
386 | if (byname) { | |
387 | /* Check unit number (-1 is wildcard) */ | |
388 | if (ifu->fu_via_if.unit != -1 | |
0a7de745 A |
389 | && ifp->if_unit != ifu->fu_via_if.unit) { |
390 | return 0; | |
391 | } | |
91447636 | 392 | /* Check name */ |
0a7de745 A |
393 | if (strncmp(ifp->if_name, ifu->fu_via_if.name, IP6FW_IFNLEN)) { |
394 | return 0; | |
395 | } | |
396 | return 1; | |
397 | } else if (!IN6_IS_ADDR_UNSPECIFIED(&ifu->fu_via_ip6)) { /* Zero == wildcard */ | |
91447636 A |
398 | struct ifaddr *ia; |
399 | ||
400 | ifnet_lock_shared(ifp); | |
6d2010ae | 401 | for (ia = ifp->if_addrlist.tqh_first; ia; |
0a7de745 | 402 | ia = ia->ifa_list.tqe_next) { |
6d2010ae A |
403 | IFA_LOCK_SPIN(ia); |
404 | if (ia->ifa_addr->sa_family != AF_INET6) { | |
405 | IFA_UNLOCK(ia); | |
91447636 | 406 | continue; |
6d2010ae | 407 | } |
91447636 A |
408 | if (!IN6_ARE_ADDR_EQUAL(&ifu->fu_via_ip6, |
409 | &(((struct sockaddr_in6 *) | |
6d2010ae A |
410 | (ia->ifa_addr))->sin6_addr))) { |
411 | IFA_UNLOCK(ia); | |
91447636 | 412 | continue; |
6d2010ae A |
413 | } |
414 | IFA_UNLOCK(ia); | |
91447636 | 415 | ifnet_lock_done(ifp); |
0a7de745 | 416 | return 1; |
91447636 A |
417 | } |
418 | ifnet_lock_done(ifp); | |
0a7de745 | 419 | return 0; |
91447636 | 420 | } |
0a7de745 | 421 | return 1; |
91447636 A |
422 | } |
423 | ||
424 | static void | |
425 | ip6fw_report(struct ip6_fw *f, struct ip6_hdr *ip6, | |
0a7de745 | 426 | struct ifnet *rif, struct ifnet *oif, int off, int nxt) |
91447636 A |
427 | { |
428 | static int counter; | |
0a7de745 A |
429 | struct tcphdr *const tcp6 = (struct tcphdr *) ((caddr_t) ip6 + off); |
430 | struct udphdr *const udp = (struct udphdr *) ((caddr_t) ip6 + off); | |
431 | struct icmp6_hdr *const icmp6 = (struct icmp6_hdr *) ((caddr_t) ip6 + off); | |
91447636 | 432 | int count; |
2d21ac55 | 433 | const char *action; |
91447636 A |
434 | char action2[32], proto[102], name[18]; |
435 | int len; | |
436 | ||
437 | count = f ? f->fw_pcnt : ++counter; | |
0a7de745 | 438 | if (fw6_verbose_limit != 0 && count > fw6_verbose_limit) { |
91447636 | 439 | return; |
0a7de745 | 440 | } |
91447636 A |
441 | |
442 | /* Print command name */ | |
443 | snprintf(SNPARGS(name, 0), "ip6fw: %d", f ? f->fw_number : -1); | |
444 | ||
445 | action = action2; | |
0a7de745 | 446 | if (!f) { |
91447636 | 447 | action = "Refuse"; |
0a7de745 | 448 | } else { |
91447636 A |
449 | switch (f->fw_flg & IPV6_FW_F_COMMAND) { |
450 | case IPV6_FW_F_DENY: | |
451 | action = "Deny"; | |
452 | break; | |
453 | case IPV6_FW_F_REJECT: | |
0a7de745 | 454 | if (f->fw_reject_code == IPV6_FW_REJECT_RST) { |
91447636 | 455 | action = "Reset"; |
0a7de745 | 456 | } else { |
91447636 | 457 | action = "Unreach"; |
0a7de745 | 458 | } |
91447636 A |
459 | break; |
460 | case IPV6_FW_F_ACCEPT: | |
461 | action = "Accept"; | |
462 | break; | |
463 | case IPV6_FW_F_COUNT: | |
464 | action = "Count"; | |
465 | break; | |
466 | case IPV6_FW_F_DIVERT: | |
467 | snprintf(SNPARGS(action2, 0), "Divert %d", | |
468 | f->fw_divert_port); | |
469 | break; | |
470 | case IPV6_FW_F_TEE: | |
471 | snprintf(SNPARGS(action2, 0), "Tee %d", | |
472 | f->fw_divert_port); | |
473 | break; | |
474 | case IPV6_FW_F_SKIPTO: | |
475 | snprintf(SNPARGS(action2, 0), "SkipTo %d", | |
476 | f->fw_skipto_rule); | |
477 | break; | |
0a7de745 | 478 | default: |
91447636 A |
479 | action = "UNKNOWN"; |
480 | break; | |
481 | } | |
482 | } | |
483 | ||
484 | switch (nxt) { | |
485 | case IPPROTO_TCP: | |
486 | len = snprintf(SNPARGS(proto, 0), "TCP [%s]", | |
487 | ip6_sprintf(&ip6->ip6_src)); | |
0a7de745 | 488 | if (off > 0) { |
91447636 A |
489 | len += snprintf(SNPARGS(proto, len), ":%d ", |
490 | ntohs(tcp6->th_sport)); | |
0a7de745 | 491 | } else { |
91447636 | 492 | len += snprintf(SNPARGS(proto, len), " "); |
0a7de745 | 493 | } |
91447636 A |
494 | len += snprintf(SNPARGS(proto, len), "[%s]", |
495 | ip6_sprintf(&ip6->ip6_dst)); | |
0a7de745 | 496 | if (off > 0) { |
91447636 A |
497 | snprintf(SNPARGS(proto, len), ":%d", |
498 | ntohs(tcp6->th_dport)); | |
0a7de745 | 499 | } |
91447636 A |
500 | break; |
501 | case IPPROTO_UDP: | |
502 | len = snprintf(SNPARGS(proto, 0), "UDP [%s]", | |
503 | ip6_sprintf(&ip6->ip6_src)); | |
0a7de745 | 504 | if (off > 0) { |
91447636 A |
505 | len += snprintf(SNPARGS(proto, len), ":%d ", |
506 | ntohs(udp->uh_sport)); | |
0a7de745 A |
507 | } else { |
508 | len += snprintf(SNPARGS(proto, len), " "); | |
509 | } | |
91447636 A |
510 | len += snprintf(SNPARGS(proto, len), "[%s]", |
511 | ip6_sprintf(&ip6->ip6_dst)); | |
0a7de745 | 512 | if (off > 0) { |
91447636 A |
513 | snprintf(SNPARGS(proto, len), ":%d", |
514 | ntohs(udp->uh_dport)); | |
0a7de745 | 515 | } |
91447636 A |
516 | break; |
517 | case IPPROTO_ICMPV6: | |
0a7de745 | 518 | if (off > 0) { |
91447636 A |
519 | len = snprintf(SNPARGS(proto, 0), "IPV6-ICMP:%u.%u ", |
520 | icmp6->icmp6_type, icmp6->icmp6_code); | |
0a7de745 | 521 | } else { |
91447636 | 522 | len = snprintf(SNPARGS(proto, 0), "IPV6-ICMP "); |
0a7de745 | 523 | } |
91447636 A |
524 | len += snprintf(SNPARGS(proto, len), "[%s]", |
525 | ip6_sprintf(&ip6->ip6_src)); | |
526 | snprintf(SNPARGS(proto, len), " [%s]", | |
527 | ip6_sprintf(&ip6->ip6_dst)); | |
528 | break; | |
529 | default: | |
530 | len = snprintf(SNPARGS(proto, 0), "P:%d [%s]", nxt, | |
531 | ip6_sprintf(&ip6->ip6_src)); | |
532 | snprintf(SNPARGS(proto, len), " [%s]", | |
533 | ip6_sprintf(&ip6->ip6_dst)); | |
534 | break; | |
535 | } | |
536 | ||
0a7de745 | 537 | if (oif) { |
91447636 A |
538 | log(LOG_AUTHPRIV | LOG_INFO, "%s %s %s out via %s\n", |
539 | name, action, proto, if_name(oif)); | |
0a7de745 | 540 | } else if (rif) { |
91447636 A |
541 | log(LOG_AUTHPRIV | LOG_INFO, "%s %s %s in via %s\n", |
542 | name, action, proto, if_name(rif)); | |
0a7de745 | 543 | } else { |
91447636 A |
544 | log(LOG_AUTHPRIV | LOG_INFO, "%s %s %s", |
545 | name, action, proto); | |
0a7de745 A |
546 | } |
547 | if (fw6_verbose_limit != 0 && count == fw6_verbose_limit) { | |
548 | log(LOG_AUTHPRIV | LOG_INFO, "ip6fw: limit reached on entry %d\n", | |
549 | f ? f->fw_number : -1); | |
550 | } | |
91447636 A |
551 | } |
552 | ||
553 | /* | |
554 | * Parameters: | |
555 | * | |
556 | * ip Pointer to packet header (struct ip6_hdr *) | |
557 | * hlen Packet header length | |
558 | * oif Outgoing interface, or NULL if packet is incoming | |
559 | * #ifndef IP6FW_DIVERT_RESTART | |
560 | * *cookie Ignore all divert/tee rules to this port (if non-zero) | |
561 | * #else | |
562 | * *cookie Skip up to the first rule past this rule number; | |
563 | * #endif | |
564 | * *m The packet; we set to NULL when/if we nuke it. | |
565 | * | |
566 | * Return value: | |
567 | * | |
568 | * 0 The packet is to be accepted and routed normally OR | |
0a7de745 | 569 | * the packet was denied/rejected and has been dropped; |
91447636 A |
570 | * in the latter case, *m is equal to NULL upon return. |
571 | * port Divert the packet to port. | |
572 | */ | |
573 | ||
574 | static int | |
575 | ip6_fw_chk(struct ip6_hdr **pip6, | |
0a7de745 | 576 | struct ifnet *oif, u_int16_t *cookie, struct mbuf **m) |
91447636 A |
577 | { |
578 | struct ip6_fw_chain *chain; | |
579 | struct ip6_fw *rule = NULL; | |
580 | struct ip6_hdr *ip6 = *pip6; | |
6d2010ae | 581 | struct ifnet *const rif = ((*m)->m_flags & M_LOOP) ? lo_ifp : (*m)->m_pkthdr.rcvif; |
91447636 A |
582 | u_short offset = 0; |
583 | int off = sizeof(struct ip6_hdr), nxt = ip6->ip6_nxt; | |
584 | u_short src_port, dst_port; | |
0a7de745 | 585 | #ifdef IP6FW_DIVERT_RESTART |
91447636 A |
586 | u_int16_t skipto = *cookie; |
587 | #else | |
588 | u_int16_t ignport = ntohs(*cookie); | |
589 | #endif | |
590 | struct timeval timenow; | |
fe8ab488 | 591 | struct tcp_respond_args tra; |
91447636 A |
592 | |
593 | getmicrotime(&timenow); | |
594 | ||
595 | *cookie = 0; | |
596 | /* | |
597 | * Go down the chain, looking for enlightment | |
598 | * #ifdef IP6FW_DIVERT_RESTART | |
599 | * If we've been asked to start at a given rule immediatly, do so. | |
600 | * #endif | |
601 | */ | |
602 | chain = LIST_FIRST(&ip6_fw_chain); | |
603 | #ifdef IP6FW_DIVERT_RESTART | |
604 | if (skipto) { | |
0a7de745 | 605 | if (skipto >= 65535) { |
91447636 | 606 | goto dropit; |
0a7de745 | 607 | } |
91447636 A |
608 | while (chain && (chain->rule->fw_number <= skipto)) { |
609 | chain = LIST_NEXT(chain, chain); | |
610 | } | |
0a7de745 A |
611 | if (!chain) { |
612 | goto dropit; | |
613 | } | |
91447636 A |
614 | } |
615 | #endif /* IP6FW_DIVERT_RESTART */ | |
616 | for (; chain; chain = LIST_NEXT(chain, chain)) { | |
617 | struct ip6_fw *const f = chain->rule; | |
618 | ||
619 | if (oif) { | |
620 | /* Check direction outbound */ | |
0a7de745 | 621 | if (!(f->fw_flg & IPV6_FW_F_OUT)) { |
91447636 | 622 | continue; |
0a7de745 | 623 | } |
91447636 A |
624 | } else { |
625 | /* Check direction inbound */ | |
0a7de745 | 626 | if (!(f->fw_flg & IPV6_FW_F_IN)) { |
91447636 | 627 | continue; |
0a7de745 | 628 | } |
91447636 A |
629 | } |
630 | ||
0a7de745 | 631 | #define IN6_ARE_ADDR_MASKEQUAL(x, y, z) (\ |
91447636 A |
632 | (((x)->s6_addr32[0] & (y)->s6_addr32[0]) == (z)->s6_addr32[0]) && \ |
633 | (((x)->s6_addr32[1] & (y)->s6_addr32[1]) == (z)->s6_addr32[1]) && \ | |
634 | (((x)->s6_addr32[2] & (y)->s6_addr32[2]) == (z)->s6_addr32[2]) && \ | |
635 | (((x)->s6_addr32[3] & (y)->s6_addr32[3]) == (z)->s6_addr32[3])) | |
636 | ||
637 | /* If src-addr doesn't match, not this rule. */ | |
638 | if (((f->fw_flg & IPV6_FW_F_INVSRC) != 0) ^ | |
0a7de745 | 639 | (!IN6_ARE_ADDR_MASKEQUAL(&ip6->ip6_src, &f->fw_smsk, &f->fw_src))) { |
91447636 | 640 | continue; |
0a7de745 | 641 | } |
91447636 A |
642 | |
643 | /* If dest-addr doesn't match, not this rule. */ | |
644 | if (((f->fw_flg & IPV6_FW_F_INVDST) != 0) ^ | |
0a7de745 | 645 | (!IN6_ARE_ADDR_MASKEQUAL(&ip6->ip6_dst, &f->fw_dmsk, &f->fw_dst))) { |
91447636 | 646 | continue; |
0a7de745 | 647 | } |
91447636 A |
648 | |
649 | #undef IN6_ARE_ADDR_MASKEQUAL | |
650 | /* Interface check */ | |
651 | if ((f->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) { | |
652 | struct ifnet *const iface = oif ? oif : rif; | |
653 | ||
654 | /* Backwards compatibility hack for "via" */ | |
655 | if (!iface || !iface_match(iface, | |
0a7de745 | 656 | &f->fw_in_if, f->fw_flg & IPV6_FW_F_OIFNAME)) { |
91447636 | 657 | continue; |
0a7de745 | 658 | } |
91447636 A |
659 | } else { |
660 | /* Check receive interface */ | |
661 | if ((f->fw_flg & IPV6_FW_F_IIFACE) | |
662 | && (!rif || !iface_match(rif, | |
0a7de745 | 663 | &f->fw_in_if, f->fw_flg & IPV6_FW_F_IIFNAME))) { |
91447636 | 664 | continue; |
0a7de745 | 665 | } |
91447636 A |
666 | /* Check outgoing interface */ |
667 | if ((f->fw_flg & IPV6_FW_F_OIFACE) | |
668 | && (!oif || !iface_match(oif, | |
0a7de745 | 669 | &f->fw_out_if, f->fw_flg & IPV6_FW_F_OIFNAME))) { |
91447636 | 670 | continue; |
0a7de745 | 671 | } |
91447636 A |
672 | } |
673 | ||
674 | /* Check IP options */ | |
0a7de745 | 675 | if (!ip6opts_match(&ip6, f, m, &off, &nxt, &offset)) { |
91447636 | 676 | continue; |
0a7de745 | 677 | } |
91447636 A |
678 | |
679 | /* Fragments */ | |
0a7de745 | 680 | if ((f->fw_flg & IPV6_FW_F_FRAG) && !offset) { |
91447636 | 681 | continue; |
0a7de745 | 682 | } |
91447636 A |
683 | |
684 | /* Check protocol; if wildcard, match */ | |
0a7de745 | 685 | if (f->fw_prot == IPPROTO_IPV6) { |
91447636 | 686 | goto got_match; |
0a7de745 | 687 | } |
91447636 A |
688 | |
689 | /* If different, don't match */ | |
0a7de745 | 690 | if (nxt != f->fw_prot) { |
91447636 | 691 | continue; |
0a7de745 | 692 | } |
91447636 | 693 | |
0a7de745 A |
694 | #define PULLUP_TO(len) do { \ |
695 | if ((*m)->m_len < (len) \ | |
696 | && (*m = m_pullup(*m, (len))) == 0) { \ | |
697 | goto dropit; \ | |
698 | } \ | |
699 | *pip6 = ip6 = mtod(*m, struct ip6_hdr *); \ | |
700 | } while (0) | |
91447636 A |
701 | |
702 | /* Protocol specific checks */ | |
703 | switch (nxt) { | |
704 | case IPPROTO_TCP: | |
0a7de745 | 705 | { |
91447636 A |
706 | struct tcphdr *tcp6; |
707 | ||
0a7de745 | 708 | if (offset == 1) { /* cf. RFC 1858 */ |
91447636 A |
709 | PULLUP_TO(off + 4); /* XXX ? */ |
710 | goto bogusfrag; | |
711 | } | |
712 | if (offset != 0) { | |
713 | /* | |
714 | * TCP flags and ports aren't available in this | |
715 | * packet -- if this rule specified either one, | |
716 | * we consider the rule a non-match. | |
717 | */ | |
718 | if (f->fw_nports != 0 || | |
0a7de745 | 719 | f->fw_tcpf != f->fw_tcpnf) { |
91447636 | 720 | continue; |
0a7de745 | 721 | } |
91447636 A |
722 | |
723 | break; | |
724 | } | |
725 | PULLUP_TO(off + 14); | |
726 | tcp6 = (struct tcphdr *) ((caddr_t)ip6 + off); | |
727 | if (((f->fw_tcpf != f->fw_tcpnf) || | |
0a7de745 A |
728 | (f->fw_ipflg & IPV6_FW_IF_TCPEST)) && |
729 | !tcp6flg_match(tcp6, f)) { | |
91447636 | 730 | continue; |
0a7de745 | 731 | } |
91447636 A |
732 | src_port = ntohs(tcp6->th_sport); |
733 | dst_port = ntohs(tcp6->th_dport); | |
734 | goto check_ports; | |
0a7de745 | 735 | } |
91447636 A |
736 | |
737 | case IPPROTO_UDP: | |
0a7de745 | 738 | { |
91447636 A |
739 | struct udphdr *udp; |
740 | ||
741 | if (offset != 0) { | |
742 | /* | |
743 | * Port specification is unavailable -- if this | |
744 | * rule specifies a port, we consider the rule | |
745 | * a non-match. | |
746 | */ | |
0a7de745 | 747 | if (f->fw_nports != 0) { |
91447636 | 748 | continue; |
0a7de745 | 749 | } |
91447636 A |
750 | |
751 | break; | |
752 | } | |
753 | PULLUP_TO(off + 4); | |
754 | udp = (struct udphdr *) ((caddr_t)ip6 + off); | |
755 | src_port = ntohs(udp->uh_sport); | |
756 | dst_port = ntohs(udp->uh_dport); | |
757 | check_ports: | |
758 | if (!port_match6(&f->fw_pts[0], | |
759 | IPV6_FW_GETNSRCP(f), src_port, | |
0a7de745 | 760 | f->fw_flg & IPV6_FW_F_SRNG)) { |
91447636 | 761 | continue; |
0a7de745 | 762 | } |
91447636 A |
763 | if (!port_match6(&f->fw_pts[IPV6_FW_GETNSRCP(f)], |
764 | IPV6_FW_GETNDSTP(f), dst_port, | |
0a7de745 | 765 | f->fw_flg & IPV6_FW_F_DRNG)) { |
91447636 | 766 | continue; |
0a7de745 | 767 | } |
91447636 | 768 | break; |
0a7de745 | 769 | } |
91447636 A |
770 | |
771 | case IPPROTO_ICMPV6: | |
0a7de745 | 772 | { |
91447636 A |
773 | struct icmp6_hdr *icmp; |
774 | ||
0a7de745 | 775 | if (offset != 0) { /* Type isn't valid */ |
91447636 | 776 | break; |
0a7de745 | 777 | } |
91447636 A |
778 | PULLUP_TO(off + 2); |
779 | icmp = (struct icmp6_hdr *) ((caddr_t)ip6 + off); | |
0a7de745 | 780 | if (!icmp6type_match(icmp, f)) { |
91447636 | 781 | continue; |
0a7de745 | 782 | } |
91447636 | 783 | break; |
0a7de745 | 784 | } |
91447636 A |
785 | #undef PULLUP_TO |
786 | ||
787 | bogusfrag: | |
0a7de745 | 788 | if (fw6_verbose) { |
91447636 | 789 | ip6fw_report(NULL, ip6, rif, oif, off, nxt); |
0a7de745 | 790 | } |
91447636 A |
791 | goto dropit; |
792 | } | |
793 | ||
794 | got_match: | |
795 | #ifndef IP6FW_DIVERT_RESTART | |
796 | /* Ignore divert/tee rule if socket port is "ignport" */ | |
797 | switch (f->fw_flg & IPV6_FW_F_COMMAND) { | |
798 | case IPV6_FW_F_DIVERT: | |
799 | case IPV6_FW_F_TEE: | |
0a7de745 | 800 | if (f->fw_divert_port == ignport) { |
91447636 | 801 | continue; /* ignore this rule */ |
0a7de745 | 802 | } |
91447636 A |
803 | break; |
804 | } | |
805 | ||
806 | #endif /* IP6FW_DIVERT_RESTART */ | |
807 | /* Update statistics */ | |
808 | f->fw_pcnt += 1; | |
809 | f->fw_bcnt += ntohs(ip6->ip6_plen); | |
810 | f->timestamp = timenow.tv_sec; | |
811 | ||
812 | /* Log to console if desired */ | |
0a7de745 | 813 | if ((f->fw_flg & IPV6_FW_F_PRN) && fw6_verbose) { |
91447636 | 814 | ip6fw_report(f, ip6, rif, oif, off, nxt); |
0a7de745 | 815 | } |
91447636 A |
816 | |
817 | /* Take appropriate action */ | |
818 | switch (f->fw_flg & IPV6_FW_F_COMMAND) { | |
819 | case IPV6_FW_F_ACCEPT: | |
0a7de745 | 820 | return 0; |
91447636 A |
821 | case IPV6_FW_F_COUNT: |
822 | continue; | |
823 | case IPV6_FW_F_DIVERT: | |
824 | #ifdef IP6FW_DIVERT_RESTART | |
825 | *cookie = f->fw_number; | |
826 | #else | |
827 | *cookie = htons(f->fw_divert_port); | |
828 | #endif /* IP6FW_DIVERT_RESTART */ | |
0a7de745 | 829 | return f->fw_divert_port; |
91447636 A |
830 | case IPV6_FW_F_TEE: |
831 | /* | |
832 | * XXX someday tee packet here, but beware that you | |
833 | * can't use m_copym() or m_copypacket() because | |
834 | * the divert input routine modifies the mbuf | |
835 | * (and these routines only increment reference | |
836 | * counts in the case of mbuf clusters), so need | |
837 | * to write custom routine. | |
838 | */ | |
839 | continue; | |
840 | case IPV6_FW_F_SKIPTO: | |
841 | #ifdef DIAGNOSTIC | |
842 | while (chain->chain.le_next | |
843 | && chain->chain.le_next->rule->fw_number | |
0a7de745 | 844 | < f->fw_skipto_rule) |
91447636 A |
845 | #else |
846 | while (chain->chain.le_next->rule->fw_number | |
847 | < f->fw_skipto_rule) | |
848 | #endif | |
0a7de745 | 849 | { chain = chain->chain.le_next;} |
91447636 A |
850 | continue; |
851 | } | |
852 | ||
853 | /* Deny/reject this packet using this rule */ | |
854 | rule = f; | |
855 | break; | |
856 | } | |
857 | ||
858 | #ifdef DIAGNOSTIC | |
859 | /* Rule 65535 should always be there and should always match */ | |
0a7de745 | 860 | if (!chain) { |
91447636 | 861 | panic("ip6_fw: chain"); |
0a7de745 | 862 | } |
91447636 A |
863 | #endif |
864 | ||
865 | /* | |
866 | * At this point, we're going to drop the packet. | |
867 | * Send a reject notice if all of the following are true: | |
868 | * | |
869 | * - The packet matched a reject rule | |
870 | * - The packet is not an ICMP packet, or is an ICMP query packet | |
871 | * - The packet is not a multicast or broadcast packet | |
872 | */ | |
873 | if ((rule->fw_flg & IPV6_FW_F_COMMAND) == IPV6_FW_F_REJECT | |
874 | && (nxt != IPPROTO_ICMPV6 || is_icmp6_query(ip6, off)) | |
0a7de745 | 875 | && !((*m)->m_flags & (M_BCAST | M_MCAST)) |
91447636 A |
876 | && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { |
877 | switch (rule->fw_reject_code) { | |
878 | case IPV6_FW_REJECT_RST: | |
0a7de745 | 879 | { |
91447636 | 880 | struct tcphdr *const tcp = |
0a7de745 | 881 | (struct tcphdr *) ((caddr_t)ip6 + off); |
91447636 A |
882 | struct { |
883 | struct ip6_hdr ip6; | |
884 | struct tcphdr th; | |
885 | } ti; | |
886 | tcp_seq ack, seq; | |
887 | int flags; | |
888 | ||
0a7de745 | 889 | if (offset != 0 || (tcp->th_flags & TH_RST)) { |
91447636 | 890 | break; |
0a7de745 | 891 | } |
91447636 A |
892 | |
893 | ti.ip6 = *ip6; | |
894 | ti.th = *tcp; | |
895 | ti.th.th_seq = ntohl(ti.th.th_seq); | |
896 | ti.th.th_ack = ntohl(ti.th.th_ack); | |
897 | ti.ip6.ip6_nxt = IPPROTO_TCP; | |
898 | if (ti.th.th_flags & TH_ACK) { | |
899 | ack = 0; | |
900 | seq = ti.th.th_ack; | |
901 | flags = TH_RST; | |
902 | } else { | |
903 | ack = ti.th.th_seq; | |
904 | if (((*m)->m_flags & M_PKTHDR) != 0) { | |
905 | ack += (*m)->m_pkthdr.len - off | |
0a7de745 | 906 | - (ti.th.th_off << 2); |
91447636 A |
907 | } else if (ip6->ip6_plen) { |
908 | ack += ntohs(ip6->ip6_plen) + sizeof(*ip6) | |
0a7de745 | 909 | - off - (ti.th.th_off << 2); |
91447636 A |
910 | } else { |
911 | m_freem(*m); | |
912 | *m = 0; | |
913 | break; | |
914 | } | |
915 | seq = 0; | |
0a7de745 | 916 | flags = TH_RST | TH_ACK; |
91447636 A |
917 | } |
918 | bcopy(&ti, ip6, sizeof(ti)); | |
fe8ab488 A |
919 | bzero(&tra, sizeof(tra)); |
920 | tra.ifscope = IFSCOPE_NONE; | |
921 | tra.awdl_unrestricted = 1; | |
91447636 | 922 | tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1), |
0a7de745 | 923 | *m, ack, seq, flags, &tra); |
91447636 A |
924 | *m = NULL; |
925 | break; | |
0a7de745 A |
926 | } |
927 | default: /* Send an ICMP unreachable using code */ | |
928 | if (oif) { | |
91447636 | 929 | (*m)->m_pkthdr.rcvif = oif; |
0a7de745 | 930 | } |
91447636 A |
931 | icmp6_error(*m, ICMP6_DST_UNREACH, |
932 | rule->fw_reject_code, 0); | |
91447636 A |
933 | *m = NULL; |
934 | break; | |
935 | } | |
936 | } | |
937 | ||
938 | dropit: | |
939 | /* | |
940 | * Finally, drop the packet. | |
941 | */ | |
942 | if (*m) { | |
943 | m_freem(*m); | |
944 | *m = NULL; | |
945 | } | |
0a7de745 | 946 | return 0; |
91447636 A |
947 | } |
948 | ||
949 | static int | |
950 | add_entry6(struct ip6_fw_head *chainptr, struct ip6_fw *frwl) | |
951 | { | |
952 | struct ip6_fw *ftmp = 0; | |
953 | struct ip6_fw_chain *fwc = 0, *fcp, *fcpl = 0; | |
954 | u_short nbr = 0; | |
91447636 A |
955 | |
956 | fwc = _MALLOC(sizeof *fwc, M_IP6FW, M_WAITOK); | |
957 | ftmp = _MALLOC(sizeof *ftmp, M_IP6FW, M_WAITOK); | |
958 | if (!fwc || !ftmp) { | |
959 | dprintf(("%s malloc said no\n", err_prefix)); | |
0a7de745 A |
960 | if (fwc) { |
961 | FREE(fwc, M_IP6FW); | |
962 | } | |
963 | if (ftmp) { | |
964 | FREE(ftmp, M_IP6FW); | |
965 | } | |
966 | return ENOSPC; | |
91447636 A |
967 | } |
968 | ||
969 | bcopy(frwl, ftmp, sizeof(struct ip6_fw)); | |
970 | ftmp->fw_in_if.fu_via_if.name[IP6FW_IFNLEN - 1] = '\0'; | |
971 | ftmp->fw_pcnt = 0L; | |
972 | ftmp->fw_bcnt = 0L; | |
973 | fwc->rule = ftmp; | |
0a7de745 | 974 | |
91447636 A |
975 | if (!chainptr->lh_first) { |
976 | LIST_INSERT_HEAD(chainptr, fwc, chain); | |
0a7de745 A |
977 | return 0; |
978 | } else if (ftmp->fw_number == (u_short) - 1) { | |
979 | if (fwc) { | |
980 | FREE(fwc, M_IP6FW); | |
981 | } | |
982 | if (ftmp) { | |
983 | FREE(ftmp, M_IP6FW); | |
984 | } | |
91447636 | 985 | dprintf(("%s bad rule number\n", err_prefix)); |
0a7de745 A |
986 | return EINVAL; |
987 | } | |
91447636 A |
988 | |
989 | /* If entry number is 0, find highest numbered rule and add 100 */ | |
990 | if (ftmp->fw_number == 0) { | |
991 | for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) { | |
0a7de745 | 992 | if (fcp->rule->fw_number != (u_short) - 1) { |
91447636 | 993 | nbr = fcp->rule->fw_number; |
0a7de745 | 994 | } else { |
91447636 | 995 | break; |
0a7de745 | 996 | } |
91447636 | 997 | } |
0a7de745 | 998 | if (nbr < (u_short) - 1 - 100) { |
91447636 | 999 | nbr += 100; |
0a7de745 | 1000 | } |
91447636 A |
1001 | ftmp->fw_number = nbr; |
1002 | } | |
1003 | ||
1004 | /* Got a valid number; now insert it, keeping the list ordered */ | |
1005 | for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) { | |
1006 | if (fcp->rule->fw_number > ftmp->fw_number) { | |
1007 | if (fcpl) { | |
1008 | LIST_INSERT_AFTER(fcpl, fwc, chain); | |
1009 | } else { | |
1010 | LIST_INSERT_HEAD(chainptr, fwc, chain); | |
1011 | } | |
1012 | break; | |
1013 | } else { | |
1014 | fcpl = fcp; | |
1015 | } | |
1016 | } | |
1017 | ||
6d2010ae | 1018 | bcopy(ftmp, frwl, sizeof(struct ip6_fw)); |
0a7de745 | 1019 | return 0; |
91447636 A |
1020 | } |
1021 | ||
1022 | static int | |
1023 | del_entry6(struct ip6_fw_head *chainptr, u_short number) | |
1024 | { | |
1025 | struct ip6_fw_chain *fcp; | |
91447636 A |
1026 | |
1027 | fcp = chainptr->lh_first; | |
0a7de745 | 1028 | if (number != (u_short) - 1) { |
91447636 A |
1029 | for (; fcp; fcp = fcp->chain.le_next) { |
1030 | if (fcp->rule->fw_number == number) { | |
1031 | LIST_REMOVE(fcp, chain); | |
91447636 A |
1032 | FREE(fcp->rule, M_IP6FW); |
1033 | FREE(fcp, M_IP6FW); | |
1034 | return 0; | |
1035 | } | |
1036 | } | |
1037 | } | |
1038 | ||
0a7de745 | 1039 | return EINVAL; |
91447636 A |
1040 | } |
1041 | ||
1042 | static int | |
1043 | zero_entry6(struct ip6_fw *frwl) | |
1044 | { | |
1045 | struct ip6_fw_chain *fcp; | |
91447636 A |
1046 | |
1047 | /* | |
1048 | * It's possible to insert multiple chain entries with the | |
1049 | * same number, so we don't stop after finding the first | |
1050 | * match if zeroing a specific entry. | |
1051 | */ | |
0a7de745 | 1052 | for (fcp = ip6_fw_chain.lh_first; fcp; fcp = fcp->chain.le_next) { |
91447636 A |
1053 | if (!frwl || frwl->fw_number == 0 || frwl->fw_number == fcp->rule->fw_number) { |
1054 | fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0; | |
1055 | fcp->rule->timestamp = 0; | |
1056 | } | |
0a7de745 | 1057 | } |
91447636 A |
1058 | |
1059 | if (fw6_verbose) { | |
0a7de745 | 1060 | if (frwl) { |
91447636 A |
1061 | log(LOG_AUTHPRIV | LOG_NOTICE, |
1062 | "ip6fw: Entry %d cleared.\n", frwl->fw_number); | |
0a7de745 | 1063 | } else { |
91447636 A |
1064 | log(LOG_AUTHPRIV | LOG_NOTICE, |
1065 | "ip6fw: Accounting cleared.\n"); | |
0a7de745 | 1066 | } |
91447636 A |
1067 | } |
1068 | ||
0a7de745 | 1069 | return 0; |
91447636 A |
1070 | } |
1071 | ||
1072 | static struct ip6_fw * | |
1073 | check_ip6fw_struct(struct ip6_fw *frwl) | |
1074 | { | |
1075 | /* Check for invalid flag bits */ | |
1076 | if ((frwl->fw_flg & ~IPV6_FW_F_MASK) != 0) { | |
1077 | dprintf(("%s undefined flag bits set (flags=%x)\n", | |
1078 | err_prefix, frwl->fw_flg)); | |
0a7de745 | 1079 | return NULL; |
91447636 A |
1080 | } |
1081 | /* Must apply to incoming or outgoing (or both) */ | |
1082 | if (!(frwl->fw_flg & (IPV6_FW_F_IN | IPV6_FW_F_OUT))) { | |
1083 | dprintf(("%s neither in nor out\n", err_prefix)); | |
0a7de745 | 1084 | return NULL; |
91447636 A |
1085 | } |
1086 | /* Empty interface name is no good */ | |
1087 | if (((frwl->fw_flg & IPV6_FW_F_IIFNAME) | |
0a7de745 | 1088 | && !*frwl->fw_in_if.fu_via_if.name) |
91447636 | 1089 | || ((frwl->fw_flg & IPV6_FW_F_OIFNAME) |
0a7de745 | 1090 | && !*frwl->fw_out_if.fu_via_if.name)) { |
91447636 | 1091 | dprintf(("%s empty interface name\n", err_prefix)); |
0a7de745 | 1092 | return NULL; |
91447636 A |
1093 | } |
1094 | /* Sanity check interface matching */ | |
1095 | if ((frwl->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) { | |
0a7de745 | 1096 | ; /* allow "via" backwards compatibility */ |
91447636 A |
1097 | } else if ((frwl->fw_flg & IPV6_FW_F_IN) |
1098 | && (frwl->fw_flg & IPV6_FW_F_OIFACE)) { | |
1099 | dprintf(("%s outgoing interface check on incoming\n", | |
1100 | err_prefix)); | |
0a7de745 | 1101 | return NULL; |
91447636 A |
1102 | } |
1103 | /* Sanity check port ranges */ | |
1104 | if ((frwl->fw_flg & IPV6_FW_F_SRNG) && IPV6_FW_GETNSRCP(frwl) < 2) { | |
1105 | dprintf(("%s src range set but n_src_p=%d\n", | |
1106 | err_prefix, IPV6_FW_GETNSRCP(frwl))); | |
0a7de745 | 1107 | return NULL; |
91447636 A |
1108 | } |
1109 | if ((frwl->fw_flg & IPV6_FW_F_DRNG) && IPV6_FW_GETNDSTP(frwl) < 2) { | |
1110 | dprintf(("%s dst range set but n_dst_p=%d\n", | |
1111 | err_prefix, IPV6_FW_GETNDSTP(frwl))); | |
0a7de745 | 1112 | return NULL; |
91447636 A |
1113 | } |
1114 | if (IPV6_FW_GETNSRCP(frwl) + IPV6_FW_GETNDSTP(frwl) > IPV6_FW_MAX_PORTS) { | |
1115 | dprintf(("%s too many ports (%d+%d)\n", | |
1116 | err_prefix, IPV6_FW_GETNSRCP(frwl), IPV6_FW_GETNDSTP(frwl))); | |
0a7de745 | 1117 | return NULL; |
91447636 A |
1118 | } |
1119 | /* | |
1120 | * Protocols other than TCP/UDP don't use port range | |
1121 | */ | |
1122 | if ((frwl->fw_prot != IPPROTO_TCP) && | |
1123 | (frwl->fw_prot != IPPROTO_UDP) && | |
1124 | (IPV6_FW_GETNSRCP(frwl) || IPV6_FW_GETNDSTP(frwl))) { | |
1125 | dprintf(("%s port(s) specified for non TCP/UDP rule\n", | |
1126 | err_prefix)); | |
0a7de745 | 1127 | return NULL; |
91447636 A |
1128 | } |
1129 | ||
1130 | /* | |
1131 | * Rather than modify the entry to make such entries work, | |
1132 | * we reject this rule and require user level utilities | |
1133 | * to enforce whatever policy they deem appropriate. | |
1134 | */ | |
1135 | if ((frwl->fw_src.s6_addr32[0] & (~frwl->fw_smsk.s6_addr32[0])) || | |
0a7de745 A |
1136 | (frwl->fw_src.s6_addr32[1] & (~frwl->fw_smsk.s6_addr32[1])) || |
1137 | (frwl->fw_src.s6_addr32[2] & (~frwl->fw_smsk.s6_addr32[2])) || | |
1138 | (frwl->fw_src.s6_addr32[3] & (~frwl->fw_smsk.s6_addr32[3])) || | |
1139 | (frwl->fw_dst.s6_addr32[0] & (~frwl->fw_dmsk.s6_addr32[0])) || | |
1140 | (frwl->fw_dst.s6_addr32[1] & (~frwl->fw_dmsk.s6_addr32[1])) || | |
1141 | (frwl->fw_dst.s6_addr32[2] & (~frwl->fw_dmsk.s6_addr32[2])) || | |
1142 | (frwl->fw_dst.s6_addr32[3] & (~frwl->fw_dmsk.s6_addr32[3]))) { | |
91447636 | 1143 | dprintf(("%s rule never matches\n", err_prefix)); |
0a7de745 | 1144 | return NULL; |
91447636 A |
1145 | } |
1146 | ||
1147 | if ((frwl->fw_flg & IPV6_FW_F_FRAG) && | |
0a7de745 | 1148 | (frwl->fw_prot == IPPROTO_UDP || frwl->fw_prot == IPPROTO_TCP)) { |
91447636 A |
1149 | if (frwl->fw_nports) { |
1150 | dprintf(("%s cannot mix 'frag' and ports\n", err_prefix)); | |
0a7de745 | 1151 | return NULL; |
91447636 A |
1152 | } |
1153 | if (frwl->fw_prot == IPPROTO_TCP && | |
0a7de745 | 1154 | frwl->fw_tcpf != frwl->fw_tcpnf) { |
91447636 | 1155 | dprintf(("%s cannot mix 'frag' with TCP flags\n", err_prefix)); |
0a7de745 | 1156 | return NULL; |
91447636 A |
1157 | } |
1158 | } | |
1159 | ||
1160 | /* Check command specific stuff */ | |
0a7de745 | 1161 | switch (frwl->fw_flg & IPV6_FW_F_COMMAND) { |
91447636 A |
1162 | case IPV6_FW_F_REJECT: |
1163 | if (frwl->fw_reject_code >= 0x100 | |
1164 | && !(frwl->fw_prot == IPPROTO_TCP | |
0a7de745 | 1165 | && frwl->fw_reject_code == IPV6_FW_REJECT_RST)) { |
91447636 | 1166 | dprintf(("%s unknown reject code\n", err_prefix)); |
0a7de745 | 1167 | return NULL; |
91447636 A |
1168 | } |
1169 | break; | |
0a7de745 | 1170 | case IPV6_FW_F_DIVERT: /* Diverting to port zero is invalid */ |
91447636 A |
1171 | case IPV6_FW_F_TEE: |
1172 | if (frwl->fw_divert_port == 0) { | |
1173 | dprintf(("%s can't divert to port 0\n", err_prefix)); | |
0a7de745 | 1174 | return NULL; |
91447636 A |
1175 | } |
1176 | break; | |
1177 | case IPV6_FW_F_DENY: | |
1178 | case IPV6_FW_F_ACCEPT: | |
1179 | case IPV6_FW_F_COUNT: | |
1180 | case IPV6_FW_F_SKIPTO: | |
1181 | break; | |
1182 | default: | |
1183 | dprintf(("%s invalid command\n", err_prefix)); | |
0a7de745 | 1184 | return NULL; |
91447636 A |
1185 | } |
1186 | ||
1187 | return frwl; | |
1188 | } | |
1189 | ||
2d21ac55 A |
1190 | static void |
1191 | ip6fw_kev_post_msg(u_int32_t event_code) | |
1192 | { | |
0a7de745 | 1193 | struct kev_msg ev_msg; |
2d21ac55 A |
1194 | |
1195 | bzero(&ev_msg, sizeof(struct kev_msg)); | |
0a7de745 | 1196 | |
2d21ac55 A |
1197 | ev_msg.vendor_code = KEV_VENDOR_APPLE; |
1198 | ev_msg.kev_class = KEV_FIREWALL_CLASS; | |
1199 | ev_msg.kev_subclass = KEV_IP6FW_SUBCLASS; | |
1200 | ev_msg.event_code = event_code; | |
1201 | ||
1202 | kev_post_msg(&ev_msg); | |
2d21ac55 A |
1203 | } |
1204 | ||
1205 | ||
b0d623f7 A |
1206 | static void |
1207 | cp_to_user_64( struct ip6_fw_64 *userrule_64, struct ip6_fw *rule) | |
1208 | { | |
1209 | userrule_64->version = rule->version; | |
1210 | userrule_64->context = CAST_USER_ADDR_T(rule->context); | |
1211 | userrule_64->fw_pcnt = rule->fw_pcnt; | |
1212 | userrule_64->fw_bcnt = rule->fw_bcnt; | |
1213 | userrule_64->fw_src = rule->fw_src; | |
1214 | userrule_64->fw_dst = rule->fw_dst; | |
1215 | userrule_64->fw_smsk = rule->fw_smsk; | |
1216 | userrule_64->fw_dmsk = rule->fw_dmsk; | |
1217 | userrule_64->fw_number = rule->fw_number; | |
1218 | userrule_64->fw_flg = rule->fw_flg; | |
1219 | userrule_64->fw_ipflg = rule->fw_ipflg; | |
1220 | bcopy( rule->fw_pts, userrule_64->fw_pts, IPV6_FW_MAX_PORTS); | |
0a7de745 | 1221 | userrule_64->fw_ip6opt = rule->fw_ip6opt; |
b0d623f7 A |
1222 | userrule_64->fw_ip6nopt = rule->fw_ip6nopt; |
1223 | userrule_64->fw_tcpf = rule->fw_tcpf; | |
1224 | userrule_64->fw_tcpnf = rule->fw_tcpnf; | |
1225 | bcopy( rule->fw_icmp6types, userrule_64->fw_icmp6types, sizeof(userrule_64->fw_icmp6types)); | |
1226 | userrule_64->fw_in_if = rule->fw_in_if; | |
1227 | userrule_64->fw_out_if = rule->fw_out_if; | |
1228 | userrule_64->timestamp = rule->timestamp; | |
1229 | userrule_64->fw_un.fu_divert_port = rule->fw_un.fu_divert_port; | |
1230 | userrule_64->fw_prot = rule->fw_prot; | |
1231 | userrule_64->fw_nports = rule->fw_nports; | |
1232 | } | |
1233 | ||
1234 | ||
1235 | static void | |
1236 | cp_from_user_64( struct ip6_fw_64 *userrule_64, struct ip6_fw *rule) | |
1237 | { | |
1238 | rule->version = userrule_64->version; | |
1239 | rule->context = CAST_DOWN(void *, userrule_64->context); | |
1240 | rule->fw_pcnt = userrule_64->fw_pcnt; | |
1241 | rule->fw_bcnt = userrule_64->fw_bcnt; | |
1242 | rule->fw_src = userrule_64->fw_src; | |
1243 | rule->fw_dst = userrule_64->fw_dst; | |
1244 | rule->fw_smsk = userrule_64->fw_smsk; | |
1245 | rule->fw_dmsk = userrule_64->fw_dmsk; | |
1246 | rule->fw_number = userrule_64->fw_number; | |
1247 | rule->fw_flg = userrule_64->fw_flg; | |
1248 | rule->fw_ipflg = userrule_64->fw_ipflg; | |
1249 | bcopy( userrule_64->fw_pts, rule->fw_pts, IPV6_FW_MAX_PORTS); | |
1250 | rule->fw_ip6opt = userrule_64->fw_ip6opt; | |
1251 | rule->fw_ip6nopt = userrule_64->fw_ip6nopt; | |
1252 | rule->fw_tcpf = userrule_64->fw_tcpf; | |
1253 | rule->fw_tcpnf = userrule_64->fw_tcpnf; | |
1254 | bcopy( userrule_64->fw_icmp6types, rule->fw_icmp6types, sizeof(userrule_64->fw_icmp6types)); | |
1255 | rule->fw_in_if = userrule_64->fw_in_if; | |
1256 | rule->fw_out_if = userrule_64->fw_out_if; | |
1257 | rule->timestamp = CAST_DOWN( long, userrule_64->timestamp); | |
1258 | rule->fw_un.fu_divert_port = userrule_64->fw_un.fu_divert_port; | |
1259 | rule->fw_prot = userrule_64->fw_prot; | |
1260 | rule->fw_nports = userrule_64->fw_nports; | |
1261 | } | |
1262 | ||
1263 | ||
1264 | static void | |
1265 | cp_to_user_32( struct ip6_fw_32 *userrule_32, struct ip6_fw *rule) | |
1266 | { | |
1267 | userrule_32->version = rule->version; | |
1268 | userrule_32->context = CAST_DOWN_EXPLICIT( user32_addr_t, rule->context); | |
1269 | userrule_32->fw_pcnt = rule->fw_pcnt; | |
1270 | userrule_32->fw_bcnt = rule->fw_bcnt; | |
1271 | userrule_32->fw_src = rule->fw_src; | |
1272 | userrule_32->fw_dst = rule->fw_dst; | |
1273 | userrule_32->fw_smsk = rule->fw_smsk; | |
1274 | userrule_32->fw_dmsk = rule->fw_dmsk; | |
1275 | userrule_32->fw_number = rule->fw_number; | |
1276 | userrule_32->fw_flg = rule->fw_flg; | |
1277 | userrule_32->fw_ipflg = rule->fw_ipflg; | |
1278 | bcopy( rule->fw_pts, userrule_32->fw_pts, IPV6_FW_MAX_PORTS); | |
0a7de745 | 1279 | userrule_32->fw_ip6opt = rule->fw_ip6opt; |
b0d623f7 A |
1280 | userrule_32->fw_ip6nopt = rule->fw_ip6nopt; |
1281 | userrule_32->fw_tcpf = rule->fw_tcpf; | |
1282 | userrule_32->fw_tcpnf = rule->fw_tcpnf; | |
1283 | bcopy( rule->fw_icmp6types, userrule_32->fw_icmp6types, sizeof(rule->fw_icmp6types)); | |
1284 | userrule_32->fw_in_if = rule->fw_in_if; | |
1285 | userrule_32->fw_out_if = rule->fw_out_if; | |
1286 | userrule_32->timestamp = rule->timestamp; | |
1287 | userrule_32->fw_un.fu_divert_port = rule->fw_un.fu_divert_port; | |
1288 | userrule_32->fw_prot = rule->fw_prot; | |
1289 | userrule_32->fw_nports = rule->fw_nports; | |
1290 | } | |
1291 | ||
1292 | ||
1293 | static void | |
1294 | cp_from_user_32( struct ip6_fw_32 *userrule_32, struct ip6_fw *rule) | |
1295 | { | |
1296 | rule->version = userrule_32->version; | |
1297 | rule->context = CAST_DOWN(void *, userrule_32->context); | |
1298 | rule->fw_pcnt = userrule_32->fw_pcnt; | |
1299 | rule->fw_bcnt = userrule_32->fw_bcnt; | |
1300 | rule->fw_src = userrule_32->fw_src; | |
1301 | rule->fw_dst = userrule_32->fw_dst; | |
1302 | rule->fw_smsk = userrule_32->fw_smsk; | |
1303 | rule->fw_dmsk = userrule_32->fw_dmsk; | |
1304 | rule->fw_number = userrule_32->fw_number; | |
1305 | rule->fw_flg = userrule_32->fw_flg; | |
1306 | rule->fw_ipflg = userrule_32->fw_ipflg; | |
1307 | bcopy( userrule_32->fw_pts, rule->fw_pts, IPV6_FW_MAX_PORTS); | |
1308 | rule->fw_ip6opt = userrule_32->fw_ip6opt; | |
1309 | rule->fw_ip6nopt = userrule_32->fw_ip6nopt; | |
1310 | rule->fw_tcpf = userrule_32->fw_tcpf; | |
1311 | rule->fw_tcpnf = userrule_32->fw_tcpnf; | |
1312 | bcopy( userrule_32->fw_icmp6types, rule->fw_icmp6types, sizeof(userrule_32->fw_icmp6types)); | |
1313 | rule->fw_in_if = userrule_32->fw_in_if; | |
1314 | rule->fw_out_if = userrule_32->fw_out_if; | |
1315 | rule->timestamp = CAST_DOWN(long, userrule_32->timestamp); | |
1316 | rule->fw_un.fu_divert_port = userrule_32->fw_un.fu_divert_port; | |
1317 | rule->fw_prot = userrule_32->fw_prot; | |
1318 | rule->fw_nports = userrule_32->fw_nports; | |
1319 | } | |
1320 | ||
91447636 A |
1321 | static int |
1322 | ip6_fw_ctl(struct sockopt *sopt) | |
1323 | { | |
1324 | int error = 0; | |
91447636 A |
1325 | int valsize; |
1326 | struct ip6_fw rule; | |
0a7de745 A |
1327 | int is64user = 0; |
1328 | size_t userrulesize; | |
91447636 A |
1329 | |
1330 | if (securelevel >= 3 && | |
0a7de745 A |
1331 | (sopt->sopt_dir != SOPT_GET || sopt->sopt_name != IPV6_FW_GET)) { |
1332 | return EPERM; | |
1333 | } | |
91447636 | 1334 | |
0a7de745 | 1335 | if (proc_is64bit(sopt->sopt_p)) { |
b0d623f7 | 1336 | is64user = 1; |
0a7de745 A |
1337 | userrulesize = sizeof(struct ip6_fw_64); |
1338 | } else { | |
1339 | userrulesize = sizeof(struct ip6_fw_32); | |
1340 | } | |
1341 | ||
91447636 A |
1342 | /* We ALWAYS expect the client to pass in a rule structure so that we can |
1343 | * check the version of the API that they are using. In the case of a | |
1344 | * IPV6_FW_GET operation, the first rule of the output buffer passed to us | |
1345 | * must have the version set. */ | |
0a7de745 A |
1346 | if (!sopt->sopt_val || sopt->sopt_valsize < userrulesize) { |
1347 | return EINVAL; | |
1348 | } | |
91447636 A |
1349 | |
1350 | /* save sopt->sopt_valsize */ | |
1351 | valsize = sopt->sopt_valsize; | |
0a7de745 A |
1352 | |
1353 | if (is64user) { | |
b0d623f7 | 1354 | struct ip6_fw_64 userrule_64; |
0a7de745 A |
1355 | |
1356 | if ((error = sooptcopyin(sopt, &userrule_64, userrulesize, userrulesize))) { | |
b0d623f7 | 1357 | return error; |
0a7de745 A |
1358 | } |
1359 | ||
b0d623f7 | 1360 | cp_from_user_64( &userrule_64, &rule ); |
0a7de745 | 1361 | } else { |
b0d623f7 | 1362 | struct ip6_fw_32 userrule_32; |
0a7de745 A |
1363 | |
1364 | if ((error = sooptcopyin(sopt, &userrule_32, userrulesize, userrulesize))) { | |
b0d623f7 | 1365 | return error; |
0a7de745 A |
1366 | } |
1367 | ||
b0d623f7 A |
1368 | cp_from_user_32( &userrule_32, &rule ); |
1369 | } | |
91447636 | 1370 | |
0a7de745 A |
1371 | if (rule.version != IPV6_FW_CURRENT_API_VERSION) { |
1372 | return EINVAL; | |
1373 | } | |
1374 | rule.version = 0xFFFFFFFF; /* version is meaningless once rules "make it in the door". */ | |
1375 | ||
1376 | switch (sopt->sopt_name) { | |
1377 | case IPV6_FW_GET: | |
91447636 | 1378 | { |
0a7de745 A |
1379 | struct ip6_fw_chain *fcp; |
1380 | struct ip6_fw *buf; | |
1381 | size_t size = 0; | |
1382 | size_t rulesize = 0; | |
1383 | ||
1384 | if (is64user) { | |
1385 | rulesize = sizeof(struct ip6_fw_64); | |
1386 | } else { | |
1387 | rulesize = sizeof(struct ip6_fw_32); | |
1388 | } | |
1389 | ||
1390 | LIST_FOREACH(fcp, &ip6_fw_chain, chain) | |
1391 | size += rulesize; | |
1392 | ||
1393 | buf = _MALLOC(size, M_TEMP, M_WAITOK); | |
1394 | if (!buf) { | |
1395 | error = ENOBUFS; | |
1396 | } else { | |
1397 | //struct ip6_fw *bp = buf; | |
1398 | caddr_t bp = (caddr_t)buf; | |
91447636 | 1399 | |
0a7de745 | 1400 | LIST_FOREACH(fcp, &ip6_fw_chain, chain) |
91447636 | 1401 | { |
0a7de745 A |
1402 | //bcopy(fcp->rule, bp, sizeof *bp); |
1403 | if (is64user) { | |
1404 | cp_to_user_64((struct ip6_fw_64*)bp, fcp->rule); | |
1405 | } else { | |
1406 | cp_to_user_32((struct ip6_fw_32*)bp, fcp->rule); | |
91447636 | 1407 | } |
91447636 | 1408 | |
0a7de745 A |
1409 | ((struct ip6_fw*)bp)->version = IPV6_FW_CURRENT_API_VERSION; |
1410 | //bp++; | |
1411 | bp += rulesize; | |
91447636 | 1412 | } |
91447636 A |
1413 | } |
1414 | ||
0a7de745 A |
1415 | if (buf) { |
1416 | sopt->sopt_valsize = valsize; | |
1417 | error = sooptcopyout(sopt, buf, size); | |
1418 | FREE(buf, M_TEMP); | |
1419 | } | |
91447636 | 1420 | |
0a7de745 A |
1421 | break; |
1422 | } | |
91447636 | 1423 | |
0a7de745 A |
1424 | case IPV6_FW_FLUSH: |
1425 | while (ip6_fw_chain.lh_first && | |
1426 | ip6_fw_chain.lh_first->rule->fw_number != (u_short) - 1) { | |
1427 | struct ip6_fw_chain *fcp = ip6_fw_chain.lh_first; | |
1428 | LIST_REMOVE(ip6_fw_chain.lh_first, chain); | |
1429 | FREE(fcp->rule, M_IP6FW); | |
1430 | FREE(fcp, M_IP6FW); | |
1431 | } | |
1432 | ip6fw_kev_post_msg(KEV_IP6FW_FLUSH); | |
1433 | break; | |
2d21ac55 | 1434 | |
0a7de745 A |
1435 | case IPV6_FW_ZERO: |
1436 | error = zero_entry6(&rule); | |
1437 | break; | |
6d2010ae | 1438 | |
0a7de745 A |
1439 | case IPV6_FW_ADD: |
1440 | if (check_ip6fw_struct(&rule)) { | |
1441 | error = add_entry6(&ip6_fw_chain, &rule); | |
91447636 | 1442 | |
0a7de745 A |
1443 | ip6fw_kev_post_msg(KEV_IP6FW_ADD); |
1444 | } else { | |
1445 | error = EINVAL; | |
1446 | } | |
2d21ac55 | 1447 | |
0a7de745 A |
1448 | if (is64user) { |
1449 | struct ip6_fw_64 userrule_64; | |
1450 | cp_to_user_64( &userrule_64, &rule); | |
1451 | error = sooptcopyout(sopt, &userrule_64, userrulesize); | |
1452 | } else { | |
1453 | struct ip6_fw_32 userrule_32; | |
1454 | cp_to_user_32( &userrule_32, &rule); | |
1455 | error = sooptcopyout(sopt, &userrule_32, userrulesize); | |
1456 | } | |
1457 | break; | |
91447636 | 1458 | |
0a7de745 A |
1459 | case IPV6_FW_DEL: |
1460 | if (rule.fw_number == (u_short) - 1) { | |
1461 | dprintf(("%s can't delete rule 65535\n", err_prefix)); | |
91447636 | 1462 | error = EINVAL; |
0a7de745 A |
1463 | } else { |
1464 | error = del_entry6(&ip6_fw_chain, rule.fw_number); | |
1465 | ||
1466 | ip6fw_kev_post_msg(KEV_IP6FW_DEL); | |
1467 | } | |
1468 | break; | |
1469 | ||
1470 | default: | |
1471 | dprintf(("%s invalid option %d\n", err_prefix, sopt->sopt_name)); | |
1472 | error = EINVAL; | |
91447636 A |
1473 | } |
1474 | ||
1475 | return error; | |
1476 | } | |
1477 | ||
1478 | void | |
1479 | ip6_fw_init(void) | |
1480 | { | |
1481 | struct ip6_fw default_rule; | |
1482 | ||
1483 | ip6_fw_chk_ptr = ip6_fw_chk; | |
1484 | ip6_fw_ctl_ptr = ip6_fw_ctl; | |
1485 | LIST_INIT(&ip6_fw_chain); | |
1486 | ||
1487 | bzero(&default_rule, sizeof default_rule); | |
1488 | default_rule.fw_prot = IPPROTO_IPV6; | |
0a7de745 | 1489 | default_rule.fw_number = (u_short) - 1; |
91447636 A |
1490 | #ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT |
1491 | default_rule.fw_flg |= IPV6_FW_F_ACCEPT; | |
1492 | #else | |
1493 | default_rule.fw_flg |= IPV6_FW_F_DENY; | |
1494 | #endif | |
1495 | default_rule.fw_flg |= IPV6_FW_F_IN | IPV6_FW_F_OUT; | |
1496 | if (check_ip6fw_struct(&default_rule) == NULL || | |
0a7de745 | 1497 | add_entry6(&ip6_fw_chain, &default_rule)) { |
2d21ac55 | 1498 | panic("%s", __FUNCTION__); |
0a7de745 | 1499 | } |
91447636 A |
1500 | |
1501 | printf("IPv6 packet filtering initialized, "); | |
1502 | #ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT | |
1503 | printf("default to accept, "); | |
1504 | #endif | |
1505 | #ifndef IPV6FIREWALL_VERBOSE | |
1506 | printf("logging disabled\n"); | |
1507 | #else | |
0a7de745 | 1508 | if (fw6_verbose_limit == 0) { |
91447636 | 1509 | printf("unlimited logging\n"); |
0a7de745 | 1510 | } else { |
91447636 A |
1511 | printf("logging limited to %d packets/entry\n", |
1512 | fw6_verbose_limit); | |
0a7de745 | 1513 | } |
91447636 A |
1514 | #endif |
1515 | } |