Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 4 Sep 2012 19:43:26 +0000 (UTC)
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r240099 - in head/sys: net netinet netinet/ipfw
Message-ID:  <201209041943.q84JhQ7h008917@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: melifaro
Date: Tue Sep  4 19:43:26 2012
New Revision: 240099
URL: http://svn.freebsd.org/changeset/base/240099

Log:
  Introduce new link-layer PFIL hook V_link_pfil_hook.
  Merge ether_ipfw_chk() and part of bridge_pfil() into
  unified ipfw_check_frame() function called by PFIL.
  This change was suggested by rwatson? @ DevSummit.
  
  Remove ipfw headers from ether/bridge code since they are unneeded now.
  
  Note this thange introduce some (temporary) performance penalty since
  PFIL read lock has to be acquired for every link-level packet.
  
  MFC after:     3 weeks

Modified:
  head/sys/net/if_bridge.c
  head/sys/net/if_ethersubr.c
  head/sys/net/if_var.h
  head/sys/netinet/ip_var.h
  head/sys/netinet/ipfw/ip_fw2.c
  head/sys/netinet/ipfw/ip_fw_pfil.c
  head/sys/netinet/ipfw/ip_fw_private.h

Modified: head/sys/net/if_bridge.c
==============================================================================
--- head/sys/net/if_bridge.c	Tue Sep  4 19:19:36 2012	(r240098)
+++ head/sys/net/if_bridge.c	Tue Sep  4 19:43:26 2012	(r240099)
@@ -100,7 +100,6 @@ __FBSDID("$FreeBSD$");
 #include <sys/proc.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
-#include <sys/rwlock.h>
 
 #include <net/bpf.h>
 #include <net/if.h>
@@ -131,8 +130,6 @@ __FBSDID("$FreeBSD$");
 #include <net/if_vlan_var.h>
 
 #include <net/route.h>
-#include <netinet/ip_fw.h>
-#include <netinet/ipfw/ip_fw_private.h>
 
 /*
  * Size of the route hash table.  Must be a power of two.
@@ -2981,7 +2978,6 @@ bridge_pfil(struct mbuf **mp, struct ifn
 {
 	int snap, error, i, hlen;
 	struct ether_header *eh1, eh2;
-	struct ip_fw_args args;
 	struct ip *ip;
 	struct llc llc1;
 	u_int16_t ether_type;
@@ -3055,6 +3051,16 @@ bridge_pfil(struct mbuf **mp, struct ifn
 				goto bad;
 	}
 
+	/* Run the packet through pfil before stripping link headers */
+	if (PFIL_HOOKED(&V_link_pfil_hook) && pfil_ipfw != 0 &&
+			dir == PFIL_OUT && ifp != NULL) {
+
+		error = pfil_run_hooks(&V_link_pfil_hook, mp, ifp, dir, NULL);
+
+		if (*mp == NULL || error != 0) /* packet consumed by filter */
+			return (error);
+	}
+
 	/* Strip off the Ethernet header and keep a copy. */
 	m_copydata(*mp, 0, ETHER_HDR_LEN, (caddr_t) &eh2);
 	m_adj(*mp, ETHER_HDR_LEN);
@@ -3085,63 +3091,6 @@ bridge_pfil(struct mbuf **mp, struct ifn
 			goto bad;
 	}
 
-	/* XXX this section is also in if_ethersubr.c */
-	// XXX PFIL_OUT or DIR_OUT ?
-	if (V_ip_fw_chk_ptr && pfil_ipfw != 0 &&
-			dir == PFIL_OUT && ifp != NULL) {
-		struct m_tag *mtag;
-
-		error = -1;
-		/* fetch the start point from existing tags, if any */
-		mtag = m_tag_locate(*mp, MTAG_IPFW_RULE, 0, NULL);
-		if (mtag == NULL) {
-			args.rule.slot = 0;
-		} else {
-			struct ipfw_rule_ref *r;
-
-			/* XXX can we free the tag after use ? */
-			mtag->m_tag_id = PACKET_TAG_NONE;
-			r = (struct ipfw_rule_ref *)(mtag + 1);
-			/* packet already partially processed ? */
-			if (r->info & IPFW_ONEPASS)
-				goto ipfwpass;
-			args.rule = *r;
-		}
-
-		args.m = *mp;
-		args.oif = ifp;
-		args.next_hop = NULL;
-		args.next_hop6 = NULL;
-		args.eh = &eh2;
-		args.inp = NULL;	/* used by ipfw uid/gid/jail rules */
-		i = V_ip_fw_chk_ptr(&args);
-		*mp = args.m;
-
-		if (*mp == NULL)
-			return (error);
-
-		if (ip_dn_io_ptr && (i == IP_FW_DUMMYNET)) {
-
-			/* put the Ethernet header back on */
-			M_PREPEND(*mp, ETHER_HDR_LEN, M_DONTWAIT);
-			if (*mp == NULL)
-				return (error);
-			bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN);
-
-			/*
-			 * Pass the pkt to dummynet, which consumes it. The
-			 * packet will return to us via bridge_dummynet().
-			 */
-			args.oif = ifp;
-			ip_dn_io_ptr(mp, DIR_FWD | PROTO_IFB, &args);
-			return (error);
-		}
-
-		if (i != IP_FW_PASS) /* drop */
-			goto bad;
-	}
-
-ipfwpass:
 	error = 0;
 
 	/*

Modified: head/sys/net/if_ethersubr.c
==============================================================================
--- head/sys/net/if_ethersubr.c	Tue Sep  4 19:19:36 2012	(r240098)
+++ head/sys/net/if_ethersubr.c	Tue Sep  4 19:43:26 2012	(r240099)
@@ -45,7 +45,6 @@
 #include <sys/module.h>
 #include <sys/mbuf.h>
 #include <sys/random.h>
-#include <sys/rwlock.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
 #include <sys/sysctl.h>
@@ -63,6 +62,7 @@
 #include <net/if_vlan_var.h>
 #include <net/if_llatbl.h>
 #include <net/pf_mtag.h>
+#include <net/pfil.h>
 #include <net/vnet.h>
 
 #if defined(INET) || defined(INET6)
@@ -71,8 +71,6 @@
 #include <netinet/if_ether.h>
 #include <netinet/ip_carp.h>
 #include <netinet/ip_var.h>
-#include <netinet/ip_fw.h>
-#include <netinet/ipfw/ip_fw_private.h>
 #endif
 #ifdef INET6
 #include <netinet6/nd6.h>
@@ -106,6 +104,8 @@ CTASSERT(sizeof (struct ether_header) ==
 CTASSERT(sizeof (struct ether_addr) == ETHER_ADDR_LEN);
 #endif
 
+VNET_DEFINE(struct pfil_head, link_pfil_hook);	/* Packet filter hooks */
+
 /* netgraph node hooks for ng_ether(4) */
 void	(*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp);
 void	(*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m);
@@ -141,14 +141,6 @@ static MALLOC_DEFINE(M_ARPCOM, "arpcom",
 
 #define senderr(e) do { error = (e); goto bad;} while (0)
 
-#if defined(INET) || defined(INET6)
-int
-ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, int shared);
-static VNET_DEFINE(int, ether_ipfw);
-#define	V_ether_ipfw	VNET(ether_ipfw)
-#endif
-
-
 /*
  * Ethernet output routine.
  * Encapsulate a packet of type family for the local net.
@@ -427,18 +419,17 @@ bad:			if (m != NULL)
 int
 ether_output_frame(struct ifnet *ifp, struct mbuf *m)
 {
-#if defined(INET) || defined(INET6)
+	int i;
 
-	if (V_ip_fw_chk_ptr && V_ether_ipfw != 0) {
-		if (ether_ipfw_chk(&m, ifp, 0) == 0) {
-			if (m) {
-				m_freem(m);
-				return EACCES;	/* pkt dropped */
-			} else
-				return 0;	/* consumed e.g. in a pipe */
-		}
+	if (PFIL_HOOKED(&V_link_pfil_hook)) {
+		i = pfil_run_hooks(&V_link_pfil_hook, &m, ifp, PFIL_OUT, NULL);
+
+		if (i != 0)
+			return (EACCES);
+
+		if (m == NULL)
+			return (0);
 	}
-#endif
 
 	/*
 	 * Queue message on interface, update output statistics if
@@ -448,113 +439,6 @@ ether_output_frame(struct ifnet *ifp, st
 }
 
 #if defined(INET) || defined(INET6)
-/*
- * ipfw processing for ethernet packets (in and out).
- * The second parameter is NULL from ether_demux, and ifp from
- * ether_output_frame.
- */
-int
-ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, int shared)
-{
-	struct ether_header *eh;
-	struct ether_header save_eh;
-	struct mbuf *m;
-	int i;
-	struct ip_fw_args args;
-	struct m_tag *mtag;
-
-	/* fetch start point from rule, if any */
-	mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
-	if (mtag == NULL) {
-		args.rule.slot = 0;
-	} else {
-		/* dummynet packet, already partially processed */
-		struct ipfw_rule_ref *r;
-
-		/* XXX can we free it after use ? */
-		mtag->m_tag_id = PACKET_TAG_NONE;
-		r = (struct ipfw_rule_ref *)(mtag + 1);
-		if (r->info & IPFW_ONEPASS)
-			return (1);
-		args.rule = *r;
-	}
-
-	/*
-	 * I need some amt of data to be contiguous, and in case others need
-	 * the packet (shared==1) also better be in the first mbuf.
-	 */
-	m = *m0;
-	i = min( m->m_pkthdr.len, max_protohdr);
-	if ( shared || m->m_len < i) {
-		m = m_pullup(m, i);
-		if (m == NULL) {
-			*m0 = m;
-			return 0;
-		}
-	}
-	eh = mtod(m, struct ether_header *);
-	save_eh = *eh;			/* save copy for restore below */
-	m_adj(m, ETHER_HDR_LEN);	/* strip ethernet header */
-
-	args.m = m;		/* the packet we are looking at		*/
-	args.oif = dst;		/* destination, if any			*/
-	args.next_hop = NULL;	/* we do not support forward yet	*/
-	args.next_hop6 = NULL;	/* we do not support forward yet	*/
-	args.eh = &save_eh;	/* MAC header for bridged/MAC packets	*/
-	args.inp = NULL;	/* used by ipfw uid/gid/jail rules	*/
-	i = V_ip_fw_chk_ptr(&args);
-	m = args.m;
-	if (m != NULL) {
-		/*
-		 * Restore Ethernet header, as needed, in case the
-		 * mbuf chain was replaced by ipfw.
-		 */
-		M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT);
-		if (m == NULL) {
-			*m0 = m;
-			return 0;
-		}
-		if (eh != mtod(m, struct ether_header *))
-			bcopy(&save_eh, mtod(m, struct ether_header *),
-				ETHER_HDR_LEN);
-	}
-	*m0 = m;
-
-	if (i == IP_FW_DENY) /* drop */
-		return 0;
-
-	KASSERT(m != NULL, ("ether_ipfw_chk: m is NULL"));
-
-	if (i == IP_FW_PASS) /* a PASS rule.  */
-		return 1;
-
-	if (ip_dn_io_ptr && (i == IP_FW_DUMMYNET)) {
-		int dir;
-		/*
-		 * Pass the pkt to dummynet, which consumes it.
-		 * If shared, make a copy and keep the original.
-		 */
-		if (shared) {
-			m = m_copypacket(m, M_DONTWAIT);
-			if (m == NULL)
-				return 0;
-		} else {
-			/*
-			 * Pass the original to dummynet and
-			 * nothing back to the caller
-			 */
-			*m0 = NULL ;
-		}
-		dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN);
-		ip_dn_io_ptr(&m, dir, &args);
-		return 0;
-	}
-	/*
-	 * XXX at some point add support for divert/forward actions.
-	 * If none of the above matches, we have to drop the pkt.
-	 */
-	return 0;
-}
 #endif
 
 /*
@@ -788,6 +672,35 @@ ether_init(__unused void *arg)
 SYSINIT(ether, SI_SUB_INIT_IF, SI_ORDER_ANY, ether_init, NULL);
 
 static void
+vnet_ether_init(__unused void *arg)
+{
+	int i;
+
+	/* Initialize packet filter hooks. */
+	V_link_pfil_hook.ph_type = PFIL_TYPE_AF;
+	V_link_pfil_hook.ph_af = AF_LINK;
+	if ((i = pfil_head_register(&V_link_pfil_hook)) != 0)
+		printf("%s: WARNING: unable to register pfil link hook, "
+			"error %d\n", __func__, i);
+}
+VNET_SYSINIT(vnet_ether_init, SI_SUB_PROTO_IF, SI_ORDER_ANY,
+    vnet_ether_init, NULL);
+ 
+static void
+vnet_ether_destroy(__unused void *arg)
+{
+	int i;
+
+	if ((i = pfil_head_unregister(&V_link_pfil_hook)) != 0)
+		printf("%s: WARNING: unable to unregister pfil link hook, "
+			"error %d\n", __func__, i);
+}
+VNET_SYSUNINIT(vnet_ether_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY,
+    vnet_ether_destroy, NULL);
+
+
+
+static void
 ether_input(struct ifnet *ifp, struct mbuf *m)
 {
 
@@ -807,7 +720,7 @@ void
 ether_demux(struct ifnet *ifp, struct mbuf *m)
 {
 	struct ether_header *eh;
-	int isr;
+	int i, isr;
 	u_short ether_type;
 #if defined(NETATALK)
 	struct llc *l;
@@ -815,19 +728,14 @@ ether_demux(struct ifnet *ifp, struct mb
 
 	KASSERT(ifp != NULL, ("%s: NULL interface pointer", __func__));
 
-#if defined(INET) || defined(INET6)
-	/*
-	 * Allow dummynet and/or ipfw to claim the frame.
-	 * Do not do this for PROMISC frames in case we are re-entered.
-	 */
-	if (V_ip_fw_chk_ptr && V_ether_ipfw != 0 && !(m->m_flags & M_PROMISC)) {
-		if (ether_ipfw_chk(&m, NULL, 0) == 0) {
-			if (m)
-				m_freem(m);	/* dropped; free mbuf chain */
-			return;			/* consumed */
-		}
+	/* Do not grab PROMISC frames in case we are re-entered. */
+	if (PFIL_HOOKED(&V_link_pfil_hook) && !(m->m_flags & M_PROMISC)) {
+		i = pfil_run_hooks(&V_link_pfil_hook, &m, ifp, PFIL_IN, NULL);
+
+		if (i != 0 || m == NULL)
+			return;
 	}
-#endif
+
 	eh = mtod(m, struct ether_header *);
 	ether_type = ntohs(eh->ether_type);
 
@@ -1056,10 +964,6 @@ ether_reassign(struct ifnet *ifp, struct
 
 SYSCTL_DECL(_net_link);
 SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet");
-#if defined(INET) || defined(INET6)
-SYSCTL_VNET_INT(_net_link_ether, OID_AUTO, ipfw, CTLFLAG_RW,
-	     &VNET_NAME(ether_ipfw), 0, "Pass ether pkts through firewall");
-#endif
 
 #if 0
 /*

Modified: head/sys/net/if_var.h
==============================================================================
--- head/sys/net/if_var.h	Tue Sep  4 19:19:36 2012	(r240098)
+++ head/sys/net/if_var.h	Tue Sep  4 19:43:26 2012	(r240099)
@@ -99,6 +99,9 @@ TAILQ_HEAD(ifaddrhead, ifaddr);	/* insta
 TAILQ_HEAD(ifmultihead, ifmultiaddr);
 TAILQ_HEAD(ifgrouphead, ifg_group);
 
+VNET_DECLARE(struct pfil_head, link_pfil_hook);	/* packet filter hooks */
+#define	V_link_pfil_hook	VNET(link_pfil_hook)
+
 /*
  * Structure defining a queue for a network interface.
  */

Modified: head/sys/netinet/ip_var.h
==============================================================================
--- head/sys/netinet/ip_var.h	Tue Sep  4 19:19:36 2012	(r240098)
+++ head/sys/netinet/ip_var.h	Tue Sep  4 19:43:26 2012	(r240099)
@@ -292,9 +292,7 @@ enum {
 struct ip_fw_args;
 typedef int	(*ip_fw_chk_ptr_t)(struct ip_fw_args *args);
 typedef int	(*ip_fw_ctl_ptr_t)(struct sockopt *);
-VNET_DECLARE(ip_fw_chk_ptr_t, ip_fw_chk_ptr);
 VNET_DECLARE(ip_fw_ctl_ptr_t, ip_fw_ctl_ptr);
-#define	V_ip_fw_chk_ptr		VNET(ip_fw_chk_ptr)
 #define	V_ip_fw_ctl_ptr		VNET(ip_fw_ctl_ptr)
 
 /* Divert hooks. */

Modified: head/sys/netinet/ipfw/ip_fw2.c
==============================================================================
--- head/sys/netinet/ipfw/ip_fw2.c	Tue Sep  4 19:19:36 2012	(r240098)
+++ head/sys/netinet/ipfw/ip_fw2.c	Tue Sep  4 19:43:26 2012	(r240099)
@@ -2645,10 +2645,9 @@ vnet_ipfw_init(const void *unused)
 	V_ipfw_vnet_ready = 1;		/* Open for business */
 
 	/*
-	 * Hook the sockopt handler, and the layer2 (V_ip_fw_chk_ptr)
-	 * and pfil hooks for ipv4 and ipv6. Even if the latter two fail
-	 * we still keep the module alive because the sockopt and
-	 * layer2 paths are still useful.
+	 * Hook the sockopt handler and pfil hooks for ipv4 and ipv6.
+	 * Even if the latter two fail we still keep the module alive
+	 * because the sockopt and layer2 paths are still useful.
 	 * ipfw[6]_hook return 0 on success, ENOENT on failure,
 	 * so we can ignore the exact return value and just set a flag.
 	 *
@@ -2659,7 +2658,6 @@ vnet_ipfw_init(const void *unused)
 	 * is checked on each packet because there are no pfil hooks.
 	 */
 	V_ip_fw_ctl_ptr = ipfw_ctl;
-	V_ip_fw_chk_ptr = ipfw_chk;
 	error = ipfw_attach_hooks(1);
 	return (error);
 }
@@ -2681,7 +2679,6 @@ vnet_ipfw_uninit(const void *unused)
 	 * sure the update is propagated and nobody will be in.
 	 */
 	(void)ipfw_attach_hooks(0 /* detach */);
-	V_ip_fw_chk_ptr = NULL;
 	V_ip_fw_ctl_ptr = NULL;
 	IPFW_UH_WLOCK(chain);
 	IPFW_UH_WUNLOCK(chain);

Modified: head/sys/netinet/ipfw/ip_fw_pfil.c
==============================================================================
--- head/sys/netinet/ipfw/ip_fw_pfil.c	Tue Sep  4 19:19:36 2012	(r240098)
+++ head/sys/netinet/ipfw/ip_fw_pfil.c	Tue Sep  4 19:43:26 2012	(r240099)
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
 
 #include <net/if.h>
 #include <net/route.h>
+#include <net/ethernet.h>
 #include <net/pfil.h>
 #include <net/vnet.h>
 
@@ -72,10 +73,17 @@ static VNET_DEFINE(int, fw6_enable) = 1;
 #define V_fw6_enable	VNET(fw6_enable)
 #endif
 
+static VNET_DEFINE(int, fwlink_enable) = 0;
+#define V_fwlink_enable	VNET(fwlink_enable)
+
 int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
 
 /* Forward declarations. */
 static int ipfw_divert(struct mbuf **, int, struct ipfw_rule_ref *, int);
+static int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int,
+	struct inpcb *);
+static int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int,
+	struct inpcb *);
 
 #ifdef SYSCTL_NODE
 
@@ -92,6 +100,11 @@ SYSCTL_VNET_PROC(_net_inet6_ip6_fw, OID_
     ipfw_chg_hook, "I", "Enable ipfw+6");
 #endif /* INET6 */
 
+SYSCTL_DECL(_net_link_ether);
+SYSCTL_VNET_PROC(_net_link_ether, OID_AUTO, ipfw,
+    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fwlink_enable), 0,
+    ipfw_chg_hook, "I", "Pass ether pkts through firewall");
+
 SYSEND
 
 #endif /* SYSCTL_NODE */
@@ -101,8 +114,8 @@ SYSEND
  * dummynet, divert, netgraph or other modules.
  * The packet may be consumed.
  */
-int
-ipfw_check_hook(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
+static int
+ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
     struct inpcb *inp)
 {
 	struct ip_fw_args args;
@@ -278,6 +291,111 @@ again:
 	return ret;
 }
 
+/*
+ * ipfw processing for ethernet packets (in and out).
+ * Inteface is NULL from ether_demux, and ifp from
+ * ether_output_frame.
+ */
+static int
+ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir,
+    struct inpcb *inp)
+{
+	struct ether_header *eh;
+	struct ether_header save_eh;
+	struct mbuf *m;
+	int i, ret;
+	struct ip_fw_args args;
+	struct m_tag *mtag;
+
+	/* fetch start point from rule, if any */
+	mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
+	if (mtag == NULL) {
+		args.rule.slot = 0;
+	} else {
+		/* dummynet packet, already partially processed */
+		struct ipfw_rule_ref *r;
+
+		/* XXX can we free it after use ? */
+		mtag->m_tag_id = PACKET_TAG_NONE;
+		r = (struct ipfw_rule_ref *)(mtag + 1);
+		if (r->info & IPFW_ONEPASS)
+			return (0);
+		args.rule = *r;
+	}
+
+	/* I need some amt of data to be contiguous */
+	m = *m0;
+	i = min(m->m_pkthdr.len, max_protohdr);
+	if (m->m_len < i) {
+		m = m_pullup(m, i);
+		if (m == NULL) {
+			*m0 = m;
+			return (0);
+		}
+	}
+	eh = mtod(m, struct ether_header *);
+	save_eh = *eh;			/* save copy for restore below */
+	m_adj(m, ETHER_HDR_LEN);	/* strip ethernet header */
+
+	args.m = m;		/* the packet we are looking at		*/
+	args.oif = dst;		/* destination, if any			*/
+	args.next_hop = NULL;	/* we do not support forward yet	*/
+	args.next_hop6 = NULL;	/* we do not support forward yet	*/
+	args.eh = &save_eh;	/* MAC header for bridged/MAC packets	*/
+	args.inp = NULL;	/* used by ipfw uid/gid/jail rules	*/
+	i = ipfw_chk(&args);
+	m = args.m;
+	if (m != NULL) {
+		/*
+		 * Restore Ethernet header, as needed, in case the
+		 * mbuf chain was replaced by ipfw.
+		 */
+		M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT);
+		if (m == NULL) {
+			*m0 = NULL;
+			return (0);
+		}
+		if (eh != mtod(m, struct ether_header *))
+			bcopy(&save_eh, mtod(m, struct ether_header *),
+				ETHER_HDR_LEN);
+	}
+	*m0 = m;
+
+	ret = 0;
+	/* Check result of ipfw_chk() */
+	switch (i) {
+	case IP_FW_PASS:
+		break;
+
+	case IP_FW_DENY:
+		ret = EACCES;
+		break; /* i.e. drop */
+
+	case IP_FW_DUMMYNET:
+		ret = EACCES;
+		int dir;
+
+		if (ip_dn_io_ptr == NULL)
+			break; /* i.e. drop */
+
+		*m0 = NULL;
+		dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN);
+		ip_dn_io_ptr(&m, dir, &args);
+		return 0;
+
+	default:
+		KASSERT(0, ("%s: unknown retval", __func__));
+	}
+
+	if (ret != 0) {
+		if (*m0)
+			FREE_PKT(*m0);
+		*m0 = NULL;
+	}
+
+	return ret;
+}
+
 /* do the divert, return 1 on error 0 on success */
 static int
 ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
@@ -379,13 +497,16 @@ static int
 ipfw_hook(int onoff, int pf)
 {
 	struct pfil_head *pfh;
+	void *hook_func;
 
 	pfh = pfil_head_get(PFIL_TYPE_AF, pf);
 	if (pfh == NULL)
 		return ENOENT;
 
+	hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet;
+
 	(void) (onoff ? pfil_add_hook : pfil_remove_hook)
-	    (ipfw_check_hook, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh);
+	    (hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh);
 
 	return 0;
 }
@@ -409,51 +530,58 @@ ipfw_attach_hooks(int arg)
                 printf("ipfw6_hook() error\n");
         }
 #endif
+	if (arg == 0) /* detach */
+		ipfw_hook(0, AF_LINK);
+	else if (V_fwlink_enable && ipfw_hook(1, AF_LINK) != 0) {
+                error = ENOENT;
+                printf("ipfw_link_hook() error\n");
+        }
 	return error;
 }
 
 int
 ipfw_chg_hook(SYSCTL_HANDLER_ARGS)
 {
-	int enable;
-	int oldenable;
+	int *enable;
+	int newval;
 	int error;
 	int af;
 
 	if (arg1 == &VNET_NAME(fw_enable)) {
-		enable = V_fw_enable;
+		enable = &V_fw_enable;
 		af = AF_INET;
 	}
 #ifdef INET6
 	else if (arg1 == &VNET_NAME(fw6_enable)) {
-		enable = V_fw6_enable;
+		enable = &V_fw6_enable;
 		af = AF_INET6;
 	}
 #endif
+	else if (arg1 == &VNET_NAME(fwlink_enable)) {
+		enable = &V_fwlink_enable;
+		af = AF_LINK;
+	}
 	else 
 		return (EINVAL);
 
-	oldenable = enable;
+	newval = *enable;
 
-	error = sysctl_handle_int(oidp, &enable, 0, req);
+	/* Handle sysctl change */
+	error = sysctl_handle_int(oidp, &newval, 0, req);
 
 	if (error)
 		return (error);
 
-	enable = (enable) ? 1 : 0;
+	/* Formalize new value */
+	newval = (newval) ? 1 : 0;
 
-	if (enable == oldenable)
+	if (*enable == newval)
 		return (0);
 
-	error = ipfw_hook(enable, af);
+	error = ipfw_hook(newval, af);
 	if (error)
 		return (error);
-	if (af == AF_INET)
-		V_fw_enable = enable;
-#ifdef INET6
-	else if (af == AF_INET6)
-		V_fw6_enable = enable;
-#endif
+	*enable = newval;
 
 	return (0);
 }

Modified: head/sys/netinet/ipfw/ip_fw_private.h
==============================================================================
--- head/sys/netinet/ipfw/ip_fw_private.h	Tue Sep  4 19:19:36 2012	(r240098)
+++ head/sys/netinet/ipfw/ip_fw_private.h	Tue Sep  4 19:43:26 2012	(r240099)
@@ -270,10 +270,6 @@ int ipfw_ctl(struct sockopt *sopt);
 int ipfw_chk(struct ip_fw_args *args);
 void ipfw_reap_rules(struct ip_fw *head);
 
-/* In ip_fw_pfil */
-int ipfw_check_hook(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
-     struct inpcb *inp);
-
 /* In ip_fw_table.c */
 struct radix_node;
 int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201209041943.q84JhQ7h008917>