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