git: 8aaffd78c0f5 - main - Add dummymbuf module for testing purposes

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Thu, 15 Aug 2024 07:34:51 UTC
The branch main has been updated by kp:

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

commit 8aaffd78c0f517985c12fd1e3cbceeb6c6b98ef5
Author:     Igor Ostapenko <pm@igoro.pro>
AuthorDate: 2024-08-14 12:45:38 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-08-15 07:28:13 +0000

    Add dummymbuf module for testing purposes
    
    Reviewed by:    kp
    Differential Revision:  https://reviews.freebsd.org/D45928
---
 share/man/man4/Makefile        |   1 +
 share/man/man4/dummymbuf.4     | 209 ++++++++++++++++++++
 sys/conf/files                 |   1 +
 sys/modules/Makefile           |   1 +
 sys/modules/dummymbuf/Makefile |   9 +
 sys/net/dummymbuf.c            | 436 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 657 insertions(+)

diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 4e685cac3ecf..6195211d2eb9 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -135,6 +135,7 @@ MAN=	aac.4 \
 	ds1307.4 \
 	ds3231.4 \
 	${_dtrace_provs} \
+	dummymbuf.4 \
 	dummynet.4 \
 	edsc.4 \
 	ehci.4 \
diff --git a/share/man/man4/dummymbuf.4 b/share/man/man4/dummymbuf.4
new file mode 100644
index 000000000000..844a7c0565bd
--- /dev/null
+++ b/share/man/man4/dummymbuf.4
@@ -0,0 +1,209 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" Note: The date here should be updated whenever a non-trivial
+.\" change is made to the manual page.
+.Dd August 2, 2024
+.Dt DUMMYMBUF 4
+.Os
+.Sh NAME
+.Nm dummymbuf
+.Nd "mbuf alteration pfil hooks"
+.Sh SYNOPSIS
+To compile the driver into the kernel,
+place the following line in the
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device dummymbuf"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+dummymbuf_load="YES"
+.Ed
+.Sh DESCRIPTION
+This module is intended to test networking code in the face of unusual mbuf
+layouts.
+The special
+.Xr pfil 9
+hooks are provided for mbuf alteration and can be listed with
+.Xr pfilctl 8
+as follows:
+.Bd -literal -offset indent
+            Hook                      Type
+       dummymbuf:ethernet         Ethernet
+       dummymbuf:inet6                IPv6
+       dummymbuf:inet                 IPv4
+.Ed
+.Pp
+To activate a hook it must be linked to the respective
+.Xr pfil 9
+head.
+.Xr pfilctl 8
+can be used for the linking.
+.Pp
+Each time a hook is invoked it reads a single shared set of
+.Sx RULES
+from
+.Va net.dummymbuf.rules
+sysctl.
+The rules are evaluated sequentially and each matching rule performs the
+specified operation over the mbuf.
+.Pp
+After every successfully applied operation the
+.Va net.dummymbuf.hits
+sysctl counter is increased.
+.Pp
+A hook returns an altered mbuf for further processing, but it drops a packet
+if rules parsing or an operation fails.
+Also, the first mbuf of the original chain may be changed.
+.Pp
+The module is
+.Xr VNET 9
+based, hence every
+.Xr jail 2
+provides its own set of hooks and sysctl variables.
+.Sh RULES
+The set of rules is a semicolon separated list.
+An empty string is treated as a parsing failure.
+A rule conceptually has two parts, filter and operation, with the following
+syntax:
+.Bd -literal -offset indent
+{inet | inet6 | ethernet} {in | out} <ifname> <opname>[ <opargs>];
+.Ed
+.Ss Filter
+The first word of a rule matches
+.Xr pfil 9
+type.
+The second matches packet's direction, and the third matches the network
+interface a packet is coming from.
+.Ss Operation
+An operation may have arguments separated from its name by space.
+The available operations are:
+.Bl -tag -width indent
+.It pull-head <number-of-bytes>
+Unconditionally creates a brand new cluster-based mbuf and links it to be the
+first mbuf of the original mbuf chain, with respective packet header moving.
+After, the given number of bytes are pulled from the original mbuf chain.
+.Pp
+If it is asked to pull 0 bytes then the first mbuf of the resulting chain will
+be left empty.
+Asking to pull more than
+.Dv MCLBYTES
+is treated as an operation failure.
+If a mbuf chain has less data than asked then the entire packet is pulled with
+tail mbuf(s) left empty.
+.Pp
+As a result, only the layout of a mbuf chain is altered, its content logically
+is left intact.
+.El
+.Sh SYSCTL VARIABLES
+The following variables are available:
+.Bl -tag -width indent
+.It Va net.dummymbuf.rules
+A string representing a single set of
+.Sx RULES
+shared among all
+.Nm
+hooks.
+.It Va net.dummymbuf.hits
+Number of times a rule has been applied.
+It is reset to zero upon writing.
+.El
+.Sh EXAMPLES
+As it was intended,
+.Nm
+can be found useful for firewall testing.
+A mbuf chain could be altered before it hits a firewall to test that the latter
+can handle a case respectively.
+Thus, it is important to have correct sequence of hooks.
+A test case should prepare and enable a firewall first to get its hooks linked.
+After, the
+.Xr pfilctl 8
+should be used to link
+.Nm
+hook(s) to put them in front of a firewall.
+.Pp
+The following links
+.Va dummymbuf:inet6
+hook for inbound and puts it in front of other hooks:
+.Bd -literal -offset indent
+pfilctl link -i dummymbuf:inet6 inet6
+.Ed
+.Pp
+And this does the same for outbound:
+.Bd -literal -offset indent
+pfilctl link -o -a dummymbuf:inet6 inet6
+.Ed
+.Pp
+For instance, we want to test a scenario in which the very first mbuf in a
+chain has zero m_len, to verify that a firewall can correctly read the
+packet data despite that.
+The following set of rules does it for inbound and outbound:
+.Bd -literal -offset indent
+sysctl net.dummymbuf.rules="inet6 in em0 pull-head 0; inet6 out em0 pull-head 0;"
+.Ed
+.Pp
+It is encouraged to verify
+.Va net.dummymbuf.hits
+sysctl counter along with other test assertions to make sure that
+.Nm
+really does its work and there is no false positive due to misconfiguration.
+It is a good idea to reset it before the action:
+.Bd -literal -offset indent
+sysctl net.dummymbuf.hits=0
+.Ed
+.Pp
+It is equally important to cleanup the things after the test case:
+.Bd -literal -offset indent
+pfilctl unlink -i dummymbuf:inet6 inet6
+pfilctl unlink -o dummymbuf:inet6 inet6
+sysctl net.dummymbuf.rules=""
+.Ed
+.Pp
+If a test case operates within a temporary vnet then explicit cleanup can be
+omitted, the
+.Nm
+facilities will vanish along with its vnet instance.
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "dummymbuf: <filter match>: rule parsing failed: <the rule in question>"
+If everything looks fine then extra spaces removal may help due to the parser
+is kept very simple.
+.It "dummymbuf: <filter match>: mbuf operation failed: <the rule in question>"
+Incorrect operation argument has been found, mbuf allocation has failed, etc.
+.El
+.Sh SEE ALSO
+.Xr jail 2 ,
+.Xr pfilctl 8 ,
+.Xr mbuf 9 ,
+.Xr pfil 9 ,
+.Xr VNET 9
+.Sh AUTHORS
+The module and this manual page were written by
+.An Igor Ostapenko Aq Mt pm@igoro.pro .
diff --git a/sys/conf/files b/sys/conf/files
index 6e69c128ee65..df4c702540ae 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4151,6 +4151,7 @@ net/bpf_jitter.c		optional bpf_jitter
 net/bpf_filter.c		optional bpf | netgraph_bpf
 net/bpf_zerocopy.c		optional bpf
 net/bridgestp.c			optional bridge | if_bridge
+net/dummymbuf.c			optional dummymbuf
 net/ieee8023ad_lacp.c		optional lagg
 net/if.c			standard
 net/ifq.c			standard
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 19c34f81851e..84d1735e8e07 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -100,6 +100,7 @@ SUBDIR=	\
 	${_dpdk_lpm4} \
 	${_dpdk_lpm6} \
 	${_dpms} \
+	dummymbuf \
 	dummynet \
 	${_dwwdt} \
 	${_e6000sw} \
diff --git a/sys/modules/dummymbuf/Makefile b/sys/modules/dummymbuf/Makefile
new file mode 100644
index 000000000000..aefede45dbd3
--- /dev/null
+++ b/sys/modules/dummymbuf/Makefile
@@ -0,0 +1,9 @@
+.PATH: ${SRCTOP}/sys/net
+
+KMOD=	dummymbuf
+SRCS=	dummymbuf.c
+SRCS+=	opt_inet.h opt_inet6.h
+
+EXPORT_SYMS=	YES
+
+.include <bsd.kmod.mk>
diff --git a/sys/net/dummymbuf.c b/sys/net/dummymbuf.c
new file mode 100644
index 000000000000..cb92889c5b77
--- /dev/null
+++ b/sys/net/dummymbuf.c
@@ -0,0 +1,436 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/vnet.h>
+#include <net/pfil.h>
+
+/*
+ * Separate sysctl sub-tree
+ */
+
+SYSCTL_NODE(_net, OID_AUTO, dummymbuf, 0, NULL,
+    "Dummy mbuf sysctl");
+
+/*
+ * Rules
+ */
+
+static MALLOC_DEFINE(M_DUMMYMBUF_RULES, "dummymbuf_rules",
+    "dummymbuf rules string buffer");
+
+#define RULES_MAXLEN		1024
+VNET_DEFINE_STATIC(char *,	dmb_rules) = NULL;
+#define V_dmb_rules		VNET(dmb_rules)
+
+VNET_DEFINE_STATIC(struct sx,	dmb_rules_lock);
+#define V_dmb_rules_lock	VNET(dmb_rules_lock)
+
+#define DMB_RULES_SLOCK()	sx_slock(&V_dmb_rules_lock)
+#define DMB_RULES_SUNLOCK()	sx_sunlock(&V_dmb_rules_lock)
+#define DMB_RULES_XLOCK()	sx_xlock(&V_dmb_rules_lock)
+#define DMB_RULES_XUNLOCK()	sx_xunlock(&V_dmb_rules_lock)
+
+static int
+dmb_sysctl_handle_rules(SYSCTL_HANDLER_ARGS)
+{
+	int error = 0;
+	char empty = '\0';
+	char **rulesp = (char **)arg1;
+
+	if (req->newptr == NULL) {
+		// read only
+		DMB_RULES_SLOCK();
+		arg1 = *rulesp;
+		if (arg1 == NULL) {
+			arg1 = &empty;
+			arg2 = 0;
+		}
+		error = sysctl_handle_string(oidp, arg1, arg2, req);
+		DMB_RULES_SUNLOCK();
+	} else {
+		// read and write
+		DMB_RULES_XLOCK();
+		if (*rulesp == NULL)
+			*rulesp = malloc(arg2, M_DUMMYMBUF_RULES, M_WAITOK);
+		arg1 = *rulesp;
+		error = sysctl_handle_string(oidp, arg1, arg2, req);
+		DMB_RULES_XUNLOCK();
+	}
+
+	return (error);
+}
+
+SYSCTL_PROC(_net_dummymbuf, OID_AUTO, rules,
+    CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW | CTLFLAG_VNET,
+    &VNET_NAME(dmb_rules), RULES_MAXLEN, dmb_sysctl_handle_rules, "A",
+    "{inet | inet6 | ethernet} {in | out} <ifname> <opname>[ <opargs>];"
+    " ...;");
+
+/*
+ * Statistics
+ */
+
+VNET_DEFINE_STATIC(counter_u64_t,	dmb_hits);
+#define V_dmb_hits			VNET(dmb_hits)
+SYSCTL_PROC(_net_dummymbuf, OID_AUTO, hits,
+    CTLTYPE_U64 | CTLFLAG_MPSAFE | CTLFLAG_STATS | CTLFLAG_RW | CTLFLAG_VNET,
+    &VNET_NAME(dmb_hits), 0, sysctl_handle_counter_u64,
+    "QU", "Number of times a rule has been applied");
+
+/*
+ * pfil(9) context
+ */
+
+VNET_DEFINE_STATIC(pfil_hook_t,		dmb_pfil_inet_hook);
+#define V_dmb_pfil_inet_hook		VNET(dmb_pfil_inet_hook)
+
+VNET_DEFINE_STATIC(pfil_hook_t,		dmb_pfil_inet6_hook);
+#define V_dmb_pfil_inet6_hook		VNET(dmb_pfil_inet6_hook)
+
+VNET_DEFINE_STATIC(pfil_hook_t,		dmb_pfil_ethernet_hook);
+#define V_dmb_pfil_ethernet_hook	VNET(dmb_pfil_ethernet_hook)
+
+/*
+ * Logging
+ */
+
+#define FEEDBACK(pfil_type, pfil_flags, ifp, rule, msg)			\
+	printf("dummymbuf: %s %b %s: %s: %.*s\n",			\
+	    (pfil_type == PFIL_TYPE_IP4 ?	"PFIL_TYPE_IP4" :	\
+	     pfil_type == PFIL_TYPE_IP6 ?	"PFIL_TYPE_IP6" :	\
+	     pfil_type == PFIL_TYPE_ETHERNET ?	"PFIL_TYPE_ETHERNET" :	\
+						"PFIL_TYPE_UNKNOWN"),	\
+	    (pfil_flags), "\20\21PFIL_IN\22PFIL_OUT",			\
+	    (ifp)->if_xname,						\
+	    (msg),							\
+	    (rule).syntax_len, (rule).syntax_begin			\
+	)
+
+/*
+ * Internals
+ */
+
+struct rule;
+typedef struct mbuf * (*op_t)(struct mbuf *, struct rule *);
+struct rule {
+	const char	*syntax_begin;
+	int		 syntax_len;
+	int		 pfil_type;
+	int		 pfil_dir;
+	char		 ifname[IFNAMSIZ];
+	op_t		 op;
+	const char	*opargs;
+};
+
+static struct mbuf *
+dmb_m_pull_head(struct mbuf *m, struct rule *rule)
+{
+	struct mbuf *n;
+	int count;
+
+	count = (int)strtol(rule->opargs, NULL, 10);
+	if (count < 0 || count > MCLBYTES)
+		goto bad;
+
+	if (!(m->m_flags & M_PKTHDR))
+		goto bad;
+	if (m->m_pkthdr.len <= 0)
+		return (m);
+	if (count > m->m_pkthdr.len)
+		count = m->m_pkthdr.len;
+
+	if ((n = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR)) == NULL)
+		goto bad;
+
+	m_move_pkthdr(n, m);
+	m_copydata(m, 0, count, n->m_ext.ext_buf);
+	n->m_len = count;
+
+	m_adj(m, count);
+	n->m_next = m;
+
+	return (n);
+
+bad:
+	m_freem(m);
+	return (NULL);
+}
+
+static bool
+read_rule(const char **cur, struct rule *rule)
+{
+	// {inet | inet6 | ethernet} {in | out} <ifname> <opname>[ <opargs>];
+
+	rule->syntax_begin = NULL;
+	rule->syntax_len = 0;
+
+	if (*cur == NULL)
+		return (false);
+
+	// syntax_begin
+	while (**cur == ' ')
+		(*cur)++;
+	rule->syntax_begin = *cur;
+
+	// syntax_len
+	char *delim = strchr(*cur, ';');
+	if (delim == NULL)
+		return (false);
+	rule->syntax_len = (int)(delim - *cur + 1);
+
+	// pfil_type
+	if (strstr(*cur, "inet6") == *cur) {
+		rule->pfil_type = PFIL_TYPE_IP6;
+		*cur += strlen("inet6");
+	} else if (strstr(*cur, "inet") == *cur) {
+		rule->pfil_type = PFIL_TYPE_IP4;
+		*cur += strlen("inet");
+	} else if (strstr(*cur, "ethernet")) {
+		rule->pfil_type = PFIL_TYPE_ETHERNET;
+		*cur += strlen("ethernet");
+	} else {
+		return (false);
+	}
+	while (**cur == ' ')
+		(*cur)++;
+
+	// pfil_dir
+	if (strstr(*cur, "in") == *cur) {
+		rule->pfil_dir = PFIL_IN;
+		*cur += strlen("in");
+	} else if (strstr(*cur, "out") == *cur) {
+		rule->pfil_dir = PFIL_OUT;
+		*cur += strlen("out");
+	} else {
+		return (false);
+	}
+	while (**cur == ' ')
+		(*cur)++;
+
+	// ifname
+	char *sp = strchr(*cur, ' ');
+	if (sp == NULL || sp > delim)
+		return (false);
+	size_t len = sp - *cur;
+	if (len >= sizeof(rule->ifname))
+		return (false);
+	strncpy(rule->ifname, *cur, len);
+	rule->ifname[len] = 0;
+	*cur = sp;
+	while (**cur == ' ')
+		(*cur)++;
+
+	// opname
+	if (strstr(*cur, "pull-head") == *cur) {
+		rule->op = dmb_m_pull_head;
+		*cur += strlen("pull-head");
+	} else {
+		return (false);
+	}
+	while (**cur == ' ')
+		(*cur)++;
+
+	// opargs
+	if (*cur > delim)
+		return (false);
+	rule->opargs = *cur;
+
+	*cur = delim + 1;
+
+	return (true);
+}
+
+static pfil_return_t
+dmb_pfil_mbuf_chk(int pfil_type, struct mbuf **mp, struct ifnet *ifp,
+    int flags, void *ruleset, void *unused)
+{
+	struct mbuf *m = *mp;
+	const char *cursor;
+	bool parsed;
+	struct rule rule;
+
+	DMB_RULES_SLOCK();
+	cursor = V_dmb_rules;
+	while ((parsed = read_rule(&cursor, &rule))) {
+		if (rule.pfil_type == pfil_type &&
+		    rule.pfil_dir == (flags & rule.pfil_dir)  &&
+		    strcmp(rule.ifname, ifp->if_xname) == 0) {
+			m = rule.op(m, &rule);
+			if (m == NULL) {
+				FEEDBACK(pfil_type, flags, ifp, rule,
+				    "mbuf operation failed");
+				break;
+			}
+			counter_u64_add(V_dmb_hits, 1);
+		}
+		if (strlen(cursor) == 0)
+			break;
+	}
+	if (!parsed) {
+		FEEDBACK(pfil_type, flags, ifp, rule, "rule parsing failed");
+		m_freem(m);
+		m = NULL;
+	}
+	DMB_RULES_SUNLOCK();
+
+	if (m == NULL) {
+		*mp = NULL;
+		return (PFIL_DROPPED);
+	}
+	if (m != *mp) {
+		*mp = m;
+		return (PFIL_REALLOCED);
+	}
+
+	return (PFIL_PASS);
+}
+
+static pfil_return_t
+dmb_pfil_inet_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
+    void *ruleset, struct inpcb *inp)
+{
+	return (dmb_pfil_mbuf_chk(PFIL_TYPE_IP4, mp, ifp, flags,
+	    ruleset, inp));
+}
+
+static pfil_return_t
+dmb_pfil_inet6_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
+    void *ruleset, struct inpcb *inp)
+{
+	return (dmb_pfil_mbuf_chk(PFIL_TYPE_IP6, mp, ifp, flags,
+	    ruleset, inp));
+}
+
+static pfil_return_t
+dmb_pfil_ethernet_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
+    void *ruleset, struct inpcb *inp)
+{
+	return (dmb_pfil_mbuf_chk(PFIL_TYPE_ETHERNET, mp, ifp, flags,
+	    ruleset, inp));
+}
+
+static void
+dmb_pfil_init(void)
+{
+	struct pfil_hook_args pha = {
+		.pa_version = PFIL_VERSION,
+		.pa_modname = "dummymbuf",
+		.pa_flags = PFIL_IN | PFIL_OUT,
+	};
+
+#ifdef INET
+	pha.pa_type = PFIL_TYPE_IP4;
+	pha.pa_mbuf_chk = dmb_pfil_inet_mbuf_chk;
+	pha.pa_rulname = "inet";
+	V_dmb_pfil_inet_hook = pfil_add_hook(&pha);
+#endif
+
+#ifdef INET6
+	pha.pa_type = PFIL_TYPE_IP6;
+	pha.pa_mbuf_chk = dmb_pfil_inet6_mbuf_chk;
+	pha.pa_rulname = "inet6";
+	V_dmb_pfil_inet6_hook = pfil_add_hook(&pha);
+#endif
+
+	pha.pa_type = PFIL_TYPE_ETHERNET;
+	pha.pa_mbuf_chk = dmb_pfil_ethernet_mbuf_chk;
+	pha.pa_rulname = "ethernet";
+	V_dmb_pfil_ethernet_hook = pfil_add_hook(&pha);
+}
+
+static void
+dmb_pfil_uninit(void)
+{
+#ifdef INET
+	pfil_remove_hook(V_dmb_pfil_inet_hook);
+#endif
+
+#ifdef INET6
+	pfil_remove_hook(V_dmb_pfil_inet6_hook);
+#endif
+
+	pfil_remove_hook(V_dmb_pfil_ethernet_hook);
+}
+
+static void
+vnet_dmb_init(void *unused __unused)
+{
+	sx_init(&V_dmb_rules_lock, "dummymbuf rules");
+	V_dmb_hits = counter_u64_alloc(M_WAITOK);
+	dmb_pfil_init();
+}
+VNET_SYSINIT(vnet_dmb_init, SI_SUB_PROTO_PFIL, SI_ORDER_ANY,
+    vnet_dmb_init, NULL);
+
+static void
+vnet_dmb_uninit(void *unused __unused)
+{
+	dmb_pfil_uninit();
+	counter_u64_free(V_dmb_hits);
+	sx_destroy(&V_dmb_rules_lock);
+	free(V_dmb_rules, M_DUMMYMBUF_RULES);
+}
+VNET_SYSUNINIT(vnet_dmb_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_ANY,
+    vnet_dmb_uninit, NULL);
+
+static int
+dmb_modevent(module_t mod __unused, int event, void *arg __unused)
+{
+	int error = 0;
+
+	switch (event) {
+	case MOD_LOAD:
+	case MOD_UNLOAD:
+		break;
+	default:
+		error = EOPNOTSUPP;
+		break;
+	}
+
+	return (error);
+}
+
+static moduledata_t dmb_mod = {
+	"dummymbuf",
+	dmb_modevent,
+	NULL
+};
+
+DECLARE_MODULE(dummymbuf, dmb_mod, SI_SUB_PROTO_PFIL, SI_ORDER_ANY);
+MODULE_VERSION(dummymbuf, 1);