]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet6/ip6_fw.c
xnu-792.10.96.tar.gz
[apple/xnu.git] / bsd / netinet6 / ip6_fw.c
CommitLineData
91447636
A
1/*
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
37839358
A
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.
91447636 11 *
37839358
A
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
91447636
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
37839358
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
91447636
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
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 $ */
25
26/*
27 * Copyright (C) 1998, 1999, 2000 and 2001 WIDE Project.
28 * All rights reserved.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
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.
41 *
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
52 * SUCH DAMAGE.
53 */
54
55/*
56 * Copyright (c) 1993 Daniel Boulet
57 * Copyright (c) 1994 Ugen J.S.Antsilevich
58 * Copyright (c) 1996 Alex Nash
59 *
60 * Redistribution and use in source forms, with and without modification,
61 * are permitted provided that this entire comment appears intact.
62 *
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.
66 *
67 * This software is provided ``AS IS'' without any warranties of any kind.
68 */
69
70/*
71 * Implement IPv6 packet firewall
72 */
73
74
75#ifdef IP6DIVERT
76#error "NOT SUPPORTED IPV6 DIVERT"
77#endif
78#ifdef IP6FW_DIVERT_RESTART
79#error "NOT SUPPORTED IPV6 DIVERT"
80#endif
81
82#include <string.h>
83#include <machine/spl.h>
84
85#include <sys/param.h>
86#include <sys/systm.h>
87#include <sys/malloc.h>
88#include <sys/mbuf.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>
94#include <sys/lock.h>
95#include <sys/time.h>
96#include <net/if.h>
97#include <net/route.h>
98#include <netinet/in_systm.h>
99#include <netinet/in.h>
100#include <netinet/ip.h>
101
102#include <netinet/ip6.h>
103#include <netinet6/ip6_var.h>
104#include <netinet6/in6_var.h>
105#include <netinet/icmp6.h>
106
107#include <netinet/in_pcb.h>
108
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>
116
117#include <sys/sysctl.h>
118
119#include <net/net_osdep.h>
120
121MALLOC_DEFINE(M_IP6FW, "Ip6Fw/Ip6Acct", "Ip6Fw/Ip6Acct chain's");
122
123static int fw6_debug = 1;
124#ifdef IPV6FIREWALL_VERBOSE
125static int fw6_verbose = 1;
126#else
127static int fw6_verbose = 0;
128#endif
129#ifdef IPV6FIREWALL_VERBOSE_LIMIT
130static int fw6_verbose_limit = IPV6FIREWALL_VERBOSE_LIMIT;
131#else
132static int fw6_verbose_limit = 0;
133#endif
134
135LIST_HEAD (ip6_fw_head, ip6_fw_chain) ip6_fw_chain;
136
137#ifdef SYSCTL_NODE
138SYSCTL_DECL(_net_inet6_ip6);
139SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall");
140SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, enable, CTLFLAG_RW,
141 &ip6_fw_enable, 0, "Enable ip6fw");
142SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, debug, CTLFLAG_RW, &fw6_debug, 0, "");
143SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose, CTLFLAG_RW, &fw6_verbose, 0, "");
144SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw6_verbose_limit, 0, "");
145#endif
146
147#define dprintf(a) do { \
148 if (fw6_debug) \
149 printf a; \
150 } while (0)
151#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
152
153static int add_entry6 __P((struct ip6_fw_head *chainptr, struct ip6_fw *frwl));
154static int del_entry6 __P((struct ip6_fw_head *chainptr, u_short number));
155static int zero_entry6 __P((struct ip6_fw *frwl));
156static struct ip6_fw *check_ip6fw_struct __P((struct ip6_fw *m));
157static int ip6opts_match __P((struct ip6_hdr **ip6, struct ip6_fw *f,
158 struct mbuf **m,
159 int *off, int *nxt, u_short *offset));
160static int port_match6 __P((u_short *portptr, int nports, u_short port,
161 int range_flag));
162static int tcp6flg_match __P((struct tcphdr *tcp6, struct ip6_fw *f));
163static int icmp6type_match __P((struct icmp6_hdr * icmp, struct ip6_fw * f));
164static void ip6fw_report __P((struct ip6_fw *f, struct ip6_hdr *ip6,
165 struct ifnet *rif, struct ifnet *oif, int off, int nxt));
166
167static int ip6_fw_chk __P((struct ip6_hdr **pip6,
168 struct ifnet *oif, u_int16_t *cookie, struct mbuf **m));
169static int ip6_fw_ctl __P((struct sockopt *));
170
171static char err_prefix[] = "ip6_fw_ctl:";
172extern lck_mtx_t *ip6_mutex;
173
174/*
175 * Returns 1 if the port is matched by the vector, 0 otherwise
176 */
177static
178__inline int
179port_match6(u_short *portptr, int nports, u_short port, int range_flag)
180{
181 if (!nports)
182 return 1;
183 if (range_flag) {
184 if (portptr[0] <= port && port <= portptr[1]) {
185 return 1;
186 }
187 nports -= 2;
188 portptr += 2;
189 }
190 while (nports-- > 0) {
191 if (*portptr++ == port) {
192 return 1;
193 }
194 }
195 return 0;
196}
197
198static int
199tcp6flg_match(struct tcphdr *tcp6, struct ip6_fw *f)
200{
201 u_char flg_set, flg_clr;
202
203 /*
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.
207 */
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))
211 return 0;
212
213 flg_set = tcp6->th_flags & f->fw_tcpf;
214 flg_clr = tcp6->th_flags & f->fw_tcpnf;
215
216 if (flg_set != f->fw_tcpf)
217 return 0;
218 if (flg_clr)
219 return 0;
220
221 return 1;
222}
223
224static int
225icmp6type_match(struct icmp6_hdr *icmp6, struct ip6_fw *f)
226{
227 int type;
228
229 if (!(f->fw_flg & IPV6_FW_F_ICMPBIT))
230 return(1);
231
232 type = icmp6->icmp6_type;
233
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))))))
238 return(1);
239
240 return(0); /* no match */
241}
242
243static int
244is_icmp6_query(struct ip6_hdr *ip6, int off)
245{
246 const struct icmp6_hdr *icmp6;
247 int icmp6_type;
248
249 icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
250 icmp6_type = icmp6->icmp6_type;
251
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)
257 return(1);
258
259 return(0);
260}
261
262static int
263ip6opts_match(struct ip6_hdr **pip6, struct ip6_fw *f, struct mbuf **m,
264 int *off, int *nxt, u_short *offset)
265{
266 int len;
267 struct ip6_hdr *ip6 = *pip6;
268 struct ip6_ext *ip6e;
269 u_char opts, nopts, nopts_sve;
270
271 opts = f->fw_ip6opt;
272 nopts = nopts_sve = f->fw_ip6nopt;
273
274 *nxt = ip6->ip6_nxt;
275 *off = sizeof(struct ip6_hdr);
276 len = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr);
277 while (*off < len) {
278 ip6e = (struct ip6_ext *)((caddr_t) ip6 + *off);
279 if ((*m)->m_len < *off + sizeof(*ip6e))
280 goto opts_check; /* XXX */
281
282 switch(*nxt) {
283 case IPPROTO_FRAGMENT:
284 if ((*m)->m_len >= *off + sizeof(struct ip6_frag)) {
285 struct ip6_frag *ip6f;
286
287 ip6f = (struct ip6_frag *) ((caddr_t)ip6 + *off);
288 *offset = ip6f->ip6f_offlg & IP6F_OFF_MASK;
289 }
290 opts &= ~IPV6_FW_IP6OPT_FRAG;
291 nopts &= ~IPV6_FW_IP6OPT_FRAG;
292 *off += sizeof(struct ip6_frag);
293 break;
294 case IPPROTO_AH:
295 opts &= ~IPV6_FW_IP6OPT_AH;
296 nopts &= ~IPV6_FW_IP6OPT_AH;
297 *off += (ip6e->ip6e_len + 2) << 2;
298 break;
299 default:
300 switch (*nxt) {
301 case IPPROTO_HOPOPTS:
302 opts &= ~IPV6_FW_IP6OPT_HOPOPT;
303 nopts &= ~IPV6_FW_IP6OPT_HOPOPT;
304 break;
305 case IPPROTO_ROUTING:
306 opts &= ~IPV6_FW_IP6OPT_ROUTE;
307 nopts &= ~IPV6_FW_IP6OPT_ROUTE;
308 break;
309 case IPPROTO_ESP:
310 opts &= ~IPV6_FW_IP6OPT_ESP;
311 nopts &= ~IPV6_FW_IP6OPT_ESP;
312 break;
313 case IPPROTO_NONE:
314 opts &= ~IPV6_FW_IP6OPT_NONXT;
315 nopts &= ~IPV6_FW_IP6OPT_NONXT;
316 goto opts_check;
317 break;
318 case IPPROTO_DSTOPTS:
319 opts &= ~IPV6_FW_IP6OPT_OPTS;
320 nopts &= ~IPV6_FW_IP6OPT_OPTS;
321 break;
322 default:
323 goto opts_check;
324 break;
325 }
326 *off += (ip6e->ip6e_len + 1) << 3;
327 break;
328 }
329 *nxt = ip6e->ip6e_nxt;
330
331 }
332 opts_check:
333 if (f->fw_ip6opt == f->fw_ip6nopt) /* XXX */
334 return 1;
335
336 if (opts == 0 && nopts == nopts_sve)
337 return 1;
338 else
339 return 0;
340}
341
342static
343__inline int
344iface_match(struct ifnet *ifp, union ip6_fw_if *ifu, int byname)
345{
346 /* Check by name or by IP address */
347 if (byname) {
348 /* Check unit number (-1 is wildcard) */
349 if (ifu->fu_via_if.unit != -1
350 && ifp->if_unit != ifu->fu_via_if.unit)
351 return(0);
352 /* Check name */
353 if (strncmp(ifp->if_name, ifu->fu_via_if.name, IP6FW_IFNLEN))
354 return(0);
355 return(1);
356 } else if (!IN6_IS_ADDR_UNSPECIFIED(&ifu->fu_via_ip6)) { /* Zero == wildcard */
357 struct ifaddr *ia;
358
359 ifnet_lock_shared(ifp);
360 for (ia = ifp->if_addrlist.tqh_first; ia; ia = ia->ifa_list.tqe_next)
361 {
362
363 if (ia->ifa_addr == NULL)
364 continue;
365 if (ia->ifa_addr->sa_family != AF_INET6)
366 continue;
367 if (!IN6_ARE_ADDR_EQUAL(&ifu->fu_via_ip6,
368 &(((struct sockaddr_in6 *)
369 (ia->ifa_addr))->sin6_addr)))
370 continue;
371 ifnet_lock_done(ifp);
372 return(1);
373 }
374 ifnet_lock_done(ifp);
375 return(0);
376 }
377 return(1);
378}
379
380static void
381ip6fw_report(struct ip6_fw *f, struct ip6_hdr *ip6,
382 struct ifnet *rif, struct ifnet *oif, int off, int nxt)
383{
384 static int counter;
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);
388 int count;
389 char *action;
390 char action2[32], proto[102], name[18];
391 int len;
392
393 count = f ? f->fw_pcnt : ++counter;
394 if (fw6_verbose_limit != 0 && count > fw6_verbose_limit)
395 return;
396
397 /* Print command name */
398 snprintf(SNPARGS(name, 0), "ip6fw: %d", f ? f->fw_number : -1);
399
400 action = action2;
401 if (!f)
402 action = "Refuse";
403 else {
404 switch (f->fw_flg & IPV6_FW_F_COMMAND) {
405 case IPV6_FW_F_DENY:
406 action = "Deny";
407 break;
408 case IPV6_FW_F_REJECT:
409 if (f->fw_reject_code == IPV6_FW_REJECT_RST)
410 action = "Reset";
411 else
412 action = "Unreach";
413 break;
414 case IPV6_FW_F_ACCEPT:
415 action = "Accept";
416 break;
417 case IPV6_FW_F_COUNT:
418 action = "Count";
419 break;
420 case IPV6_FW_F_DIVERT:
421 snprintf(SNPARGS(action2, 0), "Divert %d",
422 f->fw_divert_port);
423 break;
424 case IPV6_FW_F_TEE:
425 snprintf(SNPARGS(action2, 0), "Tee %d",
426 f->fw_divert_port);
427 break;
428 case IPV6_FW_F_SKIPTO:
429 snprintf(SNPARGS(action2, 0), "SkipTo %d",
430 f->fw_skipto_rule);
431 break;
432 default:
433 action = "UNKNOWN";
434 break;
435 }
436 }
437
438 switch (nxt) {
439 case IPPROTO_TCP:
440 len = snprintf(SNPARGS(proto, 0), "TCP [%s]",
441 ip6_sprintf(&ip6->ip6_src));
442 if (off > 0)
443 len += snprintf(SNPARGS(proto, len), ":%d ",
444 ntohs(tcp6->th_sport));
445 else
446 len += snprintf(SNPARGS(proto, len), " ");
447 len += snprintf(SNPARGS(proto, len), "[%s]",
448 ip6_sprintf(&ip6->ip6_dst));
449 if (off > 0)
450 snprintf(SNPARGS(proto, len), ":%d",
451 ntohs(tcp6->th_dport));
452 break;
453 case IPPROTO_UDP:
454 len = snprintf(SNPARGS(proto, 0), "UDP [%s]",
455 ip6_sprintf(&ip6->ip6_src));
456 if (off > 0)
457 len += snprintf(SNPARGS(proto, len), ":%d ",
458 ntohs(udp->uh_sport));
459 else
460 len += snprintf(SNPARGS(proto, len), " ");
461 len += snprintf(SNPARGS(proto, len), "[%s]",
462 ip6_sprintf(&ip6->ip6_dst));
463 if (off > 0)
464 snprintf(SNPARGS(proto, len), ":%d",
465 ntohs(udp->uh_dport));
466 break;
467 case IPPROTO_ICMPV6:
468 if (off > 0)
469 len = snprintf(SNPARGS(proto, 0), "IPV6-ICMP:%u.%u ",
470 icmp6->icmp6_type, icmp6->icmp6_code);
471 else
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));
477 break;
478 default:
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));
483 break;
484 }
485
486 if (oif)
487 log(LOG_AUTHPRIV | LOG_INFO, "%s %s %s out via %s\n",
488 name, action, proto, if_name(oif));
489 else if (rif)
490 log(LOG_AUTHPRIV | LOG_INFO, "%s %s %s in via %s\n",
491 name, action, proto, if_name(rif));
492 else
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);
498}
499
500/*
501 * Parameters:
502 *
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)
508 * #else
509 * *cookie Skip up to the first rule past this rule number;
510 * #endif
511 * *m The packet; we set to NULL when/if we nuke it.
512 *
513 * Return value:
514 *
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.
519 */
520
521static int
522ip6_fw_chk(struct ip6_hdr **pip6,
523 struct ifnet *oif, u_int16_t *cookie, struct mbuf **m)
524{
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;
529 u_short offset = 0;
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;
534#else
535 u_int16_t ignport = ntohs(*cookie);
536#endif
537 struct timeval timenow;
538
539 getmicrotime(&timenow);
540
541 *cookie = 0;
542 /*
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.
546 * #endif
547 */
548 chain = LIST_FIRST(&ip6_fw_chain);
549#ifdef IP6FW_DIVERT_RESTART
550 if (skipto) {
551 if (skipto >= 65535)
552 goto dropit;
553 while (chain && (chain->rule->fw_number <= skipto)) {
554 chain = LIST_NEXT(chain, chain);
555 }
556 if (! chain) goto dropit;
557 }
558#endif /* IP6FW_DIVERT_RESTART */
559 for (; chain; chain = LIST_NEXT(chain, chain)) {
560 struct ip6_fw *const f = chain->rule;
561
562 if (oif) {
563 /* Check direction outbound */
564 if (!(f->fw_flg & IPV6_FW_F_OUT))
565 continue;
566 } else {
567 /* Check direction inbound */
568 if (!(f->fw_flg & IPV6_FW_F_IN))
569 continue;
570 }
571
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]))
577
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)))
581 continue;
582
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)))
586 continue;
587
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;
592
593 /* Backwards compatibility hack for "via" */
594 if (!iface || !iface_match(iface,
595 &f->fw_in_if, f->fw_flg & IPV6_FW_F_OIFNAME))
596 continue;
597 } else {
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)))
602 continue;
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)))
607 continue;
608 }
609
610 /* Check IP options */
611 if (!ip6opts_match(&ip6, f, m, &off, &nxt, &offset))
612 continue;
613
614 /* Fragments */
615 if ((f->fw_flg & IPV6_FW_F_FRAG) && !offset)
616 continue;
617
618 /* Check protocol; if wildcard, match */
619 if (f->fw_prot == IPPROTO_IPV6)
620 goto got_match;
621
622 /* If different, don't match */
623 if (nxt != f->fw_prot)
624 continue;
625
626#define PULLUP_TO(len) do { \
627 if ((*m)->m_len < (len) \
628 && (*m = m_pullup(*m, (len))) == 0) { \
629 goto dropit; \
630 } \
631 *pip6 = ip6 = mtod(*m, struct ip6_hdr *); \
632 } while (0)
633
634 /* Protocol specific checks */
635 switch (nxt) {
636 case IPPROTO_TCP:
637 {
638 struct tcphdr *tcp6;
639
640 if (offset == 1) { /* cf. RFC 1858 */
641 PULLUP_TO(off + 4); /* XXX ? */
642 goto bogusfrag;
643 }
644 if (offset != 0) {
645 /*
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.
649 */
650 if (f->fw_nports != 0 ||
651 f->fw_tcpf != f->fw_tcpnf)
652 continue;
653
654 break;
655 }
656 PULLUP_TO(off + 14);
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))
661 continue;
662 src_port = ntohs(tcp6->th_sport);
663 dst_port = ntohs(tcp6->th_dport);
664 goto check_ports;
665 }
666
667 case IPPROTO_UDP:
668 {
669 struct udphdr *udp;
670
671 if (offset != 0) {
672 /*
673 * Port specification is unavailable -- if this
674 * rule specifies a port, we consider the rule
675 * a non-match.
676 */
677 if (f->fw_nports != 0)
678 continue;
679
680 break;
681 }
682 PULLUP_TO(off + 4);
683 udp = (struct udphdr *) ((caddr_t)ip6 + off);
684 src_port = ntohs(udp->uh_sport);
685 dst_port = ntohs(udp->uh_dport);
686check_ports:
687 if (!port_match6(&f->fw_pts[0],
688 IPV6_FW_GETNSRCP(f), src_port,
689 f->fw_flg & IPV6_FW_F_SRNG))
690 continue;
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))
694 continue;
695 break;
696 }
697
698 case IPPROTO_ICMPV6:
699 {
700 struct icmp6_hdr *icmp;
701
702 if (offset != 0) /* Type isn't valid */
703 break;
704 PULLUP_TO(off + 2);
705 icmp = (struct icmp6_hdr *) ((caddr_t)ip6 + off);
706 if (!icmp6type_match(icmp, f))
707 continue;
708 break;
709 }
710#undef PULLUP_TO
711
712bogusfrag:
713 if (fw6_verbose)
714 ip6fw_report(NULL, ip6, rif, oif, off, nxt);
715 goto dropit;
716 }
717
718got_match:
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:
723 case IPV6_FW_F_TEE:
724 if (f->fw_divert_port == ignport)
725 continue; /* ignore this rule */
726 break;
727 }
728
729#endif /* IP6FW_DIVERT_RESTART */
730 /* Update statistics */
731 f->fw_pcnt += 1;
732 f->fw_bcnt += ntohs(ip6->ip6_plen);
733 f->timestamp = timenow.tv_sec;
734
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);
738
739 /* Take appropriate action */
740 switch (f->fw_flg & IPV6_FW_F_COMMAND) {
741 case IPV6_FW_F_ACCEPT:
742 return(0);
743 case IPV6_FW_F_COUNT:
744 continue;
745 case IPV6_FW_F_DIVERT:
746#ifdef IP6FW_DIVERT_RESTART
747 *cookie = f->fw_number;
748#else
749 *cookie = htons(f->fw_divert_port);
750#endif /* IP6FW_DIVERT_RESTART */
751 return(f->fw_divert_port);
752 case IPV6_FW_F_TEE:
753 /*
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.
760 */
761 continue;
762 case IPV6_FW_F_SKIPTO:
763#ifdef DIAGNOSTIC
764 while (chain->chain.le_next
765 && chain->chain.le_next->rule->fw_number
766 < f->fw_skipto_rule)
767#else
768 while (chain->chain.le_next->rule->fw_number
769 < f->fw_skipto_rule)
770#endif
771 chain = chain->chain.le_next;
772 continue;
773 }
774
775 /* Deny/reject this packet using this rule */
776 rule = f;
777 break;
778 }
779
780#ifdef DIAGNOSTIC
781 /* Rule 65535 should always be there and should always match */
782 if (!chain)
783 panic("ip6_fw: chain");
784#endif
785
786 /*
787 * At this point, we're going to drop the packet.
788 * Send a reject notice if all of the following are true:
789 *
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
793 */
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:
800 {
801 struct tcphdr *const tcp =
802 (struct tcphdr *) ((caddr_t)ip6 + off);
803 struct {
804 struct ip6_hdr ip6;
805 struct tcphdr th;
806 } ti;
807 tcp_seq ack, seq;
808 int flags;
809
810 if (offset != 0 || (tcp->th_flags & TH_RST))
811 break;
812
813 ti.ip6 = *ip6;
814 ti.th = *tcp;
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) {
819 ack = 0;
820 seq = ti.th.th_ack;
821 flags = TH_RST;
822 } else {
823 ack = ti.th.th_seq;
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);
830 } else {
831 m_freem(*m);
832 *m = 0;
833 break;
834 }
835 seq = 0;
836 flags = TH_RST|TH_ACK;
837 }
838 bcopy(&ti, ip6, sizeof(ti));
839 tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1),
840 *m, ack, seq, flags);
841 *m = NULL;
842 break;
843 }
844 default: /* Send an ICMP unreachable using code */
845 if (oif)
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);
852 *m = NULL;
853 break;
854 }
855 }
856
857dropit:
858 /*
859 * Finally, drop the packet.
860 */
861 if (*m) {
862 m_freem(*m);
863 *m = NULL;
864 }
865 return(0);
866}
867
868static int
869add_entry6(struct ip6_fw_head *chainptr, struct ip6_fw *frwl)
870{
871 struct ip6_fw *ftmp = 0;
872 struct ip6_fw_chain *fwc = 0, *fcp, *fcpl = 0;
873 u_short nbr = 0;
874 int s;
875
876 fwc = _MALLOC(sizeof *fwc, M_IP6FW, M_WAITOK);
877 ftmp = _MALLOC(sizeof *ftmp, M_IP6FW, M_WAITOK);
878 if (!fwc || !ftmp) {
879 dprintf(("%s malloc said no\n", err_prefix));
880 if (fwc) FREE(fwc, M_IP6FW);
881 if (ftmp) FREE(ftmp, M_IP6FW);
882 return (ENOSPC);
883 }
884
885 bcopy(frwl, ftmp, sizeof(struct ip6_fw));
886 ftmp->fw_in_if.fu_via_if.name[IP6FW_IFNLEN - 1] = '\0';
887 ftmp->fw_pcnt = 0L;
888 ftmp->fw_bcnt = 0L;
889 fwc->rule = ftmp;
890
891 s = splnet();
892
893 if (!chainptr->lh_first) {
894 LIST_INSERT_HEAD(chainptr, fwc, chain);
895 splx(s);
896 return(0);
897 } else if (ftmp->fw_number == (u_short)-1) {
898 if (fwc) FREE(fwc, M_IP6FW);
899 if (ftmp) FREE(ftmp, M_IP6FW);
900 splx(s);
901 dprintf(("%s bad rule number\n", err_prefix));
902 return (EINVAL);
903 }
904
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;
910 else
911 break;
912 }
913 if (nbr < (u_short)-1 - 100)
914 nbr += 100;
915 ftmp->fw_number = nbr;
916 }
917
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) {
921 if (fcpl) {
922 LIST_INSERT_AFTER(fcpl, fwc, chain);
923 } else {
924 LIST_INSERT_HEAD(chainptr, fwc, chain);
925 }
926 break;
927 } else {
928 fcpl = fcp;
929 }
930 }
931
932 splx(s);
933 return (0);
934}
935
936static int
937del_entry6(struct ip6_fw_head *chainptr, u_short number)
938{
939 struct ip6_fw_chain *fcp;
940 int s;
941
942 s = splnet();
943
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);
949 splx(s);
950 FREE(fcp->rule, M_IP6FW);
951 FREE(fcp, M_IP6FW);
952 return 0;
953 }
954 }
955 }
956
957 splx(s);
958 return (EINVAL);
959}
960
961static int
962zero_entry6(struct ip6_fw *frwl)
963{
964 struct ip6_fw_chain *fcp;
965 int s;
966
967 /*
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.
971 */
972 s = splnet();
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;
977 }
978 splx(s);
979
980 if (fw6_verbose) {
981 if (frwl)
982 log(LOG_AUTHPRIV | LOG_NOTICE,
983 "ip6fw: Entry %d cleared.\n", frwl->fw_number);
984 else
985 log(LOG_AUTHPRIV | LOG_NOTICE,
986 "ip6fw: Accounting cleared.\n");
987 }
988
989 return(0);
990}
991
992static struct ip6_fw *
993check_ip6fw_struct(struct ip6_fw *frwl)
994{
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));
999 return (NULL);
1000 }
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));
1004 return (NULL);
1005 }
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));
1012 return (NULL);
1013 }
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",
1020 err_prefix));
1021 return (NULL);
1022 }
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)));
1027 return (NULL);
1028 }
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)));
1032 return (NULL);
1033 }
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)));
1037 return (NULL);
1038 }
1039 /*
1040 * Protocols other than TCP/UDP don't use port range
1041 */
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",
1046 err_prefix));
1047 return(NULL);
1048 }
1049
1050 /*
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.
1054 */
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));
1064 return(NULL);
1065 }
1066
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));
1071 return(NULL);
1072 }
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));
1076 return(NULL);
1077 }
1078 }
1079
1080 /* Check command specific stuff */
1081 switch (frwl->fw_flg & IPV6_FW_F_COMMAND)
1082 {
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));
1088 return(NULL);
1089 }
1090 break;
1091 case IPV6_FW_F_DIVERT: /* Diverting to port zero is invalid */
1092 case IPV6_FW_F_TEE:
1093 if (frwl->fw_divert_port == 0) {
1094 dprintf(("%s can't divert to port 0\n", err_prefix));
1095 return (NULL);
1096 }
1097 break;
1098 case IPV6_FW_F_DENY:
1099 case IPV6_FW_F_ACCEPT:
1100 case IPV6_FW_F_COUNT:
1101 case IPV6_FW_F_SKIPTO:
1102 break;
1103 default:
1104 dprintf(("%s invalid command\n", err_prefix));
1105 return(NULL);
1106 }
1107
1108 return frwl;
1109}
1110
1111/*#####*/
1112#if 0
1113static int
1114ip6_fw_ctl(int stage, struct mbuf **mm)
1115{
1116 int error;
1117 struct mbuf *m;
1118
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 */
1122 if (!m)
1123 return(ENOBUFS);
1124 if (sizeof *(fcp->rule) > MLEN) {
1125 MCLGET(m, M_WAIT);
1126 if ((m->m_flags & M_EXT) == 0) {
1127 m_free(m);
1128 return(ENOBUFS);
1129 }
1130 }
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 */
1135 if (!m->m_next) {
1136 m_freem(*mm);
1137 return(ENOBUFS);
1138 }
1139 m = m->m_next;
1140 if (sizeof *(fcp->rule) > MLEN) {
1141 MCLGET(m, M_WAIT);
1142 if ((m->m_flags & M_EXT) == 0) {
1143 m_freem(*mm);
1144 return(ENOBUFS);
1145 }
1146 }
1147 m->m_len = 0;
1148 }
1149 return (0);
1150 }
1151 m = *mm;
1152 /* only allow get calls if secure mode > 2 */
1153 if (securelevel > 2) {
1154 if (m) {
1155 (void)m_freem(m);
1156 *mm = 0;
1157 }
1158 return(EPERM);
1159 }
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;
1164 int s = splnet();
1165 LIST_REMOVE(ip6_fw_chain.lh_first, chain);
1166 splx(s);
1167 FREE(fcp->rule, M_IP6FW);
1168 FREE(fcp, M_IP6FW);
1169 }
1170 if (m) {
1171 (void)m_freem(m);
1172 *mm = 0;
1173 }
1174 return (0);
1175 }
1176 if (stage == IPV6_FW_ZERO) {
1177 error = zero_entry6(m);
1178 if (m) {
1179 (void)m_freem(m);
1180 *mm = 0;
1181 }
1182 return (error);
1183 }
1184 if (m == NULL) {
1185 printf("%s NULL mbuf ptr\n", err_prefix);
1186 return (EINVAL);
1187 }
1188
1189 if (stage == IPV6_FW_ADD) {
1190 struct ip6_fw *frwl = check_ip6fw_mbuf(m);
1191
1192 if (!frwl)
1193 error = EINVAL;
1194 else
1195 error = add_entry6(&ip6_fw_chain, frwl);
1196 if (m) {
1197 (void)m_freem(m);
1198 *mm = 0;
1199 }
1200 return error;
1201 }
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)));
1206 error = EINVAL;
1207 } else if (mtod(m, struct ip6_fw *)->fw_number == (u_short)-1) {
1208 dprintf(("%s can't delete rule 65535\n", err_prefix));
1209 error = EINVAL;
1210 } else
1211 error = del_entry6(&ip6_fw_chain,
1212 mtod(m, struct ip6_fw *)->fw_number);
1213 if (m) {
1214 (void)m_freem(m);
1215 *mm = 0;
1216 }
1217 return error;
1218 }
1219
1220 dprintf(("%s unknown request %d\n", err_prefix, stage));
1221 if (m) {
1222 (void)m_freem(m);
1223 *mm = 0;
1224 }
1225 return (EINVAL);
1226}
1227#endif
1228
1229static int
1230ip6_fw_ctl(struct sockopt *sopt)
1231{
1232 int error = 0;
1233 int spl;
1234 int valsize;
1235 struct ip6_fw rule;
1236
1237 if (securelevel >= 3 &&
1238 (sopt->sopt_dir != SOPT_GET || sopt->sopt_name != IPV6_FW_GET))
1239 return (EPERM);
1240
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;
1246
1247 /* save sopt->sopt_valsize */
1248 valsize = sopt->sopt_valsize;
1249 if (error = sooptcopyin(sopt, &rule, sizeof(rule), sizeof(rule)))
1250 return error;
1251
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". */
1254
1255 switch (sopt->sopt_name)
1256 {
1257 case IPV6_FW_GET:
1258 {
1259 struct ip6_fw_chain *fcp;
1260 struct ip6_fw *buf;
1261 size_t size = 0;
1262
1263 spl = splnet();
1264 LIST_FOREACH(fcp, &ip6_fw_chain, chain)
1265 size += sizeof *buf;
1266
1267 buf = _MALLOC(size, M_TEMP, M_WAITOK);
1268 if (!buf) error = ENOBUFS;
1269 else
1270 {
1271 struct ip6_fw *bp = buf;
1272 LIST_FOREACH(fcp, &ip6_fw_chain, chain)
1273 {
1274 bcopy(fcp->rule, bp, sizeof *bp);
1275 bp->version = IPV6_FW_CURRENT_API_VERSION;
1276 bp++;
1277 }
1278 }
1279
1280 splx(spl);
1281 if (buf)
1282 {
1283 sopt->sopt_valsize = valsize;
1284 error = sooptcopyout(sopt, buf, size);
1285 FREE(buf, M_TEMP);
1286 }
1287
1288 break;
1289 }
1290
1291 case IPV6_FW_FLUSH:
1292 spl = splnet();
1293 while (ip6_fw_chain.lh_first &&
1294 ip6_fw_chain.lh_first->rule->fw_number != (u_short)-1)
1295 {
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);
1299 FREE(fcp, M_IP6FW);
1300 }
1301 splx(spl);
1302 break;
1303
1304 case IPV6_FW_ZERO:
1305 error = zero_entry6(&rule);
1306 break;
1307
1308 case IPV6_FW_ADD:
1309 if (check_ip6fw_struct(&rule))
1310 error = add_entry6(&ip6_fw_chain, &rule);
1311 else
1312 error = EINVAL;
1313 break;
1314
1315 case IPV6_FW_DEL:
1316 if (rule.fw_number == (u_short)-1)
1317 {
1318 dprintf(("%s can't delete rule 65535\n", err_prefix));
1319 error = EINVAL;
1320 }
1321 else
1322 error = del_entry6(&ip6_fw_chain, rule.fw_number);
1323 break;
1324
1325 default:
1326 dprintf(("%s invalid option %d\n", err_prefix, sopt->sopt_name));
1327 error = EINVAL;
1328 }
1329
1330 return error;
1331}
1332
1333void
1334ip6_fw_init(void)
1335{
1336 struct ip6_fw default_rule;
1337
1338 ip6_fw_chk_ptr = ip6_fw_chk;
1339 ip6_fw_ctl_ptr = ip6_fw_ctl;
1340 LIST_INIT(&ip6_fw_chain);
1341
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;
1347#else
1348 default_rule.fw_flg |= IPV6_FW_F_DENY;
1349#endif
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__);
1354
1355 printf("IPv6 packet filtering initialized, ");
1356#ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT
1357 printf("default to accept, ");
1358#endif
1359#ifndef IPV6FIREWALL_VERBOSE
1360 printf("logging disabled\n");
1361#else
1362 if (fw6_verbose_limit == 0)
1363 printf("unlimited logging\n");
1364 else
1365 printf("logging limited to %d packets/entry\n",
1366 fw6_verbose_limit);
1367#endif
1368}
1369