]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet6/ip6_fw.c
xnu-6153.11.26.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@
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
127MALLOC_DEFINE(M_IP6FW, "Ip6Fw/Ip6Acct", "Ip6Fw/Ip6Acct chain's");
128
2d21ac55 129static int fw6_debug = 0;
91447636
A
130#ifdef IPV6FIREWALL_VERBOSE
131static int fw6_verbose = 1;
132#else
133static int fw6_verbose = 0;
134#endif
135#ifdef IPV6FIREWALL_VERBOSE_LIMIT
136static int fw6_verbose_limit = IPV6FIREWALL_VERBOSE_LIMIT;
137#else
138static int fw6_verbose_limit = 0;
139#endif
140
0a7de745 141LIST_HEAD(ip6_fw_head, ip6_fw_chain) ip6_fw_chain;
91447636 142
2d21ac55
A
143static void ip6fw_kev_post_msg(u_int32_t );
144
91447636 145#ifdef SYSCTL_NODE
2d21ac55
A
146static int ip6fw_sysctl SYSCTL_HANDLER_ARGS;
147
91447636 148SYSCTL_DECL(_net_inet6_ip6);
0a7de745
A
149SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, fw, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "Firewall");
150SYSCTL_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
153SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED, &fw6_debug, 0, "");
154SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose, CTLFLAG_RW | CTLFLAG_LOCKED, &fw6_verbose, 0, "");
155SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose_limit, CTLFLAG_RW | CTLFLAG_LOCKED, &fw6_verbose_limit, 0, "");
2d21ac55
A
156
157static int
158ip6fw_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
181static int add_entry6 __P((struct ip6_fw_head *chainptr, struct ip6_fw *frwl));
182static int del_entry6 __P((struct ip6_fw_head *chainptr, u_short number));
183static int zero_entry6 __P((struct ip6_fw *frwl));
91447636 184static struct ip6_fw *check_ip6fw_struct __P((struct ip6_fw *m));
0a7de745
A
185static int ip6opts_match __P((struct ip6_hdr **ip6, struct ip6_fw *f,
186 struct mbuf **m,
187 int *off, int *nxt, u_short *offset));
188static int port_match6 __P((u_short *portptr, int nports, u_short port,
189 int range_flag));
190static int tcp6flg_match __P((struct tcphdr *tcp6, struct ip6_fw *f));
191static int icmp6type_match __P((struct icmp6_hdr * icmp, struct ip6_fw * f));
192static void ip6fw_report __P((struct ip6_fw *f, struct ip6_hdr *ip6,
193 struct ifnet *rif, struct ifnet *oif, int off, int nxt));
194
195static int ip6_fw_chk __P((struct ip6_hdr **pip6,
196 struct ifnet *oif, u_int16_t *cookie, struct mbuf **m));
197static int ip6_fw_ctl __P((struct sockopt *));
b0d623f7
A
198static void cp_to_user_64( struct ip6_fw_64 *userrule_64, struct ip6_fw *rule);
199static void cp_from_user_64( struct ip6_fw_64 *userrule_64, struct ip6_fw *rule);
200static void cp_to_user_32( struct ip6_fw_32 *userrule_32, struct ip6_fw *rule);
201static void cp_from_user_32( struct ip6_fw_32 *userrule_32, struct ip6_fw *rule);
91447636
A
202
203static char err_prefix[] = "ip6_fw_ctl:";
91447636
A
204
205/*
206 * Returns 1 if the port is matched by the vector, 0 otherwise
207 */
208static
209__inline int
210port_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
230static int
231tcp6flg_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
259static int
260icmp6type_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
280static int
281is_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
300static int
301ip6opts_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
369opts_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
381static
382__inline int
383iface_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
424static void
425ip6fw_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
574static int
575ip6_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);
757check_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
787bogusfrag:
0a7de745 788 if (fw6_verbose) {
91447636 789 ip6fw_report(NULL, ip6, rif, oif, off, nxt);
0a7de745 790 }
91447636
A
791 goto dropit;
792 }
793
794got_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
938dropit:
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
949static int
950add_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
1022static int
1023del_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
1042static int
1043zero_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
1072static struct ip6_fw *
1073check_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
1190static void
1191ip6fw_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
1206static void
1207cp_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
1235static void
1236cp_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
1264static void
1265cp_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
1293static void
1294cp_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
1321static int
1322ip6_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
1478void
1479ip6_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}