xref: /freebsd/sys/netpfil/ipfw/ip_fw_pfil.c (revision ff1aec7ccb547b778c7fb2263ecb50ceeea971f5)
13b3a8eb9SGleb Smirnoff /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni  *
43b3a8eb9SGleb Smirnoff  * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG
53b3a8eb9SGleb Smirnoff  * All rights reserved.
63b3a8eb9SGleb Smirnoff  *
73b3a8eb9SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
83b3a8eb9SGleb Smirnoff  * modification, are permitted provided that the following conditions
93b3a8eb9SGleb Smirnoff  * are met:
103b3a8eb9SGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
113b3a8eb9SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
123b3a8eb9SGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
133b3a8eb9SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
143b3a8eb9SGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
153b3a8eb9SGleb Smirnoff  *
163b3a8eb9SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
173b3a8eb9SGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
183b3a8eb9SGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
193b3a8eb9SGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
203b3a8eb9SGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
213b3a8eb9SGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
223b3a8eb9SGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
233b3a8eb9SGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
243b3a8eb9SGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
253b3a8eb9SGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
263b3a8eb9SGleb Smirnoff  * SUCH DAMAGE.
273b3a8eb9SGleb Smirnoff  */
283b3a8eb9SGleb Smirnoff 
293b3a8eb9SGleb Smirnoff #include <sys/cdefs.h>
303b3a8eb9SGleb Smirnoff #include "opt_ipfw.h"
313b3a8eb9SGleb Smirnoff #include "opt_inet.h"
323b3a8eb9SGleb Smirnoff #include "opt_inet6.h"
333b3a8eb9SGleb Smirnoff #ifndef INET
343b3a8eb9SGleb Smirnoff #error IPFIREWALL requires INET.
353b3a8eb9SGleb Smirnoff #endif /* INET */
363b3a8eb9SGleb Smirnoff 
373b3a8eb9SGleb Smirnoff #include <sys/param.h>
383b3a8eb9SGleb Smirnoff #include <sys/systm.h>
393b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
403b3a8eb9SGleb Smirnoff #include <sys/mbuf.h>
413b3a8eb9SGleb Smirnoff #include <sys/module.h>
423b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
433b3a8eb9SGleb Smirnoff #include <sys/lock.h>
443b3a8eb9SGleb Smirnoff #include <sys/rwlock.h>
453b3a8eb9SGleb Smirnoff #include <sys/socket.h>
463b3a8eb9SGleb Smirnoff #include <sys/sysctl.h>
473b3a8eb9SGleb Smirnoff 
483b3a8eb9SGleb Smirnoff #include <net/if.h>
49b252313fSGleb Smirnoff #include <net/if_var.h>
503b3a8eb9SGleb Smirnoff #include <net/route.h>
513b3a8eb9SGleb Smirnoff #include <net/ethernet.h>
523b3a8eb9SGleb Smirnoff #include <net/pfil.h>
533b3a8eb9SGleb Smirnoff #include <net/vnet.h>
543b3a8eb9SGleb Smirnoff 
553b3a8eb9SGleb Smirnoff #include <netinet/in.h>
563b3a8eb9SGleb Smirnoff #include <netinet/in_systm.h>
573b3a8eb9SGleb Smirnoff #include <netinet/ip.h>
583b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h>
593b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
603b3a8eb9SGleb Smirnoff #ifdef INET6
613b3a8eb9SGleb Smirnoff #include <netinet/ip6.h>
623b3a8eb9SGleb Smirnoff #include <netinet6/ip6_var.h>
632530ed9eSAndrey V. Elsukov #include <netinet6/scope6_var.h>
643b3a8eb9SGleb Smirnoff #endif
653b3a8eb9SGleb Smirnoff 
663b3a8eb9SGleb Smirnoff #include <netgraph/ng_ipfw.h>
673b3a8eb9SGleb Smirnoff 
683b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
693b3a8eb9SGleb Smirnoff 
703b3a8eb9SGleb Smirnoff #include <machine/in_cksum.h>
713b3a8eb9SGleb Smirnoff 
725f901c92SAndrew Turner VNET_DEFINE_STATIC(int, fw_enable) = 1;
733b3a8eb9SGleb Smirnoff #define V_fw_enable	VNET(fw_enable)
743b3a8eb9SGleb Smirnoff 
753b3a8eb9SGleb Smirnoff #ifdef INET6
765f901c92SAndrew Turner VNET_DEFINE_STATIC(int, fw6_enable) = 1;
773b3a8eb9SGleb Smirnoff #define V_fw6_enable	VNET(fw6_enable)
783b3a8eb9SGleb Smirnoff #endif
793b3a8eb9SGleb Smirnoff 
805f901c92SAndrew Turner VNET_DEFINE_STATIC(int, fwlink_enable) = 0;
813b3a8eb9SGleb Smirnoff #define V_fwlink_enable	VNET(fwlink_enable)
823b3a8eb9SGleb Smirnoff 
833b3a8eb9SGleb Smirnoff int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
843b3a8eb9SGleb Smirnoff 
853b3a8eb9SGleb Smirnoff /* Forward declarations. */
86b00b7e03SGleb Smirnoff static int ipfw_divert(struct mbuf **, struct ip_fw_args *, bool);
873b3a8eb9SGleb Smirnoff 
883b3a8eb9SGleb Smirnoff #ifdef SYSCTL_NODE
893b3a8eb9SGleb Smirnoff 
903b3a8eb9SGleb Smirnoff SYSBEGIN(f1)
913b3a8eb9SGleb Smirnoff 
923b3a8eb9SGleb Smirnoff SYSCTL_DECL(_net_inet_ip_fw);
936df8a710SGleb Smirnoff SYSCTL_PROC(_net_inet_ip_fw, OID_AUTO, enable,
9449197c39SZhenlei Huang     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_SECURE3 |
957029da5cSPawel Biernacki     CTLFLAG_NEEDGIANT, &VNET_NAME(fw_enable), 0, ipfw_chg_hook, "I",
967029da5cSPawel Biernacki     "Enable ipfw");
973b3a8eb9SGleb Smirnoff #ifdef INET6
983b3a8eb9SGleb Smirnoff SYSCTL_DECL(_net_inet6_ip6_fw);
996df8a710SGleb Smirnoff SYSCTL_PROC(_net_inet6_ip6_fw, OID_AUTO, enable,
10049197c39SZhenlei Huang     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_SECURE3 |
1017029da5cSPawel Biernacki     CTLFLAG_NEEDGIANT, &VNET_NAME(fw6_enable), 0, ipfw_chg_hook, "I",
1027029da5cSPawel Biernacki     "Enable ipfw+6");
1033b3a8eb9SGleb Smirnoff #endif /* INET6 */
1043b3a8eb9SGleb Smirnoff 
1053b3a8eb9SGleb Smirnoff SYSCTL_DECL(_net_link_ether);
1066df8a710SGleb Smirnoff SYSCTL_PROC(_net_link_ether, OID_AUTO, ipfw,
10749197c39SZhenlei Huang     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_SECURE3 |
1087029da5cSPawel Biernacki     CTLFLAG_NEEDGIANT, &VNET_NAME(fwlink_enable), 0, ipfw_chg_hook, "I",
1096df8a710SGleb Smirnoff     "Pass ether pkts through firewall");
1103b3a8eb9SGleb Smirnoff 
1113b3a8eb9SGleb Smirnoff SYSEND
1123b3a8eb9SGleb Smirnoff 
1133b3a8eb9SGleb Smirnoff #endif /* SYSCTL_NODE */
1143b3a8eb9SGleb Smirnoff 
1153b3a8eb9SGleb Smirnoff /*
1163b3a8eb9SGleb Smirnoff  * The pfilter hook to pass packets to ipfw_chk and then to
1173b3a8eb9SGleb Smirnoff  * dummynet, divert, netgraph or other modules.
1183b3a8eb9SGleb Smirnoff  * The packet may be consumed.
1193b3a8eb9SGleb Smirnoff  */
120b252313fSGleb Smirnoff static pfil_return_t
ipfw_check_packet(struct mbuf ** m0,struct ifnet * ifp,int flags,void * ruleset __unused,struct inpcb * inp)121b7795b67SGleb Smirnoff ipfw_check_packet(struct mbuf **m0, struct ifnet *ifp, int flags,
122b252313fSGleb Smirnoff     void *ruleset __unused, struct inpcb *inp)
1233b3a8eb9SGleb Smirnoff {
1243b3a8eb9SGleb Smirnoff 	struct ip_fw_args args;
1253b3a8eb9SGleb Smirnoff 	struct m_tag *tag;
126b252313fSGleb Smirnoff 	pfil_return_t ret;
127dc0fa4f7SGleb Smirnoff 	int ipfw;
1283b3a8eb9SGleb Smirnoff 
129b7795b67SGleb Smirnoff 	args.flags = (flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT;
130*ff1aec7cSAndrey V. Elsukov 	args.rule.pkt_mark = 0;
1313b3a8eb9SGleb Smirnoff again:
1323b3a8eb9SGleb Smirnoff 	/*
1333b3a8eb9SGleb Smirnoff 	 * extract and remove the tag if present. If we are left
1343b3a8eb9SGleb Smirnoff 	 * with onepass, optimize the outgoing path.
1353b3a8eb9SGleb Smirnoff 	 */
1363b3a8eb9SGleb Smirnoff 	tag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
1373b3a8eb9SGleb Smirnoff 	if (tag != NULL) {
1383b3a8eb9SGleb Smirnoff 		args.rule = *((struct ipfw_rule_ref *)(tag+1));
1393b3a8eb9SGleb Smirnoff 		m_tag_delete(*m0, tag);
1408f35d5f3SGleb Smirnoff 		if (args.rule.info & IPFW_ONEPASS)
1411854fb8fSDag-Erling Smørgrav 			return (PFIL_PASS);
1421cdf23bcSAndrey V. Elsukov 		args.flags |= IPFW_ARGS_REF;
1433b3a8eb9SGleb Smirnoff 	}
1443b3a8eb9SGleb Smirnoff 
1453b3a8eb9SGleb Smirnoff 	args.m = *m0;
146b7795b67SGleb Smirnoff 	args.ifp = ifp;
1473b3a8eb9SGleb Smirnoff 	args.inp = inp;
1483b3a8eb9SGleb Smirnoff 
1493b3a8eb9SGleb Smirnoff 	ipfw = ipfw_chk(&args);
1503b3a8eb9SGleb Smirnoff 	*m0 = args.m;
1513b3a8eb9SGleb Smirnoff 
152b11efc1eSAndrey V. Elsukov 	KASSERT(*m0 != NULL || ipfw == IP_FW_DENY ||
153b11efc1eSAndrey V. Elsukov 	    ipfw == IP_FW_NAT64, ("%s: m0 is NULL", __func__));
1543b3a8eb9SGleb Smirnoff 
155b252313fSGleb Smirnoff 	ret = PFIL_PASS;
1563b3a8eb9SGleb Smirnoff 	switch (ipfw) {
1573b3a8eb9SGleb Smirnoff 	case IP_FW_PASS:
1583b3a8eb9SGleb Smirnoff 		/* next_hop may be set by ipfw_chk */
1591cdf23bcSAndrey V. Elsukov 		if ((args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR |
160b252313fSGleb Smirnoff 		    IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) == 0)
1611cdf23bcSAndrey V. Elsukov 			break;
162c1de64a4SAndrey V. Elsukov #if (!defined(INET6) && !defined(INET))
163b252313fSGleb Smirnoff 		ret = PFIL_DROPPED;
1643b3a8eb9SGleb Smirnoff #else
1653b3a8eb9SGleb Smirnoff 	    {
1661cdf23bcSAndrey V. Elsukov 		void *psa;
1673b3a8eb9SGleb Smirnoff 		size_t len;
1683b3a8eb9SGleb Smirnoff #ifdef INET
1691cdf23bcSAndrey V. Elsukov 		if (args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR)) {
1701cdf23bcSAndrey V. Elsukov 			MPASS((args.flags & (IPFW_ARGS_NH4 |
1711cdf23bcSAndrey V. Elsukov 			    IPFW_ARGS_NH4PTR)) != (IPFW_ARGS_NH4 |
1721cdf23bcSAndrey V. Elsukov 			    IPFW_ARGS_NH4PTR));
1731cdf23bcSAndrey V. Elsukov 			MPASS((args.flags & (IPFW_ARGS_NH6 |
1741cdf23bcSAndrey V. Elsukov 			    IPFW_ARGS_NH6PTR)) == 0);
1753b3a8eb9SGleb Smirnoff 			len = sizeof(struct sockaddr_in);
1761cdf23bcSAndrey V. Elsukov 			psa = (args.flags & IPFW_ARGS_NH4) ?
1771cdf23bcSAndrey V. Elsukov 			    &args.hopstore : args.next_hop;
1781cdf23bcSAndrey V. Elsukov 			if (in_localip(satosin(psa)->sin_addr))
1791cdf23bcSAndrey V. Elsukov 				(*m0)->m_flags |= M_FASTFWD_OURS;
1801cdf23bcSAndrey V. Elsukov 			(*m0)->m_flags |= M_IP_NEXTHOP;
1811cdf23bcSAndrey V. Elsukov 		}
1821cdf23bcSAndrey V. Elsukov #endif /* INET */
1831cdf23bcSAndrey V. Elsukov #ifdef INET6
1841cdf23bcSAndrey V. Elsukov 		if (args.flags & (IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) {
1851cdf23bcSAndrey V. Elsukov 			MPASS((args.flags & (IPFW_ARGS_NH6 |
1861cdf23bcSAndrey V. Elsukov 			    IPFW_ARGS_NH6PTR)) != (IPFW_ARGS_NH6 |
1871cdf23bcSAndrey V. Elsukov 			    IPFW_ARGS_NH6PTR));
1881cdf23bcSAndrey V. Elsukov 			MPASS((args.flags & (IPFW_ARGS_NH4 |
1891cdf23bcSAndrey V. Elsukov 			    IPFW_ARGS_NH4PTR)) == 0);
1901cdf23bcSAndrey V. Elsukov 			len = sizeof(struct sockaddr_in6);
1911cdf23bcSAndrey V. Elsukov 			psa = args.next_hop6;
1921cdf23bcSAndrey V. Elsukov 			(*m0)->m_flags |= M_IP6_NEXTHOP;
1931cdf23bcSAndrey V. Elsukov 		}
1941cdf23bcSAndrey V. Elsukov #endif /* INET6 */
1951cdf23bcSAndrey V. Elsukov 		/*
1961cdf23bcSAndrey V. Elsukov 		 * Incoming packets should not be tagged so we do not
1973b3a8eb9SGleb Smirnoff 		 * m_tag_find. Outgoing packets may be tagged, so we
1983b3a8eb9SGleb Smirnoff 		 * reuse the tag if present.
1993b3a8eb9SGleb Smirnoff 		 */
200b7795b67SGleb Smirnoff 		tag = (flags & PFIL_IN) ? NULL :
2013b3a8eb9SGleb Smirnoff 			m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
2021cdf23bcSAndrey V. Elsukov 		if (tag != NULL) {
2031cdf23bcSAndrey V. Elsukov 			m_tag_unlink(*m0, tag);
2043b3a8eb9SGleb Smirnoff 		} else {
2051cdf23bcSAndrey V. Elsukov 			tag = m_tag_get(PACKET_TAG_IPFORWARD, len,
2063b3a8eb9SGleb Smirnoff 			    M_NOWAIT);
2071cdf23bcSAndrey V. Elsukov 			if (tag == NULL) {
208b252313fSGleb Smirnoff 				ret = PFIL_DROPPED;
209b252313fSGleb Smirnoff 				break;
2103b3a8eb9SGleb Smirnoff 			}
2113b3a8eb9SGleb Smirnoff 		}
2121cdf23bcSAndrey V. Elsukov 		if ((args.flags & IPFW_ARGS_NH6) == 0)
2131cdf23bcSAndrey V. Elsukov 			bcopy(psa, tag + 1, len);
2141cdf23bcSAndrey V. Elsukov 		m_tag_prepend(*m0, tag);
2151854fb8fSDag-Erling Smørgrav 		ret = PFIL_PASS;
2163b3a8eb9SGleb Smirnoff #ifdef INET6
2171cdf23bcSAndrey V. Elsukov 		/* IPv6 next hop needs additional handling */
2181cdf23bcSAndrey V. Elsukov 		if (args.flags & (IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) {
2192530ed9eSAndrey V. Elsukov 			struct sockaddr_in6 *sa6;
2202530ed9eSAndrey V. Elsukov 
2211cdf23bcSAndrey V. Elsukov 			sa6 = satosin6(tag + 1);
2221cdf23bcSAndrey V. Elsukov 			if (args.flags & IPFW_ARGS_NH6) {
2231cdf23bcSAndrey V. Elsukov 				sa6->sin6_family = AF_INET6;
2241cdf23bcSAndrey V. Elsukov 				sa6->sin6_len = sizeof(*sa6);
2251cdf23bcSAndrey V. Elsukov 				sa6->sin6_addr = args.hopstore6.sin6_addr;
2261cdf23bcSAndrey V. Elsukov 				sa6->sin6_port = args.hopstore6.sin6_port;
2271cdf23bcSAndrey V. Elsukov 				sa6->sin6_scope_id =
2281cdf23bcSAndrey V. Elsukov 				    args.hopstore6.sin6_scope_id;
2291cdf23bcSAndrey V. Elsukov 			}
2302530ed9eSAndrey V. Elsukov 			/*
2312530ed9eSAndrey V. Elsukov 			 * If nh6 address is link-local we should convert
2322530ed9eSAndrey V. Elsukov 			 * it to kernel internal form before doing any
2332530ed9eSAndrey V. Elsukov 			 * comparisons.
2342530ed9eSAndrey V. Elsukov 			 */
2352530ed9eSAndrey V. Elsukov 			if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) {
236b252313fSGleb Smirnoff 				ret = PFIL_DROPPED;
2372530ed9eSAndrey V. Elsukov 				break;
2382530ed9eSAndrey V. Elsukov 			}
2392530ed9eSAndrey V. Elsukov 			if (in6_localip(&sa6->sin6_addr))
2403b3a8eb9SGleb Smirnoff 				(*m0)->m_flags |= M_FASTFWD_OURS;
2413b3a8eb9SGleb Smirnoff 		}
2421cdf23bcSAndrey V. Elsukov #endif /* INET6 */
2433b3a8eb9SGleb Smirnoff 	    }
244c1de64a4SAndrey V. Elsukov #endif /* INET || INET6 */
2453b3a8eb9SGleb Smirnoff 		break;
2463b3a8eb9SGleb Smirnoff 
2473b3a8eb9SGleb Smirnoff 	case IP_FW_DENY:
248b252313fSGleb Smirnoff 		ret = PFIL_DROPPED;
249b252313fSGleb Smirnoff 		break;
2503b3a8eb9SGleb Smirnoff 
2513b3a8eb9SGleb Smirnoff 	case IP_FW_DUMMYNET:
252b252313fSGleb Smirnoff 		if (ip_dn_io_ptr == NULL) {
253b252313fSGleb Smirnoff 			ret = PFIL_DROPPED;
254b252313fSGleb Smirnoff 			break;
255b252313fSGleb Smirnoff 		}
2563b1522c2SAndrey V. Elsukov 		MPASS(args.flags & IPFW_ARGS_REF);
257dc0fa4f7SGleb Smirnoff 		if (args.flags & (IPFW_ARGS_IP4 | IPFW_ARGS_IP6))
258dc0fa4f7SGleb Smirnoff 			(void )ip_dn_io_ptr(m0, &args);
259b252313fSGleb Smirnoff 		else {
260b252313fSGleb Smirnoff 			ret = PFIL_DROPPED;
261b252313fSGleb Smirnoff 			break;
262b252313fSGleb Smirnoff 		}
2633b3a8eb9SGleb Smirnoff 		/*
2643b3a8eb9SGleb Smirnoff 		 * XXX should read the return value.
2653b3a8eb9SGleb Smirnoff 		 * dummynet normally eats the packet and sets *m0=NULL
2663b3a8eb9SGleb Smirnoff 		 * unless the packet can be sent immediately. In this
2673b3a8eb9SGleb Smirnoff 		 * case args is updated and we should re-run the
2683b3a8eb9SGleb Smirnoff 		 * check without clearing args.
2693b3a8eb9SGleb Smirnoff 		 */
2703b3a8eb9SGleb Smirnoff 		if (*m0 != NULL)
2713b3a8eb9SGleb Smirnoff 			goto again;
272b252313fSGleb Smirnoff 		ret = PFIL_CONSUMED;
2733b3a8eb9SGleb Smirnoff 		break;
2743b3a8eb9SGleb Smirnoff 
2753b3a8eb9SGleb Smirnoff 	case IP_FW_TEE:
2763b3a8eb9SGleb Smirnoff 	case IP_FW_DIVERT:
2773b3a8eb9SGleb Smirnoff 		if (ip_divert_ptr == NULL) {
278b252313fSGleb Smirnoff 			ret = PFIL_DROPPED;
279b252313fSGleb Smirnoff 			break;
2803b3a8eb9SGleb Smirnoff 		}
2813b1522c2SAndrey V. Elsukov 		MPASS(args.flags & IPFW_ARGS_REF);
282b00b7e03SGleb Smirnoff 		(void )ipfw_divert(m0, &args, ipfw == IP_FW_TEE);
2833b3a8eb9SGleb Smirnoff 		/* continue processing for the original packet (tee). */
2843b3a8eb9SGleb Smirnoff 		if (*m0)
2853b3a8eb9SGleb Smirnoff 			goto again;
286b252313fSGleb Smirnoff 		ret = PFIL_CONSUMED;
2873b3a8eb9SGleb Smirnoff 		break;
2883b3a8eb9SGleb Smirnoff 
2893b3a8eb9SGleb Smirnoff 	case IP_FW_NGTEE:
2903b3a8eb9SGleb Smirnoff 	case IP_FW_NETGRAPH:
2913b3a8eb9SGleb Smirnoff 		if (ng_ipfw_input_p == NULL) {
292b252313fSGleb Smirnoff 			ret = PFIL_DROPPED;
293b252313fSGleb Smirnoff 			break;
2943b3a8eb9SGleb Smirnoff 		}
2953b1522c2SAndrey V. Elsukov 		MPASS(args.flags & IPFW_ARGS_REF);
296cef9f220SGleb Smirnoff 		(void )ng_ipfw_input_p(m0, &args, ipfw == IP_FW_NGTEE);
2973b3a8eb9SGleb Smirnoff 		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
2983b3a8eb9SGleb Smirnoff 			goto again;	/* continue with packet */
299b252313fSGleb Smirnoff 		ret = PFIL_CONSUMED;
3003b3a8eb9SGleb Smirnoff 		break;
3013b3a8eb9SGleb Smirnoff 
3023b3a8eb9SGleb Smirnoff 	case IP_FW_NAT:
3033b3a8eb9SGleb Smirnoff 		/* honor one-pass in case of successful nat */
304b252313fSGleb Smirnoff 		if (V_fw_one_pass)
3051cdf23bcSAndrey V. Elsukov 			break;
3063b3a8eb9SGleb Smirnoff 		goto again;
3073b3a8eb9SGleb Smirnoff 
3083b3a8eb9SGleb Smirnoff 	case IP_FW_REASS:
3093b3a8eb9SGleb Smirnoff 		goto again;		/* continue with packet */
3103b3a8eb9SGleb Smirnoff 
311ca0f03e8SAndrey V. Elsukov 	case IP_FW_NAT64:
312ca0f03e8SAndrey V. Elsukov 		ret = PFIL_CONSUMED;
313ca0f03e8SAndrey V. Elsukov 		break;
314ca0f03e8SAndrey V. Elsukov 
3153b3a8eb9SGleb Smirnoff 	default:
3163b3a8eb9SGleb Smirnoff 		KASSERT(0, ("%s: unknown retval", __func__));
3173b3a8eb9SGleb Smirnoff 	}
3183b3a8eb9SGleb Smirnoff 
319b252313fSGleb Smirnoff 	if (ret != PFIL_PASS) {
3203b3a8eb9SGleb Smirnoff 		if (*m0)
3213b3a8eb9SGleb Smirnoff 			FREE_PKT(*m0);
3223b3a8eb9SGleb Smirnoff 		*m0 = NULL;
3233b3a8eb9SGleb Smirnoff 	}
32421d172a3SGleb Smirnoff 
3251cdf23bcSAndrey V. Elsukov 	return (ret);
3263b3a8eb9SGleb Smirnoff }
3273b3a8eb9SGleb Smirnoff 
3283b3a8eb9SGleb Smirnoff /*
329caf32b26SGleb Smirnoff  * ipfw processing for ethernet packets (in and out), mbuf version.
3303b3a8eb9SGleb Smirnoff  */
331b252313fSGleb Smirnoff static pfil_return_t
ipfw_check_frame_mbuf(struct mbuf ** m0,struct ifnet * ifp,const int flags,void * ruleset __unused,struct inpcb * inp)332caf32b26SGleb Smirnoff ipfw_check_frame_mbuf(struct mbuf **m0, struct ifnet *ifp, const int flags,
333b252313fSGleb Smirnoff     void *ruleset __unused, struct inpcb *inp)
3343b3a8eb9SGleb Smirnoff {
335caf32b26SGleb Smirnoff 	struct ip_fw_args args = {
336caf32b26SGleb Smirnoff 		.flags = IPFW_ARGS_ETHER |
337caf32b26SGleb Smirnoff 		    ((flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT),
338caf32b26SGleb Smirnoff 		.ifp = ifp,
339caf32b26SGleb Smirnoff 		.inp = inp,
340caf32b26SGleb Smirnoff 	};
341caf32b26SGleb Smirnoff 	struct m_tag *mtag;
342b252313fSGleb Smirnoff 	pfil_return_t ret;
343f355cb3eSGleb Smirnoff 	int ipfw;
3443b3a8eb9SGleb Smirnoff 
3455310c191SEugene Grosbein again:
346f355cb3eSGleb Smirnoff 	/*
347f355cb3eSGleb Smirnoff 	 * Fetch start point from rule, if any.
348f355cb3eSGleb Smirnoff 	 * Remove the tag if present.
349f355cb3eSGleb Smirnoff 	 */
350caf32b26SGleb Smirnoff 	mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
3515310c191SEugene Grosbein 	if (mtag != NULL) {
352e4014585SLuiz Otavio O Souza 		args.rule = *((struct ipfw_rule_ref *)(mtag+1));
353caf32b26SGleb Smirnoff 		m_tag_delete(*m0, mtag);
354e4014585SLuiz Otavio O Souza 		if (args.rule.info & IPFW_ONEPASS)
355f355cb3eSGleb Smirnoff 			return (PFIL_PASS);
3561cdf23bcSAndrey V. Elsukov 		args.flags |= IPFW_ARGS_REF;
3573b3a8eb9SGleb Smirnoff 	}
358fc727ad6SBoris Lytochkin 	args.m = *m0;
3593b3a8eb9SGleb Smirnoff 
360f355cb3eSGleb Smirnoff 	ipfw = ipfw_chk(&args);
361caf32b26SGleb Smirnoff 	*m0 = args.m;
3623b3a8eb9SGleb Smirnoff 
363b252313fSGleb Smirnoff 	ret = PFIL_PASS;
364f355cb3eSGleb Smirnoff 	switch (ipfw) {
3653b3a8eb9SGleb Smirnoff 	case IP_FW_PASS:
3663b3a8eb9SGleb Smirnoff 		break;
3673b3a8eb9SGleb Smirnoff 
3683b3a8eb9SGleb Smirnoff 	case IP_FW_DENY:
369b252313fSGleb Smirnoff 		ret = PFIL_DROPPED;
370b252313fSGleb Smirnoff 		break;
3713b3a8eb9SGleb Smirnoff 
3723b3a8eb9SGleb Smirnoff 	case IP_FW_DUMMYNET:
373b252313fSGleb Smirnoff 		if (ip_dn_io_ptr == NULL) {
374b252313fSGleb Smirnoff 			ret = PFIL_DROPPED;
375b252313fSGleb Smirnoff 			break;
376b252313fSGleb Smirnoff 		}
3773b1522c2SAndrey V. Elsukov 		MPASS(args.flags & IPFW_ARGS_REF);
378caf32b26SGleb Smirnoff 		ip_dn_io_ptr(m0, &args);
379b252313fSGleb Smirnoff 		return (PFIL_CONSUMED);
3803b3a8eb9SGleb Smirnoff 
3815310c191SEugene Grosbein 	case IP_FW_NGTEE:
3825310c191SEugene Grosbein 	case IP_FW_NETGRAPH:
3835310c191SEugene Grosbein 		if (ng_ipfw_input_p == NULL) {
384b252313fSGleb Smirnoff 			ret = PFIL_DROPPED;
385b252313fSGleb Smirnoff 			break;
3865310c191SEugene Grosbein 		}
3873b1522c2SAndrey V. Elsukov 		MPASS(args.flags & IPFW_ARGS_REF);
388caf32b26SGleb Smirnoff 		(void )ng_ipfw_input_p(m0, &args, ipfw == IP_FW_NGTEE);
389f355cb3eSGleb Smirnoff 		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
3905310c191SEugene Grosbein 			goto again;	/* continue with packet */
391b252313fSGleb Smirnoff 		ret = PFIL_CONSUMED;
3925310c191SEugene Grosbein 		break;
3935310c191SEugene Grosbein 
3943b3a8eb9SGleb Smirnoff 	default:
3953b3a8eb9SGleb Smirnoff 		KASSERT(0, ("%s: unknown retval", __func__));
3963b3a8eb9SGleb Smirnoff 	}
3973b3a8eb9SGleb Smirnoff 
398caf32b26SGleb Smirnoff 	if (ret != PFIL_PASS) {
399caf32b26SGleb Smirnoff 		if (*m0)
400caf32b26SGleb Smirnoff 			FREE_PKT(*m0);
401caf32b26SGleb Smirnoff 		*m0 = NULL;
4023b3a8eb9SGleb Smirnoff 	}
4033b3a8eb9SGleb Smirnoff 
404caf32b26SGleb Smirnoff 	return (ret);
405caf32b26SGleb Smirnoff }
406caf32b26SGleb Smirnoff 
407caf32b26SGleb Smirnoff /*
408caf32b26SGleb Smirnoff  * ipfw processing for ethernet packets (in and out), memory pointer version,
409caf32b26SGleb Smirnoff  * two in/out accessors.
410caf32b26SGleb Smirnoff  */
411caf32b26SGleb Smirnoff static pfil_return_t
ipfw_check_frame_mem(void * mem,u_int len,int flags,struct ifnet * ifp,void * ruleset __unused,struct mbuf ** m)412caf32b26SGleb Smirnoff ipfw_check_frame_mem(void *mem, u_int len, int flags, struct ifnet *ifp,
413caf32b26SGleb Smirnoff     void *ruleset __unused, struct mbuf **m)
414caf32b26SGleb Smirnoff {
415caf32b26SGleb Smirnoff 	struct ip_fw_args args = {
416caf32b26SGleb Smirnoff 		.flags = len | IPFW_ARGS_ETHER |
417caf32b26SGleb Smirnoff 		    ((flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT),
418caf32b26SGleb Smirnoff 		.ifp = ifp,
419caf32b26SGleb Smirnoff 		.mem = mem,
420caf32b26SGleb Smirnoff 	};
421caf32b26SGleb Smirnoff 	pfil_return_t ret;
422caf32b26SGleb Smirnoff 	int ipfw;
423caf32b26SGleb Smirnoff 
424caf32b26SGleb Smirnoff 	*m = NULL;
425caf32b26SGleb Smirnoff again:
426caf32b26SGleb Smirnoff 	ipfw = ipfw_chk(&args);
427caf32b26SGleb Smirnoff 
428caf32b26SGleb Smirnoff 	ret = PFIL_PASS;
429caf32b26SGleb Smirnoff 	switch (ipfw) {
430caf32b26SGleb Smirnoff 	case IP_FW_PASS:
431caf32b26SGleb Smirnoff 		break;
432caf32b26SGleb Smirnoff 
433caf32b26SGleb Smirnoff 	case IP_FW_DENY:
434caf32b26SGleb Smirnoff 		ret = PFIL_DROPPED;
435caf32b26SGleb Smirnoff 		break;
436caf32b26SGleb Smirnoff 
437caf32b26SGleb Smirnoff 	case IP_FW_DUMMYNET:
438caf32b26SGleb Smirnoff 		if (ip_dn_io_ptr == NULL) {
439caf32b26SGleb Smirnoff 			ret = PFIL_DROPPED;
440caf32b26SGleb Smirnoff 			break;
441caf32b26SGleb Smirnoff 		}
442caf32b26SGleb Smirnoff 		*m = m_devget(mem, len, 0, ifp, NULL);
443caf32b26SGleb Smirnoff 		if (*m == NULL) {
444caf32b26SGleb Smirnoff 			ret = PFIL_DROPPED;
445caf32b26SGleb Smirnoff 			break;
446caf32b26SGleb Smirnoff 		}
447caf32b26SGleb Smirnoff 		MPASS(args.flags & IPFW_ARGS_REF);
448caf32b26SGleb Smirnoff 		ip_dn_io_ptr(m, &args);
449caf32b26SGleb Smirnoff 		return (PFIL_CONSUMED);
450caf32b26SGleb Smirnoff 
451caf32b26SGleb Smirnoff 	case IP_FW_NGTEE:
452caf32b26SGleb Smirnoff 	case IP_FW_NETGRAPH:
453caf32b26SGleb Smirnoff 		if (ng_ipfw_input_p == NULL) {
454caf32b26SGleb Smirnoff 			ret = PFIL_DROPPED;
455caf32b26SGleb Smirnoff 			break;
456caf32b26SGleb Smirnoff 		}
457caf32b26SGleb Smirnoff 		*m = m_devget(mem, len, 0, ifp, NULL);
458caf32b26SGleb Smirnoff 		if (*m == NULL) {
459caf32b26SGleb Smirnoff 			ret = PFIL_DROPPED;
460caf32b26SGleb Smirnoff 			break;
461caf32b26SGleb Smirnoff 		}
462caf32b26SGleb Smirnoff 		MPASS(args.flags & IPFW_ARGS_REF);
463caf32b26SGleb Smirnoff 		(void )ng_ipfw_input_p(m, &args, ipfw == IP_FW_NGTEE);
464caf32b26SGleb Smirnoff 		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
465caf32b26SGleb Smirnoff 			goto again;	/* continue with packet */
466caf32b26SGleb Smirnoff 		ret = PFIL_CONSUMED;
467caf32b26SGleb Smirnoff 		break;
468caf32b26SGleb Smirnoff 
469caf32b26SGleb Smirnoff 	default:
470caf32b26SGleb Smirnoff 		KASSERT(0, ("%s: unknown retval", __func__));
471caf32b26SGleb Smirnoff 	}
472caf32b26SGleb Smirnoff 
473caf32b26SGleb Smirnoff 	if (*m != NULL && ret == PFIL_PASS)
474f355cb3eSGleb Smirnoff 		ret = PFIL_REALLOCED;
475f355cb3eSGleb Smirnoff 
4761cdf23bcSAndrey V. Elsukov 	return (ret);
4773b3a8eb9SGleb Smirnoff }
4783b3a8eb9SGleb Smirnoff 
4793b3a8eb9SGleb Smirnoff /* do the divert, return 1 on error 0 on success */
4803b3a8eb9SGleb Smirnoff static int
ipfw_divert(struct mbuf ** m0,struct ip_fw_args * args,bool tee)481b00b7e03SGleb Smirnoff ipfw_divert(struct mbuf **m0, struct ip_fw_args *args, bool tee)
4823b3a8eb9SGleb Smirnoff {
4833b3a8eb9SGleb Smirnoff 	/*
4843b3a8eb9SGleb Smirnoff 	 * ipfw_chk() has already tagged the packet with the divert tag.
4853b3a8eb9SGleb Smirnoff 	 * If tee is set, copy packet and return original.
4863b3a8eb9SGleb Smirnoff 	 * If not tee, consume packet and send it to divert socket.
4873b3a8eb9SGleb Smirnoff 	 */
4883b3a8eb9SGleb Smirnoff 	struct mbuf *clone;
4893b3a8eb9SGleb Smirnoff 	struct ip *ip = mtod(*m0, struct ip *);
4903b3a8eb9SGleb Smirnoff 	struct m_tag *tag;
4913b3a8eb9SGleb Smirnoff 
4923b3a8eb9SGleb Smirnoff 	/* Cloning needed for tee? */
493b00b7e03SGleb Smirnoff 	if (tee == false) {
4943b3a8eb9SGleb Smirnoff 		clone = *m0;	/* use the original mbuf */
4953b3a8eb9SGleb Smirnoff 		*m0 = NULL;
4963b3a8eb9SGleb Smirnoff 	} else {
497eb1b1807SGleb Smirnoff 		clone = m_dup(*m0, M_NOWAIT);
4983b3a8eb9SGleb Smirnoff 		/* If we cannot duplicate the mbuf, we sacrifice the divert
4993b3a8eb9SGleb Smirnoff 		 * chain and continue with the tee-ed packet.
5003b3a8eb9SGleb Smirnoff 		 */
5013b3a8eb9SGleb Smirnoff 		if (clone == NULL)
5023b3a8eb9SGleb Smirnoff 			return 1;
5033b3a8eb9SGleb Smirnoff 	}
5043b3a8eb9SGleb Smirnoff 
5053b3a8eb9SGleb Smirnoff 	/*
5063b3a8eb9SGleb Smirnoff 	 * Divert listeners can normally handle non-fragmented packets,
5073b3a8eb9SGleb Smirnoff 	 * but we can only reass in the non-tee case.
5083b3a8eb9SGleb Smirnoff 	 * This means that listeners on a tee rule may get fragments,
5093b3a8eb9SGleb Smirnoff 	 * and have to live with that.
5103b3a8eb9SGleb Smirnoff 	 * Note that we now have the 'reass' ipfw option so if we care
5113b3a8eb9SGleb Smirnoff 	 * we can do it before a 'tee'.
5123b3a8eb9SGleb Smirnoff 	 */
513b00b7e03SGleb Smirnoff 	if (tee == false) switch (ip->ip_v) {
5143b3a8eb9SGleb Smirnoff 	case IPVERSION:
5153b3a8eb9SGleb Smirnoff 	    if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) {
5163b3a8eb9SGleb Smirnoff 		int hlen;
5173b3a8eb9SGleb Smirnoff 		struct mbuf *reass;
5183b3a8eb9SGleb Smirnoff 
5193b3a8eb9SGleb Smirnoff 		reass = ip_reass(clone); /* Reassemble packet. */
5203b3a8eb9SGleb Smirnoff 		if (reass == NULL)
5213b3a8eb9SGleb Smirnoff 			return 0; /* not an error */
5223b3a8eb9SGleb Smirnoff 		/* if reass = NULL then it was consumed by ip_reass */
5233b3a8eb9SGleb Smirnoff 		/*
5243b3a8eb9SGleb Smirnoff 		 * IP header checksum fixup after reassembly and leave header
5253b3a8eb9SGleb Smirnoff 		 * in network byte order.
5263b3a8eb9SGleb Smirnoff 		 */
5273b3a8eb9SGleb Smirnoff 		ip = mtod(reass, struct ip *);
5283b3a8eb9SGleb Smirnoff 		hlen = ip->ip_hl << 2;
5293b3a8eb9SGleb Smirnoff 		ip->ip_sum = 0;
5303b3a8eb9SGleb Smirnoff 		if (hlen == sizeof(struct ip))
5313b3a8eb9SGleb Smirnoff 			ip->ip_sum = in_cksum_hdr(ip);
5323b3a8eb9SGleb Smirnoff 		else
5333b3a8eb9SGleb Smirnoff 			ip->ip_sum = in_cksum(reass, hlen);
5343b3a8eb9SGleb Smirnoff 		clone = reass;
5353b3a8eb9SGleb Smirnoff 	    }
5363b3a8eb9SGleb Smirnoff 	    break;
5373b3a8eb9SGleb Smirnoff #ifdef INET6
5383b3a8eb9SGleb Smirnoff 	case IPV6_VERSION >> 4:
5393b3a8eb9SGleb Smirnoff 	    {
5403b3a8eb9SGleb Smirnoff 	    struct ip6_hdr *const ip6 = mtod(clone, struct ip6_hdr *);
5413b3a8eb9SGleb Smirnoff 
5423b3a8eb9SGleb Smirnoff 		if (ip6->ip6_nxt == IPPROTO_FRAGMENT) {
5433b3a8eb9SGleb Smirnoff 			int nxt, off;
5443b3a8eb9SGleb Smirnoff 
5453b3a8eb9SGleb Smirnoff 			off = sizeof(struct ip6_hdr);
5463b3a8eb9SGleb Smirnoff 			nxt = frag6_input(&clone, &off, 0);
5473b3a8eb9SGleb Smirnoff 			if (nxt == IPPROTO_DONE)
5483b3a8eb9SGleb Smirnoff 				return (0);
5493b3a8eb9SGleb Smirnoff 		}
5503b3a8eb9SGleb Smirnoff 		break;
5513b3a8eb9SGleb Smirnoff 	    }
5523b3a8eb9SGleb Smirnoff #endif
5533b3a8eb9SGleb Smirnoff 	}
5543b3a8eb9SGleb Smirnoff 
5553b3a8eb9SGleb Smirnoff 	/* attach a tag to the packet with the reinject info */
5563b3a8eb9SGleb Smirnoff 	tag = m_tag_alloc(MTAG_IPFW_RULE, 0,
5573b3a8eb9SGleb Smirnoff 		    sizeof(struct ipfw_rule_ref), M_NOWAIT);
5583b3a8eb9SGleb Smirnoff 	if (tag == NULL) {
5593b3a8eb9SGleb Smirnoff 		FREE_PKT(clone);
5603b3a8eb9SGleb Smirnoff 		return 1;
5613b3a8eb9SGleb Smirnoff 	}
562b00b7e03SGleb Smirnoff 	*((struct ipfw_rule_ref *)(tag+1)) = args->rule;
5633b3a8eb9SGleb Smirnoff 	m_tag_prepend(clone, tag);
5643b3a8eb9SGleb Smirnoff 
5653b3a8eb9SGleb Smirnoff 	/* Do the dirty job... */
566b00b7e03SGleb Smirnoff 	ip_divert_ptr(clone, args->flags & IPFW_ARGS_IN);
5673b3a8eb9SGleb Smirnoff 	return 0;
5683b3a8eb9SGleb Smirnoff }
5693b3a8eb9SGleb Smirnoff 
5703b3a8eb9SGleb Smirnoff /*
5713b3a8eb9SGleb Smirnoff  * attach or detach hooks for a given protocol family
5723b3a8eb9SGleb Smirnoff  */
573b252313fSGleb Smirnoff VNET_DEFINE_STATIC(pfil_hook_t, ipfw_inet_hook);
574b252313fSGleb Smirnoff #define	V_ipfw_inet_hook	VNET(ipfw_inet_hook)
5752790ca97SGleb Smirnoff #ifdef INET6
5762790ca97SGleb Smirnoff VNET_DEFINE_STATIC(pfil_hook_t, ipfw_inet6_hook);
577b252313fSGleb Smirnoff #define	V_ipfw_inet6_hook	VNET(ipfw_inet6_hook)
5782790ca97SGleb Smirnoff #endif
5792790ca97SGleb Smirnoff VNET_DEFINE_STATIC(pfil_hook_t, ipfw_link_hook);
580b252313fSGleb Smirnoff #define	V_ipfw_link_hook	VNET(ipfw_link_hook)
581b252313fSGleb Smirnoff 
58297245d40SGleb Smirnoff static void
ipfw_hook(int pf)58397245d40SGleb Smirnoff ipfw_hook(int pf)
5843b3a8eb9SGleb Smirnoff {
585caf32b26SGleb Smirnoff 	struct pfil_hook_args pha = {
586caf32b26SGleb Smirnoff 		.pa_version = PFIL_VERSION,
587caf32b26SGleb Smirnoff 		.pa_flags = PFIL_IN | PFIL_OUT,
588caf32b26SGleb Smirnoff 		.pa_modname = "ipfw",
589caf32b26SGleb Smirnoff 	};
590b252313fSGleb Smirnoff 	pfil_hook_t *h;
5913b3a8eb9SGleb Smirnoff 
592b252313fSGleb Smirnoff 	switch (pf) {
593b252313fSGleb Smirnoff 	case AF_INET:
594caf32b26SGleb Smirnoff 		pha.pa_mbuf_chk = ipfw_check_packet;
595b252313fSGleb Smirnoff 		pha.pa_type = PFIL_TYPE_IP4;
596b252313fSGleb Smirnoff 		pha.pa_rulname = "default";
597b252313fSGleb Smirnoff 		h = &V_ipfw_inet_hook;
598b252313fSGleb Smirnoff 		break;
599b252313fSGleb Smirnoff #ifdef INET6
600b252313fSGleb Smirnoff 	case AF_INET6:
601caf32b26SGleb Smirnoff 		pha.pa_mbuf_chk = ipfw_check_packet;
602b252313fSGleb Smirnoff 		pha.pa_type = PFIL_TYPE_IP6;
603b252313fSGleb Smirnoff 		pha.pa_rulname = "default6";
604b252313fSGleb Smirnoff 		h = &V_ipfw_inet6_hook;
605b252313fSGleb Smirnoff 		break;
606b252313fSGleb Smirnoff #endif
607b252313fSGleb Smirnoff 	case AF_LINK:
608caf32b26SGleb Smirnoff 		pha.pa_mbuf_chk = ipfw_check_frame_mbuf;
609caf32b26SGleb Smirnoff 		pha.pa_mem_chk = ipfw_check_frame_mem;
610b252313fSGleb Smirnoff 		pha.pa_type = PFIL_TYPE_ETHERNET;
611b252313fSGleb Smirnoff 		pha.pa_rulname = "default-link";
612b252313fSGleb Smirnoff 		h = &V_ipfw_link_hook;
613b252313fSGleb Smirnoff 		break;
614b252313fSGleb Smirnoff 	}
615b252313fSGleb Smirnoff 
616b252313fSGleb Smirnoff 	*h = pfil_add_hook(&pha);
61797245d40SGleb Smirnoff }
6183b3a8eb9SGleb Smirnoff 
61997245d40SGleb Smirnoff static void
ipfw_unhook(int pf)62097245d40SGleb Smirnoff ipfw_unhook(int pf)
62197245d40SGleb Smirnoff {
62297245d40SGleb Smirnoff 
62397245d40SGleb Smirnoff 	switch (pf) {
62497245d40SGleb Smirnoff 	case AF_INET:
62597245d40SGleb Smirnoff 		pfil_remove_hook(V_ipfw_inet_hook);
62697245d40SGleb Smirnoff 		break;
62797245d40SGleb Smirnoff #ifdef INET6
62897245d40SGleb Smirnoff 	case AF_INET6:
62997245d40SGleb Smirnoff 		pfil_remove_hook(V_ipfw_inet6_hook);
63097245d40SGleb Smirnoff 		break;
63197245d40SGleb Smirnoff #endif
63297245d40SGleb Smirnoff 	case AF_LINK:
63397245d40SGleb Smirnoff 		pfil_remove_hook(V_ipfw_link_hook);
63497245d40SGleb Smirnoff 		break;
63597245d40SGleb Smirnoff 	}
63697245d40SGleb Smirnoff }
63797245d40SGleb Smirnoff 
63897245d40SGleb Smirnoff static int
ipfw_link(int pf,bool unlink)63997245d40SGleb Smirnoff ipfw_link(int pf, bool unlink)
64097245d40SGleb Smirnoff {
64197245d40SGleb Smirnoff 	struct pfil_link_args pla;
64297245d40SGleb Smirnoff 
64397245d40SGleb Smirnoff 	pla.pa_version = PFIL_VERSION;
64497245d40SGleb Smirnoff 	pla.pa_flags = PFIL_IN | PFIL_OUT | PFIL_HEADPTR | PFIL_HOOKPTR;
64597245d40SGleb Smirnoff 	if (unlink)
64697245d40SGleb Smirnoff 		pla.pa_flags |= PFIL_UNLINK;
64797245d40SGleb Smirnoff 
64897245d40SGleb Smirnoff 	switch (pf) {
64997245d40SGleb Smirnoff 	case AF_INET:
65097245d40SGleb Smirnoff 		pla.pa_head = V_inet_pfil_head;
65197245d40SGleb Smirnoff 		pla.pa_hook = V_ipfw_inet_hook;
65297245d40SGleb Smirnoff 		break;
65397245d40SGleb Smirnoff #ifdef INET6
65497245d40SGleb Smirnoff 	case AF_INET6:
65597245d40SGleb Smirnoff 		pla.pa_head = V_inet6_pfil_head;
65697245d40SGleb Smirnoff 		pla.pa_hook = V_ipfw_inet6_hook;
65797245d40SGleb Smirnoff 		break;
65897245d40SGleb Smirnoff #endif
65997245d40SGleb Smirnoff 	case AF_LINK:
66097245d40SGleb Smirnoff 		pla.pa_head = V_link_pfil_head;
66197245d40SGleb Smirnoff 		pla.pa_hook = V_ipfw_link_hook;
66297245d40SGleb Smirnoff 		break;
66397245d40SGleb Smirnoff 	}
66497245d40SGleb Smirnoff 
66597245d40SGleb Smirnoff 	return (pfil_link(&pla));
6663b3a8eb9SGleb Smirnoff }
6673b3a8eb9SGleb Smirnoff 
6683b3a8eb9SGleb Smirnoff int
ipfw_attach_hooks(void)66997245d40SGleb Smirnoff ipfw_attach_hooks(void)
6703b3a8eb9SGleb Smirnoff {
6713b3a8eb9SGleb Smirnoff 	int error = 0;
6723b3a8eb9SGleb Smirnoff 
67397245d40SGleb Smirnoff 	ipfw_hook(AF_INET);
67497245d40SGleb Smirnoff 	TUNABLE_INT_FETCH("net.inet.ip.fw.enable", &V_fw_enable);
67597245d40SGleb Smirnoff 	if (V_fw_enable && (error = ipfw_link(AF_INET, false)) != 0)
6763b3a8eb9SGleb Smirnoff                 printf("ipfw_hook() error\n");
6773b3a8eb9SGleb Smirnoff #ifdef INET6
67897245d40SGleb Smirnoff 	ipfw_hook(AF_INET6);
67997245d40SGleb Smirnoff 	TUNABLE_INT_FETCH("net.inet6.ip6.fw.enable", &V_fw6_enable);
68097245d40SGleb Smirnoff 	if (V_fw6_enable && (error = ipfw_link(AF_INET6, false)) != 0)
6813b3a8eb9SGleb Smirnoff                 printf("ipfw6_hook() error\n");
6823b3a8eb9SGleb Smirnoff #endif
68397245d40SGleb Smirnoff 	ipfw_hook(AF_LINK);
68497245d40SGleb Smirnoff 	TUNABLE_INT_FETCH("net.link.ether.ipfw", &V_fwlink_enable);
68597245d40SGleb Smirnoff 	if (V_fwlink_enable && (error = ipfw_link(AF_LINK, false)) != 0)
6863b3a8eb9SGleb Smirnoff                 printf("ipfw_link_hook() error\n");
68797245d40SGleb Smirnoff 
68897245d40SGleb Smirnoff 	return (error);
6893b3a8eb9SGleb Smirnoff }
69097245d40SGleb Smirnoff 
69197245d40SGleb Smirnoff void
ipfw_detach_hooks(void)69297245d40SGleb Smirnoff ipfw_detach_hooks(void)
69397245d40SGleb Smirnoff {
69497245d40SGleb Smirnoff 
69597245d40SGleb Smirnoff 	ipfw_unhook(AF_INET);
69697245d40SGleb Smirnoff #ifdef INET6
69797245d40SGleb Smirnoff 	ipfw_unhook(AF_INET6);
69897245d40SGleb Smirnoff #endif
69997245d40SGleb Smirnoff 	ipfw_unhook(AF_LINK);
7003b3a8eb9SGleb Smirnoff }
7013b3a8eb9SGleb Smirnoff 
7023b3a8eb9SGleb Smirnoff int
ipfw_chg_hook(SYSCTL_HANDLER_ARGS)7033b3a8eb9SGleb Smirnoff ipfw_chg_hook(SYSCTL_HANDLER_ARGS)
7043b3a8eb9SGleb Smirnoff {
7053b3a8eb9SGleb Smirnoff 	int newval;
7063b3a8eb9SGleb Smirnoff 	int error;
7073b3a8eb9SGleb Smirnoff 	int af;
7083b3a8eb9SGleb Smirnoff 
709620ee5d3SGleb Smirnoff 	if (arg1 == &V_fw_enable)
7103b3a8eb9SGleb Smirnoff 		af = AF_INET;
7113b3a8eb9SGleb Smirnoff #ifdef INET6
712620ee5d3SGleb Smirnoff 	else if (arg1 == &V_fw6_enable)
7133b3a8eb9SGleb Smirnoff 		af = AF_INET6;
7143b3a8eb9SGleb Smirnoff #endif
715620ee5d3SGleb Smirnoff 	else if (arg1 == &V_fwlink_enable)
7163b3a8eb9SGleb Smirnoff 		af = AF_LINK;
7173b3a8eb9SGleb Smirnoff 	else
7183b3a8eb9SGleb Smirnoff 		return (EINVAL);
7193b3a8eb9SGleb Smirnoff 
720620ee5d3SGleb Smirnoff 	newval = *(int *)arg1;
7213b3a8eb9SGleb Smirnoff 	/* Handle sysctl change */
7223b3a8eb9SGleb Smirnoff 	error = sysctl_handle_int(oidp, &newval, 0, req);
7233b3a8eb9SGleb Smirnoff 
7243b3a8eb9SGleb Smirnoff 	if (error)
7253b3a8eb9SGleb Smirnoff 		return (error);
7263b3a8eb9SGleb Smirnoff 
7273b3a8eb9SGleb Smirnoff 	/* Formalize new value */
7283b3a8eb9SGleb Smirnoff 	newval = (newval) ? 1 : 0;
7293b3a8eb9SGleb Smirnoff 
730620ee5d3SGleb Smirnoff 	if (*(int *)arg1 == newval)
7313b3a8eb9SGleb Smirnoff 		return (0);
7323b3a8eb9SGleb Smirnoff 
73397245d40SGleb Smirnoff 	error = ipfw_link(af, newval == 0 ? true : false);
7343b3a8eb9SGleb Smirnoff 	if (error)
7353b3a8eb9SGleb Smirnoff 		return (error);
736620ee5d3SGleb Smirnoff 	*(int *)arg1 = newval;
7373b3a8eb9SGleb Smirnoff 
7383b3a8eb9SGleb Smirnoff 	return (0);
7393b3a8eb9SGleb Smirnoff }
7403b3a8eb9SGleb Smirnoff /* end of file */
741