2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 /* $FreeBSD: src/sys/netinet6/ip6_fw.c,v 1.2.2.9 2002/04/28 05:40:27 suz Exp $ */
24 /* $KAME: ip6_fw.c,v 1.21 2001/01/24 01:25:32 itojun Exp $ */
27 * Copyright (C) 1998, 1999, 2000 and 2001 WIDE Project.
28 * All rights reserved.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. Neither the name of the project nor the names of its contributors
39 * may be used to endorse or promote products derived from this software
40 * without specific prior written permission.
42 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * Copyright (c) 1993 Daniel Boulet
57 * Copyright (c) 1994 Ugen J.S.Antsilevich
58 * Copyright (c) 1996 Alex Nash
60 * Redistribution and use in source forms, with and without modification,
61 * are permitted provided that this entire comment appears intact.
63 * Redistribution in binary form may occur without any restrictions.
64 * Obviously, it would be nice if you gave credit where credit is due
65 * but requiring it would be too onerous.
67 * This software is provided ``AS IS'' without any warranties of any kind.
71 * Implement IPv6 packet firewall
76 #error "NOT SUPPORTED IPV6 DIVERT"
78 #ifdef IP6FW_DIVERT_RESTART
79 #error "NOT SUPPORTED IPV6 DIVERT"
83 #include <machine/spl.h>
85 #include <sys/param.h>
86 #include <sys/systm.h>
87 #include <sys/malloc.h>
89 #include <sys/queue.h>
90 #include <sys/kernel.h>
91 #include <sys/socket.h>
92 #include <sys/socketvar.h>
93 #include <sys/syslog.h>
97 #include <net/route.h>
98 #include <netinet/in_systm.h>
99 #include <netinet/in.h>
100 #include <netinet/ip.h>
102 #include <netinet/ip6.h>
103 #include <netinet6/ip6_var.h>
104 #include <netinet6/in6_var.h>
105 #include <netinet/icmp6.h>
107 #include <netinet/in_pcb.h>
109 #include <netinet6/ip6_fw.h>
110 #include <netinet/ip_var.h>
111 #include <netinet/tcp.h>
112 #include <netinet/tcp_seq.h>
113 #include <netinet/tcp_timer.h>
114 #include <netinet/tcp_var.h>
115 #include <netinet/udp.h>
117 #include <sys/sysctl.h>
119 #include <net/net_osdep.h>
121 MALLOC_DEFINE(M_IP6FW
, "Ip6Fw/Ip6Acct", "Ip6Fw/Ip6Acct chain's");
123 static int fw6_debug
= 1;
124 #ifdef IPV6FIREWALL_VERBOSE
125 static int fw6_verbose
= 1;
127 static int fw6_verbose
= 0;
129 #ifdef IPV6FIREWALL_VERBOSE_LIMIT
130 static int fw6_verbose_limit
= IPV6FIREWALL_VERBOSE_LIMIT
;
132 static int fw6_verbose_limit
= 0;
135 LIST_HEAD (ip6_fw_head
, ip6_fw_chain
) ip6_fw_chain
;
138 SYSCTL_DECL(_net_inet6_ip6
);
139 SYSCTL_NODE(_net_inet6_ip6
, OID_AUTO
, fw
, CTLFLAG_RW
, 0, "Firewall");
140 SYSCTL_INT(_net_inet6_ip6_fw
, OID_AUTO
, enable
, CTLFLAG_RW
,
141 &ip6_fw_enable
, 0, "Enable ip6fw");
142 SYSCTL_INT(_net_inet6_ip6_fw
, OID_AUTO
, debug
, CTLFLAG_RW
, &fw6_debug
, 0, "");
143 SYSCTL_INT(_net_inet6_ip6_fw
, OID_AUTO
, verbose
, CTLFLAG_RW
, &fw6_verbose
, 0, "");
144 SYSCTL_INT(_net_inet6_ip6_fw
, OID_AUTO
, verbose_limit
, CTLFLAG_RW
, &fw6_verbose_limit
, 0, "");
147 #define dprintf(a) do { \
151 #define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
153 static int add_entry6
__P((struct ip6_fw_head
*chainptr
, struct ip6_fw
*frwl
));
154 static int del_entry6
__P((struct ip6_fw_head
*chainptr
, u_short number
));
155 static int zero_entry6
__P((struct ip6_fw
*frwl
));
156 static struct ip6_fw
*check_ip6fw_struct
__P((struct ip6_fw
*m
));
157 static int ip6opts_match
__P((struct ip6_hdr
**ip6
, struct ip6_fw
*f
,
159 int *off
, int *nxt
, u_short
*offset
));
160 static int port_match6
__P((u_short
*portptr
, int nports
, u_short port
,
162 static int tcp6flg_match
__P((struct tcphdr
*tcp6
, struct ip6_fw
*f
));
163 static int icmp6type_match
__P((struct icmp6_hdr
* icmp
, struct ip6_fw
* f
));
164 static void ip6fw_report
__P((struct ip6_fw
*f
, struct ip6_hdr
*ip6
,
165 struct ifnet
*rif
, struct ifnet
*oif
, int off
, int nxt
));
167 static int ip6_fw_chk
__P((struct ip6_hdr
**pip6
,
168 struct ifnet
*oif
, u_int16_t
*cookie
, struct mbuf
**m
));
169 static int ip6_fw_ctl
__P((struct sockopt
*));
171 static char err_prefix
[] = "ip6_fw_ctl:";
172 extern lck_mtx_t
*ip6_mutex
;
175 * Returns 1 if the port is matched by the vector, 0 otherwise
179 port_match6(u_short
*portptr
, int nports
, u_short port
, int range_flag
)
184 if (portptr
[0] <= port
&& port
<= portptr
[1]) {
190 while (nports
-- > 0) {
191 if (*portptr
++ == port
) {
199 tcp6flg_match(struct tcphdr
*tcp6
, struct ip6_fw
*f
)
201 u_char flg_set
, flg_clr
;
204 * If an established connection is required, reject packets that
205 * have only SYN of RST|ACK|SYN set. Otherwise, fall through to
206 * other flag requirements.
208 if ((f
->fw_ipflg
& IPV6_FW_IF_TCPEST
) &&
209 ((tcp6
->th_flags
& (IPV6_FW_TCPF_RST
| IPV6_FW_TCPF_ACK
|
210 IPV6_FW_TCPF_SYN
)) == IPV6_FW_TCPF_SYN
))
213 flg_set
= tcp6
->th_flags
& f
->fw_tcpf
;
214 flg_clr
= tcp6
->th_flags
& f
->fw_tcpnf
;
216 if (flg_set
!= f
->fw_tcpf
)
225 icmp6type_match(struct icmp6_hdr
*icmp6
, struct ip6_fw
*f
)
229 if (!(f
->fw_flg
& IPV6_FW_F_ICMPBIT
))
232 type
= icmp6
->icmp6_type
;
234 /* check for matching type in the bitmap */
235 if (type
< IPV6_FW_ICMPTYPES_DIM
* sizeof(unsigned) * 8 &&
236 (f
->fw_icmp6types
[type
/ (sizeof(unsigned) * 8)] &
237 (1U << (type
% (8 * sizeof(unsigned))))))
240 return(0); /* no match */
244 is_icmp6_query(struct ip6_hdr
*ip6
, int off
)
246 const struct icmp6_hdr
*icmp6
;
249 icmp6
= (struct icmp6_hdr
*)((caddr_t
)ip6
+ off
);
250 icmp6_type
= icmp6
->icmp6_type
;
252 if (icmp6_type
== ICMP6_ECHO_REQUEST
||
253 icmp6_type
== ICMP6_MEMBERSHIP_QUERY
||
254 icmp6_type
== ICMP6_WRUREQUEST
||
255 icmp6_type
== ICMP6_FQDN_QUERY
||
256 icmp6_type
== ICMP6_NI_QUERY
)
263 ip6opts_match(struct ip6_hdr
**pip6
, struct ip6_fw
*f
, struct mbuf
**m
,
264 int *off
, int *nxt
, u_short
*offset
)
267 struct ip6_hdr
*ip6
= *pip6
;
268 struct ip6_ext
*ip6e
;
269 u_char opts
, nopts
, nopts_sve
;
272 nopts
= nopts_sve
= f
->fw_ip6nopt
;
275 *off
= sizeof(struct ip6_hdr
);
276 len
= ntohs(ip6
->ip6_plen
) + sizeof(struct ip6_hdr
);
278 ip6e
= (struct ip6_ext
*)((caddr_t
) ip6
+ *off
);
279 if ((*m
)->m_len
< *off
+ sizeof(*ip6e
))
280 goto opts_check
; /* XXX */
283 case IPPROTO_FRAGMENT
:
284 if ((*m
)->m_len
>= *off
+ sizeof(struct ip6_frag
)) {
285 struct ip6_frag
*ip6f
;
287 ip6f
= (struct ip6_frag
*) ((caddr_t
)ip6
+ *off
);
288 *offset
= ip6f
->ip6f_offlg
& IP6F_OFF_MASK
;
290 opts
&= ~IPV6_FW_IP6OPT_FRAG
;
291 nopts
&= ~IPV6_FW_IP6OPT_FRAG
;
292 *off
+= sizeof(struct ip6_frag
);
295 opts
&= ~IPV6_FW_IP6OPT_AH
;
296 nopts
&= ~IPV6_FW_IP6OPT_AH
;
297 *off
+= (ip6e
->ip6e_len
+ 2) << 2;
301 case IPPROTO_HOPOPTS
:
302 opts
&= ~IPV6_FW_IP6OPT_HOPOPT
;
303 nopts
&= ~IPV6_FW_IP6OPT_HOPOPT
;
305 case IPPROTO_ROUTING
:
306 opts
&= ~IPV6_FW_IP6OPT_ROUTE
;
307 nopts
&= ~IPV6_FW_IP6OPT_ROUTE
;
310 opts
&= ~IPV6_FW_IP6OPT_ESP
;
311 nopts
&= ~IPV6_FW_IP6OPT_ESP
;
314 opts
&= ~IPV6_FW_IP6OPT_NONXT
;
315 nopts
&= ~IPV6_FW_IP6OPT_NONXT
;
318 case IPPROTO_DSTOPTS
:
319 opts
&= ~IPV6_FW_IP6OPT_OPTS
;
320 nopts
&= ~IPV6_FW_IP6OPT_OPTS
;
326 *off
+= (ip6e
->ip6e_len
+ 1) << 3;
329 *nxt
= ip6e
->ip6e_nxt
;
333 if (f
->fw_ip6opt
== f
->fw_ip6nopt
) /* XXX */
336 if (opts
== 0 && nopts
== nopts_sve
)
344 iface_match(struct ifnet
*ifp
, union ip6_fw_if
*ifu
, int byname
)
346 /* Check by name or by IP address */
348 /* Check unit number (-1 is wildcard) */
349 if (ifu
->fu_via_if
.unit
!= -1
350 && ifp
->if_unit
!= ifu
->fu_via_if
.unit
)
353 if (strncmp(ifp
->if_name
, ifu
->fu_via_if
.name
, IP6FW_IFNLEN
))
356 } else if (!IN6_IS_ADDR_UNSPECIFIED(&ifu
->fu_via_ip6
)) { /* Zero == wildcard */
359 ifnet_lock_shared(ifp
);
360 for (ia
= ifp
->if_addrlist
.tqh_first
; ia
; ia
= ia
->ifa_list
.tqe_next
)
363 if (ia
->ifa_addr
== NULL
)
365 if (ia
->ifa_addr
->sa_family
!= AF_INET6
)
367 if (!IN6_ARE_ADDR_EQUAL(&ifu
->fu_via_ip6
,
368 &(((struct sockaddr_in6
*)
369 (ia
->ifa_addr
))->sin6_addr
)))
371 ifnet_lock_done(ifp
);
374 ifnet_lock_done(ifp
);
381 ip6fw_report(struct ip6_fw
*f
, struct ip6_hdr
*ip6
,
382 struct ifnet
*rif
, struct ifnet
*oif
, int off
, int nxt
)
385 struct tcphdr
*const tcp6
= (struct tcphdr
*) ((caddr_t
) ip6
+ off
);
386 struct udphdr
*const udp
= (struct udphdr
*) ((caddr_t
) ip6
+ off
);
387 struct icmp6_hdr
*const icmp6
= (struct icmp6_hdr
*) ((caddr_t
) ip6
+ off
);
390 char action2
[32], proto
[102], name
[18];
393 count
= f
? f
->fw_pcnt
: ++counter
;
394 if (fw6_verbose_limit
!= 0 && count
> fw6_verbose_limit
)
397 /* Print command name */
398 snprintf(SNPARGS(name
, 0), "ip6fw: %d", f
? f
->fw_number
: -1);
404 switch (f
->fw_flg
& IPV6_FW_F_COMMAND
) {
408 case IPV6_FW_F_REJECT
:
409 if (f
->fw_reject_code
== IPV6_FW_REJECT_RST
)
414 case IPV6_FW_F_ACCEPT
:
417 case IPV6_FW_F_COUNT
:
420 case IPV6_FW_F_DIVERT
:
421 snprintf(SNPARGS(action2
, 0), "Divert %d",
425 snprintf(SNPARGS(action2
, 0), "Tee %d",
428 case IPV6_FW_F_SKIPTO
:
429 snprintf(SNPARGS(action2
, 0), "SkipTo %d",
440 len
= snprintf(SNPARGS(proto
, 0), "TCP [%s]",
441 ip6_sprintf(&ip6
->ip6_src
));
443 len
+= snprintf(SNPARGS(proto
, len
), ":%d ",
444 ntohs(tcp6
->th_sport
));
446 len
+= snprintf(SNPARGS(proto
, len
), " ");
447 len
+= snprintf(SNPARGS(proto
, len
), "[%s]",
448 ip6_sprintf(&ip6
->ip6_dst
));
450 snprintf(SNPARGS(proto
, len
), ":%d",
451 ntohs(tcp6
->th_dport
));
454 len
= snprintf(SNPARGS(proto
, 0), "UDP [%s]",
455 ip6_sprintf(&ip6
->ip6_src
));
457 len
+= snprintf(SNPARGS(proto
, len
), ":%d ",
458 ntohs(udp
->uh_sport
));
460 len
+= snprintf(SNPARGS(proto
, len
), " ");
461 len
+= snprintf(SNPARGS(proto
, len
), "[%s]",
462 ip6_sprintf(&ip6
->ip6_dst
));
464 snprintf(SNPARGS(proto
, len
), ":%d",
465 ntohs(udp
->uh_dport
));
469 len
= snprintf(SNPARGS(proto
, 0), "IPV6-ICMP:%u.%u ",
470 icmp6
->icmp6_type
, icmp6
->icmp6_code
);
472 len
= snprintf(SNPARGS(proto
, 0), "IPV6-ICMP ");
473 len
+= snprintf(SNPARGS(proto
, len
), "[%s]",
474 ip6_sprintf(&ip6
->ip6_src
));
475 snprintf(SNPARGS(proto
, len
), " [%s]",
476 ip6_sprintf(&ip6
->ip6_dst
));
479 len
= snprintf(SNPARGS(proto
, 0), "P:%d [%s]", nxt
,
480 ip6_sprintf(&ip6
->ip6_src
));
481 snprintf(SNPARGS(proto
, len
), " [%s]",
482 ip6_sprintf(&ip6
->ip6_dst
));
487 log(LOG_AUTHPRIV
| LOG_INFO
, "%s %s %s out via %s\n",
488 name
, action
, proto
, if_name(oif
));
490 log(LOG_AUTHPRIV
| LOG_INFO
, "%s %s %s in via %s\n",
491 name
, action
, proto
, if_name(rif
));
493 log(LOG_AUTHPRIV
| LOG_INFO
, "%s %s %s",
494 name
, action
, proto
);
495 if (fw6_verbose_limit
!= 0 && count
== fw6_verbose_limit
)
496 log(LOG_AUTHPRIV
| LOG_INFO
, "ip6fw: limit reached on entry %d\n",
497 f
? f
->fw_number
: -1);
503 * ip Pointer to packet header (struct ip6_hdr *)
504 * hlen Packet header length
505 * oif Outgoing interface, or NULL if packet is incoming
506 * #ifndef IP6FW_DIVERT_RESTART
507 * *cookie Ignore all divert/tee rules to this port (if non-zero)
509 * *cookie Skip up to the first rule past this rule number;
511 * *m The packet; we set to NULL when/if we nuke it.
515 * 0 The packet is to be accepted and routed normally OR
516 * the packet was denied/rejected and has been dropped;
517 * in the latter case, *m is equal to NULL upon return.
518 * port Divert the packet to port.
522 ip6_fw_chk(struct ip6_hdr
**pip6
,
523 struct ifnet
*oif
, u_int16_t
*cookie
, struct mbuf
**m
)
525 struct ip6_fw_chain
*chain
;
526 struct ip6_fw
*rule
= NULL
;
527 struct ip6_hdr
*ip6
= *pip6
;
528 struct ifnet
*const rif
= (*m
)->m_pkthdr
.rcvif
;
530 int off
= sizeof(struct ip6_hdr
), nxt
= ip6
->ip6_nxt
;
531 u_short src_port
, dst_port
;
532 #ifdef IP6FW_DIVERT_RESTART
533 u_int16_t skipto
= *cookie
;
535 u_int16_t ignport
= ntohs(*cookie
);
537 struct timeval timenow
;
539 getmicrotime(&timenow
);
543 * Go down the chain, looking for enlightment
544 * #ifdef IP6FW_DIVERT_RESTART
545 * If we've been asked to start at a given rule immediatly, do so.
548 chain
= LIST_FIRST(&ip6_fw_chain
);
549 #ifdef IP6FW_DIVERT_RESTART
553 while (chain
&& (chain
->rule
->fw_number
<= skipto
)) {
554 chain
= LIST_NEXT(chain
, chain
);
556 if (! chain
) goto dropit
;
558 #endif /* IP6FW_DIVERT_RESTART */
559 for (; chain
; chain
= LIST_NEXT(chain
, chain
)) {
560 struct ip6_fw
*const f
= chain
->rule
;
563 /* Check direction outbound */
564 if (!(f
->fw_flg
& IPV6_FW_F_OUT
))
567 /* Check direction inbound */
568 if (!(f
->fw_flg
& IPV6_FW_F_IN
))
572 #define IN6_ARE_ADDR_MASKEQUAL(x,y,z) (\
573 (((x)->s6_addr32[0] & (y)->s6_addr32[0]) == (z)->s6_addr32[0]) && \
574 (((x)->s6_addr32[1] & (y)->s6_addr32[1]) == (z)->s6_addr32[1]) && \
575 (((x)->s6_addr32[2] & (y)->s6_addr32[2]) == (z)->s6_addr32[2]) && \
576 (((x)->s6_addr32[3] & (y)->s6_addr32[3]) == (z)->s6_addr32[3]))
578 /* If src-addr doesn't match, not this rule. */
579 if (((f
->fw_flg
& IPV6_FW_F_INVSRC
) != 0) ^
580 (!IN6_ARE_ADDR_MASKEQUAL(&ip6
->ip6_src
,&f
->fw_smsk
,&f
->fw_src
)))
583 /* If dest-addr doesn't match, not this rule. */
584 if (((f
->fw_flg
& IPV6_FW_F_INVDST
) != 0) ^
585 (!IN6_ARE_ADDR_MASKEQUAL(&ip6
->ip6_dst
,&f
->fw_dmsk
,&f
->fw_dst
)))
588 #undef IN6_ARE_ADDR_MASKEQUAL
589 /* Interface check */
590 if ((f
->fw_flg
& IF6_FW_F_VIAHACK
) == IF6_FW_F_VIAHACK
) {
591 struct ifnet
*const iface
= oif
? oif
: rif
;
593 /* Backwards compatibility hack for "via" */
594 if (!iface
|| !iface_match(iface
,
595 &f
->fw_in_if
, f
->fw_flg
& IPV6_FW_F_OIFNAME
))
598 /* Check receive interface */
599 if ((f
->fw_flg
& IPV6_FW_F_IIFACE
)
600 && (!rif
|| !iface_match(rif
,
601 &f
->fw_in_if
, f
->fw_flg
& IPV6_FW_F_IIFNAME
)))
603 /* Check outgoing interface */
604 if ((f
->fw_flg
& IPV6_FW_F_OIFACE
)
605 && (!oif
|| !iface_match(oif
,
606 &f
->fw_out_if
, f
->fw_flg
& IPV6_FW_F_OIFNAME
)))
610 /* Check IP options */
611 if (!ip6opts_match(&ip6
, f
, m
, &off
, &nxt
, &offset
))
615 if ((f
->fw_flg
& IPV6_FW_F_FRAG
) && !offset
)
618 /* Check protocol; if wildcard, match */
619 if (f
->fw_prot
== IPPROTO_IPV6
)
622 /* If different, don't match */
623 if (nxt
!= f
->fw_prot
)
626 #define PULLUP_TO(len) do { \
627 if ((*m)->m_len < (len) \
628 && (*m = m_pullup(*m, (len))) == 0) { \
631 *pip6 = ip6 = mtod(*m, struct ip6_hdr *); \
634 /* Protocol specific checks */
640 if (offset
== 1) { /* cf. RFC 1858 */
641 PULLUP_TO(off
+ 4); /* XXX ? */
646 * TCP flags and ports aren't available in this
647 * packet -- if this rule specified either one,
648 * we consider the rule a non-match.
650 if (f
->fw_nports
!= 0 ||
651 f
->fw_tcpf
!= f
->fw_tcpnf
)
657 tcp6
= (struct tcphdr
*) ((caddr_t
)ip6
+ off
);
658 if (((f
->fw_tcpf
!= f
->fw_tcpnf
) ||
659 (f
->fw_ipflg
& IPV6_FW_IF_TCPEST
)) &&
660 !tcp6flg_match(tcp6
, f
))
662 src_port
= ntohs(tcp6
->th_sport
);
663 dst_port
= ntohs(tcp6
->th_dport
);
673 * Port specification is unavailable -- if this
674 * rule specifies a port, we consider the rule
677 if (f
->fw_nports
!= 0)
683 udp
= (struct udphdr
*) ((caddr_t
)ip6
+ off
);
684 src_port
= ntohs(udp
->uh_sport
);
685 dst_port
= ntohs(udp
->uh_dport
);
687 if (!port_match6(&f
->fw_pts
[0],
688 IPV6_FW_GETNSRCP(f
), src_port
,
689 f
->fw_flg
& IPV6_FW_F_SRNG
))
691 if (!port_match6(&f
->fw_pts
[IPV6_FW_GETNSRCP(f
)],
692 IPV6_FW_GETNDSTP(f
), dst_port
,
693 f
->fw_flg
& IPV6_FW_F_DRNG
))
700 struct icmp6_hdr
*icmp
;
702 if (offset
!= 0) /* Type isn't valid */
705 icmp
= (struct icmp6_hdr
*) ((caddr_t
)ip6
+ off
);
706 if (!icmp6type_match(icmp
, f
))
714 ip6fw_report(NULL
, ip6
, rif
, oif
, off
, nxt
);
719 #ifndef IP6FW_DIVERT_RESTART
720 /* Ignore divert/tee rule if socket port is "ignport" */
721 switch (f
->fw_flg
& IPV6_FW_F_COMMAND
) {
722 case IPV6_FW_F_DIVERT
:
724 if (f
->fw_divert_port
== ignport
)
725 continue; /* ignore this rule */
729 #endif /* IP6FW_DIVERT_RESTART */
730 /* Update statistics */
732 f
->fw_bcnt
+= ntohs(ip6
->ip6_plen
);
733 f
->timestamp
= timenow
.tv_sec
;
735 /* Log to console if desired */
736 if ((f
->fw_flg
& IPV6_FW_F_PRN
) && fw6_verbose
)
737 ip6fw_report(f
, ip6
, rif
, oif
, off
, nxt
);
739 /* Take appropriate action */
740 switch (f
->fw_flg
& IPV6_FW_F_COMMAND
) {
741 case IPV6_FW_F_ACCEPT
:
743 case IPV6_FW_F_COUNT
:
745 case IPV6_FW_F_DIVERT
:
746 #ifdef IP6FW_DIVERT_RESTART
747 *cookie
= f
->fw_number
;
749 *cookie
= htons(f
->fw_divert_port
);
750 #endif /* IP6FW_DIVERT_RESTART */
751 return(f
->fw_divert_port
);
754 * XXX someday tee packet here, but beware that you
755 * can't use m_copym() or m_copypacket() because
756 * the divert input routine modifies the mbuf
757 * (and these routines only increment reference
758 * counts in the case of mbuf clusters), so need
759 * to write custom routine.
762 case IPV6_FW_F_SKIPTO
:
764 while (chain
->chain
.le_next
765 && chain
->chain
.le_next
->rule
->fw_number
768 while (chain
->chain
.le_next
->rule
->fw_number
771 chain
= chain
->chain
.le_next
;
775 /* Deny/reject this packet using this rule */
781 /* Rule 65535 should always be there and should always match */
783 panic("ip6_fw: chain");
787 * At this point, we're going to drop the packet.
788 * Send a reject notice if all of the following are true:
790 * - The packet matched a reject rule
791 * - The packet is not an ICMP packet, or is an ICMP query packet
792 * - The packet is not a multicast or broadcast packet
794 if ((rule
->fw_flg
& IPV6_FW_F_COMMAND
) == IPV6_FW_F_REJECT
795 && (nxt
!= IPPROTO_ICMPV6
|| is_icmp6_query(ip6
, off
))
796 && !((*m
)->m_flags
& (M_BCAST
|M_MCAST
))
797 && !IN6_IS_ADDR_MULTICAST(&ip6
->ip6_dst
)) {
798 switch (rule
->fw_reject_code
) {
799 case IPV6_FW_REJECT_RST
:
801 struct tcphdr
*const tcp
=
802 (struct tcphdr
*) ((caddr_t
)ip6
+ off
);
810 if (offset
!= 0 || (tcp
->th_flags
& TH_RST
))
815 ti
.th
.th_seq
= ntohl(ti
.th
.th_seq
);
816 ti
.th
.th_ack
= ntohl(ti
.th
.th_ack
);
817 ti
.ip6
.ip6_nxt
= IPPROTO_TCP
;
818 if (ti
.th
.th_flags
& TH_ACK
) {
824 if (((*m
)->m_flags
& M_PKTHDR
) != 0) {
825 ack
+= (*m
)->m_pkthdr
.len
- off
826 - (ti
.th
.th_off
<< 2);
827 } else if (ip6
->ip6_plen
) {
828 ack
+= ntohs(ip6
->ip6_plen
) + sizeof(*ip6
)
829 - off
- (ti
.th
.th_off
<< 2);
836 flags
= TH_RST
|TH_ACK
;
838 bcopy(&ti
, ip6
, sizeof(ti
));
839 tcp_respond(NULL
, ip6
, (struct tcphdr
*)(ip6
+ 1),
840 *m
, ack
, seq
, flags
);
844 default: /* Send an ICMP unreachable using code */
846 (*m
)->m_pkthdr
.rcvif
= oif
;
847 lck_mtx_assert(ip6_mutex
, LCK_MTX_ASSERT_OWNED
);
848 lck_mtx_unlock(ip6_mutex
);
849 icmp6_error(*m
, ICMP6_DST_UNREACH
,
850 rule
->fw_reject_code
, 0);
851 lck_mtx_lock(ip6_mutex
);
859 * Finally, drop the packet.
869 add_entry6(struct ip6_fw_head
*chainptr
, struct ip6_fw
*frwl
)
871 struct ip6_fw
*ftmp
= 0;
872 struct ip6_fw_chain
*fwc
= 0, *fcp
, *fcpl
= 0;
876 fwc
= _MALLOC(sizeof *fwc
, M_IP6FW
, M_WAITOK
);
877 ftmp
= _MALLOC(sizeof *ftmp
, M_IP6FW
, M_WAITOK
);
879 dprintf(("%s malloc said no\n", err_prefix
));
880 if (fwc
) FREE(fwc
, M_IP6FW
);
881 if (ftmp
) FREE(ftmp
, M_IP6FW
);
885 bcopy(frwl
, ftmp
, sizeof(struct ip6_fw
));
886 ftmp
->fw_in_if
.fu_via_if
.name
[IP6FW_IFNLEN
- 1] = '\0';
893 if (!chainptr
->lh_first
) {
894 LIST_INSERT_HEAD(chainptr
, fwc
, chain
);
897 } else if (ftmp
->fw_number
== (u_short
)-1) {
898 if (fwc
) FREE(fwc
, M_IP6FW
);
899 if (ftmp
) FREE(ftmp
, M_IP6FW
);
901 dprintf(("%s bad rule number\n", err_prefix
));
905 /* If entry number is 0, find highest numbered rule and add 100 */
906 if (ftmp
->fw_number
== 0) {
907 for (fcp
= chainptr
->lh_first
; fcp
; fcp
= fcp
->chain
.le_next
) {
908 if (fcp
->rule
->fw_number
!= (u_short
)-1)
909 nbr
= fcp
->rule
->fw_number
;
913 if (nbr
< (u_short
)-1 - 100)
915 ftmp
->fw_number
= nbr
;
918 /* Got a valid number; now insert it, keeping the list ordered */
919 for (fcp
= chainptr
->lh_first
; fcp
; fcp
= fcp
->chain
.le_next
) {
920 if (fcp
->rule
->fw_number
> ftmp
->fw_number
) {
922 LIST_INSERT_AFTER(fcpl
, fwc
, chain
);
924 LIST_INSERT_HEAD(chainptr
, fwc
, chain
);
937 del_entry6(struct ip6_fw_head
*chainptr
, u_short number
)
939 struct ip6_fw_chain
*fcp
;
944 fcp
= chainptr
->lh_first
;
945 if (number
!= (u_short
)-1) {
946 for (; fcp
; fcp
= fcp
->chain
.le_next
) {
947 if (fcp
->rule
->fw_number
== number
) {
948 LIST_REMOVE(fcp
, chain
);
950 FREE(fcp
->rule
, M_IP6FW
);
962 zero_entry6(struct ip6_fw
*frwl
)
964 struct ip6_fw_chain
*fcp
;
968 * It's possible to insert multiple chain entries with the
969 * same number, so we don't stop after finding the first
970 * match if zeroing a specific entry.
973 for (fcp
= ip6_fw_chain
.lh_first
; fcp
; fcp
= fcp
->chain
.le_next
)
974 if (!frwl
|| frwl
->fw_number
== 0 || frwl
->fw_number
== fcp
->rule
->fw_number
) {
975 fcp
->rule
->fw_bcnt
= fcp
->rule
->fw_pcnt
= 0;
976 fcp
->rule
->timestamp
= 0;
982 log(LOG_AUTHPRIV
| LOG_NOTICE
,
983 "ip6fw: Entry %d cleared.\n", frwl
->fw_number
);
985 log(LOG_AUTHPRIV
| LOG_NOTICE
,
986 "ip6fw: Accounting cleared.\n");
992 static struct ip6_fw
*
993 check_ip6fw_struct(struct ip6_fw
*frwl
)
995 /* Check for invalid flag bits */
996 if ((frwl
->fw_flg
& ~IPV6_FW_F_MASK
) != 0) {
997 dprintf(("%s undefined flag bits set (flags=%x)\n",
998 err_prefix
, frwl
->fw_flg
));
1001 /* Must apply to incoming or outgoing (or both) */
1002 if (!(frwl
->fw_flg
& (IPV6_FW_F_IN
| IPV6_FW_F_OUT
))) {
1003 dprintf(("%s neither in nor out\n", err_prefix
));
1006 /* Empty interface name is no good */
1007 if (((frwl
->fw_flg
& IPV6_FW_F_IIFNAME
)
1008 && !*frwl
->fw_in_if
.fu_via_if
.name
)
1009 || ((frwl
->fw_flg
& IPV6_FW_F_OIFNAME
)
1010 && !*frwl
->fw_out_if
.fu_via_if
.name
)) {
1011 dprintf(("%s empty interface name\n", err_prefix
));
1014 /* Sanity check interface matching */
1015 if ((frwl
->fw_flg
& IF6_FW_F_VIAHACK
) == IF6_FW_F_VIAHACK
) {
1016 ; /* allow "via" backwards compatibility */
1017 } else if ((frwl
->fw_flg
& IPV6_FW_F_IN
)
1018 && (frwl
->fw_flg
& IPV6_FW_F_OIFACE
)) {
1019 dprintf(("%s outgoing interface check on incoming\n",
1023 /* Sanity check port ranges */
1024 if ((frwl
->fw_flg
& IPV6_FW_F_SRNG
) && IPV6_FW_GETNSRCP(frwl
) < 2) {
1025 dprintf(("%s src range set but n_src_p=%d\n",
1026 err_prefix
, IPV6_FW_GETNSRCP(frwl
)));
1029 if ((frwl
->fw_flg
& IPV6_FW_F_DRNG
) && IPV6_FW_GETNDSTP(frwl
) < 2) {
1030 dprintf(("%s dst range set but n_dst_p=%d\n",
1031 err_prefix
, IPV6_FW_GETNDSTP(frwl
)));
1034 if (IPV6_FW_GETNSRCP(frwl
) + IPV6_FW_GETNDSTP(frwl
) > IPV6_FW_MAX_PORTS
) {
1035 dprintf(("%s too many ports (%d+%d)\n",
1036 err_prefix
, IPV6_FW_GETNSRCP(frwl
), IPV6_FW_GETNDSTP(frwl
)));
1040 * Protocols other than TCP/UDP don't use port range
1042 if ((frwl
->fw_prot
!= IPPROTO_TCP
) &&
1043 (frwl
->fw_prot
!= IPPROTO_UDP
) &&
1044 (IPV6_FW_GETNSRCP(frwl
) || IPV6_FW_GETNDSTP(frwl
))) {
1045 dprintf(("%s port(s) specified for non TCP/UDP rule\n",
1051 * Rather than modify the entry to make such entries work,
1052 * we reject this rule and require user level utilities
1053 * to enforce whatever policy they deem appropriate.
1055 if ((frwl
->fw_src
.s6_addr32
[0] & (~frwl
->fw_smsk
.s6_addr32
[0])) ||
1056 (frwl
->fw_src
.s6_addr32
[1] & (~frwl
->fw_smsk
.s6_addr32
[1])) ||
1057 (frwl
->fw_src
.s6_addr32
[2] & (~frwl
->fw_smsk
.s6_addr32
[2])) ||
1058 (frwl
->fw_src
.s6_addr32
[3] & (~frwl
->fw_smsk
.s6_addr32
[3])) ||
1059 (frwl
->fw_dst
.s6_addr32
[0] & (~frwl
->fw_dmsk
.s6_addr32
[0])) ||
1060 (frwl
->fw_dst
.s6_addr32
[1] & (~frwl
->fw_dmsk
.s6_addr32
[1])) ||
1061 (frwl
->fw_dst
.s6_addr32
[2] & (~frwl
->fw_dmsk
.s6_addr32
[2])) ||
1062 (frwl
->fw_dst
.s6_addr32
[3] & (~frwl
->fw_dmsk
.s6_addr32
[3]))) {
1063 dprintf(("%s rule never matches\n", err_prefix
));
1067 if ((frwl
->fw_flg
& IPV6_FW_F_FRAG
) &&
1068 (frwl
->fw_prot
== IPPROTO_UDP
|| frwl
->fw_prot
== IPPROTO_TCP
)) {
1069 if (frwl
->fw_nports
) {
1070 dprintf(("%s cannot mix 'frag' and ports\n", err_prefix
));
1073 if (frwl
->fw_prot
== IPPROTO_TCP
&&
1074 frwl
->fw_tcpf
!= frwl
->fw_tcpnf
) {
1075 dprintf(("%s cannot mix 'frag' with TCP flags\n", err_prefix
));
1080 /* Check command specific stuff */
1081 switch (frwl
->fw_flg
& IPV6_FW_F_COMMAND
)
1083 case IPV6_FW_F_REJECT
:
1084 if (frwl
->fw_reject_code
>= 0x100
1085 && !(frwl
->fw_prot
== IPPROTO_TCP
1086 && frwl
->fw_reject_code
== IPV6_FW_REJECT_RST
)) {
1087 dprintf(("%s unknown reject code\n", err_prefix
));
1091 case IPV6_FW_F_DIVERT
: /* Diverting to port zero is invalid */
1093 if (frwl
->fw_divert_port
== 0) {
1094 dprintf(("%s can't divert to port 0\n", err_prefix
));
1098 case IPV6_FW_F_DENY
:
1099 case IPV6_FW_F_ACCEPT
:
1100 case IPV6_FW_F_COUNT
:
1101 case IPV6_FW_F_SKIPTO
:
1104 dprintf(("%s invalid command\n", err_prefix
));
1114 ip6_fw_ctl(int stage
, struct mbuf
**mm
)
1119 if (stage
== IPV6_FW_GET
) {
1120 struct ip6_fw_chain
*fcp
= ip6_fw_chain
.lh_first
;
1121 *mm
= m
= m_get(M_WAIT
, MT_DATA
); /* XXX */
1124 if (sizeof *(fcp
->rule
) > MLEN
) {
1126 if ((m
->m_flags
& M_EXT
) == 0) {
1131 for (; fcp
; fcp
= fcp
->chain
.le_next
) {
1132 bcopy(fcp
->rule
, m
->m_data
, sizeof *(fcp
->rule
));
1133 m
->m_len
= sizeof *(fcp
->rule
);
1134 m
->m_next
= m_get(M_WAIT
, MT_DATA
); /* XXX */
1140 if (sizeof *(fcp
->rule
) > MLEN
) {
1142 if ((m
->m_flags
& M_EXT
) == 0) {
1152 /* only allow get calls if secure mode > 2 */
1153 if (securelevel
> 2) {
1160 if (stage
== IPV6_FW_FLUSH
) {
1161 while (ip6_fw_chain
.lh_first
!= NULL
&&
1162 ip6_fw_chain
.lh_first
->rule
->fw_number
!= (u_short
)-1) {
1163 struct ip6_fw_chain
*fcp
= ip6_fw_chain
.lh_first
;
1165 LIST_REMOVE(ip6_fw_chain
.lh_first
, chain
);
1167 FREE(fcp
->rule
, M_IP6FW
);
1176 if (stage
== IPV6_FW_ZERO
) {
1177 error
= zero_entry6(m
);
1185 printf("%s NULL mbuf ptr\n", err_prefix
);
1189 if (stage
== IPV6_FW_ADD
) {
1190 struct ip6_fw
*frwl
= check_ip6fw_mbuf(m
);
1195 error
= add_entry6(&ip6_fw_chain
, frwl
);
1202 if (stage
== IPV6_FW_DEL
) {
1203 if (m
->m_len
!= sizeof(struct ip6_fw
)) {
1204 dprintf(("%s len=%ld, want %lu\n", err_prefix
, m
->m_len
,
1205 sizeof(struct ip6_fw
)));
1207 } else if (mtod(m
, struct ip6_fw
*)->fw_number
== (u_short
)-1) {
1208 dprintf(("%s can't delete rule 65535\n", err_prefix
));
1211 error
= del_entry6(&ip6_fw_chain
,
1212 mtod(m
, struct ip6_fw
*)->fw_number
);
1220 dprintf(("%s unknown request %d\n", err_prefix
, stage
));
1230 ip6_fw_ctl(struct sockopt
*sopt
)
1237 if (securelevel
>= 3 &&
1238 (sopt
->sopt_dir
!= SOPT_GET
|| sopt
->sopt_name
!= IPV6_FW_GET
))
1241 /* We ALWAYS expect the client to pass in a rule structure so that we can
1242 * check the version of the API that they are using. In the case of a
1243 * IPV6_FW_GET operation, the first rule of the output buffer passed to us
1244 * must have the version set. */
1245 if (!sopt
->sopt_val
|| sopt
->sopt_valsize
< sizeof rule
) return EINVAL
;
1247 /* save sopt->sopt_valsize */
1248 valsize
= sopt
->sopt_valsize
;
1249 if (error
= sooptcopyin(sopt
, &rule
, sizeof(rule
), sizeof(rule
)))
1252 if (rule
.version
!= IPV6_FW_CURRENT_API_VERSION
) return EINVAL
;
1253 rule
.version
= 0xFFFFFFFF; /* version is meaningless once rules "make it in the door". */
1255 switch (sopt
->sopt_name
)
1259 struct ip6_fw_chain
*fcp
;
1264 LIST_FOREACH(fcp
, &ip6_fw_chain
, chain
)
1265 size
+= sizeof *buf
;
1267 buf
= _MALLOC(size
, M_TEMP
, M_WAITOK
);
1268 if (!buf
) error
= ENOBUFS
;
1271 struct ip6_fw
*bp
= buf
;
1272 LIST_FOREACH(fcp
, &ip6_fw_chain
, chain
)
1274 bcopy(fcp
->rule
, bp
, sizeof *bp
);
1275 bp
->version
= IPV6_FW_CURRENT_API_VERSION
;
1283 sopt
->sopt_valsize
= valsize
;
1284 error
= sooptcopyout(sopt
, buf
, size
);
1293 while (ip6_fw_chain
.lh_first
&&
1294 ip6_fw_chain
.lh_first
->rule
->fw_number
!= (u_short
)-1)
1296 struct ip6_fw_chain
*fcp
= ip6_fw_chain
.lh_first
;
1297 LIST_REMOVE(ip6_fw_chain
.lh_first
, chain
);
1298 FREE(fcp
->rule
, M_IP6FW
);
1305 error
= zero_entry6(&rule
);
1309 if (check_ip6fw_struct(&rule
))
1310 error
= add_entry6(&ip6_fw_chain
, &rule
);
1316 if (rule
.fw_number
== (u_short
)-1)
1318 dprintf(("%s can't delete rule 65535\n", err_prefix
));
1322 error
= del_entry6(&ip6_fw_chain
, rule
.fw_number
);
1326 dprintf(("%s invalid option %d\n", err_prefix
, sopt
->sopt_name
));
1336 struct ip6_fw default_rule
;
1338 ip6_fw_chk_ptr
= ip6_fw_chk
;
1339 ip6_fw_ctl_ptr
= ip6_fw_ctl
;
1340 LIST_INIT(&ip6_fw_chain
);
1342 bzero(&default_rule
, sizeof default_rule
);
1343 default_rule
.fw_prot
= IPPROTO_IPV6
;
1344 default_rule
.fw_number
= (u_short
)-1;
1345 #ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT
1346 default_rule
.fw_flg
|= IPV6_FW_F_ACCEPT
;
1348 default_rule
.fw_flg
|= IPV6_FW_F_DENY
;
1350 default_rule
.fw_flg
|= IPV6_FW_F_IN
| IPV6_FW_F_OUT
;
1351 if (check_ip6fw_struct(&default_rule
) == NULL
||
1352 add_entry6(&ip6_fw_chain
, &default_rule
))
1353 panic(__FUNCTION__
);
1355 printf("IPv6 packet filtering initialized, ");
1356 #ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT
1357 printf("default to accept, ");
1359 #ifndef IPV6FIREWALL_VERBOSE
1360 printf("logging disabled\n");
1362 if (fw6_verbose_limit
== 0)
1363 printf("unlimited logging\n");
1365 printf("logging limited to %d packets/entry\n",