1 /* $KAME: ip6_fw.c,v 1.11 2000/03/10 04:22:18 k-sugyou Exp $ */
4 * Copyright (c) 1993 Daniel Boulet
5 * Copyright (c) 1994 Ugen J.S.Antsilevich
6 * Copyright (c) 1996 Alex Nash
8 * Redistribution and use in source forms, with and without modification,
9 * are permitted provided that this entire comment appears intact.
11 * Redistribution in binary form may occur without any restrictions.
12 * Obviously, it would be nice if you gave credit where credit is due
13 * but requiring it would be too onerous.
15 * This software is provided ``AS IS'' without any warranties of any kind.
17 * $Id: ip6_fw.c,v 1.3 2001/05/01 21:52:50 lindak Exp $
21 * Implement IPv6 packet firewall
25 #include "opt_ip6fw.h"
32 #error "NOT SUPPORTED IPV6 DIVERT"
34 #if IP6FW_DIVERT_RESTART
35 #error "NOT SUPPORTED IPV6 DIVERT"
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/malloc.h>
42 #include <kern/queue.h>
43 #include <sys/kernel.h>
44 #include <sys/socket.h>
45 #if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
46 #include <sys/socketvar.h>
50 #include <net/route.h>
51 #include <netinet/in_systm.h>
52 #include <netinet/in.h>
53 #include <netinet/ip.h>
55 #include <netinet/ip6.h>
56 #include <netinet6/ip6_var.h>
57 #include <netinet6/in6_var.h>
58 #include <netinet/icmp6.h>
60 #include <netinet/in_pcb.h>
62 #include <netinet6/ip6_fw.h>
64 #include <netinet6/tcp6.h>
65 #include <netinet6/tcp6_timer.h>
66 #include <netinet6/tcp6_var.h>
68 #include <netinet/ip_var.h>
69 #include <netinet/tcp.h>
70 #include <netinet/tcp_seq.h>
71 #include <netinet/tcp_timer.h>
72 #include <netinet/tcp_var.h>
73 #include <netinet/udp.h>
75 #if defined(__NetBSD__) || defined(__OpenBSD__)
78 #if __FreeBSD__ || defined (__APPLE__)
79 #include <sys/sysctl.h>
82 #include <net/net_osdep.h>
84 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
85 MALLOC_DEFINE(M_IP6FW
, "Ip6Fw/Ip6Acct", "Ip6Fw/Ip6Acct chain's");
88 #define M_IP6FW M_TEMP
92 static int fw6_debug
= 1;
93 #if IPV6FIREWALL_VERBOSE
94 static int fw6_verbose
= 1;
96 static int fw6_verbose
= 0;
98 #if IPV6FIREWALL_VERBOSE_LIMIT
99 static int fw6_verbose_limit
= IPV6FIREWALL_VERBOSE_LIMIT
;
101 static int fw6_verbose_limit
= 0;
104 LIST_HEAD (ip6_fw_head
, ip6_fw_chain
) ip6_fw_chain
;
106 SYSCTL_DECL(_net_inet6_ip6
);
107 SYSCTL_NODE(_net_inet6_ip6
, OID_AUTO
, fw
, CTLFLAG_RW
, 0, "Firewall");
108 SYSCTL_INT(_net_inet6_ip6_fw
, IP6FWCTL_DEBUG
, debug
, CTLFLAG_RW
, &fw6_debug
, 0, "");
109 SYSCTL_INT(_net_inet6_ip6_fw
, IP6FWCTL_VERBOSE
, verbose
, CTLFLAG_RW
, &fw6_verbose
, 0, "");
110 SYSCTL_INT(_net_inet6_ip6_fw
, IP6FWCTL_VERBLIMIT
, verbose_limit
, CTLFLAG_RW
, &fw6_verbose_limit
, 0, "");
112 #define dprintf(a) if (!fw6_debug); else printf a
114 #define print_ip6(a) printf("[%s]", ip6_sprintf(a))
116 #define dprint_ip6(a) if (!fw6_debug); else print_ip6(a)
118 static int add_entry6
__P((struct ip6_fw_head
*chainptr
, struct ip6_fw
*frwl
));
119 static int del_entry6
__P((struct ip6_fw_head
*chainptr
, u_short number
));
120 static int zero_entry6
__P((struct mbuf
*m
));
121 static struct ip6_fw
*check_ip6fw_struct
__P((struct ip6_fw
*m
));
122 static struct ip6_fw
*check_ip6fw_mbuf
__P((struct mbuf
*fw
));
123 static int ip6opts_match
__P((struct ip6_hdr
**ip6
, struct ip6_fw
*f
,
125 int *off
, int *nxt
, u_short
*offset
));
126 static int port_match6
__P((u_short
*portptr
, int nports
, u_short port
,
128 static int tcp6flg_match
__P((struct tcphdr
*tcp6
, struct ip6_fw
*f
));
129 static int icmp6type_match
__P((struct icmp6_hdr
* icmp
, struct ip6_fw
* f
));
130 static void ip6fw_report
__P((struct ip6_fw
*f
, struct ip6_hdr
*ip6
,
131 struct ifnet
*rif
, struct ifnet
*oif
, int off
, int nxt
));
133 static int ip6_fw_chk
__P((struct ip6_hdr
**pip6
,
134 struct ifnet
*oif
, u_int16_t
*cookie
, struct mbuf
**m
));
135 static int ip6_fw_ctl
__P((int stage
, struct mbuf
**mm
));
137 static char err_prefix
[] = "ip6_fw_ctl:";
140 * Returns 1 if the port is matched by the vector, 0 otherwise
143 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
149 port_match6(u_short
*portptr
, int nports
, u_short port
, int range_flag
)
154 if (portptr
[0] <= port
&& port
<= portptr
[1]) {
160 while (nports
-- > 0) {
161 if (*portptr
++ == port
) {
169 tcp6flg_match(struct tcphdr
*tcp6
, struct ip6_fw
*f
)
171 u_char flg_set
, flg_clr
;
173 if ((f
->fw_tcpf
& IPV6_FW_TCPF_ESTAB
) &&
174 (tcp6
->th_flags
& (IPV6_FW_TCPF_RST
| IPV6_FW_TCPF_ACK
)))
177 flg_set
= tcp6
->th_flags
& f
->fw_tcpf
;
178 flg_clr
= tcp6
->th_flags
& f
->fw_tcpnf
;
180 if (flg_set
!= f
->fw_tcpf
)
189 icmp6type_match(struct icmp6_hdr
*icmp6
, struct ip6_fw
*f
)
193 if (!(f
->fw_flg
& IPV6_FW_F_ICMPBIT
))
196 type
= icmp6
->icmp6_type
;
198 /* check for matching type in the bitmap */
199 if (type
< IPV6_FW_ICMPTYPES_DIM
* sizeof(unsigned) * 8 &&
200 (f
->fw_icmp6types
[type
/ (sizeof(unsigned) * 8)] &
201 (1U << (type
% (8 * sizeof(unsigned))))))
204 return(0); /* no match */
208 is_icmp6_query(struct ip6_hdr
*ip6
, int off
)
210 const struct icmp6_hdr
*icmp6
;
213 icmp6
= (struct icmp6_hdr
*)((caddr_t
)ip6
+ off
);
214 icmp6_type
= icmp6
->icmp6_type
;
216 if (icmp6_type
== ICMP6_ECHO_REQUEST
||
217 icmp6_type
== ICMP6_MEMBERSHIP_QUERY
||
218 icmp6_type
== ICMP6_WRUREQUEST
||
219 icmp6_type
== ICMP6_FQDN_QUERY
||
220 icmp6_type
== ICMP6_NI_QUERY
)
227 ip6opts_match(struct ip6_hdr
**pip6
, struct ip6_fw
*f
, struct mbuf
**m
,
228 int *off
, int *nxt
, u_short
*offset
)
231 struct ip6_hdr
*ip6
= *pip6
;
232 struct ip6_ext
*ip6e
;
233 u_char opts
, nopts
, nopts_sve
;
236 nopts
= nopts_sve
= f
->fw_ip6nopt
;
239 *off
= sizeof(struct ip6_hdr
);
240 len
= ntohs(ip6
->ip6_plen
) + sizeof(struct ip6_hdr
);
242 ip6e
= (struct ip6_ext
*)((caddr_t
) ip6
+ *off
);
243 if ((*m
)->m_len
< *off
+ sizeof(*ip6e
))
244 goto opts_check
; /* XXX */
247 case IPPROTO_FRAGMENT
:
248 if ((*m
)->m_len
< *off
+ sizeof(struct ip6_frag
)) {
249 struct ip6_frag
*ip6f
;
251 ip6f
= (struct ip6_frag
*) ((caddr_t
)ip6
+ *off
);
252 *offset
= ip6f
->ip6f_offlg
| IP6F_OFF_MASK
;
254 opts
&= ~IPV6_FW_IP6OPT_FRAG
;
255 nopts
&= ~IPV6_FW_IP6OPT_FRAG
;
256 *off
+= sizeof(struct ip6_frag
);
259 opts
&= ~IPV6_FW_IP6OPT_AH
;
260 nopts
&= ~IPV6_FW_IP6OPT_AH
;
261 *off
+= (ip6e
->ip6e_len
+ 2) << 2;
265 case IPPROTO_HOPOPTS
:
266 opts
&= ~IPV6_FW_IP6OPT_HOPOPT
;
267 nopts
&= ~IPV6_FW_IP6OPT_HOPOPT
;
269 case IPPROTO_ROUTING
:
270 opts
&= ~IPV6_FW_IP6OPT_ROUTE
;
271 nopts
&= ~IPV6_FW_IP6OPT_ROUTE
;
274 opts
&= ~IPV6_FW_IP6OPT_ESP
;
275 nopts
&= ~IPV6_FW_IP6OPT_ESP
;
278 opts
&= ~IPV6_FW_IP6OPT_NONXT
;
279 nopts
&= ~IPV6_FW_IP6OPT_NONXT
;
282 case IPPROTO_DSTOPTS
:
283 opts
&= ~IPV6_FW_IP6OPT_OPTS
;
284 nopts
&= ~IPV6_FW_IP6OPT_OPTS
;
290 *off
+= (ip6e
->ip6e_len
+ 1) << 3;
293 *nxt
= ip6e
->ip6e_nxt
;
297 if (f
->fw_ip6opt
== f
->fw_ip6nopt
) /* XXX */
300 if (opts
== 0 && nopts
== nopts_sve
)
307 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
313 iface_match(struct ifnet
*ifp
, union ip6_fw_if
*ifu
, int byname
)
315 /* Check by name or by IP address */
319 char xname
[IFNAMSIZ
];
320 snprintf(xname
, sizeof(xname
), "%s%d", ifu
->fu_via_if
.name
,
321 ifu
->fu_via_if
.unit
);
322 if (strcmp(ifp
->if_xname
, xname
))
326 /* Check unit number (-1 is wildcard) */
327 if (ifu
->fu_via_if
.unit
!= -1
328 && ifp
->if_unit
!= ifu
->fu_via_if
.unit
)
331 if (strncmp(ifp
->if_name
, ifu
->fu_via_if
.name
, FW_IFNLEN
))
335 } else if (!IN6_IS_ADDR_UNSPECIFIED(&ifu
->fu_via_ip6
)) { /* Zero == wildcard */
338 #if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
339 for (ia
= ifp
->if_addrlist
; ia
; ia
= ia
->ifa_next
)
341 for (ia
= ifp
->if_addrlist
.tqh_first
; ia
; ia
= ia
->ifa_list
.tqe_next
)
345 if (ia
->ifa_addr
== NULL
)
347 if (ia
->ifa_addr
->sa_family
!= AF_INET6
)
349 if (!IN6_ARE_ADDR_EQUAL(&ifu
->fu_via_ip6
,
350 &(((struct sockaddr_in6
*)
351 (ia
->ifa_addr
))->sin6_addr
)))
361 ip6fw_report(struct ip6_fw
*f
, struct ip6_hdr
*ip6
,
362 struct ifnet
*rif
, struct ifnet
*oif
, int off
, int nxt
)
365 struct tcphdr
*const tcp6
= (struct tcphdr
*) ((caddr_t
) ip6
+ off
);
366 struct udphdr
*const udp
= (struct udphdr
*) ((caddr_t
) ip6
+ off
);
367 struct icmp6_hdr
*const icmp6
= (struct icmp6_hdr
*) ((caddr_t
) ip6
+ off
);
370 count
= f
? f
->fw_pcnt
: ++counter
;
371 if (fw6_verbose_limit
!= 0 && count
> fw6_verbose_limit
)
374 /* Print command name */
375 printf("ip6fw: %d ", f
? f
->fw_number
: -1);
379 switch (f
->fw_flg
& IPV6_FW_F_COMMAND
) {
383 case IPV6_FW_F_REJECT
:
384 if (f
->fw_reject_code
== IPV6_FW_REJECT_RST
)
389 case IPV6_FW_F_ACCEPT
:
392 case IPV6_FW_F_COUNT
:
395 case IPV6_FW_F_DIVERT
:
396 printf("Divert %d", f
->fw_divert_port
);
399 printf("Tee %d", f
->fw_divert_port
);
401 case IPV6_FW_F_SKIPTO
:
402 printf("SkipTo %d", f
->fw_skipto_rule
);
413 print_ip6(&ip6
->ip6_src
);
415 printf(":%d ", ntohs(tcp6
->th_sport
));
418 print_ip6(&ip6
->ip6_dst
);
420 printf(":%d", ntohs(tcp6
->th_dport
));
424 print_ip6(&ip6
->ip6_src
);
426 printf(":%d ", ntohs(udp
->uh_sport
));
429 print_ip6(&ip6
->ip6_dst
);
431 printf(":%d", ntohs(udp
->uh_dport
));
435 printf("IPV6-ICMP:%u.%u ", icmp6
->icmp6_type
, icmp6
->icmp6_code
);
437 printf("IPV6-ICMP ");
438 print_ip6(&ip6
->ip6_src
);
440 print_ip6(&ip6
->ip6_dst
);
443 printf("P:%d ", nxt
);
444 print_ip6(&ip6
->ip6_src
);
446 print_ip6(&ip6
->ip6_dst
);
450 printf(" out via %s", if_name(oif
));
452 printf(" in via %s", if_name(rif
));
454 if (fw6_verbose_limit
!= 0 && count
== fw6_verbose_limit
)
455 printf("ip6fw: limit reached on rule #%d\n",
456 f
? f
->fw_number
: -1);
462 * ip Pointer to packet header (struct ip6_hdr *)
463 * hlen Packet header length
464 * oif Outgoing interface, or NULL if packet is incoming
465 * #ifndef IP6FW_DIVERT_RESTART
466 * *cookie Ignore all divert/tee rules to this port (if non-zero)
468 * *cookie Skip up to the first rule past this rule number;
470 * *m The packet; we set to NULL when/if we nuke it.
474 * 0 The packet is to be accepted and routed normally OR
475 * the packet was denied/rejected and has been dropped;
476 * in the latter case, *m is equal to NULL upon return.
477 * port Divert the packet to port.
481 ip6_fw_chk(struct ip6_hdr
**pip6
,
482 struct ifnet
*oif
, u_int16_t
*cookie
, struct mbuf
**m
)
484 struct ip6_fw_chain
*chain
;
485 struct ip6_fw
*rule
= NULL
;
486 struct ip6_hdr
*ip6
= *pip6
;
487 struct ifnet
*const rif
= (*m
)->m_pkthdr
.rcvif
;
489 int off
= sizeof(struct ip6_hdr
), nxt
= ip6
->ip6_nxt
;
490 u_short src_port
, dst_port
;
491 #if IP6FW_DIVERT_RESTART
492 u_int16_t skipto
= *cookie
;
494 u_int16_t ignport
= ntohs(*cookie
);
499 * Go down the chain, looking for enlightment
500 * #if IP6FW_DIVERT_RESTART
501 * If we've been asked to start at a given rule immediatly, do so.
504 chain
= LIST_FIRST(&ip6_fw_chain
);
505 #if IP6FW_DIVERT_RESTART
509 while (chain
&& (chain
->rule
->fw_number
<= skipto
)) {
510 chain
= LIST_NEXT(chain
, chain
);
512 if (! chain
) goto dropit
;
514 #endif /* IP6FW_DIVERT_RESTART */
515 for (; chain
; chain
= LIST_NEXT(chain
, chain
)) {
516 register struct ip6_fw
*const f
= chain
->rule
;
519 /* Check direction outbound */
520 if (!(f
->fw_flg
& IPV6_FW_F_OUT
))
523 /* Check direction inbound */
524 if (!(f
->fw_flg
& IPV6_FW_F_IN
))
528 #define IN6_ARE_ADDR_MASKEQUAL(x,y,z) (\
529 (((x)->s6_addr32[0] & (y)->s6_addr32[0]) == (z)->s6_addr32[0]) && \
530 (((x)->s6_addr32[1] & (y)->s6_addr32[1]) == (z)->s6_addr32[1]) && \
531 (((x)->s6_addr32[2] & (y)->s6_addr32[2]) == (z)->s6_addr32[2]) && \
532 (((x)->s6_addr32[3] & (y)->s6_addr32[3]) == (z)->s6_addr32[3]))
534 /* If src-addr doesn't match, not this rule. */
535 if (((f
->fw_flg
& IPV6_FW_F_INVSRC
) != 0) ^
536 (!IN6_ARE_ADDR_MASKEQUAL(&ip6
->ip6_src
,&f
->fw_smsk
,&f
->fw_src
)))
539 /* If dest-addr doesn't match, not this rule. */
540 if (((f
->fw_flg
& IPV6_FW_F_INVDST
) != 0) ^
541 (!IN6_ARE_ADDR_MASKEQUAL(&ip6
->ip6_dst
,&f
->fw_dmsk
,&f
->fw_dst
)))
544 #undef IN6_ARE_ADDR_MASKEQUAL
545 /* Interface check */
546 if ((f
->fw_flg
& IF6_FW_F_VIAHACK
) == IF6_FW_F_VIAHACK
) {
547 struct ifnet
*const iface
= oif
? oif
: rif
;
549 /* Backwards compatibility hack for "via" */
550 if (!iface
|| !iface_match(iface
,
551 &f
->fw_in_if
, f
->fw_flg
& IPV6_FW_F_OIFNAME
))
554 /* Check receive interface */
555 if ((f
->fw_flg
& IPV6_FW_F_IIFACE
)
556 && (!rif
|| !iface_match(rif
,
557 &f
->fw_in_if
, f
->fw_flg
& IPV6_FW_F_IIFNAME
)))
559 /* Check outgoing interface */
560 if ((f
->fw_flg
& IPV6_FW_F_OIFACE
)
561 && (!oif
|| !iface_match(oif
,
562 &f
->fw_out_if
, f
->fw_flg
& IPV6_FW_F_OIFNAME
)))
566 /* Check IP options */
567 if (!ip6opts_match(&ip6
, f
, m
, &off
, &nxt
, &offset
))
571 if ((f
->fw_flg
& IPV6_FW_F_FRAG
) && !offset
)
574 /* Check protocol; if wildcard, match */
575 if (f
->fw_prot
== IPPROTO_IPV6
)
578 /* If different, don't match */
579 if (nxt
!= f
->fw_prot
)
582 #define PULLUP_TO(len) do { \
583 if ((*m)->m_len < (len) \
584 && (*m = m_pullup(*m, (len))) == 0) { \
587 *pip6 = ip6 = mtod(*m, struct ip6_hdr *); \
590 /* Protocol specific checks */
596 if (offset
== 1) { /* cf. RFC 1858 */
597 PULLUP_TO(off
+ 4); /* XXX ? */
602 * TCP flags and ports aren't available in this
603 * packet -- if this rule specified either one,
604 * we consider the rule a non-match.
606 if (f
->fw_nports
!= 0 ||
607 f
->fw_tcpf
!= f
->fw_tcpnf
)
613 tcp6
= (struct tcphdr
*) ((caddr_t
)ip6
+ off
);
614 if (f
->fw_tcpf
!= f
->fw_tcpnf
&& !tcp6flg_match(tcp6
, f
))
616 src_port
= ntohs(tcp6
->th_sport
);
617 dst_port
= ntohs(tcp6
->th_dport
);
627 * Port specification is unavailable -- if this
628 * rule specifies a port, we consider the rule
631 if (f
->fw_nports
!= 0)
637 udp
= (struct udphdr
*) ((caddr_t
)ip6
+ off
);
638 src_port
= ntohs(udp
->uh_sport
);
639 dst_port
= ntohs(udp
->uh_dport
);
641 if (!port_match6(&f
->fw_pts
[0],
642 IPV6_FW_GETNSRCP(f
), src_port
,
643 f
->fw_flg
& IPV6_FW_F_SRNG
))
645 if (!port_match6(&f
->fw_pts
[IPV6_FW_GETNSRCP(f
)],
646 IPV6_FW_GETNDSTP(f
), dst_port
,
647 f
->fw_flg
& IPV6_FW_F_DRNG
))
654 struct icmp6_hdr
*icmp
;
656 if (offset
!= 0) /* Type isn't valid */
659 icmp
= (struct icmp6_hdr
*) ((caddr_t
)ip6
+ off
);
660 if (!icmp6type_match(icmp
, f
))
668 ip6fw_report(NULL
, ip6
, rif
, oif
, off
, nxt
);
673 #ifndef IP6FW_DIVERT_RESTART
674 /* Ignore divert/tee rule if socket port is "ignport" */
675 switch (f
->fw_flg
& IPV6_FW_F_COMMAND
) {
676 case IPV6_FW_F_DIVERT
:
678 if (f
->fw_divert_port
== ignport
)
679 continue; /* ignore this rule */
683 #endif /* IP6FW_DIVERT_RESTART */
684 /* Update statistics */
686 f
->fw_bcnt
+= ntohs(ip6
->ip6_plen
);
687 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
688 f
->timestamp
= time_second
;
690 f
->timestamp
= time
.tv_sec
;
693 /* Log to console if desired */
694 if ((f
->fw_flg
& IPV6_FW_F_PRN
) && fw6_verbose
)
695 ip6fw_report(f
, ip6
, rif
, oif
, off
, nxt
);
697 /* Take appropriate action */
698 switch (f
->fw_flg
& IPV6_FW_F_COMMAND
) {
699 case IPV6_FW_F_ACCEPT
:
701 case IPV6_FW_F_COUNT
:
703 case IPV6_FW_F_DIVERT
:
704 #if IP6FW_DIVERT_RESTART
705 *cookie
= f
->fw_number
;
707 *cookie
= htons(f
->fw_divert_port
);
708 #endif /* IP6FW_DIVERT_RESTART */
709 return(f
->fw_divert_port
);
712 * XXX someday tee packet here, but beware that you
713 * can't use m_copym() or m_copypacket() because
714 * the divert input routine modifies the mbuf
715 * (and these routines only increment reference
716 * counts in the case of mbuf clusters), so need
717 * to write custom routine.
720 case IPV6_FW_F_SKIPTO
:
722 while (chain
->chain
.le_next
723 && chain
->chain
.le_next
->rule
->fw_number
726 while (chain
->chain
.le_next
->rule
->fw_number
729 chain
= chain
->chain
.le_next
;
733 /* Deny/reject this packet using this rule */
739 /* Rule 65535 should always be there and should always match */
741 panic("ip6_fw: chain");
745 * At this point, we're going to drop the packet.
746 * Send a reject notice if all of the following are true:
748 * - The packet matched a reject rule
749 * - The packet is not an ICMP packet, or is an ICMP query packet
750 * - The packet is not a multicast or broadcast packet
752 if ((rule
->fw_flg
& IPV6_FW_F_COMMAND
) == IPV6_FW_F_REJECT
753 && (nxt
!= IPPROTO_ICMPV6
|| is_icmp6_query(ip6
, off
))
754 && !((*m
)->m_flags
& (M_BCAST
|M_MCAST
))
755 && !IN6_IS_ADDR_MULTICAST(&ip6
->ip6_dst
)) {
756 switch (rule
->fw_reject_code
) {
757 case IPV6_FW_REJECT_RST
:
760 struct tcphdr
*const tcp
=
761 (struct tcphdr
*) ((caddr_t
)ip6
+ off
);
769 if (offset
!= 0 || (tcp
->th_flags
& TH_RST
))
776 ti
.ip6
.ip6_nxt
= IPPROTO_TCP
;
777 if (ti
.th
.th_flags
& TH_ACK
) {
783 if (((*m
)->m_flags
& M_PKTHDR
) != 0) {
784 ack
+= (*m
)->m_pkthdr
.len
- off
785 - (ti
.th
.th_off
<< 2);
786 } else if (ip6
->ip6_plen
) {
787 ack
+= ntohs(ip6
->ip6_plen
) + sizeof(*ip6
)
788 - off
- (ti
.th
.th_off
<< 2);
795 flags
= TH_RST
|TH_ACK
;
797 bcopy(&ti
, ip6
, sizeof(ti
));
799 tcp6_respond(NULL
, ip6
, (struct tcp6hdr
*)(ip6
+ 1),
800 *m
, ack
, seq
, flags
);
801 #elif defined(__NetBSD__)
802 tcp_respond(NULL
, NULL
, *m
, (struct tcphdr
*)(ip6
+ 1),
804 #elif defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
805 tcp_respond(NULL
, ip6
, (struct tcphdr
*)(ip6
+ 1),
806 *m
, ack
, seq
, flags
, 1);
814 default: /* Send an ICMP unreachable using code */
816 (*m
)->m_pkthdr
.rcvif
= oif
;
817 icmp6_error(*m
, ICMP6_DST_UNREACH
,
818 rule
->fw_reject_code
, 0);
826 * Finally, drop the packet.
836 add_entry6(struct ip6_fw_head
*chainptr
, struct ip6_fw
*frwl
)
838 struct ip6_fw
*ftmp
= 0;
839 struct ip6_fw_chain
*fwc
= 0, *fcp
, *fcpl
= 0;
843 fwc
= _MALLOC(sizeof *fwc
, M_IP6FW
, M_NOWAIT
);
844 ftmp
= _MALLOC(sizeof *ftmp
, M_IP6FW
, M_NOWAIT
);
846 dprintf(("%s malloc said no\n", err_prefix
));
847 if (fwc
) _FREE(fwc
, M_IP6FW
);
848 if (ftmp
) _FREE(ftmp
, M_IP6FW
);
852 bcopy(frwl
, ftmp
, sizeof(struct ip6_fw
));
853 ftmp
->fw_in_if
.fu_via_if
.name
[FW_IFNLEN
- 1] = '\0';
860 if (!chainptr
->lh_first
) {
861 LIST_INSERT_HEAD(chainptr
, fwc
, chain
);
864 } else if (ftmp
->fw_number
== (u_short
)-1) {
865 if (fwc
) _FREE(fwc
, M_IP6FW
);
866 if (ftmp
) _FREE(ftmp
, M_IP6FW
);
868 dprintf(("%s bad rule number\n", err_prefix
));
872 /* If entry number is 0, find highest numbered rule and add 100 */
873 if (ftmp
->fw_number
== 0) {
874 for (fcp
= chainptr
->lh_first
; fcp
; fcp
= fcp
->chain
.le_next
) {
875 if (fcp
->rule
->fw_number
!= (u_short
)-1)
876 nbr
= fcp
->rule
->fw_number
;
880 if (nbr
< (u_short
)-1 - 100)
882 ftmp
->fw_number
= nbr
;
885 /* Got a valid number; now insert it, keeping the list ordered */
886 for (fcp
= chainptr
->lh_first
; fcp
; fcp
= fcp
->chain
.le_next
) {
887 if (fcp
->rule
->fw_number
> ftmp
->fw_number
) {
889 LIST_INSERT_AFTER(fcpl
, fwc
, chain
);
891 LIST_INSERT_HEAD(chainptr
, fwc
, chain
);
904 del_entry6(struct ip6_fw_head
*chainptr
, u_short number
)
906 struct ip6_fw_chain
*fcp
;
911 fcp
= chainptr
->lh_first
;
912 if (number
!= (u_short
)-1) {
913 for (; fcp
; fcp
= fcp
->chain
.le_next
) {
914 if (fcp
->rule
->fw_number
== number
) {
915 LIST_REMOVE(fcp
, chain
);
917 _FREE(fcp
->rule
, M_IP6FW
);
929 zero_entry6(struct mbuf
*m
)
932 struct ip6_fw_chain
*fcp
;
936 if (m
->m_len
!= sizeof(struct ip6_fw
))
938 frwl
= mtod(m
, struct ip6_fw
*);
944 * It's possible to insert multiple chain entries with the
945 * same number, so we don't stop after finding the first
946 * match if zeroing a specific entry.
949 for (fcp
= ip6_fw_chain
.lh_first
; fcp
; fcp
= fcp
->chain
.le_next
)
950 if (!frwl
|| frwl
->fw_number
== fcp
->rule
->fw_number
) {
951 fcp
->rule
->fw_bcnt
= fcp
->rule
->fw_pcnt
= 0;
952 fcp
->rule
->timestamp
= 0;
958 printf("ip6fw: Entry %d cleared.\n", frwl
->fw_number
);
960 printf("ip6fw: Accounting cleared.\n");
966 static struct ip6_fw
*
967 check_ip6fw_mbuf(struct mbuf
*m
)
970 if (m
->m_len
!= sizeof(struct ip6_fw
)) {
971 dprintf(("%s len=%d, want %d\n", err_prefix
, m
->m_len
,
972 sizeof(struct ip6_fw
)));
975 return(check_ip6fw_struct(mtod(m
, struct ip6_fw
*)));
978 static struct ip6_fw
*
979 check_ip6fw_struct(struct ip6_fw
*frwl
)
981 /* Check for invalid flag bits */
982 if ((frwl
->fw_flg
& ~IPV6_FW_F_MASK
) != 0) {
983 dprintf(("%s undefined flag bits set (flags=%x)\n",
984 err_prefix
, frwl
->fw_flg
));
987 /* Must apply to incoming or outgoing (or both) */
988 if (!(frwl
->fw_flg
& (IPV6_FW_F_IN
| IPV6_FW_F_OUT
))) {
989 dprintf(("%s neither in nor out\n", err_prefix
));
992 /* Empty interface name is no good */
993 if (((frwl
->fw_flg
& IPV6_FW_F_IIFNAME
)
994 && !*frwl
->fw_in_if
.fu_via_if
.name
)
995 || ((frwl
->fw_flg
& IPV6_FW_F_OIFNAME
)
996 && !*frwl
->fw_out_if
.fu_via_if
.name
)) {
997 dprintf(("%s empty interface name\n", err_prefix
));
1000 /* Sanity check interface matching */
1001 if ((frwl
->fw_flg
& IF6_FW_F_VIAHACK
) == IF6_FW_F_VIAHACK
) {
1002 ; /* allow "via" backwards compatibility */
1003 } else if ((frwl
->fw_flg
& IPV6_FW_F_IN
)
1004 && (frwl
->fw_flg
& IPV6_FW_F_OIFACE
)) {
1005 dprintf(("%s outgoing interface check on incoming\n",
1009 /* Sanity check port ranges */
1010 if ((frwl
->fw_flg
& IPV6_FW_F_SRNG
) && IPV6_FW_GETNSRCP(frwl
) < 2) {
1011 dprintf(("%s src range set but n_src_p=%d\n",
1012 err_prefix
, IPV6_FW_GETNSRCP(frwl
)));
1015 if ((frwl
->fw_flg
& IPV6_FW_F_DRNG
) && IPV6_FW_GETNDSTP(frwl
) < 2) {
1016 dprintf(("%s dst range set but n_dst_p=%d\n",
1017 err_prefix
, IPV6_FW_GETNDSTP(frwl
)));
1020 if (IPV6_FW_GETNSRCP(frwl
) + IPV6_FW_GETNDSTP(frwl
) > IPV6_FW_MAX_PORTS
) {
1021 dprintf(("%s too many ports (%d+%d)\n",
1022 err_prefix
, IPV6_FW_GETNSRCP(frwl
), IPV6_FW_GETNDSTP(frwl
)));
1026 * Protocols other than TCP/UDP don't use port range
1028 if ((frwl
->fw_prot
!= IPPROTO_TCP
) &&
1029 (frwl
->fw_prot
!= IPPROTO_UDP
) &&
1030 (IPV6_FW_GETNSRCP(frwl
) || IPV6_FW_GETNDSTP(frwl
))) {
1031 dprintf(("%s port(s) specified for non TCP/UDP rule\n",
1037 * Rather than modify the entry to make such entries work,
1038 * we reject this rule and require user level utilities
1039 * to enforce whatever policy they deem appropriate.
1041 if ((frwl
->fw_src
.s6_addr32
[0] & (~frwl
->fw_smsk
.s6_addr32
[0])) ||
1042 (frwl
->fw_src
.s6_addr32
[1] & (~frwl
->fw_smsk
.s6_addr32
[1])) ||
1043 (frwl
->fw_src
.s6_addr32
[2] & (~frwl
->fw_smsk
.s6_addr32
[2])) ||
1044 (frwl
->fw_src
.s6_addr32
[3] & (~frwl
->fw_smsk
.s6_addr32
[3])) ||
1045 (frwl
->fw_dst
.s6_addr32
[0] & (~frwl
->fw_dmsk
.s6_addr32
[0])) ||
1046 (frwl
->fw_dst
.s6_addr32
[1] & (~frwl
->fw_dmsk
.s6_addr32
[1])) ||
1047 (frwl
->fw_dst
.s6_addr32
[2] & (~frwl
->fw_dmsk
.s6_addr32
[2])) ||
1048 (frwl
->fw_dst
.s6_addr32
[3] & (~frwl
->fw_dmsk
.s6_addr32
[3]))) {
1049 dprintf(("%s rule never matches\n", err_prefix
));
1053 if ((frwl
->fw_flg
& IPV6_FW_F_FRAG
) &&
1054 (frwl
->fw_prot
== IPPROTO_UDP
|| frwl
->fw_prot
== IPPROTO_TCP
)) {
1055 if (frwl
->fw_nports
) {
1056 dprintf(("%s cannot mix 'frag' and ports\n", err_prefix
));
1059 if (frwl
->fw_prot
== IPPROTO_TCP
&&
1060 frwl
->fw_tcpf
!= frwl
->fw_tcpnf
) {
1061 dprintf(("%s cannot mix 'frag' with TCP flags\n", err_prefix
));
1066 /* Check command specific stuff */
1067 switch (frwl
->fw_flg
& IPV6_FW_F_COMMAND
)
1069 case IPV6_FW_F_REJECT
:
1070 if (frwl
->fw_reject_code
>= 0x100
1071 && !(frwl
->fw_prot
== IPPROTO_TCP
1072 && frwl
->fw_reject_code
== IPV6_FW_REJECT_RST
)) {
1073 dprintf(("%s unknown reject code\n", err_prefix
));
1077 case IPV6_FW_F_DIVERT
: /* Diverting to port zero is invalid */
1079 if (frwl
->fw_divert_port
== 0) {
1080 dprintf(("%s can't divert to port 0\n", err_prefix
));
1084 case IPV6_FW_F_DENY
:
1085 case IPV6_FW_F_ACCEPT
:
1086 case IPV6_FW_F_COUNT
:
1087 case IPV6_FW_F_SKIPTO
:
1090 dprintf(("%s invalid command\n", err_prefix
));
1098 ip6_fw_ctl(int stage
, struct mbuf
**mm
)
1103 if (stage
== IPV6_FW_GET
) {
1104 struct ip6_fw_chain
*fcp
= ip6_fw_chain
.lh_first
;
1105 #if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
1106 *mm
= m
= m_get(M_WAIT
, MT_DATA
); /* XXX */
1108 *mm
= m
= m_get(M_WAIT
, MT_SOOPTS
);
1112 if (sizeof *(fcp
->rule
) > MLEN
) {
1114 if ((m
->m_flags
& M_EXT
) == 0) {
1119 for (; fcp
; fcp
= fcp
->chain
.le_next
) {
1120 memcpy(m
->m_data
, fcp
->rule
, sizeof *(fcp
->rule
));
1121 m
->m_len
= sizeof *(fcp
->rule
);
1122 #if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
1123 m
->m_next
= m_get(M_WAIT
, MT_DATA
); /* XXX */
1125 m
->m_next
= m_get(M_WAIT
, MT_SOOPTS
);
1132 if (sizeof *(fcp
->rule
) > MLEN
) {
1134 if ((m
->m_flags
& M_EXT
) == 0) {
1144 /* only allow get calls if secure mode > 2 */
1145 if (securelevel
> 2) {
1152 if (stage
== IPV6_FW_FLUSH
) {
1153 while (ip6_fw_chain
.lh_first
!= NULL
&&
1154 ip6_fw_chain
.lh_first
->rule
->fw_number
!= (u_short
)-1) {
1155 struct ip6_fw_chain
*fcp
= ip6_fw_chain
.lh_first
;
1157 LIST_REMOVE(ip6_fw_chain
.lh_first
, chain
);
1159 _FREE(fcp
->rule
, M_IP6FW
);
1160 _FREE(fcp
, M_IP6FW
);
1168 if (stage
== IPV6_FW_ZERO
) {
1169 error
= zero_entry6(m
);
1177 printf("%s NULL mbuf ptr\n", err_prefix
);
1181 if (stage
== IPV6_FW_ADD
) {
1182 struct ip6_fw
*frwl
= check_ip6fw_mbuf(m
);
1187 error
= add_entry6(&ip6_fw_chain
, frwl
);
1194 if (stage
== IPV6_FW_DEL
) {
1195 if (m
->m_len
!= sizeof(struct ip6_fw
)) {
1196 dprintf(("%s len=%d, want %d\n", err_prefix
, m
->m_len
,
1197 sizeof(struct ip6_fw
)));
1199 } else if (mtod(m
, struct ip6_fw
*)->fw_number
== (u_short
)-1) {
1200 dprintf(("%s can't delete rule 65535\n", err_prefix
));
1203 error
= del_entry6(&ip6_fw_chain
,
1204 mtod(m
, struct ip6_fw
*)->fw_number
);
1212 dprintf(("%s unknown request %d\n", err_prefix
, stage
));
1223 struct ip6_fw default_rule
;
1225 ip6_fw_chk_ptr
= ip6_fw_chk
;
1226 ip6_fw_ctl_ptr
= ip6_fw_ctl
;
1227 LIST_INIT(&ip6_fw_chain
);
1229 bzero(&default_rule
, sizeof default_rule
);
1230 default_rule
.fw_prot
= IPPROTO_IPV6
;
1231 default_rule
.fw_number
= (u_short
)-1;
1232 #if IPV6FIREWALL_DEFAULT_TO_ACCEPT
1233 default_rule
.fw_flg
|= IPV6_FW_F_ACCEPT
;
1235 default_rule
.fw_flg
|= IPV6_FW_F_DENY
;
1237 default_rule
.fw_flg
|= IPV6_FW_F_IN
| IPV6_FW_F_OUT
;
1238 if (check_ip6fw_struct(&default_rule
) == NULL
||
1239 add_entry6(&ip6_fw_chain
, &default_rule
))
1240 panic(__FUNCTION__
);
1242 #if 1 /* NOT SUPPORTED IPV6 DIVERT */
1243 printf("IPv6 packet filtering initialized, ");
1245 printf("IPv6 packet filtering initialized, "
1247 "divert enabled, ");
1249 "divert disabled, ");
1252 #if IPV6FIREWALL_DEFAULT_TO_ACCEPT
1253 printf("default to accept, ");
1255 #ifndef IPV6FIREWALL_VERBOSE
1256 printf("logging disabled\n");
1258 if (fw6_verbose_limit
== 0)
1259 printf("unlimited logging\n");
1261 printf("logging limited to %d packets/entry\n",