]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/ip6_fw.c
0a7943e02a3b322031583de7e6f28b6f2102a04c
[apple/xnu.git] / bsd / netinet6 / ip6_fw.c
1 /* $KAME: ip6_fw.c,v 1.11 2000/03/10 04:22:18 k-sugyou Exp $ */
2
3 /*
4 * Copyright (c) 1993 Daniel Boulet
5 * Copyright (c) 1994 Ugen J.S.Antsilevich
6 * Copyright (c) 1996 Alex Nash
7 *
8 * Redistribution and use in source forms, with and without modification,
9 * are permitted provided that this entire comment appears intact.
10 *
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.
14 *
15 * This software is provided ``AS IS'' without any warranties of any kind.
16 *
17 * $Id: ip6_fw.c,v 1.3 2001/05/01 21:52:50 lindak Exp $
18 */
19
20 /*
21 * Implement IPv6 packet firewall
22 */
23
24 #ifdef __FreeBSD__
25 #include "opt_ip6fw.h"
26 #if __FreeBSD__ >= 3
27 #include "opt_inet.h"
28 #endif
29 #endif
30
31 #if IP6DIVERT
32 #error "NOT SUPPORTED IPV6 DIVERT"
33 #endif
34 #if IP6FW_DIVERT_RESTART
35 #error "NOT SUPPORTED IPV6 DIVERT"
36 #endif
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/malloc.h>
41 #include <sys/mbuf.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>
47 #endif
48 #include <sys/time.h>
49 #include <net/if.h>
50 #include <net/route.h>
51 #include <netinet/in_systm.h>
52 #include <netinet/in.h>
53 #include <netinet/ip.h>
54
55 #include <netinet/ip6.h>
56 #include <netinet6/ip6_var.h>
57 #include <netinet6/in6_var.h>
58 #include <netinet/icmp6.h>
59
60 #include <netinet/in_pcb.h>
61
62 #include <netinet6/ip6_fw.h>
63 #if TCP6
64 #include <netinet6/tcp6.h>
65 #include <netinet6/tcp6_timer.h>
66 #include <netinet6/tcp6_var.h>
67 #endif
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>
74
75 #if defined(__NetBSD__) || defined(__OpenBSD__)
76 #include <vm/vm.h>
77 #endif
78 #if __FreeBSD__ || defined (__APPLE__)
79 #include <sys/sysctl.h>
80 #endif
81
82 #include <net/net_osdep.h>
83
84 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
85 MALLOC_DEFINE(M_IP6FW, "Ip6Fw/Ip6Acct", "Ip6Fw/Ip6Acct chain's");
86 #else
87 #ifndef M_IP6FW
88 #define M_IP6FW M_TEMP
89 #endif
90 #endif
91
92 static int fw6_debug = 1;
93 #if IPV6FIREWALL_VERBOSE
94 static int fw6_verbose = 1;
95 #else
96 static int fw6_verbose = 0;
97 #endif
98 #if IPV6FIREWALL_VERBOSE_LIMIT
99 static int fw6_verbose_limit = IPV6FIREWALL_VERBOSE_LIMIT;
100 #else
101 static int fw6_verbose_limit = 0;
102 #endif
103
104 LIST_HEAD (ip6_fw_head, ip6_fw_chain) ip6_fw_chain;
105
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, "");
111
112 #define dprintf(a) if (!fw6_debug); else printf a
113
114 #define print_ip6(a) printf("[%s]", ip6_sprintf(a))
115
116 #define dprint_ip6(a) if (!fw6_debug); else print_ip6(a)
117
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,
124 struct mbuf **m,
125 int *off, int *nxt, u_short *offset));
126 static int port_match6 __P((u_short *portptr, int nports, u_short port,
127 int range_flag));
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));
132
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));
136
137 static char err_prefix[] = "ip6_fw_ctl:";
138
139 /*
140 * Returns 1 if the port is matched by the vector, 0 otherwise
141 */
142 static
143 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
144 __inline
145 #else
146 inline
147 #endif
148 int
149 port_match6(u_short *portptr, int nports, u_short port, int range_flag)
150 {
151 if (!nports)
152 return 1;
153 if (range_flag) {
154 if (portptr[0] <= port && port <= portptr[1]) {
155 return 1;
156 }
157 nports -= 2;
158 portptr += 2;
159 }
160 while (nports-- > 0) {
161 if (*portptr++ == port) {
162 return 1;
163 }
164 }
165 return 0;
166 }
167
168 static int
169 tcp6flg_match(struct tcphdr *tcp6, struct ip6_fw *f)
170 {
171 u_char flg_set, flg_clr;
172
173 if ((f->fw_tcpf & IPV6_FW_TCPF_ESTAB) &&
174 (tcp6->th_flags & (IPV6_FW_TCPF_RST | IPV6_FW_TCPF_ACK)))
175 return 1;
176
177 flg_set = tcp6->th_flags & f->fw_tcpf;
178 flg_clr = tcp6->th_flags & f->fw_tcpnf;
179
180 if (flg_set != f->fw_tcpf)
181 return 0;
182 if (flg_clr)
183 return 0;
184
185 return 1;
186 }
187
188 static int
189 icmp6type_match(struct icmp6_hdr *icmp6, struct ip6_fw *f)
190 {
191 int type;
192
193 if (!(f->fw_flg & IPV6_FW_F_ICMPBIT))
194 return(1);
195
196 type = icmp6->icmp6_type;
197
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))))))
202 return(1);
203
204 return(0); /* no match */
205 }
206
207 static int
208 is_icmp6_query(struct ip6_hdr *ip6, int off)
209 {
210 const struct icmp6_hdr *icmp6;
211 int icmp6_type;
212
213 icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
214 icmp6_type = icmp6->icmp6_type;
215
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)
221 return(1);
222
223 return(0);
224 }
225
226 static int
227 ip6opts_match(struct ip6_hdr **pip6, struct ip6_fw *f, struct mbuf **m,
228 int *off, int *nxt, u_short *offset)
229 {
230 int len;
231 struct ip6_hdr *ip6 = *pip6;
232 struct ip6_ext *ip6e;
233 u_char opts, nopts, nopts_sve;
234
235 opts = f->fw_ip6opt;
236 nopts = nopts_sve = f->fw_ip6nopt;
237
238 *nxt = ip6->ip6_nxt;
239 *off = sizeof(struct ip6_hdr);
240 len = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr);
241 while (*off < len) {
242 ip6e = (struct ip6_ext *)((caddr_t) ip6 + *off);
243 if ((*m)->m_len < *off + sizeof(*ip6e))
244 goto opts_check; /* XXX */
245
246 switch(*nxt) {
247 case IPPROTO_FRAGMENT:
248 if ((*m)->m_len < *off + sizeof(struct ip6_frag)) {
249 struct ip6_frag *ip6f;
250
251 ip6f = (struct ip6_frag *) ((caddr_t)ip6 + *off);
252 *offset = ip6f->ip6f_offlg | IP6F_OFF_MASK;
253 }
254 opts &= ~IPV6_FW_IP6OPT_FRAG;
255 nopts &= ~IPV6_FW_IP6OPT_FRAG;
256 *off += sizeof(struct ip6_frag);
257 break;
258 case IPPROTO_AH:
259 opts &= ~IPV6_FW_IP6OPT_AH;
260 nopts &= ~IPV6_FW_IP6OPT_AH;
261 *off += (ip6e->ip6e_len + 2) << 2;
262 break;
263 default:
264 switch (*nxt) {
265 case IPPROTO_HOPOPTS:
266 opts &= ~IPV6_FW_IP6OPT_HOPOPT;
267 nopts &= ~IPV6_FW_IP6OPT_HOPOPT;
268 break;
269 case IPPROTO_ROUTING:
270 opts &= ~IPV6_FW_IP6OPT_ROUTE;
271 nopts &= ~IPV6_FW_IP6OPT_ROUTE;
272 break;
273 case IPPROTO_ESP:
274 opts &= ~IPV6_FW_IP6OPT_ESP;
275 nopts &= ~IPV6_FW_IP6OPT_ESP;
276 break;
277 case IPPROTO_NONE:
278 opts &= ~IPV6_FW_IP6OPT_NONXT;
279 nopts &= ~IPV6_FW_IP6OPT_NONXT;
280 goto opts_check;
281 break;
282 case IPPROTO_DSTOPTS:
283 opts &= ~IPV6_FW_IP6OPT_OPTS;
284 nopts &= ~IPV6_FW_IP6OPT_OPTS;
285 break;
286 default:
287 goto opts_check;
288 break;
289 }
290 *off += (ip6e->ip6e_len + 1) << 3;
291 break;
292 }
293 *nxt = ip6e->ip6e_nxt;
294
295 }
296 opts_check:
297 if (f->fw_ip6opt == f->fw_ip6nopt) /* XXX */
298 return 1;
299
300 if (opts == 0 && nopts == nopts_sve)
301 return 1;
302 else
303 return 0;
304 }
305
306 static
307 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
308 __inline
309 #else
310 inline
311 #endif
312 int
313 iface_match(struct ifnet *ifp, union ip6_fw_if *ifu, int byname)
314 {
315 /* Check by name or by IP address */
316 if (byname) {
317 #if __NetBSD__
318 {
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))
323 return(0);
324 }
325 #else
326 /* Check unit number (-1 is wildcard) */
327 if (ifu->fu_via_if.unit != -1
328 && ifp->if_unit != ifu->fu_via_if.unit)
329 return(0);
330 /* Check name */
331 if (strncmp(ifp->if_name, ifu->fu_via_if.name, FW_IFNLEN))
332 return(0);
333 #endif
334 return(1);
335 } else if (!IN6_IS_ADDR_UNSPECIFIED(&ifu->fu_via_ip6)) { /* Zero == wildcard */
336 struct ifaddr *ia;
337
338 #if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
339 for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next)
340 #else
341 for (ia = ifp->if_addrlist.tqh_first; ia; ia = ia->ifa_list.tqe_next)
342 #endif
343 {
344
345 if (ia->ifa_addr == NULL)
346 continue;
347 if (ia->ifa_addr->sa_family != AF_INET6)
348 continue;
349 if (!IN6_ARE_ADDR_EQUAL(&ifu->fu_via_ip6,
350 &(((struct sockaddr_in6 *)
351 (ia->ifa_addr))->sin6_addr)))
352 continue;
353 return(1);
354 }
355 return(0);
356 }
357 return(1);
358 }
359
360 static void
361 ip6fw_report(struct ip6_fw *f, struct ip6_hdr *ip6,
362 struct ifnet *rif, struct ifnet *oif, int off, int nxt)
363 {
364 static int counter;
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);
368 int count;
369
370 count = f ? f->fw_pcnt : ++counter;
371 if (fw6_verbose_limit != 0 && count > fw6_verbose_limit)
372 return;
373
374 /* Print command name */
375 printf("ip6fw: %d ", f ? f->fw_number : -1);
376 if (!f)
377 printf("Refuse");
378 else
379 switch (f->fw_flg & IPV6_FW_F_COMMAND) {
380 case IPV6_FW_F_DENY:
381 printf("Deny");
382 break;
383 case IPV6_FW_F_REJECT:
384 if (f->fw_reject_code == IPV6_FW_REJECT_RST)
385 printf("Reset");
386 else
387 printf("Unreach");
388 break;
389 case IPV6_FW_F_ACCEPT:
390 printf("Accept");
391 break;
392 case IPV6_FW_F_COUNT:
393 printf("Count");
394 break;
395 case IPV6_FW_F_DIVERT:
396 printf("Divert %d", f->fw_divert_port);
397 break;
398 case IPV6_FW_F_TEE:
399 printf("Tee %d", f->fw_divert_port);
400 break;
401 case IPV6_FW_F_SKIPTO:
402 printf("SkipTo %d", f->fw_skipto_rule);
403 break;
404 default:
405 printf("UNKNOWN");
406 break;
407 }
408 printf(" ");
409
410 switch (nxt) {
411 case IPPROTO_TCP:
412 printf("TCP ");
413 print_ip6(&ip6->ip6_src);
414 if (off > 0)
415 printf(":%d ", ntohs(tcp6->th_sport));
416 else
417 printf(" ");
418 print_ip6(&ip6->ip6_dst);
419 if (off > 0)
420 printf(":%d", ntohs(tcp6->th_dport));
421 break;
422 case IPPROTO_UDP:
423 printf("UDP ");
424 print_ip6(&ip6->ip6_src);
425 if (off > 0)
426 printf(":%d ", ntohs(udp->uh_sport));
427 else
428 printf(" ");
429 print_ip6(&ip6->ip6_dst);
430 if (off > 0)
431 printf(":%d", ntohs(udp->uh_dport));
432 break;
433 case IPPROTO_ICMPV6:
434 if (off > 0)
435 printf("IPV6-ICMP:%u.%u ", icmp6->icmp6_type, icmp6->icmp6_code);
436 else
437 printf("IPV6-ICMP ");
438 print_ip6(&ip6->ip6_src);
439 printf(" ");
440 print_ip6(&ip6->ip6_dst);
441 break;
442 default:
443 printf("P:%d ", nxt);
444 print_ip6(&ip6->ip6_src);
445 printf(" ");
446 print_ip6(&ip6->ip6_dst);
447 break;
448 }
449 if (oif)
450 printf(" out via %s", if_name(oif));
451 else if (rif)
452 printf(" in via %s", if_name(rif));
453 printf("\n");
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);
457 }
458
459 /*
460 * Parameters:
461 *
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)
467 * #else
468 * *cookie Skip up to the first rule past this rule number;
469 * #endif
470 * *m The packet; we set to NULL when/if we nuke it.
471 *
472 * Return value:
473 *
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.
478 */
479
480 static int
481 ip6_fw_chk(struct ip6_hdr **pip6,
482 struct ifnet *oif, u_int16_t *cookie, struct mbuf **m)
483 {
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;
488 u_short offset = 0;
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;
493 #else
494 u_int16_t ignport = ntohs(*cookie);
495 #endif
496
497 *cookie = 0;
498 /*
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.
502 * #endif
503 */
504 chain = LIST_FIRST(&ip6_fw_chain);
505 #if IP6FW_DIVERT_RESTART
506 if (skipto) {
507 if (skipto >= 65535)
508 goto dropit;
509 while (chain && (chain->rule->fw_number <= skipto)) {
510 chain = LIST_NEXT(chain, chain);
511 }
512 if (! chain) goto dropit;
513 }
514 #endif /* IP6FW_DIVERT_RESTART */
515 for (; chain; chain = LIST_NEXT(chain, chain)) {
516 register struct ip6_fw *const f = chain->rule;
517
518 if (oif) {
519 /* Check direction outbound */
520 if (!(f->fw_flg & IPV6_FW_F_OUT))
521 continue;
522 } else {
523 /* Check direction inbound */
524 if (!(f->fw_flg & IPV6_FW_F_IN))
525 continue;
526 }
527
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]))
533
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)))
537 continue;
538
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)))
542 continue;
543
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;
548
549 /* Backwards compatibility hack for "via" */
550 if (!iface || !iface_match(iface,
551 &f->fw_in_if, f->fw_flg & IPV6_FW_F_OIFNAME))
552 continue;
553 } else {
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)))
558 continue;
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)))
563 continue;
564 }
565
566 /* Check IP options */
567 if (!ip6opts_match(&ip6, f, m, &off, &nxt, &offset))
568 continue;
569
570 /* Fragments */
571 if ((f->fw_flg & IPV6_FW_F_FRAG) && !offset)
572 continue;
573
574 /* Check protocol; if wildcard, match */
575 if (f->fw_prot == IPPROTO_IPV6)
576 goto got_match;
577
578 /* If different, don't match */
579 if (nxt != f->fw_prot)
580 continue;
581
582 #define PULLUP_TO(len) do { \
583 if ((*m)->m_len < (len) \
584 && (*m = m_pullup(*m, (len))) == 0) { \
585 goto dropit; \
586 } \
587 *pip6 = ip6 = mtod(*m, struct ip6_hdr *); \
588 } while (0)
589
590 /* Protocol specific checks */
591 switch (nxt) {
592 case IPPROTO_TCP:
593 {
594 struct tcphdr *tcp6;
595
596 if (offset == 1) { /* cf. RFC 1858 */
597 PULLUP_TO(off + 4); /* XXX ? */
598 goto bogusfrag;
599 }
600 if (offset != 0) {
601 /*
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.
605 */
606 if (f->fw_nports != 0 ||
607 f->fw_tcpf != f->fw_tcpnf)
608 continue;
609
610 break;
611 }
612 PULLUP_TO(off + 14);
613 tcp6 = (struct tcphdr *) ((caddr_t)ip6 + off);
614 if (f->fw_tcpf != f->fw_tcpnf && !tcp6flg_match(tcp6, f))
615 continue;
616 src_port = ntohs(tcp6->th_sport);
617 dst_port = ntohs(tcp6->th_dport);
618 goto check_ports;
619 }
620
621 case IPPROTO_UDP:
622 {
623 struct udphdr *udp;
624
625 if (offset != 0) {
626 /*
627 * Port specification is unavailable -- if this
628 * rule specifies a port, we consider the rule
629 * a non-match.
630 */
631 if (f->fw_nports != 0)
632 continue;
633
634 break;
635 }
636 PULLUP_TO(off + 4);
637 udp = (struct udphdr *) ((caddr_t)ip6 + off);
638 src_port = ntohs(udp->uh_sport);
639 dst_port = ntohs(udp->uh_dport);
640 check_ports:
641 if (!port_match6(&f->fw_pts[0],
642 IPV6_FW_GETNSRCP(f), src_port,
643 f->fw_flg & IPV6_FW_F_SRNG))
644 continue;
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))
648 continue;
649 break;
650 }
651
652 case IPPROTO_ICMPV6:
653 {
654 struct icmp6_hdr *icmp;
655
656 if (offset != 0) /* Type isn't valid */
657 break;
658 PULLUP_TO(off + 2);
659 icmp = (struct icmp6_hdr *) ((caddr_t)ip6 + off);
660 if (!icmp6type_match(icmp, f))
661 continue;
662 break;
663 }
664 #undef PULLUP_TO
665
666 bogusfrag:
667 if (fw6_verbose)
668 ip6fw_report(NULL, ip6, rif, oif, off, nxt);
669 goto dropit;
670 }
671
672 got_match:
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:
677 case IPV6_FW_F_TEE:
678 if (f->fw_divert_port == ignport)
679 continue; /* ignore this rule */
680 break;
681 }
682
683 #endif /* IP6FW_DIVERT_RESTART */
684 /* Update statistics */
685 f->fw_pcnt += 1;
686 f->fw_bcnt += ntohs(ip6->ip6_plen);
687 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
688 f->timestamp = time_second;
689 #else
690 f->timestamp = time.tv_sec;
691 #endif
692
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);
696
697 /* Take appropriate action */
698 switch (f->fw_flg & IPV6_FW_F_COMMAND) {
699 case IPV6_FW_F_ACCEPT:
700 return(0);
701 case IPV6_FW_F_COUNT:
702 continue;
703 case IPV6_FW_F_DIVERT:
704 #if IP6FW_DIVERT_RESTART
705 *cookie = f->fw_number;
706 #else
707 *cookie = htons(f->fw_divert_port);
708 #endif /* IP6FW_DIVERT_RESTART */
709 return(f->fw_divert_port);
710 case IPV6_FW_F_TEE:
711 /*
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.
718 */
719 continue;
720 case IPV6_FW_F_SKIPTO:
721 #if DIAGNOSTIC
722 while (chain->chain.le_next
723 && chain->chain.le_next->rule->fw_number
724 < f->fw_skipto_rule)
725 #else
726 while (chain->chain.le_next->rule->fw_number
727 < f->fw_skipto_rule)
728 #endif
729 chain = chain->chain.le_next;
730 continue;
731 }
732
733 /* Deny/reject this packet using this rule */
734 rule = f;
735 break;
736 }
737
738 #if DIAGNOSTIC
739 /* Rule 65535 should always be there and should always match */
740 if (!chain)
741 panic("ip6_fw: chain");
742 #endif
743
744 /*
745 * At this point, we're going to drop the packet.
746 * Send a reject notice if all of the following are true:
747 *
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
751 */
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:
758 #if 1 /*not tested*/
759 {
760 struct tcphdr *const tcp =
761 (struct tcphdr *) ((caddr_t)ip6 + off);
762 struct {
763 struct ip6_hdr ip6;
764 struct tcphdr th;
765 } ti;
766 tcp_seq ack, seq;
767 int flags;
768
769 if (offset != 0 || (tcp->th_flags & TH_RST))
770 break;
771
772 ti.ip6 = *ip6;
773 ti.th = *tcp;
774 NTOHL(ti.th.th_seq);
775 NTOHL(ti.th.th_ack);
776 ti.ip6.ip6_nxt = IPPROTO_TCP;
777 if (ti.th.th_flags & TH_ACK) {
778 ack = 0;
779 seq = ti.th.th_ack;
780 flags = TH_RST;
781 } else {
782 ack = ti.th.th_seq;
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);
789 } else {
790 m_freem(*m);
791 *m = 0;
792 break;
793 }
794 seq = 0;
795 flags = TH_RST|TH_ACK;
796 }
797 bcopy(&ti, ip6, sizeof(ti));
798 #if TCP6
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),
803 ack, seq, flags);
804 #elif defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
805 tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1),
806 *m, ack, seq, flags, 1);
807 #else
808 m_freem(*m);
809 #endif
810 *m = NULL;
811 break;
812 }
813 #endif
814 default: /* Send an ICMP unreachable using code */
815 if (oif)
816 (*m)->m_pkthdr.rcvif = oif;
817 icmp6_error(*m, ICMP6_DST_UNREACH,
818 rule->fw_reject_code, 0);
819 *m = NULL;
820 break;
821 }
822 }
823
824 dropit:
825 /*
826 * Finally, drop the packet.
827 */
828 if (*m) {
829 m_freem(*m);
830 *m = NULL;
831 }
832 return(0);
833 }
834
835 static int
836 add_entry6(struct ip6_fw_head *chainptr, struct ip6_fw *frwl)
837 {
838 struct ip6_fw *ftmp = 0;
839 struct ip6_fw_chain *fwc = 0, *fcp, *fcpl = 0;
840 u_short nbr = 0;
841 int s;
842
843 fwc = _MALLOC(sizeof *fwc, M_IP6FW, M_NOWAIT);
844 ftmp = _MALLOC(sizeof *ftmp, M_IP6FW, M_NOWAIT);
845 if (!fwc || !ftmp) {
846 dprintf(("%s malloc said no\n", err_prefix));
847 if (fwc) _FREE(fwc, M_IP6FW);
848 if (ftmp) _FREE(ftmp, M_IP6FW);
849 return (ENOSPC);
850 }
851
852 bcopy(frwl, ftmp, sizeof(struct ip6_fw));
853 ftmp->fw_in_if.fu_via_if.name[FW_IFNLEN - 1] = '\0';
854 ftmp->fw_pcnt = 0L;
855 ftmp->fw_bcnt = 0L;
856 fwc->rule = ftmp;
857
858 s = splnet();
859
860 if (!chainptr->lh_first) {
861 LIST_INSERT_HEAD(chainptr, fwc, chain);
862 splx(s);
863 return(0);
864 } else if (ftmp->fw_number == (u_short)-1) {
865 if (fwc) _FREE(fwc, M_IP6FW);
866 if (ftmp) _FREE(ftmp, M_IP6FW);
867 splx(s);
868 dprintf(("%s bad rule number\n", err_prefix));
869 return (EINVAL);
870 }
871
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;
877 else
878 break;
879 }
880 if (nbr < (u_short)-1 - 100)
881 nbr += 100;
882 ftmp->fw_number = nbr;
883 }
884
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) {
888 if (fcpl) {
889 LIST_INSERT_AFTER(fcpl, fwc, chain);
890 } else {
891 LIST_INSERT_HEAD(chainptr, fwc, chain);
892 }
893 break;
894 } else {
895 fcpl = fcp;
896 }
897 }
898
899 splx(s);
900 return (0);
901 }
902
903 static int
904 del_entry6(struct ip6_fw_head *chainptr, u_short number)
905 {
906 struct ip6_fw_chain *fcp;
907 int s;
908
909 s = splnet();
910
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);
916 splx(s);
917 _FREE(fcp->rule, M_IP6FW);
918 _FREE(fcp, M_IP6FW);
919 return 0;
920 }
921 }
922 }
923
924 splx(s);
925 return (EINVAL);
926 }
927
928 static int
929 zero_entry6(struct mbuf *m)
930 {
931 struct ip6_fw *frwl;
932 struct ip6_fw_chain *fcp;
933 int s;
934
935 if (m) {
936 if (m->m_len != sizeof(struct ip6_fw))
937 return(EINVAL);
938 frwl = mtod(m, struct ip6_fw *);
939 }
940 else
941 frwl = NULL;
942
943 /*
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.
947 */
948 s = splnet();
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;
953 }
954 splx(s);
955
956 if (fw6_verbose) {
957 if (frwl)
958 printf("ip6fw: Entry %d cleared.\n", frwl->fw_number);
959 else
960 printf("ip6fw: Accounting cleared.\n");
961 }
962
963 return(0);
964 }
965
966 static struct ip6_fw *
967 check_ip6fw_mbuf(struct mbuf *m)
968 {
969 /* Check length */
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)));
973 return (NULL);
974 }
975 return(check_ip6fw_struct(mtod(m, struct ip6_fw *)));
976 }
977
978 static struct ip6_fw *
979 check_ip6fw_struct(struct ip6_fw *frwl)
980 {
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));
985 return (NULL);
986 }
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));
990 return (NULL);
991 }
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));
998 return (NULL);
999 }
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",
1006 err_prefix));
1007 return (NULL);
1008 }
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)));
1013 return (NULL);
1014 }
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)));
1018 return (NULL);
1019 }
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)));
1023 return (NULL);
1024 }
1025 /*
1026 * Protocols other than TCP/UDP don't use port range
1027 */
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",
1032 err_prefix));
1033 return(NULL);
1034 }
1035
1036 /*
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.
1040 */
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));
1050 return(NULL);
1051 }
1052
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));
1057 return(NULL);
1058 }
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));
1062 return(NULL);
1063 }
1064 }
1065
1066 /* Check command specific stuff */
1067 switch (frwl->fw_flg & IPV6_FW_F_COMMAND)
1068 {
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));
1074 return(NULL);
1075 }
1076 break;
1077 case IPV6_FW_F_DIVERT: /* Diverting to port zero is invalid */
1078 case IPV6_FW_F_TEE:
1079 if (frwl->fw_divert_port == 0) {
1080 dprintf(("%s can't divert to port 0\n", err_prefix));
1081 return (NULL);
1082 }
1083 break;
1084 case IPV6_FW_F_DENY:
1085 case IPV6_FW_F_ACCEPT:
1086 case IPV6_FW_F_COUNT:
1087 case IPV6_FW_F_SKIPTO:
1088 break;
1089 default:
1090 dprintf(("%s invalid command\n", err_prefix));
1091 return(NULL);
1092 }
1093
1094 return frwl;
1095 }
1096
1097 static int
1098 ip6_fw_ctl(int stage, struct mbuf **mm)
1099 {
1100 int error;
1101 struct mbuf *m;
1102
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 */
1107 #else
1108 *mm = m = m_get(M_WAIT, MT_SOOPTS);
1109 #endif
1110 if (!m)
1111 return(ENOBUFS);
1112 if (sizeof *(fcp->rule) > MLEN) {
1113 MCLGET(m, M_WAIT);
1114 if ((m->m_flags & M_EXT) == 0) {
1115 m_free(m);
1116 return(ENOBUFS);
1117 }
1118 }
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 */
1124 #else
1125 m->m_next = m_get(M_WAIT, MT_SOOPTS);
1126 #endif
1127 if (!m->m_next) {
1128 m_freem(*mm);
1129 return(ENOBUFS);
1130 }
1131 m = m->m_next;
1132 if (sizeof *(fcp->rule) > MLEN) {
1133 MCLGET(m, M_WAIT);
1134 if ((m->m_flags & M_EXT) == 0) {
1135 m_freem(*mm);
1136 return(ENOBUFS);
1137 }
1138 }
1139 m->m_len = 0;
1140 }
1141 return (0);
1142 }
1143 m = *mm;
1144 /* only allow get calls if secure mode > 2 */
1145 if (securelevel > 2) {
1146 if (m) {
1147 (void)m_freem(m);
1148 *mm = 0;
1149 }
1150 return(EPERM);
1151 }
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;
1156 int s = splnet();
1157 LIST_REMOVE(ip6_fw_chain.lh_first, chain);
1158 splx(s);
1159 _FREE(fcp->rule, M_IP6FW);
1160 _FREE(fcp, M_IP6FW);
1161 }
1162 if (m) {
1163 (void)m_freem(m);
1164 *mm = 0;
1165 }
1166 return (0);
1167 }
1168 if (stage == IPV6_FW_ZERO) {
1169 error = zero_entry6(m);
1170 if (m) {
1171 (void)m_freem(m);
1172 *mm = 0;
1173 }
1174 return (error);
1175 }
1176 if (m == NULL) {
1177 printf("%s NULL mbuf ptr\n", err_prefix);
1178 return (EINVAL);
1179 }
1180
1181 if (stage == IPV6_FW_ADD) {
1182 struct ip6_fw *frwl = check_ip6fw_mbuf(m);
1183
1184 if (!frwl)
1185 error = EINVAL;
1186 else
1187 error = add_entry6(&ip6_fw_chain, frwl);
1188 if (m) {
1189 (void)m_freem(m);
1190 *mm = 0;
1191 }
1192 return error;
1193 }
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)));
1198 error = EINVAL;
1199 } else if (mtod(m, struct ip6_fw *)->fw_number == (u_short)-1) {
1200 dprintf(("%s can't delete rule 65535\n", err_prefix));
1201 error = EINVAL;
1202 } else
1203 error = del_entry6(&ip6_fw_chain,
1204 mtod(m, struct ip6_fw *)->fw_number);
1205 if (m) {
1206 (void)m_freem(m);
1207 *mm = 0;
1208 }
1209 return error;
1210 }
1211
1212 dprintf(("%s unknown request %d\n", err_prefix, stage));
1213 if (m) {
1214 (void)m_freem(m);
1215 *mm = 0;
1216 }
1217 return (EINVAL);
1218 }
1219
1220 void
1221 ip6_fw_init(void)
1222 {
1223 struct ip6_fw default_rule;
1224
1225 ip6_fw_chk_ptr = ip6_fw_chk;
1226 ip6_fw_ctl_ptr = ip6_fw_ctl;
1227 LIST_INIT(&ip6_fw_chain);
1228
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;
1234 #else
1235 default_rule.fw_flg |= IPV6_FW_F_DENY;
1236 #endif
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__);
1241
1242 #if 1 /* NOT SUPPORTED IPV6 DIVERT */
1243 printf("IPv6 packet filtering initialized, ");
1244 #else
1245 printf("IPv6 packet filtering initialized, "
1246 #if IP6DIVERT
1247 "divert enabled, ");
1248 #else
1249 "divert disabled, ");
1250 #endif
1251 #endif
1252 #if IPV6FIREWALL_DEFAULT_TO_ACCEPT
1253 printf("default to accept, ");
1254 #endif
1255 #ifndef IPV6FIREWALL_VERBOSE
1256 printf("logging disabled\n");
1257 #else
1258 if (fw6_verbose_limit == 0)
1259 printf("unlimited logging\n");
1260 else
1261 printf("logging limited to %d packets/entry\n",
1262 fw6_verbose_limit);
1263 #endif
1264 }