File:  [DragonFly] / src / sys / net / ip6fw / ip6_fw.c
Revision 1.6: download - view: text, annotated - select for diffs
Tue Jan 6 03:17:26 2004 UTC (10 years, 8 months ago) by dillon
Branches: MAIN
CVS tags: HEAD
if_xname support Part 2/2: Convert remaining netif devices and implement full
support for if_xname.  Restructure struct ifnet in net/if_var.h, pulling in
a few minor additional changes from current including making if_dunit an int,
and making if_flags an int.

Submitted-by: Max Laier <max@love2party.net>

    1: /*	$FreeBSD: src/sys/netinet6/ip6_fw.c,v 1.2.2.9 2002/04/28 05:40:27 suz Exp $	*/
    2: /*	$DragonFly: src/sys/net/ip6fw/ip6_fw.c,v 1.6 2004/01/06 03:17:26 dillon Exp $	*/
    3: /*	$KAME: ip6_fw.c,v 1.21 2001/01/24 01:25:32 itojun Exp $	*/
    4: 
    5: /*
    6:  * Copyright (C) 1998, 1999, 2000 and 2001 WIDE Project.
    7:  * All rights reserved.
    8:  *
    9:  * Redistribution and use in source and binary forms, with or without
   10:  * modification, are permitted provided that the following conditions
   11:  * are met:
   12:  * 1. Redistributions of source code must retain the above copyright
   13:  *    notice, this list of conditions and the following disclaimer.
   14:  * 2. Redistributions in binary form must reproduce the above copyright
   15:  *    notice, this list of conditions and the following disclaimer in the
   16:  *    documentation and/or other materials provided with the distribution.
   17:  * 3. Neither the name of the project nor the names of its contributors
   18:  *    may be used to endorse or promote products derived from this software
   19:  *    without specific prior written permission.
   20:  *
   21:  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
   22:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   23:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
   25:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   27:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   28:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   29:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   30:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   31:  * SUCH DAMAGE.
   32:  */
   33: 
   34: /*
   35:  * Copyright (c) 1993 Daniel Boulet
   36:  * Copyright (c) 1994 Ugen J.S.Antsilevich
   37:  * Copyright (c) 1996 Alex Nash
   38:  *
   39:  * Redistribution and use in source forms, with and without modification,
   40:  * are permitted provided that this entire comment appears intact.
   41:  *
   42:  * Redistribution in binary form may occur without any restrictions.
   43:  * Obviously, it would be nice if you gave credit where credit is due
   44:  * but requiring it would be too onerous.
   45:  *
   46:  * This software is provided ``AS IS'' without any warranties of any kind.
   47:  */
   48: 
   49: /*
   50:  * Implement IPv6 packet firewall
   51:  */
   52: 
   53: #if !defined(KLD_MODULE)
   54: #include "opt_ip6fw.h"
   55: #include "opt_inet.h"
   56: #include "opt_inet6.h"
   57: #endif
   58: 
   59: #ifdef IP6DIVERT
   60: #error "NOT SUPPORTED IPV6 DIVERT"
   61: #endif
   62: #ifdef IP6FW_DIVERT_RESTART
   63: #error "NOT SUPPORTED IPV6 DIVERT"
   64: #endif
   65: 
   66: #include <sys/param.h>
   67: #include <sys/systm.h>
   68: #include <sys/malloc.h>
   69: #include <sys/mbuf.h>
   70: #include <sys/queue.h>
   71: #include <sys/kernel.h>
   72: #include <sys/socket.h>
   73: #include <sys/socketvar.h>
   74: #include <sys/syslog.h>
   75: #include <sys/time.h>
   76: #include <net/if.h>
   77: #include <net/route.h>
   78: #include <netinet/in_systm.h>
   79: #include <netinet/in.h>
   80: #include <netinet/ip.h>
   81: 
   82: #include <netinet/ip6.h>
   83: #include <netinet6/ip6_var.h>
   84: #include <netinet6/in6_var.h>
   85: #include <netinet/icmp6.h>
   86: 
   87: #include <netinet/in_pcb.h>
   88: 
   89: #include "ip6_fw.h"
   90: #include <netinet/ip_var.h>
   91: #include <netinet/tcp.h>
   92: #include <netinet/tcp_seq.h>
   93: #include <netinet/tcp_timer.h>
   94: #include <netinet/tcp_var.h>
   95: #include <netinet/udp.h>
   96: 
   97: #include <sys/sysctl.h>
   98: 
   99: #include <net/net_osdep.h>
  100: 
  101: MALLOC_DEFINE(M_IP6FW, "Ip6Fw/Ip6Acct", "Ip6Fw/Ip6Acct chain's");
  102: 
  103: static int fw6_debug = 1;
  104: #ifdef IPV6FIREWALL_VERBOSE
  105: static int fw6_verbose = 1;
  106: #else
  107: static int fw6_verbose = 0;
  108: #endif
  109: #ifdef IPV6FIREWALL_VERBOSE_LIMIT
  110: static int fw6_verbose_limit = IPV6FIREWALL_VERBOSE_LIMIT;
  111: #else
  112: static int fw6_verbose_limit = 0;
  113: #endif
  114: 
  115: LIST_HEAD (ip6_fw_head, ip6_fw_chain) ip6_fw_chain;
  116: 
  117: #ifdef SYSCTL_NODE
  118: SYSCTL_DECL(_net_inet6_ip6);
  119: SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall");
  120: SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, enable, CTLFLAG_RW,
  121: 	&ip6_fw_enable, 0, "Enable ip6fw");
  122: SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, debug, CTLFLAG_RW, &fw6_debug, 0, "");
  123: SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose, CTLFLAG_RW, &fw6_verbose, 0, "");
  124: SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw6_verbose_limit, 0, "");
  125: #endif
  126: 
  127: #define dprintf(a)	do {						\
  128: 				if (fw6_debug)				\
  129: 					printf a;			\
  130: 			} while (0)
  131: #define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
  132: 
  133: static int	add_entry6 (struct ip6_fw_head *chainptr, struct ip6_fw *frwl);
  134: static int	del_entry6 (struct ip6_fw_head *chainptr, u_short number);
  135: static int	zero_entry6 (struct mbuf *m);
  136: static struct ip6_fw *check_ip6fw_struct (struct ip6_fw *m);
  137: static struct ip6_fw *check_ip6fw_mbuf (struct mbuf *fw);
  138: static int	ip6opts_match (struct ip6_hdr **ip6, struct ip6_fw *f,
  139: 				   struct mbuf **m,
  140: 				   int *off, int *nxt, u_short *offset);
  141: static int	port_match6 (u_short *portptr, int nports, u_short port,
  142: 				int range_flag);
  143: static int	tcp6flg_match (struct tcphdr *tcp6, struct ip6_fw *f);
  144: static int	icmp6type_match (struct icmp6_hdr *  icmp, struct ip6_fw * f);
  145: static void	ip6fw_report (struct ip6_fw *f, struct ip6_hdr *ip6,
  146: 				struct ifnet *rif, struct ifnet *oif, int off, int nxt);
  147: 
  148: static int	ip6_fw_chk (struct ip6_hdr **pip6,
  149: 			struct ifnet *oif, u_int16_t *cookie, struct mbuf **m);
  150: static int	ip6_fw_ctl (int stage, struct mbuf **mm);
  151: 
  152: static char err_prefix[] = "ip6_fw_ctl:";
  153: 
  154: /*
  155:  * Returns 1 if the port is matched by the vector, 0 otherwise
  156:  */
  157: static
  158: __inline int
  159: port_match6(u_short *portptr, int nports, u_short port, int range_flag)
  160: {
  161: 	if (!nports)
  162: 		return 1;
  163: 	if (range_flag) {
  164: 		if (portptr[0] <= port && port <= portptr[1]) {
  165: 			return 1;
  166: 		}
  167: 		nports -= 2;
  168: 		portptr += 2;
  169: 	}
  170: 	while (nports-- > 0) {
  171: 		if (*portptr++ == port) {
  172: 			return 1;
  173: 		}
  174: 	}
  175: 	return 0;
  176: }
  177: 
  178: static int
  179: tcp6flg_match(struct tcphdr *tcp6, struct ip6_fw *f)
  180: {
  181: 	u_char		flg_set, flg_clr;
  182: 	
  183: 	/*
  184: 	 * If an established connection is required, reject packets that
  185: 	 * have only SYN of RST|ACK|SYN set.  Otherwise, fall through to
  186: 	 * other flag requirements.
  187: 	 */
  188: 	if ((f->fw_ipflg & IPV6_FW_IF_TCPEST) &&
  189: 	    ((tcp6->th_flags & (IPV6_FW_TCPF_RST | IPV6_FW_TCPF_ACK |
  190: 	    IPV6_FW_TCPF_SYN)) == IPV6_FW_TCPF_SYN))
  191: 		return 0;
  192: 
  193: 	flg_set = tcp6->th_flags & f->fw_tcpf;
  194: 	flg_clr = tcp6->th_flags & f->fw_tcpnf;
  195: 
  196: 	if (flg_set != f->fw_tcpf)
  197: 		return 0;
  198: 	if (flg_clr)
  199: 		return 0;
  200: 
  201: 	return 1;
  202: }
  203: 
  204: static int
  205: icmp6type_match(struct icmp6_hdr *icmp6, struct ip6_fw *f)
  206: {
  207: 	int type;
  208: 
  209: 	if (!(f->fw_flg & IPV6_FW_F_ICMPBIT))
  210: 		return(1);
  211: 
  212: 	type = icmp6->icmp6_type;
  213: 
  214: 	/* check for matching type in the bitmap */
  215: 	if (type < IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8 &&
  216: 		(f->fw_icmp6types[type / (sizeof(unsigned) * 8)] &
  217: 		(1U << (type % (8 * sizeof(unsigned))))))
  218: 		return(1);
  219: 
  220: 	return(0); /* no match */
  221: }
  222: 
  223: static int
  224: is_icmp6_query(struct ip6_hdr *ip6, int off)
  225: {
  226: 	const struct icmp6_hdr *icmp6;
  227: 	int icmp6_type;
  228: 
  229: 	icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
  230: 	icmp6_type = icmp6->icmp6_type;
  231: 
  232: 	if (icmp6_type == ICMP6_ECHO_REQUEST ||
  233: 	    icmp6_type == ICMP6_MEMBERSHIP_QUERY ||
  234: 	    icmp6_type == ICMP6_WRUREQUEST ||
  235: 	    icmp6_type == ICMP6_FQDN_QUERY ||
  236: 	    icmp6_type == ICMP6_NI_QUERY)
  237: 		return(1);
  238: 
  239: 	return(0);
  240: }
  241: 
  242: static int
  243: ip6opts_match(struct ip6_hdr **pip6, struct ip6_fw *f, struct mbuf **m,
  244: 	      int *off, int *nxt, u_short *offset)
  245: {
  246: 	int len;
  247: 	struct ip6_hdr *ip6 = *pip6;
  248: 	struct ip6_ext *ip6e;
  249: 	u_char	opts, nopts, nopts_sve;
  250: 
  251: 	opts = f->fw_ip6opt;
  252: 	nopts = nopts_sve = f->fw_ip6nopt;
  253: 
  254: 	*nxt = ip6->ip6_nxt;
  255: 	*off = sizeof(struct ip6_hdr);
  256: 	len = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr);
  257: 	while (*off < len) {
  258: 		ip6e = (struct ip6_ext *)((caddr_t) ip6 + *off);
  259: 		if ((*m)->m_len < *off + sizeof(*ip6e))
  260: 			goto opts_check;	/* XXX */
  261: 
  262: 		switch(*nxt) {
  263: 		case IPPROTO_FRAGMENT:
  264: 			if ((*m)->m_len >= *off + sizeof(struct ip6_frag)) {
  265: 				struct ip6_frag *ip6f;
  266: 
  267: 				ip6f = (struct ip6_frag *) ((caddr_t)ip6 + *off);
  268: 				*offset = ip6f->ip6f_offlg & IP6F_OFF_MASK;
  269: 			}
  270: 			opts &= ~IPV6_FW_IP6OPT_FRAG;
  271: 			nopts &= ~IPV6_FW_IP6OPT_FRAG;
  272: 			*off += sizeof(struct ip6_frag);
  273: 			break;
  274: 		case IPPROTO_AH:
  275: 			opts &= ~IPV6_FW_IP6OPT_AH;
  276: 			nopts &= ~IPV6_FW_IP6OPT_AH;
  277: 			*off += (ip6e->ip6e_len + 2) << 2;
  278: 			break;
  279: 		default:
  280: 			switch (*nxt) {
  281: 			case IPPROTO_HOPOPTS:
  282: 				opts &= ~IPV6_FW_IP6OPT_HOPOPT;
  283: 				nopts &= ~IPV6_FW_IP6OPT_HOPOPT;
  284: 				break;
  285: 			case IPPROTO_ROUTING:
  286: 				opts &= ~IPV6_FW_IP6OPT_ROUTE;
  287: 				nopts &= ~IPV6_FW_IP6OPT_ROUTE;
  288: 				break;
  289: 			case IPPROTO_ESP:
  290: 				opts &= ~IPV6_FW_IP6OPT_ESP;
  291: 				nopts &= ~IPV6_FW_IP6OPT_ESP;
  292: 				break;
  293: 			case IPPROTO_NONE:
  294: 				opts &= ~IPV6_FW_IP6OPT_NONXT;
  295: 				nopts &= ~IPV6_FW_IP6OPT_NONXT;
  296: 				goto opts_check;
  297: 				break;
  298: 			case IPPROTO_DSTOPTS:
  299: 				opts &= ~IPV6_FW_IP6OPT_OPTS;
  300: 				nopts &= ~IPV6_FW_IP6OPT_OPTS;
  301: 				break;
  302: 			default:
  303: 				goto opts_check;
  304: 				break;
  305: 			}
  306: 			*off += (ip6e->ip6e_len + 1) << 3;
  307: 			break;
  308: 		}
  309: 		*nxt = ip6e->ip6e_nxt;
  310: 
  311: 	}
  312:  opts_check:
  313: 	if (f->fw_ip6opt == f->fw_ip6nopt)	/* XXX */
  314: 		return 1;
  315: 
  316: 	if (opts == 0 && nopts == nopts_sve)
  317: 		return 1;
  318: 	else
  319: 		return 0;
  320: }
  321: 
  322: static
  323: __inline int
  324: iface_match(struct ifnet *ifp, union ip6_fw_if *ifu, int byname)
  325: {
  326: 	/* Check by name or by IP address */
  327: 	if (byname) {
  328: 		/* Check name */
  329: 		if (ifu->fu_via_if.glob) {
  330: 			if (fnmatch(ifu->fu_via_if.name, ifp->if_xname, 0)
  331: 			    == FNM_NOMATCH)
  332: 				return(0);
  333: 		} else {
  334: 			if (strncmp(ifp->if_xname, ifu->fu_via_if.name,
  335: 			    IP6FW_IFNLEN) != 0)
  336: 				return(0);
  337: 		}
  338: 		return(1);
  339: 	} else if (!IN6_IS_ADDR_UNSPECIFIED(&ifu->fu_via_ip6)) {	/* Zero == wildcard */
  340: 		struct ifaddr *ia;
  341: 
  342: 		for (ia = ifp->if_addrlist.tqh_first; ia; ia = ia->ifa_list.tqe_next)
  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: 	char *action;
  370: 	char action2[32], proto[102], name[18];
  371: 	int len;
  372: 
  373: 	count = f ? f->fw_pcnt : ++counter;
  374: 	if (fw6_verbose_limit != 0 && count > fw6_verbose_limit)
  375: 		return;
  376: 
  377: 	/* Print command name */
  378: 	snprintf(SNPARGS(name, 0), "ip6fw: %d", f ? f->fw_number : -1);
  379: 
  380: 	action = action2;
  381: 	if (!f)
  382: 		action = "Refuse";
  383: 	else {
  384: 		switch (f->fw_flg & IPV6_FW_F_COMMAND) {
  385: 		case IPV6_FW_F_DENY:
  386: 			action = "Deny";
  387: 			break;
  388: 		case IPV6_FW_F_REJECT:
  389: 			if (f->fw_reject_code == IPV6_FW_REJECT_RST)
  390: 				action = "Reset";
  391: 			else
  392: 				action = "Unreach";
  393: 			break;
  394: 		case IPV6_FW_F_ACCEPT:
  395: 			action = "Accept";
  396: 			break;
  397: 		case IPV6_FW_F_COUNT:
  398: 			action = "Count";
  399: 			break;
  400: 		case IPV6_FW_F_DIVERT:
  401: 			snprintf(SNPARGS(action2, 0), "Divert %d",
  402: 			    f->fw_divert_port);
  403: 			break;
  404: 		case IPV6_FW_F_TEE:
  405: 			snprintf(SNPARGS(action2, 0), "Tee %d",
  406: 			    f->fw_divert_port);
  407: 			break;
  408: 		case IPV6_FW_F_SKIPTO:
  409: 			snprintf(SNPARGS(action2, 0), "SkipTo %d",
  410: 			    f->fw_skipto_rule);
  411: 			break;
  412: 		default:	
  413: 			action = "UNKNOWN";
  414: 			break;
  415: 		}
  416: 	}
  417: 
  418: 	switch (nxt) {
  419: 	case IPPROTO_TCP:
  420: 		len = snprintf(SNPARGS(proto, 0), "TCP [%s]",
  421: 		    ip6_sprintf(&ip6->ip6_src));
  422: 		if (off > 0)
  423: 			len += snprintf(SNPARGS(proto, len), ":%d ",
  424: 			    ntohs(tcp6->th_sport));
  425: 		else
  426: 			len += snprintf(SNPARGS(proto, len), " ");
  427: 		len += snprintf(SNPARGS(proto, len), "[%s]",
  428: 		    ip6_sprintf(&ip6->ip6_dst));
  429: 		if (off > 0)
  430: 			snprintf(SNPARGS(proto, len), ":%d",
  431: 			    ntohs(tcp6->th_dport));
  432: 		break;
  433: 	case IPPROTO_UDP:
  434: 		len = snprintf(SNPARGS(proto, 0), "UDP [%s]",
  435: 		    ip6_sprintf(&ip6->ip6_src));
  436: 		if (off > 0)
  437: 			len += snprintf(SNPARGS(proto, len), ":%d ",
  438: 			    ntohs(udp->uh_sport));
  439: 		else
  440: 		    len += snprintf(SNPARGS(proto, len), " ");
  441: 		len += snprintf(SNPARGS(proto, len), "[%s]",
  442: 		    ip6_sprintf(&ip6->ip6_dst));
  443: 		if (off > 0)
  444: 			snprintf(SNPARGS(proto, len), ":%d",
  445: 			    ntohs(udp->uh_dport));
  446: 		break;
  447: 	case IPPROTO_ICMPV6:
  448: 		if (off > 0)
  449: 			len = snprintf(SNPARGS(proto, 0), "IPV6-ICMP:%u.%u ",
  450: 			    icmp6->icmp6_type, icmp6->icmp6_code);
  451: 		else
  452: 			len = snprintf(SNPARGS(proto, 0), "IPV6-ICMP ");
  453: 		len += snprintf(SNPARGS(proto, len), "[%s]",
  454: 		    ip6_sprintf(&ip6->ip6_src));
  455: 		snprintf(SNPARGS(proto, len), " [%s]",
  456: 		    ip6_sprintf(&ip6->ip6_dst));
  457: 		break;
  458: 	default:
  459: 		len = snprintf(SNPARGS(proto, 0), "P:%d [%s]", nxt,
  460: 		    ip6_sprintf(&ip6->ip6_src));
  461: 		snprintf(SNPARGS(proto, len), " [%s]",
  462: 		    ip6_sprintf(&ip6->ip6_dst));
  463: 		break;
  464: 	}
  465: 
  466: 	if (oif)
  467: 		log(LOG_SECURITY | LOG_INFO, "%s %s %s out via %s\n",
  468: 		    name, action, proto, if_name(oif));
  469: 	else if (rif)
  470: 		log(LOG_SECURITY | LOG_INFO, "%s %s %s in via %s\n",
  471: 		    name, action, proto, if_name(rif));
  472: 	else
  473: 		log(LOG_SECURITY | LOG_INFO, "%s %s %s",
  474: 		    name, action, proto);
  475: 	if (fw6_verbose_limit != 0 && count == fw6_verbose_limit)
  476: 	    log(LOG_SECURITY | LOG_INFO, "ip6fw: limit reached on entry %d\n",
  477: 		f ? f->fw_number : -1);
  478: }
  479: 
  480: /*
  481:  * Parameters:
  482:  *
  483:  *	ip	Pointer to packet header (struct ip6_hdr *)
  484:  *	hlen	Packet header length
  485:  *	oif	Outgoing interface, or NULL if packet is incoming
  486:  * #ifndef IP6FW_DIVERT_RESTART
  487:  *	*cookie	Ignore all divert/tee rules to this port (if non-zero)
  488:  * #else
  489:  *	*cookie Skip up to the first rule past this rule number;
  490:  * #endif
  491:  *	*m	The packet; we set to NULL when/if we nuke it.
  492:  *
  493:  * Return value:
  494:  *
  495:  *	0	The packet is to be accepted and routed normally OR
  496:  *      	the packet was denied/rejected and has been dropped;
  497:  *		in the latter case, *m is equal to NULL upon return.
  498:  *	port	Divert the packet to port.
  499:  */
  500: 
  501: static int
  502: ip6_fw_chk(struct ip6_hdr **pip6,
  503: 	struct ifnet *oif, u_int16_t *cookie, struct mbuf **m)
  504: {
  505: 	struct ip6_fw_chain *chain;
  506: 	struct ip6_fw *rule = NULL;
  507: 	struct ip6_hdr *ip6 = *pip6;
  508: 	struct ifnet *const rif = (*m)->m_pkthdr.rcvif;
  509: 	u_short offset = 0;
  510: 	int off = sizeof(struct ip6_hdr), nxt = ip6->ip6_nxt;
  511: 	u_short src_port, dst_port;
  512: #ifdef	IP6FW_DIVERT_RESTART
  513: 	u_int16_t skipto = *cookie;
  514: #else
  515: 	u_int16_t ignport = ntohs(*cookie);
  516: #endif
  517: 
  518: 	*cookie = 0;
  519: 	/*
  520: 	 * Go down the chain, looking for enlightment
  521: 	 * #ifdef IP6FW_DIVERT_RESTART
  522: 	 * If we've been asked to start at a given rule immediatly, do so.
  523: 	 * #endif
  524: 	 */
  525: 	chain = LIST_FIRST(&ip6_fw_chain);
  526: #ifdef IP6FW_DIVERT_RESTART
  527: 	if (skipto) {
  528: 		if (skipto >= 65535)
  529: 			goto dropit;
  530: 		while (chain && (chain->rule->fw_number <= skipto)) {
  531: 			chain = LIST_NEXT(chain, chain);
  532: 		}
  533: 		if (! chain) goto dropit;
  534: 	}
  535: #endif /* IP6FW_DIVERT_RESTART */
  536: 	for (; chain; chain = LIST_NEXT(chain, chain)) {
  537: 		struct ip6_fw *const f = chain->rule;
  538: 
  539: 		if (oif) {
  540: 			/* Check direction outbound */
  541: 			if (!(f->fw_flg & IPV6_FW_F_OUT))
  542: 				continue;
  543: 		} else {
  544: 			/* Check direction inbound */
  545: 			if (!(f->fw_flg & IPV6_FW_F_IN))
  546: 				continue;
  547: 		}
  548: 
  549: #define IN6_ARE_ADDR_MASKEQUAL(x,y,z) (\
  550: 	(((x)->s6_addr32[0] & (y)->s6_addr32[0]) == (z)->s6_addr32[0]) && \
  551: 	(((x)->s6_addr32[1] & (y)->s6_addr32[1]) == (z)->s6_addr32[1]) && \
  552: 	(((x)->s6_addr32[2] & (y)->s6_addr32[2]) == (z)->s6_addr32[2]) && \
  553: 	(((x)->s6_addr32[3] & (y)->s6_addr32[3]) == (z)->s6_addr32[3]))
  554: 
  555: 		/* If src-addr doesn't match, not this rule. */
  556: 		if (((f->fw_flg & IPV6_FW_F_INVSRC) != 0) ^
  557: 			(!IN6_ARE_ADDR_MASKEQUAL(&ip6->ip6_src,&f->fw_smsk,&f->fw_src)))
  558: 			continue;
  559: 
  560: 		/* If dest-addr doesn't match, not this rule. */
  561: 		if (((f->fw_flg & IPV6_FW_F_INVDST) != 0) ^
  562: 			(!IN6_ARE_ADDR_MASKEQUAL(&ip6->ip6_dst,&f->fw_dmsk,&f->fw_dst)))
  563: 			continue;
  564: 
  565: #undef IN6_ARE_ADDR_MASKEQUAL
  566: 		/* Interface check */
  567: 		if ((f->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) {
  568: 			struct ifnet *const iface = oif ? oif : rif;
  569: 
  570: 			/* Backwards compatibility hack for "via" */
  571: 			if (!iface || !iface_match(iface,
  572: 			    &f->fw_in_if, f->fw_flg & IPV6_FW_F_OIFNAME))
  573: 				continue;
  574: 		} else {
  575: 			/* Check receive interface */
  576: 			if ((f->fw_flg & IPV6_FW_F_IIFACE)
  577: 			    && (!rif || !iface_match(rif,
  578: 			      &f->fw_in_if, f->fw_flg & IPV6_FW_F_IIFNAME)))
  579: 				continue;
  580: 			/* Check outgoing interface */
  581: 			if ((f->fw_flg & IPV6_FW_F_OIFACE)
  582: 			    && (!oif || !iface_match(oif,
  583: 			      &f->fw_out_if, f->fw_flg & IPV6_FW_F_OIFNAME)))
  584: 				continue;
  585: 		}
  586: 
  587: 		/* Check IP options */
  588: 		if (!ip6opts_match(&ip6, f, m, &off, &nxt, &offset))
  589: 			continue;
  590: 
  591: 		/* Fragments */
  592: 		if ((f->fw_flg & IPV6_FW_F_FRAG) && !offset)
  593: 			continue;
  594: 
  595: 		/* Check protocol; if wildcard, match */
  596: 		if (f->fw_prot == IPPROTO_IPV6)
  597: 			goto got_match;
  598: 
  599: 		/* If different, don't match */
  600: 		if (nxt != f->fw_prot)
  601: 			continue;
  602: 
  603: #define PULLUP_TO(len)	do {						\
  604: 			    if ((*m)->m_len < (len)			\
  605: 				&& (*m = m_pullup(*m, (len))) == 0) {	\
  606: 				    goto dropit;			\
  607: 			    }						\
  608: 			    *pip6 = ip6 = mtod(*m, struct ip6_hdr *);	\
  609: 			} while (0)
  610: 
  611: 		/* Protocol specific checks */
  612: 		switch (nxt) {
  613: 		case IPPROTO_TCP:
  614: 		    {
  615: 			struct tcphdr *tcp6;
  616: 
  617: 			if (offset == 1) {	/* cf. RFC 1858 */
  618: 				PULLUP_TO(off + 4); /* XXX ? */
  619: 				goto bogusfrag;
  620: 			}
  621: 			if (offset != 0) {
  622: 				/*
  623: 				 * TCP flags and ports aren't available in this
  624: 				 * packet -- if this rule specified either one,
  625: 				 * we consider the rule a non-match.
  626: 				 */
  627: 				if (f->fw_nports != 0 ||
  628: 				    f->fw_tcpf != f->fw_tcpnf)
  629: 					continue;
  630: 
  631: 				break;
  632: 			}
  633: 			PULLUP_TO(off + 14);
  634: 			tcp6 = (struct tcphdr *) ((caddr_t)ip6 + off);
  635: 			if (((f->fw_tcpf != f->fw_tcpnf) ||
  636: 			   (f->fw_ipflg & IPV6_FW_IF_TCPEST))  &&
  637: 			   !tcp6flg_match(tcp6, f))
  638: 				continue;
  639: 			src_port = ntohs(tcp6->th_sport);
  640: 			dst_port = ntohs(tcp6->th_dport);
  641: 			goto check_ports;
  642: 		    }
  643: 
  644: 		case IPPROTO_UDP:
  645: 		    {
  646: 			struct udphdr *udp;
  647: 
  648: 			if (offset != 0) {
  649: 				/*
  650: 				 * Port specification is unavailable -- if this
  651: 				 * rule specifies a port, we consider the rule
  652: 				 * a non-match.
  653: 				 */
  654: 				if (f->fw_nports != 0)
  655: 					continue;
  656: 
  657: 				break;
  658: 			}
  659: 			PULLUP_TO(off + 4);
  660: 			udp = (struct udphdr *) ((caddr_t)ip6 + off);
  661: 			src_port = ntohs(udp->uh_sport);
  662: 			dst_port = ntohs(udp->uh_dport);
  663: check_ports:
  664: 			if (!port_match6(&f->fw_pts[0],
  665: 			    IPV6_FW_GETNSRCP(f), src_port,
  666: 			    f->fw_flg & IPV6_FW_F_SRNG))
  667: 				continue;
  668: 			if (!port_match6(&f->fw_pts[IPV6_FW_GETNSRCP(f)],
  669: 			    IPV6_FW_GETNDSTP(f), dst_port,
  670: 			    f->fw_flg & IPV6_FW_F_DRNG))
  671: 				continue;
  672: 			break;
  673: 		    }
  674: 
  675: 		case IPPROTO_ICMPV6:
  676: 		    {
  677: 			struct icmp6_hdr *icmp;
  678: 
  679: 			if (offset != 0)	/* Type isn't valid */
  680: 				break;
  681: 			PULLUP_TO(off + 2);
  682: 			icmp = (struct icmp6_hdr *) ((caddr_t)ip6 + off);
  683: 			if (!icmp6type_match(icmp, f))
  684: 				continue;
  685: 			break;
  686: 		    }
  687: #undef PULLUP_TO
  688: 
  689: bogusfrag:
  690: 			if (fw6_verbose)
  691: 				ip6fw_report(NULL, ip6, rif, oif, off, nxt);
  692: 			goto dropit;
  693: 		}
  694: 
  695: got_match:
  696: #ifndef IP6FW_DIVERT_RESTART
  697: 		/* Ignore divert/tee rule if socket port is "ignport" */
  698: 		switch (f->fw_flg & IPV6_FW_F_COMMAND) {
  699: 		case IPV6_FW_F_DIVERT:
  700: 		case IPV6_FW_F_TEE:
  701: 			if (f->fw_divert_port == ignport)
  702: 				continue;       /* ignore this rule */
  703: 			break;
  704: 		}
  705: 
  706: #endif /* IP6FW_DIVERT_RESTART */
  707: 		/* Update statistics */
  708: 		f->fw_pcnt += 1;
  709: 		f->fw_bcnt += ntohs(ip6->ip6_plen);
  710: 		f->timestamp = time_second;
  711: 
  712: 		/* Log to console if desired */
  713: 		if ((f->fw_flg & IPV6_FW_F_PRN) && fw6_verbose)
  714: 			ip6fw_report(f, ip6, rif, oif, off, nxt);
  715: 
  716: 		/* Take appropriate action */
  717: 		switch (f->fw_flg & IPV6_FW_F_COMMAND) {
  718: 		case IPV6_FW_F_ACCEPT:
  719: 			return(0);
  720: 		case IPV6_FW_F_COUNT:
  721: 			continue;
  722: 		case IPV6_FW_F_DIVERT:
  723: #ifdef IP6FW_DIVERT_RESTART
  724: 			*cookie = f->fw_number;
  725: #else
  726: 			*cookie = htons(f->fw_divert_port);
  727: #endif /* IP6FW_DIVERT_RESTART */
  728: 			return(f->fw_divert_port);
  729: 		case IPV6_FW_F_TEE:
  730: 			/*
  731: 			 * XXX someday tee packet here, but beware that you
  732: 			 * can't use m_copym() or m_copypacket() because
  733: 			 * the divert input routine modifies the mbuf
  734: 			 * (and these routines only increment reference
  735: 			 * counts in the case of mbuf clusters), so need
  736: 			 * to write custom routine.
  737: 			 */
  738: 			continue;
  739: 		case IPV6_FW_F_SKIPTO:
  740: #ifdef DIAGNOSTIC
  741: 			while (chain->chain.le_next
  742: 			    && chain->chain.le_next->rule->fw_number
  743: 				< f->fw_skipto_rule)
  744: #else
  745: 			while (chain->chain.le_next->rule->fw_number
  746: 			    < f->fw_skipto_rule)
  747: #endif
  748: 				chain = chain->chain.le_next;
  749: 			continue;
  750: 		}
  751: 
  752: 		/* Deny/reject this packet using this rule */
  753: 		rule = f;
  754: 		break;
  755: 	}
  756: 
  757: #ifdef DIAGNOSTIC
  758: 	/* Rule 65535 should always be there and should always match */
  759: 	if (!chain)
  760: 		panic("ip6_fw: chain");
  761: #endif
  762: 
  763: 	/*
  764: 	 * At this point, we're going to drop the packet.
  765: 	 * Send a reject notice if all of the following are true:
  766: 	 *
  767: 	 * - The packet matched a reject rule
  768: 	 * - The packet is not an ICMP packet, or is an ICMP query packet
  769: 	 * - The packet is not a multicast or broadcast packet
  770: 	 */
  771: 	if ((rule->fw_flg & IPV6_FW_F_COMMAND) == IPV6_FW_F_REJECT
  772: 	    && (nxt != IPPROTO_ICMPV6 || is_icmp6_query(ip6, off))
  773: 	    && !((*m)->m_flags & (M_BCAST|M_MCAST))
  774: 	    && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
  775: 		switch (rule->fw_reject_code) {
  776: 		case IPV6_FW_REJECT_RST:
  777: 		  {
  778: 			struct tcphdr *const tcp =
  779: 				(struct tcphdr *) ((caddr_t)ip6 + off);
  780: 			struct {
  781: 				struct ip6_hdr ip6;
  782: 				struct tcphdr th;
  783: 			} ti;
  784: 			tcp_seq ack, seq;
  785: 			int flags;
  786: 
  787: 			if (offset != 0 || (tcp->th_flags & TH_RST))
  788: 				break;
  789: 
  790: 			ti.ip6 = *ip6;
  791: 			ti.th = *tcp;
  792: 			NTOHL(ti.th.th_seq);
  793: 			NTOHL(ti.th.th_ack);
  794: 			ti.ip6.ip6_nxt = IPPROTO_TCP;
  795: 			if (ti.th.th_flags & TH_ACK) {
  796: 				ack = 0;
  797: 				seq = ti.th.th_ack;
  798: 				flags = TH_RST;
  799: 			} else {
  800: 				ack = ti.th.th_seq;
  801: 				if (((*m)->m_flags & M_PKTHDR) != 0) {
  802: 					ack += (*m)->m_pkthdr.len - off
  803: 						- (ti.th.th_off << 2);
  804: 				} else if (ip6->ip6_plen) {
  805: 					ack += ntohs(ip6->ip6_plen) + sizeof(*ip6)
  806: 						- off - (ti.th.th_off << 2);
  807: 				} else {
  808: 					m_freem(*m);
  809: 					*m = 0;
  810: 					break;
  811: 				}
  812: 				seq = 0;
  813: 				flags = TH_RST|TH_ACK;
  814: 			}
  815: 			bcopy(&ti, ip6, sizeof(ti));
  816: 			tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1),
  817: 				*m, ack, seq, flags);
  818: 			*m = NULL;
  819: 			break;
  820: 		  }
  821: 		default:	/* Send an ICMP unreachable using code */
  822: 			if (oif)
  823: 				(*m)->m_pkthdr.rcvif = oif;
  824: 			icmp6_error(*m, ICMP6_DST_UNREACH,
  825: 			    rule->fw_reject_code, 0);
  826: 			*m = NULL;
  827: 			break;
  828: 		}
  829: 	}
  830: 
  831: dropit:
  832: 	/*
  833: 	 * Finally, drop the packet.
  834: 	 */
  835: 	if (*m) {
  836: 		m_freem(*m);
  837: 		*m = NULL;
  838: 	}
  839: 	return(0);
  840: }
  841: 
  842: static int
  843: add_entry6(struct ip6_fw_head *chainptr, struct ip6_fw *frwl)
  844: {
  845: 	struct ip6_fw *ftmp = 0;
  846: 	struct ip6_fw_chain *fwc = 0, *fcp, *fcpl = 0;
  847: 	u_short nbr = 0;
  848: 	int s;
  849: 
  850: 	fwc = malloc(sizeof *fwc, M_IP6FW, M_NOWAIT);
  851: 	ftmp = malloc(sizeof *ftmp, M_IP6FW, M_NOWAIT);
  852: 	if (!fwc || !ftmp) {
  853: 		dprintf(("%s malloc said no\n", err_prefix));
  854: 		if (fwc)  free(fwc, M_IP6FW);
  855: 		if (ftmp) free(ftmp, M_IP6FW);
  856: 		return (ENOSPC);
  857: 	}
  858: 
  859: 	bcopy(frwl, ftmp, sizeof(struct ip6_fw));
  860: 	ftmp->fw_in_if.fu_via_if.name[IP6FW_IFNLEN - 1] = '\0';
  861: 	ftmp->fw_pcnt = 0L;
  862: 	ftmp->fw_bcnt = 0L;
  863: 	fwc->rule = ftmp;
  864: 	
  865: 	s = splnet();
  866: 
  867: 	if (!chainptr->lh_first) {
  868: 		LIST_INSERT_HEAD(chainptr, fwc, chain);
  869: 		splx(s);
  870: 		return(0);
  871:         } else if (ftmp->fw_number == (u_short)-1) {
  872: 		if (fwc)  free(fwc, M_IP6FW);
  873: 		if (ftmp) free(ftmp, M_IP6FW);
  874: 		splx(s);
  875: 		dprintf(("%s bad rule number\n", err_prefix));
  876: 		return (EINVAL);
  877:         }
  878: 
  879: 	/* If entry number is 0, find highest numbered rule and add 100 */
  880: 	if (ftmp->fw_number == 0) {
  881: 		for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) {
  882: 			if (fcp->rule->fw_number != (u_short)-1)
  883: 				nbr = fcp->rule->fw_number;
  884: 			else
  885: 				break;
  886: 		}
  887: 		if (nbr < (u_short)-1 - 100)
  888: 			nbr += 100;
  889: 		ftmp->fw_number = nbr;
  890: 	}
  891: 
  892: 	/* Got a valid number; now insert it, keeping the list ordered */
  893: 	for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) {
  894: 		if (fcp->rule->fw_number > ftmp->fw_number) {
  895: 			if (fcpl) {
  896: 				LIST_INSERT_AFTER(fcpl, fwc, chain);
  897: 			} else {
  898: 				LIST_INSERT_HEAD(chainptr, fwc, chain);
  899: 			}
  900: 			break;
  901: 		} else {
  902: 			fcpl = fcp;
  903: 		}
  904: 	}
  905: 
  906: 	splx(s);
  907: 	return (0);
  908: }
  909: 
  910: static int
  911: del_entry6(struct ip6_fw_head *chainptr, u_short number)
  912: {
  913: 	struct ip6_fw_chain *fcp;
  914: 	int s;
  915: 
  916: 	s = splnet();
  917: 
  918: 	fcp = chainptr->lh_first;
  919: 	if (number != (u_short)-1) {
  920: 		for (; fcp; fcp = fcp->chain.le_next) {
  921: 			if (fcp->rule->fw_number == number) {
  922: 				LIST_REMOVE(fcp, chain);
  923: 				splx(s);
  924: 				free(fcp->rule, M_IP6FW);
  925: 				free(fcp, M_IP6FW);
  926: 				return 0;
  927: 			}
  928: 		}
  929: 	}
  930: 
  931: 	splx(s);
  932: 	return (EINVAL);
  933: }
  934: 
  935: static int
  936: zero_entry6(struct mbuf *m)
  937: {
  938: 	struct ip6_fw *frwl;
  939: 	struct ip6_fw_chain *fcp;
  940: 	int s;
  941: 
  942: 	if (m && m->m_len != 0) {
  943: 		if (m->m_len != sizeof(struct ip6_fw))
  944: 			return(EINVAL);
  945: 		frwl = mtod(m, struct ip6_fw *);
  946: 	}
  947: 	else
  948: 		frwl = NULL;
  949: 
  950: 	/*
  951: 	 *	It's possible to insert multiple chain entries with the
  952: 	 *	same number, so we don't stop after finding the first
  953: 	 *	match if zeroing a specific entry.
  954: 	 */
  955: 	s = splnet();
  956: 	for (fcp = ip6_fw_chain.lh_first; fcp; fcp = fcp->chain.le_next)
  957: 		if (!frwl || frwl->fw_number == fcp->rule->fw_number) {
  958: 			fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0;
  959: 			fcp->rule->timestamp = 0;
  960: 		}
  961: 	splx(s);
  962: 
  963: 	if (fw6_verbose) {
  964: 		if (frwl)
  965: 			log(LOG_SECURITY | LOG_NOTICE,
  966: 			    "ip6fw: Entry %d cleared.\n", frwl->fw_number);
  967: 		else
  968: 			log(LOG_SECURITY | LOG_NOTICE,
  969: 			    "ip6fw: Accounting cleared.\n");
  970: 	}
  971: 
  972: 	return(0);
  973: }
  974: 
  975: static struct ip6_fw *
  976: check_ip6fw_mbuf(struct mbuf *m)
  977: {
  978: 	/* Check length */
  979: 	if (m->m_len != sizeof(struct ip6_fw)) {
  980: 		dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len,
  981: 		    sizeof(struct ip6_fw)));
  982: 		return (NULL);
  983: 	}
  984: 	return(check_ip6fw_struct(mtod(m, struct ip6_fw *)));
  985: }
  986: 
  987: static struct ip6_fw *
  988: check_ip6fw_struct(struct ip6_fw *frwl)
  989: {
  990: 	/* Check for invalid flag bits */
  991: 	if ((frwl->fw_flg & ~IPV6_FW_F_MASK) != 0) {
  992: 		dprintf(("%s undefined flag bits set (flags=%x)\n",
  993: 		    err_prefix, frwl->fw_flg));
  994: 		return (NULL);
  995: 	}
  996: 	/* Must apply to incoming or outgoing (or both) */
  997: 	if (!(frwl->fw_flg & (IPV6_FW_F_IN | IPV6_FW_F_OUT))) {
  998: 		dprintf(("%s neither in nor out\n", err_prefix));
  999: 		return (NULL);
 1000: 	}
 1001: 	/* Empty interface name is no good */
 1002: 	if (((frwl->fw_flg & IPV6_FW_F_IIFNAME)
 1003: 	      && !*frwl->fw_in_if.fu_via_if.name)
 1004: 	    || ((frwl->fw_flg & IPV6_FW_F_OIFNAME)
 1005: 	      && !*frwl->fw_out_if.fu_via_if.name)) {
 1006: 		dprintf(("%s empty interface name\n", err_prefix));
 1007: 		return (NULL);
 1008: 	}
 1009: 	/* Sanity check interface matching */
 1010: 	if ((frwl->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) {
 1011: 		;		/* allow "via" backwards compatibility */
 1012: 	} else if ((frwl->fw_flg & IPV6_FW_F_IN)
 1013: 	    && (frwl->fw_flg & IPV6_FW_F_OIFACE)) {
 1014: 		dprintf(("%s outgoing interface check on incoming\n",
 1015: 		    err_prefix));
 1016: 		return (NULL);
 1017: 	}
 1018: 	/* Sanity check port ranges */
 1019: 	if ((frwl->fw_flg & IPV6_FW_F_SRNG) && IPV6_FW_GETNSRCP(frwl) < 2) {
 1020: 		dprintf(("%s src range set but n_src_p=%d\n",
 1021: 		    err_prefix, IPV6_FW_GETNSRCP(frwl)));
 1022: 		return (NULL);
 1023: 	}
 1024: 	if ((frwl->fw_flg & IPV6_FW_F_DRNG) && IPV6_FW_GETNDSTP(frwl) < 2) {
 1025: 		dprintf(("%s dst range set but n_dst_p=%d\n",
 1026: 		    err_prefix, IPV6_FW_GETNDSTP(frwl)));
 1027: 		return (NULL);
 1028: 	}
 1029: 	if (IPV6_FW_GETNSRCP(frwl) + IPV6_FW_GETNDSTP(frwl) > IPV6_FW_MAX_PORTS) {
 1030: 		dprintf(("%s too many ports (%d+%d)\n",
 1031: 		    err_prefix, IPV6_FW_GETNSRCP(frwl), IPV6_FW_GETNDSTP(frwl)));
 1032: 		return (NULL);
 1033: 	}
 1034: 	/*
 1035: 	 *	Protocols other than TCP/UDP don't use port range
 1036: 	 */
 1037: 	if ((frwl->fw_prot != IPPROTO_TCP) &&
 1038: 	    (frwl->fw_prot != IPPROTO_UDP) &&
 1039: 	    (IPV6_FW_GETNSRCP(frwl) || IPV6_FW_GETNDSTP(frwl))) {
 1040: 		dprintf(("%s port(s) specified for non TCP/UDP rule\n",
 1041: 		    err_prefix));
 1042: 		return(NULL);
 1043: 	}
 1044: 
 1045: 	/*
 1046: 	 *	Rather than modify the entry to make such entries work,
 1047: 	 *	we reject this rule and require user level utilities
 1048: 	 *	to enforce whatever policy they deem appropriate.
 1049: 	 */
 1050: 	if ((frwl->fw_src.s6_addr32[0] & (~frwl->fw_smsk.s6_addr32[0])) ||
 1051: 		(frwl->fw_src.s6_addr32[1] & (~frwl->fw_smsk.s6_addr32[1])) ||
 1052: 		(frwl->fw_src.s6_addr32[2] & (~frwl->fw_smsk.s6_addr32[2])) ||
 1053: 		(frwl->fw_src.s6_addr32[3] & (~frwl->fw_smsk.s6_addr32[3])) ||
 1054: 		(frwl->fw_dst.s6_addr32[0] & (~frwl->fw_dmsk.s6_addr32[0])) ||
 1055: 		(frwl->fw_dst.s6_addr32[1] & (~frwl->fw_dmsk.s6_addr32[1])) ||
 1056: 		(frwl->fw_dst.s6_addr32[2] & (~frwl->fw_dmsk.s6_addr32[2])) ||
 1057: 		(frwl->fw_dst.s6_addr32[3] & (~frwl->fw_dmsk.s6_addr32[3]))) {
 1058: 		dprintf(("%s rule never matches\n", err_prefix));
 1059: 		return(NULL);
 1060: 	}
 1061: 
 1062: 	if ((frwl->fw_flg & IPV6_FW_F_FRAG) &&
 1063: 		(frwl->fw_prot == IPPROTO_UDP || frwl->fw_prot == IPPROTO_TCP)) {
 1064: 		if (frwl->fw_nports) {
 1065: 			dprintf(("%s cannot mix 'frag' and ports\n", err_prefix));
 1066: 			return(NULL);
 1067: 		}
 1068: 		if (frwl->fw_prot == IPPROTO_TCP &&
 1069: 			frwl->fw_tcpf != frwl->fw_tcpnf) {
 1070: 			dprintf(("%s cannot mix 'frag' with TCP flags\n", err_prefix));
 1071: 			return(NULL);
 1072: 		}
 1073: 	}
 1074: 
 1075: 	/* Check command specific stuff */
 1076: 	switch (frwl->fw_flg & IPV6_FW_F_COMMAND)
 1077: 	{
 1078: 	case IPV6_FW_F_REJECT:
 1079: 		if (frwl->fw_reject_code >= 0x100
 1080: 		    && !(frwl->fw_prot == IPPROTO_TCP
 1081: 		      && frwl->fw_reject_code == IPV6_FW_REJECT_RST)) {
 1082: 			dprintf(("%s unknown reject code\n", err_prefix));
 1083: 			return(NULL);
 1084: 		}
 1085: 		break;
 1086: 	case IPV6_FW_F_DIVERT:		/* Diverting to port zero is invalid */
 1087: 	case IPV6_FW_F_TEE:
 1088: 		if (frwl->fw_divert_port == 0) {
 1089: 			dprintf(("%s can't divert to port 0\n", err_prefix));
 1090: 			return (NULL);
 1091: 		}
 1092: 		break;
 1093: 	case IPV6_FW_F_DENY:
 1094: 	case IPV6_FW_F_ACCEPT:
 1095: 	case IPV6_FW_F_COUNT:
 1096: 	case IPV6_FW_F_SKIPTO:
 1097: 		break;
 1098: 	default:
 1099: 		dprintf(("%s invalid command\n", err_prefix));
 1100: 		return(NULL);
 1101: 	}
 1102: 
 1103: 	return frwl;
 1104: }
 1105: 
 1106: static int
 1107: ip6_fw_ctl(int stage, struct mbuf **mm)
 1108: {
 1109: 	int error;
 1110: 	struct mbuf *m;
 1111: 
 1112: 	if (stage == IPV6_FW_GET) {
 1113: 		struct ip6_fw_chain *fcp = ip6_fw_chain.lh_first;
 1114: 		*mm = m = m_get(M_WAIT, MT_DATA); /* XXX */
 1115: 		if (!m)
 1116: 			return(ENOBUFS);
 1117: 		if (sizeof *(fcp->rule) > MLEN) {
 1118: 			MCLGET(m, M_WAIT);
 1119: 			if ((m->m_flags & M_EXT) == 0) {
 1120: 				m_free(m);
 1121: 				return(ENOBUFS);
 1122: 			}
 1123: 		}
 1124: 		for (; fcp; fcp = fcp->chain.le_next) {
 1125: 			bcopy(fcp->rule, m->m_data, sizeof *(fcp->rule));
 1126: 			m->m_len = sizeof *(fcp->rule);
 1127: 			m->m_next = m_get(M_WAIT, MT_DATA); /* XXX */
 1128: 			if (!m->m_next) {
 1129: 				m_freem(*mm);
 1130: 				return(ENOBUFS);
 1131: 			}
 1132: 			m = m->m_next;
 1133: 			if (sizeof *(fcp->rule) > MLEN) {
 1134: 				MCLGET(m, M_WAIT);
 1135: 				if ((m->m_flags & M_EXT) == 0) {
 1136: 					m_freem(*mm);
 1137: 					return(ENOBUFS);
 1138: 				}
 1139: 			}
 1140: 			m->m_len = 0;
 1141: 		}
 1142: 		return (0);
 1143: 	}
 1144: 	m = *mm;
 1145: 	/* only allow get calls if secure mode > 2 */
 1146: 	if (securelevel > 2) {
 1147: 		if (m) {
 1148: 			(void)m_freem(m);
 1149: 			*mm = 0;
 1150: 		}
 1151: 		return(EPERM);
 1152: 	}
 1153: 	if (stage == IPV6_FW_FLUSH) {
 1154: 		while (ip6_fw_chain.lh_first != NULL &&
 1155: 		    ip6_fw_chain.lh_first->rule->fw_number != (u_short)-1) {
 1156: 			struct ip6_fw_chain *fcp = ip6_fw_chain.lh_first;
 1157: 			int s = splnet();
 1158: 			LIST_REMOVE(ip6_fw_chain.lh_first, chain);
 1159: 			splx(s);
 1160: 			free(fcp->rule, M_IP6FW);
 1161: 			free(fcp, M_IP6FW);
 1162: 		}
 1163: 		if (m) {
 1164: 			(void)m_freem(m);
 1165: 			*mm = 0;
 1166: 		}
 1167: 		return (0);
 1168: 	}
 1169: 	if (stage == IPV6_FW_ZERO) {
 1170: 		error = zero_entry6(m);
 1171: 		if (m) {
 1172: 			(void)m_freem(m);
 1173: 			*mm = 0;
 1174: 		}
 1175: 		return (error);
 1176: 	}
 1177: 	if (m == NULL) {
 1178: 		printf("%s NULL mbuf ptr\n", err_prefix);
 1179: 		return (EINVAL);
 1180: 	}
 1181: 
 1182: 	if (stage == IPV6_FW_ADD) {
 1183: 		struct ip6_fw *frwl = check_ip6fw_mbuf(m);
 1184: 
 1185: 		if (!frwl)
 1186: 			error = EINVAL;
 1187: 		else
 1188: 			error = add_entry6(&ip6_fw_chain, frwl);
 1189: 		if (m) {
 1190: 			(void)m_freem(m);
 1191: 			*mm = 0;
 1192: 		}
 1193: 		return error;
 1194: 	}
 1195: 	if (stage == IPV6_FW_DEL) {
 1196: 		if (m->m_len != sizeof(struct ip6_fw)) {
 1197: 			dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len,
 1198: 			    sizeof(struct ip6_fw)));
 1199: 			error = EINVAL;
 1200: 		} else if (mtod(m, struct ip6_fw *)->fw_number == (u_short)-1) {
 1201: 			dprintf(("%s can't delete rule 65535\n", err_prefix));
 1202: 			error = EINVAL;
 1203: 		} else
 1204: 			error = del_entry6(&ip6_fw_chain,
 1205: 			    mtod(m, struct ip6_fw *)->fw_number);
 1206: 		if (m) {
 1207: 			(void)m_freem(m);
 1208: 			*mm = 0;
 1209: 		}
 1210: 		return error;
 1211: 	}
 1212: 
 1213: 	dprintf(("%s unknown request %d\n", err_prefix, stage));
 1214: 	if (m) {
 1215: 		(void)m_freem(m);
 1216: 		*mm = 0;
 1217: 	}
 1218: 	return (EINVAL);
 1219: }
 1220: 
 1221: void
 1222: ip6_fw_init(void)
 1223: {
 1224: 	struct ip6_fw default_rule;
 1225: 
 1226: 	ip6_fw_chk_ptr = ip6_fw_chk;
 1227: 	ip6_fw_ctl_ptr = ip6_fw_ctl;
 1228: 	LIST_INIT(&ip6_fw_chain);
 1229: 
 1230: 	bzero(&default_rule, sizeof default_rule);
 1231: 	default_rule.fw_prot = IPPROTO_IPV6;
 1232: 	default_rule.fw_number = (u_short)-1;
 1233: #ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT
 1234: 	default_rule.fw_flg |= IPV6_FW_F_ACCEPT;
 1235: #else
 1236: 	default_rule.fw_flg |= IPV6_FW_F_DENY;
 1237: #endif
 1238: 	default_rule.fw_flg |= IPV6_FW_F_IN | IPV6_FW_F_OUT;
 1239: 	if (check_ip6fw_struct(&default_rule) == NULL ||
 1240: 		add_entry6(&ip6_fw_chain, &default_rule))
 1241: 		panic(__FUNCTION__);
 1242: 
 1243: 	printf("IPv6 packet filtering initialized, ");
 1244: #ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT
 1245: 	printf("default to accept, ");
 1246: #endif
 1247: #ifndef IPV6FIREWALL_VERBOSE
 1248: 	printf("logging disabled\n");
 1249: #else
 1250: 	if (fw6_verbose_limit == 0)
 1251: 		printf("unlimited logging\n");
 1252: 	else
 1253: 		printf("logging limited to %d packets/entry\n",
 1254: 		    fw6_verbose_limit);
 1255: #endif
 1256: }
 1257: 
 1258: static ip6_fw_chk_t *old_chk_ptr;
 1259: static ip6_fw_ctl_t *old_ctl_ptr;
 1260: 
 1261: static int
 1262: ip6fw_modevent(module_t mod, int type, void *unused)
 1263: {
 1264: 	int s;
 1265: 
 1266: 	switch (type) {
 1267: 	case MOD_LOAD:
 1268: 		s = splnet();
 1269: 
 1270: 		old_chk_ptr = ip6_fw_chk_ptr;
 1271: 		old_ctl_ptr = ip6_fw_ctl_ptr;
 1272: 
 1273: 		ip6_fw_init();
 1274: 		splx(s);
 1275: 		return 0;
 1276: 	case MOD_UNLOAD:
 1277: 		s = splnet();
 1278: 		ip6_fw_chk_ptr =  old_chk_ptr;
 1279: 		ip6_fw_ctl_ptr =  old_ctl_ptr;
 1280: 		while (LIST_FIRST(&ip6_fw_chain) != NULL) {
 1281: 			struct ip6_fw_chain *fcp = LIST_FIRST(&ip6_fw_chain);
 1282: 			LIST_REMOVE(LIST_FIRST(&ip6_fw_chain), chain);
 1283: 			free(fcp->rule, M_IP6FW);
 1284: 			free(fcp, M_IP6FW);
 1285: 		}
 1286: 
 1287: 		splx(s);
 1288: 		printf("IPv6 firewall unloaded\n");
 1289: 		return 0;
 1290: 	default:
 1291: 		break;
 1292: 	}
 1293: 	return 0;
 1294: }
 1295: 
 1296: static moduledata_t ip6fwmod = {
 1297: 	"ip6fw",
 1298: 	ip6fw_modevent,
 1299: 	0
 1300: };
 1301: DECLARE_MODULE(ip6fw, ip6fwmod, SI_SUB_PSEUDO, SI_ORDER_ANY);