git: caf32b260ad4 - main - pfil: add pfil_mem_{in,out}() and retire pfil_run_hooks()

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Tue, 14 Feb 2023 18:03:59 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=caf32b260ad46b17a4c1a8ce6383e37ac489f023

commit caf32b260ad46b17a4c1a8ce6383e37ac489f023
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2023-02-14 18:02:49 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2023-02-14 18:02:49 +0000

    pfil: add pfil_mem_{in,out}() and retire pfil_run_hooks()
    
    The 0b70e3e78b0 changed the original design of a single entry point
    into pfil(9) chains providing separate functions for the filtering
    points that always provide mbufs and know the direction of a flow.
    The motivation was to reduce branching.  The logical continuation
    would be to do the same for the filtering points that always provide
    a memory pointer and retire the single entry point.
    
    o Hooks now provide two functions: one for mbufs and optional for
      memory pointers.
    o pfil_hook_args() has a new member and pfil_add_hook() has a
      requirement to zero out uninitialized data. Bump PFIL_VERSION.
    o As it was before, a hook function for a memory pointer may realloc
      into an mbuf.  Such mbuf would be returned via a pointer that must
      be provided in argument.
    o The only hook that supports memory pointers is ipfw:default-link.
      It is rewritten to provide two functions.
    o All remaining uses of pfil_run_hooks() are converted to
      pfil_mem_in().
    o Transparent union of pfil_packet_t and tricks to fix pointer
      alignment are retired. Internal pfil_realloc() reduces down to
      m_devget() and thus is retired, too.
    
    Reviewed by:            mjg, ocochard
    Differential revision:  https://reviews.freebsd.org/D37977
---
 sys/dev/cxgbe/t4_sge.c                        |   4 +-
 sys/dev/mlx5/mlx5_en/mlx5_en_rx.c             |   6 +-
 sys/net/iflib.c                               |   5 +-
 sys/net/pfil.c                                | 122 +++++++++---------
 sys/net/pfil.h                                |  51 +++-----
 sys/netinet/siftr.c                           |  26 ++--
 sys/netpfil/ipfilter/netinet/ip_fil_freebsd.c |  30 ++---
 sys/netpfil/ipfw/ip_fw_pfil.c                 | 171 ++++++++++++++++----------
 sys/netpfil/pf/pf_ioctl.c                     |  44 ++++---
 9 files changed, 233 insertions(+), 226 deletions(-)

diff --git a/sys/dev/cxgbe/t4_sge.c b/sys/dev/cxgbe/t4_sge.c
index 8dbb9e1fe93a..88a7f44cadb7 100644
--- a/sys/dev/cxgbe/t4_sge.c
+++ b/sys/dev/cxgbe/t4_sge.c
@@ -1998,8 +1998,7 @@ eth_rx(struct adapter *sc, struct sge_rxq *rxq, const struct iq_desc *d,
 		    sc->params.sge.fl_pktshift;
 		frame = sd->cl + fl->rx_offset + sc->params.sge.fl_pktshift;
 		CURVNET_SET_QUIET(ifp->if_vnet);
-		rc = pfil_run_hooks(vi->pfil, frame, ifp,
-		    slen | PFIL_MEMPTR | PFIL_IN, NULL);
+		rc = pfil_mem_in(vi->pfil, frame, slen, ifp, &m0);
 		CURVNET_RESTORE();
 		if (rc == PFIL_DROPPED || rc == PFIL_CONSUMED) {
 			skip_fl_payload(sc, fl, plen);
@@ -2007,7 +2006,6 @@ eth_rx(struct adapter *sc, struct sge_rxq *rxq, const struct iq_desc *d,
 		}
 		if (rc == PFIL_REALLOCED) {
 			skip_fl_payload(sc, fl, plen);
-			m0 = pfil_mem2mbuf(frame);
 			goto have_mbuf;
 		}
 	}
diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_rx.c b/sys/dev/mlx5/mlx5_en/mlx5_en_rx.c
index f6caddfdf933..4b1fb25e0f82 100644
--- a/sys/dev/mlx5/mlx5_en/mlx5_en_rx.c
+++ b/sys/dev/mlx5/mlx5_en/mlx5_en_rx.c
@@ -536,9 +536,8 @@ mlx5e_poll_rx_cq(struct mlx5e_rq *rq, int budget)
 		}
 		if (pfil != NULL && PFIL_HOOKED_IN(pfil)) {
 			seglen = MIN(byte_cnt, MLX5E_MAX_RX_BYTES);
-			rv = pfil_run_hooks(rq->channel->priv->pfil,
-			    rq->mbuf[wqe_counter].data, rq->ifp,
-			    seglen | PFIL_MEMPTR | PFIL_IN, NULL);
+			rv = pfil_mem_in(rq->channel->priv->pfil,
+			    rq->mbuf[wqe_counter].data, seglen, rq->ifp, &mb);
 
 			switch (rv) {
 			case PFIL_DROPPED:
@@ -556,7 +555,6 @@ mlx5e_poll_rx_cq(struct mlx5e_rq *rq, int budget)
 				 * and receive the new mbuf allocated
 				 * by the Filter
 				 */
-				mb = pfil_mem2mbuf(rq->mbuf[wqe_counter].data);
 				goto rx_common;
 			default:
 				/*
diff --git a/sys/net/iflib.c b/sys/net/iflib.c
index 0a500f8e2810..39f3ccea4317 100644
--- a/sys/net/iflib.c
+++ b/sys/net/iflib.c
@@ -2732,8 +2732,7 @@ rxd_frag_to_sd(iflib_rxq_t rxq, if_rxd_frag_t irf, bool unload, if_rxsd_t sd,
 		payload  = *sd->ifsd_cl;
 		payload +=  ri->iri_pad;
 		len = ri->iri_len - ri->iri_pad;
-		*pf_rv = pfil_run_hooks(rxq->pfil, payload, ri->iri_ifp,
-		    len | PFIL_MEMPTR | PFIL_IN, NULL);
+		*pf_rv = pfil_mem_in(rxq->pfil, payload, len, ri->iri_ifp, &m);
 		switch (*pf_rv) {
 		case PFIL_DROPPED:
 		case PFIL_CONSUMED:
@@ -2746,8 +2745,8 @@ rxd_frag_to_sd(iflib_rxq_t rxq, if_rxd_frag_t irf, bool unload, if_rxsd_t sd,
 		case PFIL_REALLOCED:
 			/*
 			 * The filter copied it.  Everything is recycled.
+			 * 'm' points at new mbuf.
 			 */
-			m = pfil_mem2mbuf(payload);
 			unload = 0;
 			break;
 		case PFIL_PASS:
diff --git a/sys/net/pfil.c b/sys/net/pfil.c
index b68fbe8db5c5..e6f3ff8c1269 100644
--- a/sys/net/pfil.c
+++ b/sys/net/pfil.c
@@ -70,7 +70,8 @@ MTX_SYSINIT(pfil_mtxinit, &pfil_lock, "pfil(9) lock", MTX_DEF);
 #define	PFIL_LOCK_ASSERT()	mtx_assert(&pfil_lock, MA_OWNED)
 
 struct pfil_hook {
-	pfil_func_t	 hook_func;
+	pfil_mbuf_chk_t	 hook_mbuf_chk;
+	pfil_mem_chk_t	 hook_mem_chk;
 	void		*hook_ruleset;
 	int		 hook_flags;
 	int		 hook_links;
@@ -82,7 +83,8 @@ struct pfil_hook {
 
 struct pfil_link {
 	CK_STAILQ_ENTRY(pfil_link) link_chain;
-	pfil_func_t		 link_func;
+	pfil_mbuf_chk_t		 link_mbuf_chk;
+	pfil_mem_chk_t		 link_mem_chk;
 	void			*link_ruleset;
 	int			 link_flags;
 	struct pfil_hook	*link_hook;
@@ -114,92 +116,86 @@ VNET_DEFINE_STATIC(struct pfilhookhead, pfil_hook_list) =
 static struct pfil_link *pfil_link_remove(pfil_chain_t *, pfil_hook_t );
 static void pfil_link_free(epoch_context_t);
 
-int
-pfil_realloc(pfil_packet_t *p, int flags, struct ifnet *ifp)
-{
-	struct mbuf *m;
-
-	MPASS(flags & PFIL_MEMPTR);
-
-	if ((m = m_devget(p->mem, PFIL_LENGTH(flags), 0, ifp, NULL)) == NULL)
-		return (ENOMEM);
-	*p = pfil_packet_align(*p);
-	*p->m = m;
-
-	return (0);
-}
-
+/*
+ * To couple a filtering point that provides memory pointer with a filter that
+ * works on mbufs only.
+ */
 static __noinline int
-pfil_fake_mbuf(pfil_func_t func, pfil_packet_t *p, struct ifnet *ifp, int flags,
-    void *ruleset, struct inpcb *inp)
+pfil_fake_mbuf(pfil_mbuf_chk_t func, void *mem, u_int len, struct ifnet *ifp,
+    int flags, void *ruleset, struct mbuf **mp)
 {
-	struct mbuf m, *mp;
+	struct mbuf m;
 	pfil_return_t rv;
 
 	(void)m_init(&m, M_NOWAIT, MT_DATA, M_NOFREE | M_PKTHDR);
-	m_extadd(&m, p->mem, PFIL_LENGTH(flags), NULL, NULL, NULL, 0,
-	    EXT_RXRING);
-	m.m_len = m.m_pkthdr.len = PFIL_LENGTH(flags);
-	mp = &m;
-	flags &= ~(PFIL_MEMPTR | PFIL_LENMASK);
-
-	rv = func(&mp, ifp, flags, ruleset, inp);
-	if (rv == PFIL_PASS && mp != &m) {
+	m_extadd(&m, mem, len, NULL, NULL, NULL, 0, EXT_RXRING);
+	m.m_len = m.m_pkthdr.len = len;
+	*mp = &m;
+
+	rv = func(mp, ifp, flags, ruleset, NULL);
+	if (rv == PFIL_PASS && *mp != &m) {
 		/*
 		 * Firewalls that need pfil_fake_mbuf() most likely don't
 		 * know they need return PFIL_REALLOCED.
 		 */
 		rv = PFIL_REALLOCED;
-		*p = pfil_packet_align(*p);
-		*p->m = mp;
 	}
 
 	return (rv);
 }
 
-/*
- * pfil_run_hooks() runs the specified packet filter hook chain.
- */
-int
-pfil_run_hooks(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp,
-    int flags, struct inpcb *inp)
+static __always_inline int
+pfil_mem_common(pfil_chain_t *pch, void *mem, u_int len, int flags,
+    struct ifnet *ifp, struct mbuf **m)
 {
-	pfil_chain_t *pch;
 	struct pfil_link *link;
 	pfil_return_t rv;
 	bool realloc = false;
 
 	NET_EPOCH_ASSERT();
-
-	if (PFIL_DIR(flags) == PFIL_IN)
-		pch = &head->head_in;
-	else if (__predict_true(PFIL_DIR(flags) == PFIL_OUT))
-		pch = &head->head_out;
-	else
-		panic("%s: bogus flags %d", __func__, flags);
+	KASSERT(flags == PFIL_IN || flags == PFIL_OUT,
+	    ("%s: unsupported flags %d", __func__, flags));
 
 	rv = PFIL_PASS;
 	CK_STAILQ_FOREACH(link, pch, link_chain) {
-		if ((flags & PFIL_MEMPTR) && !(link->link_flags & PFIL_MEMPTR))
-			rv = pfil_fake_mbuf(link->link_func, &p, ifp, flags,
-			    link->link_ruleset, inp);
+		if (__predict_true(link->link_mem_chk != NULL && !realloc))
+			rv = link->link_mem_chk(mem, len, flags, ifp,
+			    link->link_ruleset, m);
+		else if (!realloc)
+			rv = pfil_fake_mbuf(link->link_mbuf_chk, mem, len, ifp,
+			    flags, link->link_ruleset, m);
 		else
-			rv = (*link->link_func)(p, ifp, flags,
-			    link->link_ruleset, inp);
+			rv = link->link_mbuf_chk(m, ifp, flags,
+			    link->link_ruleset, NULL);
+
 		if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED)
 			break;
-		else if (rv == PFIL_REALLOCED) {
-			flags &= ~(PFIL_MEMPTR | PFIL_LENMASK);
+		else if (rv == PFIL_REALLOCED)
 			realloc = true;
-		}
 	}
 	if (realloc && rv == PFIL_PASS)
 		rv = PFIL_REALLOCED;
 	return (rv);
 }
 
+int
+pfil_mem_in(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp,
+    struct mbuf **m)
+{
+
+	return (pfil_mem_common(&head->head_in, mem, len, PFIL_IN, ifp, m));
+}
+
+int
+pfil_mem_out(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp,
+    struct mbuf **m)
+{
+
+	return (pfil_mem_common(&head->head_out, mem, len, PFIL_OUT, ifp, m));
+}
+
 static __always_inline int
-pfil_mbuf_common(pfil_chain_t *pch, pfil_packet_t p, struct ifnet *ifp,
+pfil_mbuf_common(pfil_chain_t *pch, struct mbuf **m, struct ifnet *ifp,
     int flags, struct inpcb *inp)
 {
 	struct pfil_link *link;
@@ -211,7 +207,8 @@ pfil_mbuf_common(pfil_chain_t *pch, pfil_packet_t p, struct ifnet *ifp,
 
 	rv = PFIL_PASS;
 	CK_STAILQ_FOREACH(link, pch, link_chain) {
-		rv = (*link->link_func)(p, ifp, flags, link->link_ruleset, inp);
+		rv = link->link_mbuf_chk(m, ifp, flags, link->link_ruleset,
+		    inp);
 		if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED)
 			break;
 	}
@@ -219,19 +216,19 @@ pfil_mbuf_common(pfil_chain_t *pch, pfil_packet_t p, struct ifnet *ifp,
 }
 
 int
-pfil_mbuf_in(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp,
+pfil_mbuf_in(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
    struct inpcb *inp)
 {
 
-	return (pfil_mbuf_common(&head->head_in, p, ifp, PFIL_IN, inp));
+	return (pfil_mbuf_common(&head->head_in, m, ifp, PFIL_IN, inp));
 }
 
 int
-pfil_mbuf_out(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp,
+pfil_mbuf_out(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
     struct inpcb *inp)
 {
 
-	return (pfil_mbuf_common(&head->head_out, p, ifp, PFIL_OUT, inp));
+	return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT, inp));
 }
 
 /*
@@ -298,7 +295,8 @@ pfil_add_hook(struct pfil_hook_args *pa)
 	MPASS(pa->pa_version == PFIL_VERSION);
 
 	hook = malloc(sizeof(struct pfil_hook), M_PFIL, M_WAITOK | M_ZERO);
-	hook->hook_func = pa->pa_func;
+	hook->hook_mbuf_chk = pa->pa_mbuf_chk;
+	hook->hook_mem_chk = pa->pa_mem_chk;
 	hook->hook_ruleset = pa->pa_ruleset;
 	hook->hook_flags = pa->pa_flags;
 	hook->hook_type = pa->pa_type;
@@ -416,7 +414,8 @@ pfil_link(struct pfil_link_args *pa)
 
 	if (pa->pa_flags & PFIL_IN) {
 		in->link_hook = hook;
-		in->link_func = hook->hook_func;
+		in->link_mbuf_chk = hook->hook_mbuf_chk;
+		in->link_mem_chk = hook->hook_mem_chk;
 		in->link_flags = hook->hook_flags;
 		in->link_ruleset = hook->hook_ruleset;
 		if (pa->pa_flags & PFIL_APPEND)
@@ -428,7 +427,8 @@ pfil_link(struct pfil_link_args *pa)
 	}
 	if (pa->pa_flags & PFIL_OUT) {
 		out->link_hook = hook;
-		out->link_func = hook->hook_func;
+		out->link_mbuf_chk = hook->hook_mbuf_chk;
+		out->link_mem_chk = hook->hook_mem_chk;
 		out->link_flags = hook->hook_flags;
 		out->link_ruleset = hook->hook_ruleset;
 		if (pa->pa_flags & PFIL_APPEND)
diff --git a/sys/net/pfil.h b/sys/net/pfil.h
index d5e9eadd8b8c..b99ec6896266 100644
--- a/sys/net/pfil.h
+++ b/sys/net/pfil.h
@@ -82,41 +82,16 @@ struct pfilioc_link {
 #define	PFIL_OUT	0x00020000
 /* UNUSED		0x00040000 */
 #define	PFIL_DIR(f)	((f) & (PFIL_IN|PFIL_OUT))
-#define	PFIL_MEMPTR	0x00080000
 #define	PFIL_HEADPTR	0x00100000
 #define	PFIL_HOOKPTR	0x00200000
 #define	PFIL_APPEND	0x00400000
 #define	PFIL_UNLINK	0x00800000
-#define	PFIL_LENMASK	0x0000ffff
-#define	PFIL_LENGTH(f)	((f) & PFIL_LENMASK)
 
 #ifdef _KERNEL
 struct mbuf;
 struct ifnet;
 struct inpcb;
 
-typedef union {
-	struct mbuf	**m;
-	void		*mem;
-	uintptr_t	__ui;
-} pfil_packet_t __attribute__((__transparent_union__));
-
-static inline pfil_packet_t
-pfil_packet_align(pfil_packet_t p)
-{
-
-	return ((pfil_packet_t ) (((uintptr_t)(p).mem +
-	    (_Alignof(void *) - 1)) & - _Alignof(void *)));
-}
-
-static inline struct mbuf *
-pfil_mem2mbuf(void *v)
-{
-
-	return (*(struct mbuf **) (((uintptr_t)(v) +
-	    (_Alignof(void *) - 1)) & - _Alignof(void *)));
-}
-
 typedef enum {
 	PFIL_PASS = 0,
 	PFIL_DROPPED,
@@ -124,8 +99,11 @@ typedef enum {
 	PFIL_REALLOCED,
 } pfil_return_t;
 
-typedef	pfil_return_t	(*pfil_func_t)(pfil_packet_t, struct ifnet *, int,
+typedef	pfil_return_t	(*pfil_mbuf_chk_t)(struct mbuf **, struct ifnet *, int,
 			    void *, struct inpcb *);
+typedef pfil_return_t	(*pfil_mem_chk_t)(void *, u_int, int, struct ifnet *,
+			    void *, struct mbuf **);
+
 /*
  * A pfil head is created by a packet intercept point.
  *
@@ -142,14 +120,15 @@ typedef struct pfil_head *	pfil_head_t;
 /*
  * Give us a chance to modify pfil_xxx_args structures in future.
  */
-#define	PFIL_VERSION	1
+#define	PFIL_VERSION	2
 
 /* Argument structure used by packet filters to register themselves. */
 struct pfil_hook_args {
 	int		 pa_version;
 	int		 pa_flags;
 	enum pfil_types	 pa_type;
-	pfil_func_t	 pa_func;
+	pfil_mbuf_chk_t	 pa_mbuf_chk;
+	pfil_mem_chk_t	 pa_mem_chk;
 	void		*pa_ruleset;
 	const char	*pa_modname;
 	const char	*pa_rulname;
@@ -192,12 +171,15 @@ pfil_head_t	pfil_head_register(struct pfil_head_args *);
 void		pfil_head_unregister(pfil_head_t);
 
 /* Public functions to run the packet inspection by inspection points. */
-int	pfil_run_hooks(struct pfil_head *, pfil_packet_t, struct ifnet *, int,
+int	pfil_mem_in(struct pfil_head *, void *, u_int, struct ifnet *,
+    struct mbuf **);
+int	pfil_mem_out(struct pfil_head *, void *, u_int, struct ifnet *,
+    struct mbuf **);
+int	pfil_mbuf_in(struct pfil_head *, struct mbuf **, struct ifnet *,
     struct inpcb *inp);
-int	pfil_mbuf_in(struct pfil_head *, pfil_packet_t, struct ifnet *,
-    struct inpcb *inp);
-int	pfil_mbuf_out(struct pfil_head *, pfil_packet_t, struct ifnet *,
+int	pfil_mbuf_out(struct pfil_head *, struct mbuf **, struct ifnet *,
     struct inpcb *inp);
+
 /*
  * Minimally exposed structure to avoid function call in case of absence
  * of any filters by protocols and macros to do the check.
@@ -209,10 +191,5 @@ struct _pfil_head {
 #define	PFIL_HOOKED_IN(p) (((struct _pfil_head *)(p))->head_nhooksin > 0)
 #define	PFIL_HOOKED_OUT(p) (((struct _pfil_head *)(p))->head_nhooksout > 0)
 
-/*
- * Alloc mbuf to be used instead of memory pointer.
- */
-int	pfil_realloc(pfil_packet_t *, int, struct ifnet *);
-
 #endif /* _KERNEL */
 #endif /* _NET_PFIL_H_ */
diff --git a/sys/netinet/siftr.c b/sys/netinet/siftr.c
index e21d15212979..7861031b724b 100644
--- a/sys/netinet/siftr.c
+++ b/sys/netinet/siftr.c
@@ -1139,18 +1139,16 @@ VNET_DEFINE_STATIC(pfil_hook_t, siftr_inet6_hook);
 static int
 siftr_pfil(int action)
 {
-	struct pfil_hook_args pha;
-	struct pfil_link_args pla;
-
-	pha.pa_version = PFIL_VERSION;
-	pha.pa_flags = PFIL_IN | PFIL_OUT;
-	pha.pa_modname = "siftr";
-	pha.pa_ruleset = NULL;
-	pha.pa_rulname = "default";
-
-	pla.pa_version = PFIL_VERSION;
-	pla.pa_flags = PFIL_IN | PFIL_OUT |
-	    PFIL_HEADPTR | PFIL_HOOKPTR;
+	struct pfil_hook_args pha = {
+		.pa_version = PFIL_VERSION,
+		.pa_flags = PFIL_IN | PFIL_OUT,
+		.pa_modname = "siftr",
+		.pa_rulname = "default",
+	};
+	struct pfil_link_args pla = {
+		.pa_version = PFIL_VERSION,
+		.pa_flags = PFIL_IN | PFIL_OUT | PFIL_HEADPTR | PFIL_HOOKPTR,
+	};
 
 	VNET_ITERATOR_DECL(vnet_iter);
 
@@ -1159,14 +1157,14 @@ siftr_pfil(int action)
 		CURVNET_SET(vnet_iter);
 
 		if (action == HOOK) {
-			pha.pa_func = siftr_chkpkt;
+			pha.pa_mbuf_chk = siftr_chkpkt;
 			pha.pa_type = PFIL_TYPE_IP4;
 			V_siftr_inet_hook = pfil_add_hook(&pha);
 			pla.pa_hook = V_siftr_inet_hook;
 			pla.pa_head = V_inet_pfil_head;
 			(void)pfil_link(&pla);
 #ifdef SIFTR_IPV6
-			pha.pa_func = siftr_chkpkt6;
+			pha.pa_mbuf_chk = siftr_chkpkt6;
 			pha.pa_type = PFIL_TYPE_IP6;
 			V_siftr_inet6_hook = pfil_add_hook(&pha);
 			pla.pa_hook = V_siftr_inet6_hook;
diff --git a/sys/netpfil/ipfilter/netinet/ip_fil_freebsd.c b/sys/netpfil/ipfilter/netinet/ip_fil_freebsd.c
index 95c8b73e6388..1822d19fbb4c 100644
--- a/sys/netpfil/ipfilter/netinet/ip_fil_freebsd.c
+++ b/sys/netpfil/ipfilter/netinet/ip_fil_freebsd.c
@@ -1311,31 +1311,31 @@ int ipf_pfil_unhook(void) {
 }
 
 int ipf_pfil_hook(void) {
-	struct pfil_hook_args pha;
-	struct pfil_link_args pla;
 	int error, error6;
 
-	pha.pa_version = PFIL_VERSION;
-	pha.pa_flags = PFIL_IN | PFIL_OUT;
-	pha.pa_modname = "ipfilter";
-	pha.pa_rulname = "default-ip4";
-	pha.pa_func = ipf_check_wrapper;
-	pha.pa_ruleset = NULL;
-	pha.pa_type = PFIL_TYPE_IP4;
+	struct pfil_hook_args pha = {
+		.pa_version = PFIL_VERSION,
+		.pa_flags = PFIL_IN | PFIL_OUT,
+		.pa_modname = "ipfilter",
+		.pa_rulname = "default-ip4",
+		.pa_mbuf_chk = ipf_check_wrapper,
+		.pa_type = PFIL_TYPE_IP4,
+	};
 	V_ipf_inet_hook = pfil_add_hook(&pha);
 
 #ifdef USE_INET6
 	pha.pa_rulname = "default-ip6";
-	pha.pa_func = ipf_check_wrapper6;
+	pha.pa_mbuf_chk = ipf_check_wrapper6;
 	pha.pa_type = PFIL_TYPE_IP6;
 	V_ipf_inet6_hook = pfil_add_hook(&pha);
 #endif
 
-	pla.pa_version = PFIL_VERSION;
-	pla.pa_flags = PFIL_IN | PFIL_OUT |
-	    PFIL_HEADPTR | PFIL_HOOKPTR;
-	pla.pa_head = V_inet_pfil_head;
-	pla.pa_hook = V_ipf_inet_hook;
+	struct pfil_link_args pla = {
+		.pa_version = PFIL_VERSION,
+		.pa_flags = PFIL_IN | PFIL_OUT | PFIL_HEADPTR | PFIL_HOOKPTR,
+		.pa_head = V_inet_pfil_head,
+		.pa_hook = V_ipf_inet_hook,
+	};
 	error = pfil_link(&pla);
 
 	error6 = 0;
diff --git a/sys/netpfil/ipfw/ip_fw_pfil.c b/sys/netpfil/ipfw/ip_fw_pfil.c
index 29ae06ba7713..ec46c077d8bb 100644
--- a/sys/netpfil/ipfw/ip_fw_pfil.c
+++ b/sys/netpfil/ipfw/ip_fw_pfil.c
@@ -327,52 +327,104 @@ again:
 }
 
 /*
- * ipfw processing for ethernet packets (in and out).
+ * ipfw processing for ethernet packets (in and out), mbuf version.
  */
 static pfil_return_t
-ipfw_check_frame(pfil_packet_t p, struct ifnet *ifp, int flags,
+ipfw_check_frame_mbuf(struct mbuf **m0, struct ifnet *ifp, const int flags,
     void *ruleset __unused, struct inpcb *inp)
 {
-	struct ip_fw_args args;
+	struct ip_fw_args args = {
+		.flags = IPFW_ARGS_ETHER |
+		    ((flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT),
+		.ifp = ifp,
+		.inp = inp,
+	};
+	struct m_tag *mtag;
 	pfil_return_t ret;
-	bool mem, realloc;
 	int ipfw;
 
-	if (flags & PFIL_MEMPTR) {
-		mem = true;
-		realloc = false;
-		args.flags = PFIL_LENGTH(flags) | IPFW_ARGS_ETHER;
-		args.mem = p.mem;
-	} else {
-		mem = realloc = false;
-		args.flags = IPFW_ARGS_ETHER;
+again:
+	/*
+	 * Fetch start point from rule, if any.
+	 * Remove the tag if present.
+	 */
+	mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
+	if (mtag != NULL) {
+		args.rule = *((struct ipfw_rule_ref *)(mtag+1));
+		m_tag_delete(*m0, mtag);
+		if (args.rule.info & IPFW_ONEPASS)
+			return (PFIL_PASS);
+		args.flags |= IPFW_ARGS_REF;
 	}
-	args.flags |= (flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT;
-	args.ifp = ifp;
-	args.inp = inp;
+	args.m = *m0,
 
-again:
-	if (!mem) {
-		/*
-		 * Fetch start point from rule, if any.
-		 * Remove the tag if present.
-		 */
-		struct m_tag *mtag;
-
-		mtag = m_tag_locate(*p.m, MTAG_IPFW_RULE, 0, NULL);
-		if (mtag != NULL) {
-			args.rule = *((struct ipfw_rule_ref *)(mtag+1));
-			m_tag_delete(*p.m, mtag);
-			if (args.rule.info & IPFW_ONEPASS)
-				return (PFIL_PASS);
-			args.flags |= IPFW_ARGS_REF;
+	ipfw = ipfw_chk(&args);
+	*m0 = args.m;
+
+	ret = PFIL_PASS;
+	switch (ipfw) {
+	case IP_FW_PASS:
+		break;
+
+	case IP_FW_DENY:
+		ret = PFIL_DROPPED;
+		break;
+
+	case IP_FW_DUMMYNET:
+		if (ip_dn_io_ptr == NULL) {
+			ret = PFIL_DROPPED;
+			break;
 		}
-		args.m = *p.m;
+		MPASS(args.flags & IPFW_ARGS_REF);
+		ip_dn_io_ptr(m0, &args);
+		return (PFIL_CONSUMED);
+
+	case IP_FW_NGTEE:
+	case IP_FW_NETGRAPH:
+		if (ng_ipfw_input_p == NULL) {
+			ret = PFIL_DROPPED;
+			break;
+		}
+		MPASS(args.flags & IPFW_ARGS_REF);
+		(void )ng_ipfw_input_p(m0, &args, ipfw == IP_FW_NGTEE);
+		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
+			goto again;	/* continue with packet */
+		ret = PFIL_CONSUMED;
+		break;
+
+	default:
+		KASSERT(0, ("%s: unknown retval", __func__));
 	}
 
+	if (ret != PFIL_PASS) {
+		if (*m0)
+			FREE_PKT(*m0);
+		*m0 = NULL;
+	}
+
+	return (ret);
+}
+
+/*
+ * ipfw processing for ethernet packets (in and out), memory pointer version,
+ * two in/out accessors.
+ */
+static pfil_return_t
+ipfw_check_frame_mem(void *mem, u_int len, int flags, struct ifnet *ifp,
+    void *ruleset __unused, struct mbuf **m)
+{
+	struct ip_fw_args args = {
+		.flags = len | IPFW_ARGS_ETHER |
+		    ((flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT),
+		.ifp = ifp,
+		.mem = mem,
+	};
+	pfil_return_t ret;
+	int ipfw;
+
+	*m = NULL;
+again:
 	ipfw = ipfw_chk(&args);
-	if (!mem)
-		*p.m = args.m;
 
 	ret = PFIL_PASS;
 	switch (ipfw) {
@@ -388,16 +440,13 @@ again:
 			ret = PFIL_DROPPED;
 			break;
 		}
-		if (mem) {
-			if (pfil_realloc(&p, flags, ifp) != 0) {
-				ret = PFIL_DROPPED;
-				break;
-			}
-			mem = false;
-			realloc = true;
+		*m = m_devget(mem, len, 0, ifp, NULL);
+		if (*m == NULL) {
+			ret = PFIL_DROPPED;
+			break;
 		}
 		MPASS(args.flags & IPFW_ARGS_REF);
-		ip_dn_io_ptr(p.m, &args);
+		ip_dn_io_ptr(m, &args);
 		return (PFIL_CONSUMED);
 
 	case IP_FW_NGTEE:
@@ -406,16 +455,13 @@ again:
 			ret = PFIL_DROPPED;
 			break;
 		}
-		if (mem) {
-			if (pfil_realloc(&p, flags, ifp) != 0) {
-				ret = PFIL_DROPPED;
-				break;
-			}
-			mem = false;
-			realloc = true;
+		*m = m_devget(mem, len, 0, ifp, NULL);
+		if (*m == NULL) {
+			ret = PFIL_DROPPED;
+			break;
 		}
 		MPASS(args.flags & IPFW_ARGS_REF);
-		(void )ng_ipfw_input_p(p.m, &args, ipfw == IP_FW_NGTEE);
+		(void )ng_ipfw_input_p(m, &args, ipfw == IP_FW_NGTEE);
 		if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */
 			goto again;	/* continue with packet */
 		ret = PFIL_CONSUMED;
@@ -425,13 +471,7 @@ again:
 		KASSERT(0, ("%s: unknown retval", __func__));
 	}
 
-	if (!mem && ret != PFIL_PASS) {
-		if (*p.m)
-			FREE_PKT(*p.m);
-		*p.m = NULL;
-	}
-
-	if (realloc && ret == PFIL_PASS)
+	if (*m != NULL && ret == PFIL_PASS)
 		ret = PFIL_REALLOCED;
 
 	return (ret);
@@ -543,34 +583,33 @@ VNET_DEFINE_STATIC(pfil_hook_t, ipfw_link_hook);
 static void
 ipfw_hook(int pf)
 {
-	struct pfil_hook_args pha;
+	struct pfil_hook_args pha = {
+		.pa_version = PFIL_VERSION,
+		.pa_flags = PFIL_IN | PFIL_OUT,
+		.pa_modname = "ipfw",
+	};
 	pfil_hook_t *h;
 
-	pha.pa_version = PFIL_VERSION;
-	pha.pa_flags = PFIL_IN | PFIL_OUT;
-	pha.pa_modname = "ipfw";
-	pha.pa_ruleset = NULL;
-
 	switch (pf) {
 	case AF_INET:
-		pha.pa_func = ipfw_check_packet;
+		pha.pa_mbuf_chk = ipfw_check_packet;
 		pha.pa_type = PFIL_TYPE_IP4;
 		pha.pa_rulname = "default";
 		h = &V_ipfw_inet_hook;
 		break;
 #ifdef INET6
 	case AF_INET6:
-		pha.pa_func = ipfw_check_packet;
+		pha.pa_mbuf_chk = ipfw_check_packet;
 		pha.pa_type = PFIL_TYPE_IP6;
 		pha.pa_rulname = "default6";
 		h = &V_ipfw_inet6_hook;
 		break;
 #endif
 	case AF_LINK:
-		pha.pa_func = ipfw_check_frame;
+		pha.pa_mbuf_chk = ipfw_check_frame_mbuf;
+		pha.pa_mem_chk = ipfw_check_frame_mem;
 		pha.pa_type = PFIL_TYPE_ETHERNET;
 		pha.pa_rulname = "default-link";
-		pha.pa_flags |= PFIL_MEMPTR;
 		h = &V_ipfw_link_hook;
 		break;
 	}
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 76742aebf01a..cc1f5a5c2138 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -6532,21 +6532,20 @@ VNET_DEFINE_STATIC(pfil_hook_t, pf_ip6_out_hook);
 static void
 hook_pf_eth(void)
 {
-	struct pfil_hook_args pha;
-	struct pfil_link_args pla;
+	struct pfil_hook_args pha = {
+		.pa_version = PFIL_VERSION,
+		.pa_modname = "pf",
+		.pa_type = PFIL_TYPE_ETHERNET,
+	};
+	struct pfil_link_args pla = {
+		.pa_version = PFIL_VERSION,
+	};
 	int ret __diagused;
 
 	if (atomic_load_bool(&V_pf_pfil_eth_hooked))
 		return;
 
-	pha.pa_version = PFIL_VERSION;
-	pha.pa_modname = "pf";
-	pha.pa_ruleset = NULL;
-
-	pla.pa_version = PFIL_VERSION;
-
-	pha.pa_type = PFIL_TYPE_ETHERNET;
-	pha.pa_func = pf_eth_check_in;
+	pha.pa_mbuf_chk = pf_eth_check_in;
 	pha.pa_flags = PFIL_IN;
 	pha.pa_rulname = "eth-in";
 	V_pf_eth_in_hook = pfil_add_hook(&pha);
@@ -6555,7 +6554,7 @@ hook_pf_eth(void)
 	pla.pa_hook = V_pf_eth_in_hook;
 	ret = pfil_link(&pla);
 	MPASS(ret == 0);
-	pha.pa_func = pf_eth_check_out;
+	pha.pa_mbuf_chk = pf_eth_check_out;
 	pha.pa_flags = PFIL_OUT;
 	pha.pa_rulname = "eth-out";
 	V_pf_eth_out_hook = pfil_add_hook(&pha);
@@ -6571,22 +6570,21 @@ hook_pf_eth(void)
 static void
 hook_pf(void)
 {
-	struct pfil_hook_args pha;
-	struct pfil_link_args pla;
+	struct pfil_hook_args pha = {
+		.pa_version = PFIL_VERSION,
+		.pa_modname = "pf",
+	};
+	struct pfil_link_args pla = {
+		.pa_version = PFIL_VERSION,
+	};
 	int ret __diagused;
 
 	if (atomic_load_bool(&V_pf_pfil_hooked))
 		return;
 
-	pha.pa_version = PFIL_VERSION;
-	pha.pa_modname = "pf";
-	pha.pa_ruleset = NULL;
-
-	pla.pa_version = PFIL_VERSION;
-
 #ifdef INET
 	pha.pa_type = PFIL_TYPE_IP4;
-	pha.pa_func = pf_check_in;
+	pha.pa_mbuf_chk = pf_check_in;
 	pha.pa_flags = PFIL_IN;
 	pha.pa_rulname = "default-in";
 	V_pf_ip4_in_hook = pfil_add_hook(&pha);
@@ -6595,7 +6593,7 @@ hook_pf(void)
 	pla.pa_hook = V_pf_ip4_in_hook;
 	ret = pfil_link(&pla);
 	MPASS(ret == 0);
-	pha.pa_func = pf_check_out;
+	pha.pa_mbuf_chk = pf_check_out;
 	pha.pa_flags = PFIL_OUT;
 	pha.pa_rulname = "default-out";
 	V_pf_ip4_out_hook = pfil_add_hook(&pha);
@@ -6607,7 +6605,7 @@ hook_pf(void)
 #endif
 #ifdef INET6
 	pha.pa_type = PFIL_TYPE_IP6;
-	pha.pa_func = pf_check6_in;
+	pha.pa_mbuf_chk = pf_check6_in;
 	pha.pa_flags = PFIL_IN;
 	pha.pa_rulname = "default-in6";
 	V_pf_ip6_in_hook = pfil_add_hook(&pha);
@@ -6616,7 +6614,7 @@ hook_pf(void)
 	pla.pa_hook = V_pf_ip6_in_hook;
 	ret = pfil_link(&pla);
 	MPASS(ret == 0);
-	pha.pa_func = pf_check6_out;
+	pha.pa_mbuf_chk = pf_check6_out;
 	pha.pa_rulname = "default-out6";
 	pha.pa_flags = PFIL_OUT;
 	V_pf_ip6_out_hook = pfil_add_hook(&pha);