xref: /freebsd/sys/net/dummymbuf.c (revision 6bd8d85579a18cf69a9b02d5852f994cb1d5a1e9)
18aaffd78SIgor Ostapenko /*-
28aaffd78SIgor Ostapenko  * SPDX-License-Identifier: BSD-2-Clause
38aaffd78SIgor Ostapenko  *
48aaffd78SIgor Ostapenko  * Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
58aaffd78SIgor Ostapenko  *
68aaffd78SIgor Ostapenko  * Redistribution and use in source and binary forms, with or without
78aaffd78SIgor Ostapenko  * modification, are permitted provided that the following conditions
88aaffd78SIgor Ostapenko  * are met:
98aaffd78SIgor Ostapenko  * 1. Redistributions of source code must retain the above copyright
108aaffd78SIgor Ostapenko  *    notice, this list of conditions and the following disclaimer.
118aaffd78SIgor Ostapenko  * 2. Redistributions in binary form must reproduce the above copyright
128aaffd78SIgor Ostapenko  *    notice, this list of conditions and the following disclaimer in the
138aaffd78SIgor Ostapenko  *    documentation and/or other materials provided with the distribution.
148aaffd78SIgor Ostapenko  *
158aaffd78SIgor Ostapenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
168aaffd78SIgor Ostapenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
178aaffd78SIgor Ostapenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
188aaffd78SIgor Ostapenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
198aaffd78SIgor Ostapenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
208aaffd78SIgor Ostapenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
218aaffd78SIgor Ostapenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
228aaffd78SIgor Ostapenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
238aaffd78SIgor Ostapenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
248aaffd78SIgor Ostapenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
258aaffd78SIgor Ostapenko  * SUCH DAMAGE.
268aaffd78SIgor Ostapenko  */
278aaffd78SIgor Ostapenko 
288aaffd78SIgor Ostapenko #include "opt_inet.h"
298aaffd78SIgor Ostapenko #include "opt_inet6.h"
308aaffd78SIgor Ostapenko 
318aaffd78SIgor Ostapenko #include <sys/param.h>
328aaffd78SIgor Ostapenko #include <sys/kernel.h>
338aaffd78SIgor Ostapenko #include <sys/mbuf.h>
348aaffd78SIgor Ostapenko #include <sys/module.h>
358aaffd78SIgor Ostapenko #include <sys/socket.h>
368aaffd78SIgor Ostapenko #include <sys/sysctl.h>
378aaffd78SIgor Ostapenko 
388aaffd78SIgor Ostapenko #include <net/if.h>
398aaffd78SIgor Ostapenko #include <net/if_var.h>
408aaffd78SIgor Ostapenko #include <net/vnet.h>
418aaffd78SIgor Ostapenko #include <net/pfil.h>
428aaffd78SIgor Ostapenko 
439f146a81SIgor Ostapenko static int validate_rules(const char *);
449f146a81SIgor Ostapenko 
458aaffd78SIgor Ostapenko /*
468aaffd78SIgor Ostapenko  * Separate sysctl sub-tree
478aaffd78SIgor Ostapenko  */
488aaffd78SIgor Ostapenko 
498aaffd78SIgor Ostapenko SYSCTL_NODE(_net, OID_AUTO, dummymbuf, 0, NULL,
508aaffd78SIgor Ostapenko     "Dummy mbuf sysctl");
518aaffd78SIgor Ostapenko 
528aaffd78SIgor Ostapenko /*
538aaffd78SIgor Ostapenko  * Rules
548aaffd78SIgor Ostapenko  */
558aaffd78SIgor Ostapenko 
568aaffd78SIgor Ostapenko static MALLOC_DEFINE(M_DUMMYMBUF_RULES, "dummymbuf_rules",
578aaffd78SIgor Ostapenko     "dummymbuf rules string buffer");
588aaffd78SIgor Ostapenko 
598aaffd78SIgor Ostapenko #define RULES_MAXLEN		1024
608aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(char *,	dmb_rules) = NULL;
618aaffd78SIgor Ostapenko #define V_dmb_rules		VNET(dmb_rules)
628aaffd78SIgor Ostapenko 
638aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(struct sx,	dmb_rules_lock);
648aaffd78SIgor Ostapenko #define V_dmb_rules_lock	VNET(dmb_rules_lock)
658aaffd78SIgor Ostapenko 
668aaffd78SIgor Ostapenko #define DMB_RULES_SLOCK()	sx_slock(&V_dmb_rules_lock)
678aaffd78SIgor Ostapenko #define DMB_RULES_SUNLOCK()	sx_sunlock(&V_dmb_rules_lock)
688aaffd78SIgor Ostapenko #define DMB_RULES_XLOCK()	sx_xlock(&V_dmb_rules_lock)
698aaffd78SIgor Ostapenko #define DMB_RULES_XUNLOCK()	sx_xunlock(&V_dmb_rules_lock)
709f146a81SIgor Ostapenko #define DMB_RULES_LOCK_ASSERT()	sx_assert(&V_dmb_rules_lock, SA_LOCKED)
718aaffd78SIgor Ostapenko 
728aaffd78SIgor Ostapenko static int
738aaffd78SIgor Ostapenko dmb_sysctl_handle_rules(SYSCTL_HANDLER_ARGS)
748aaffd78SIgor Ostapenko {
758aaffd78SIgor Ostapenko 	int error = 0;
768aaffd78SIgor Ostapenko 	char empty = '\0';
778aaffd78SIgor Ostapenko 	char **rulesp = (char **)arg1;
788aaffd78SIgor Ostapenko 
798aaffd78SIgor Ostapenko 	if (req->newptr == NULL) {
8061295e09SMark Johnston 		/* read only */
818aaffd78SIgor Ostapenko 		DMB_RULES_SLOCK();
828aaffd78SIgor Ostapenko 		arg1 = *rulesp;
838aaffd78SIgor Ostapenko 		if (arg1 == NULL) {
848aaffd78SIgor Ostapenko 			arg1 = &empty;
858aaffd78SIgor Ostapenko 			arg2 = 0;
868aaffd78SIgor Ostapenko 		}
878aaffd78SIgor Ostapenko 		error = sysctl_handle_string(oidp, arg1, arg2, req);
888aaffd78SIgor Ostapenko 		DMB_RULES_SUNLOCK();
898aaffd78SIgor Ostapenko 	} else {
9061295e09SMark Johnston 		/* read and write */
918aaffd78SIgor Ostapenko 		DMB_RULES_XLOCK();
929f146a81SIgor Ostapenko 		arg1 = malloc(arg2, M_DUMMYMBUF_RULES, M_WAITOK | M_ZERO);
938aaffd78SIgor Ostapenko 		error = sysctl_handle_string(oidp, arg1, arg2, req);
949f146a81SIgor Ostapenko 		if (error == 0 && (error = validate_rules(arg1)) == 0) {
959f146a81SIgor Ostapenko 			free(*rulesp, M_DUMMYMBUF_RULES);
969f146a81SIgor Ostapenko 			*rulesp = arg1;
979f146a81SIgor Ostapenko 			arg1 = NULL;
989f146a81SIgor Ostapenko 		}
999f146a81SIgor Ostapenko 		free(arg1, M_DUMMYMBUF_RULES);
1008aaffd78SIgor Ostapenko 		DMB_RULES_XUNLOCK();
1018aaffd78SIgor Ostapenko 	}
1028aaffd78SIgor Ostapenko 
1038aaffd78SIgor Ostapenko 	return (error);
1048aaffd78SIgor Ostapenko }
1058aaffd78SIgor Ostapenko 
1068aaffd78SIgor Ostapenko SYSCTL_PROC(_net_dummymbuf, OID_AUTO, rules,
1078aaffd78SIgor Ostapenko     CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW | CTLFLAG_VNET,
1088aaffd78SIgor Ostapenko     &VNET_NAME(dmb_rules), RULES_MAXLEN, dmb_sysctl_handle_rules, "A",
10961295e09SMark Johnston     "{inet|inet6|ethernet} {in|out} <ifname> <opname>[ <opargs>]; ...;");
1108aaffd78SIgor Ostapenko 
1118aaffd78SIgor Ostapenko /*
1128aaffd78SIgor Ostapenko  * Statistics
1138aaffd78SIgor Ostapenko  */
1148aaffd78SIgor Ostapenko 
1158aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(counter_u64_t,	dmb_hits);
1168aaffd78SIgor Ostapenko #define V_dmb_hits			VNET(dmb_hits)
1178aaffd78SIgor Ostapenko SYSCTL_PROC(_net_dummymbuf, OID_AUTO, hits,
1188aaffd78SIgor Ostapenko     CTLTYPE_U64 | CTLFLAG_MPSAFE | CTLFLAG_STATS | CTLFLAG_RW | CTLFLAG_VNET,
1198aaffd78SIgor Ostapenko     &VNET_NAME(dmb_hits), 0, sysctl_handle_counter_u64,
1208aaffd78SIgor Ostapenko     "QU", "Number of times a rule has been applied");
1218aaffd78SIgor Ostapenko 
1228aaffd78SIgor Ostapenko /*
1238aaffd78SIgor Ostapenko  * pfil(9) context
1248aaffd78SIgor Ostapenko  */
1258aaffd78SIgor Ostapenko 
126aa72c5baSGleb Smirnoff #ifdef INET
1278aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(pfil_hook_t,		dmb_pfil_inet_hook);
1288aaffd78SIgor Ostapenko #define V_dmb_pfil_inet_hook		VNET(dmb_pfil_inet_hook)
129aa72c5baSGleb Smirnoff #endif
1308aaffd78SIgor Ostapenko 
131aa72c5baSGleb Smirnoff #ifdef INET6
1328aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(pfil_hook_t,		dmb_pfil_inet6_hook);
1338aaffd78SIgor Ostapenko #define V_dmb_pfil_inet6_hook		VNET(dmb_pfil_inet6_hook)
134aa72c5baSGleb Smirnoff #endif
1358aaffd78SIgor Ostapenko 
1368aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(pfil_hook_t,		dmb_pfil_ethernet_hook);
1378aaffd78SIgor Ostapenko #define V_dmb_pfil_ethernet_hook	VNET(dmb_pfil_ethernet_hook)
1388aaffd78SIgor Ostapenko 
1398aaffd78SIgor Ostapenko /*
1408aaffd78SIgor Ostapenko  * Logging
1418aaffd78SIgor Ostapenko  */
1428aaffd78SIgor Ostapenko 
1439f146a81SIgor Ostapenko #define FEEDBACK_RULE(rule, msg)					\
1449f146a81SIgor Ostapenko 	printf("dummymbuf: %s: %.*s\n",					\
1459f146a81SIgor Ostapenko 	    (msg),							\
1469f146a81SIgor Ostapenko 	    (rule).syntax_len, (rule).syntax_begin			\
1479f146a81SIgor Ostapenko 	)
1489f146a81SIgor Ostapenko 
1499f146a81SIgor Ostapenko #define FEEDBACK_PFIL(pfil_type, pfil_flags, ifp, rule, msg)		\
1508aaffd78SIgor Ostapenko 	printf("dummymbuf: %s %b %s: %s: %.*s\n",			\
151*6bd8d855SIgor Ostapenko 	    ((pfil_type) == PFIL_TYPE_IP4 ?	 "PFIL_TYPE_IP4" :	\
152*6bd8d855SIgor Ostapenko 	     (pfil_type) == PFIL_TYPE_IP6 ?	 "PFIL_TYPE_IP6" :	\
153*6bd8d855SIgor Ostapenko 	     (pfil_type) == PFIL_TYPE_ETHERNET ? "PFIL_TYPE_ETHERNET" :	\
1548aaffd78SIgor Ostapenko 						 "PFIL_TYPE_UNKNOWN"),	\
1558aaffd78SIgor Ostapenko 	    (pfil_flags), "\20\21PFIL_IN\22PFIL_OUT",			\
1568aaffd78SIgor Ostapenko 	    (ifp)->if_xname,						\
1578aaffd78SIgor Ostapenko 	    (msg),							\
1588aaffd78SIgor Ostapenko 	    (rule).syntax_len, (rule).syntax_begin			\
1598aaffd78SIgor Ostapenko 	)
1608aaffd78SIgor Ostapenko 
1618aaffd78SIgor Ostapenko /*
1628aaffd78SIgor Ostapenko  * Internals
1638aaffd78SIgor Ostapenko  */
1648aaffd78SIgor Ostapenko 
1658aaffd78SIgor Ostapenko struct rule;
1668aaffd78SIgor Ostapenko typedef struct mbuf * (*op_t)(struct mbuf *, struct rule *);
1678aaffd78SIgor Ostapenko struct rule {
1688aaffd78SIgor Ostapenko 	const char	*syntax_begin;
1698aaffd78SIgor Ostapenko 	int		 syntax_len;
1708aaffd78SIgor Ostapenko 	int		 pfil_type;
1718aaffd78SIgor Ostapenko 	int		 pfil_dir;
1728aaffd78SIgor Ostapenko 	char		 ifname[IFNAMSIZ];
1738aaffd78SIgor Ostapenko 	op_t		 op;
1748aaffd78SIgor Ostapenko 	const char	*opargs;
1758aaffd78SIgor Ostapenko };
1768aaffd78SIgor Ostapenko 
1778aaffd78SIgor Ostapenko static struct mbuf *
1788aaffd78SIgor Ostapenko dmb_m_pull_head(struct mbuf *m, struct rule *rule)
1798aaffd78SIgor Ostapenko {
1808aaffd78SIgor Ostapenko 	struct mbuf *n;
1818aaffd78SIgor Ostapenko 	int count;
1828aaffd78SIgor Ostapenko 
1838aaffd78SIgor Ostapenko 	count = (int)strtol(rule->opargs, NULL, 10);
1848aaffd78SIgor Ostapenko 	if (count < 0 || count > MCLBYTES)
1858aaffd78SIgor Ostapenko 		goto bad;
1868aaffd78SIgor Ostapenko 
1878aaffd78SIgor Ostapenko 	if (!(m->m_flags & M_PKTHDR))
1888aaffd78SIgor Ostapenko 		goto bad;
1898aaffd78SIgor Ostapenko 	if (m->m_pkthdr.len <= 0)
1908aaffd78SIgor Ostapenko 		return (m);
1918aaffd78SIgor Ostapenko 	if (count > m->m_pkthdr.len)
1928aaffd78SIgor Ostapenko 		count = m->m_pkthdr.len;
1938aaffd78SIgor Ostapenko 
1948aaffd78SIgor Ostapenko 	if ((n = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR)) == NULL)
1958aaffd78SIgor Ostapenko 		goto bad;
1968aaffd78SIgor Ostapenko 
1978aaffd78SIgor Ostapenko 	m_move_pkthdr(n, m);
1988aaffd78SIgor Ostapenko 	m_copydata(m, 0, count, n->m_ext.ext_buf);
1998aaffd78SIgor Ostapenko 	n->m_len = count;
2008aaffd78SIgor Ostapenko 
2018aaffd78SIgor Ostapenko 	m_adj(m, count);
2028aaffd78SIgor Ostapenko 	n->m_next = m;
2038aaffd78SIgor Ostapenko 
2048aaffd78SIgor Ostapenko 	return (n);
2058aaffd78SIgor Ostapenko 
2068aaffd78SIgor Ostapenko bad:
2078aaffd78SIgor Ostapenko 	m_freem(m);
2088aaffd78SIgor Ostapenko 	return (NULL);
2098aaffd78SIgor Ostapenko }
2108aaffd78SIgor Ostapenko 
2118aaffd78SIgor Ostapenko static bool
2129f146a81SIgor Ostapenko read_rule(const char **cur, struct rule *rule, bool *eof)
2138aaffd78SIgor Ostapenko {
214*6bd8d855SIgor Ostapenko 	/* {inet|inet6|ethernet} {in|out} <ifname> <opname>[ <opargs>]; */
2158aaffd78SIgor Ostapenko 
2168aaffd78SIgor Ostapenko 	rule->syntax_begin = NULL;
2178aaffd78SIgor Ostapenko 	rule->syntax_len = 0;
2188aaffd78SIgor Ostapenko 
2198aaffd78SIgor Ostapenko 	if (*cur == NULL)
2208aaffd78SIgor Ostapenko 		return (false);
2218aaffd78SIgor Ostapenko 
222*6bd8d855SIgor Ostapenko 	/* syntax_begin */
2238aaffd78SIgor Ostapenko 	while (**cur == ' ')
2248aaffd78SIgor Ostapenko 		(*cur)++;
2258aaffd78SIgor Ostapenko 	rule->syntax_begin = *cur;
2268aaffd78SIgor Ostapenko 
227*6bd8d855SIgor Ostapenko 	/* syntax_len */
2288aaffd78SIgor Ostapenko 	char *delim = strchr(*cur, ';');
2298aaffd78SIgor Ostapenko 	if (delim == NULL)
2308aaffd78SIgor Ostapenko 		return (false);
2318aaffd78SIgor Ostapenko 	rule->syntax_len = (int)(delim - *cur + 1);
2328aaffd78SIgor Ostapenko 
233*6bd8d855SIgor Ostapenko 	/* pfil_type */
2348aaffd78SIgor Ostapenko 	if (strstr(*cur, "inet6") == *cur) {
2358aaffd78SIgor Ostapenko 		rule->pfil_type = PFIL_TYPE_IP6;
2368aaffd78SIgor Ostapenko 		*cur += strlen("inet6");
2378aaffd78SIgor Ostapenko 	} else if (strstr(*cur, "inet") == *cur) {
2388aaffd78SIgor Ostapenko 		rule->pfil_type = PFIL_TYPE_IP4;
2398aaffd78SIgor Ostapenko 		*cur += strlen("inet");
2408aaffd78SIgor Ostapenko 	} else if (strstr(*cur, "ethernet")) {
2418aaffd78SIgor Ostapenko 		rule->pfil_type = PFIL_TYPE_ETHERNET;
2428aaffd78SIgor Ostapenko 		*cur += strlen("ethernet");
2438aaffd78SIgor Ostapenko 	} else {
2448aaffd78SIgor Ostapenko 		return (false);
2458aaffd78SIgor Ostapenko 	}
2468aaffd78SIgor Ostapenko 	while (**cur == ' ')
2478aaffd78SIgor Ostapenko 		(*cur)++;
2488aaffd78SIgor Ostapenko 
249*6bd8d855SIgor Ostapenko 	/* pfil_dir */
2508aaffd78SIgor Ostapenko 	if (strstr(*cur, "in") == *cur) {
2518aaffd78SIgor Ostapenko 		rule->pfil_dir = PFIL_IN;
2528aaffd78SIgor Ostapenko 		*cur += strlen("in");
2538aaffd78SIgor Ostapenko 	} else if (strstr(*cur, "out") == *cur) {
2548aaffd78SIgor Ostapenko 		rule->pfil_dir = PFIL_OUT;
2558aaffd78SIgor Ostapenko 		*cur += strlen("out");
2568aaffd78SIgor Ostapenko 	} else {
2578aaffd78SIgor Ostapenko 		return (false);
2588aaffd78SIgor Ostapenko 	}
2598aaffd78SIgor Ostapenko 	while (**cur == ' ')
2608aaffd78SIgor Ostapenko 		(*cur)++;
2618aaffd78SIgor Ostapenko 
262*6bd8d855SIgor Ostapenko 	/* ifname */
2638aaffd78SIgor Ostapenko 	char *sp = strchr(*cur, ' ');
2648aaffd78SIgor Ostapenko 	if (sp == NULL || sp > delim)
2658aaffd78SIgor Ostapenko 		return (false);
2668aaffd78SIgor Ostapenko 	size_t len = sp - *cur;
2678aaffd78SIgor Ostapenko 	if (len >= sizeof(rule->ifname))
2688aaffd78SIgor Ostapenko 		return (false);
2698aaffd78SIgor Ostapenko 	strncpy(rule->ifname, *cur, len);
2708aaffd78SIgor Ostapenko 	rule->ifname[len] = 0;
2718aaffd78SIgor Ostapenko 	*cur = sp;
2728aaffd78SIgor Ostapenko 	while (**cur == ' ')
2738aaffd78SIgor Ostapenko 		(*cur)++;
2748aaffd78SIgor Ostapenko 
275*6bd8d855SIgor Ostapenko 	/* opname */
2768aaffd78SIgor Ostapenko 	if (strstr(*cur, "pull-head") == *cur) {
2778aaffd78SIgor Ostapenko 		rule->op = dmb_m_pull_head;
2788aaffd78SIgor Ostapenko 		*cur += strlen("pull-head");
2798aaffd78SIgor Ostapenko 	} else {
2808aaffd78SIgor Ostapenko 		return (false);
2818aaffd78SIgor Ostapenko 	}
2828aaffd78SIgor Ostapenko 	while (**cur == ' ')
2838aaffd78SIgor Ostapenko 		(*cur)++;
2848aaffd78SIgor Ostapenko 
285*6bd8d855SIgor Ostapenko 	/* opargs */
2868aaffd78SIgor Ostapenko 	if (*cur > delim)
2878aaffd78SIgor Ostapenko 		return (false);
2888aaffd78SIgor Ostapenko 	rule->opargs = *cur;
2898aaffd78SIgor Ostapenko 
290*6bd8d855SIgor Ostapenko 	/* the next rule & eof */
2918aaffd78SIgor Ostapenko 	*cur = delim + 1;
2929f146a81SIgor Ostapenko 	while (**cur == ' ')
2939f146a81SIgor Ostapenko 		(*cur)++;
2949f146a81SIgor Ostapenko 	*eof = strlen(*cur) == 0;
2958aaffd78SIgor Ostapenko 
2968aaffd78SIgor Ostapenko 	return (true);
2978aaffd78SIgor Ostapenko }
2988aaffd78SIgor Ostapenko 
2999f146a81SIgor Ostapenko static int
3009f146a81SIgor Ostapenko validate_rules(const char *rules)
3019f146a81SIgor Ostapenko {
3029f146a81SIgor Ostapenko 	const char *cursor = rules;
3039f146a81SIgor Ostapenko 	bool parsed;
3049f146a81SIgor Ostapenko 	struct rule rule;
3059f146a81SIgor Ostapenko 	bool eof = false;
3069f146a81SIgor Ostapenko 
3079f146a81SIgor Ostapenko 	DMB_RULES_LOCK_ASSERT();
3089f146a81SIgor Ostapenko 
3099f146a81SIgor Ostapenko 	while (!eof && (parsed = read_rule(&cursor, &rule, &eof))) {
3109f146a81SIgor Ostapenko 		/* noop */
3119f146a81SIgor Ostapenko 	}
3129f146a81SIgor Ostapenko 
3139f146a81SIgor Ostapenko 	if (!parsed) {
3149f146a81SIgor Ostapenko 		FEEDBACK_RULE(rule, "rule parsing failed");
3159f146a81SIgor Ostapenko 		return (EINVAL);
3169f146a81SIgor Ostapenko 	}
3179f146a81SIgor Ostapenko 
3189f146a81SIgor Ostapenko 	return (0);
3199f146a81SIgor Ostapenko }
3209f146a81SIgor Ostapenko 
3218aaffd78SIgor Ostapenko static pfil_return_t
3228aaffd78SIgor Ostapenko dmb_pfil_mbuf_chk(int pfil_type, struct mbuf **mp, struct ifnet *ifp,
3238aaffd78SIgor Ostapenko     int flags, void *ruleset, void *unused)
3248aaffd78SIgor Ostapenko {
3258aaffd78SIgor Ostapenko 	struct mbuf *m = *mp;
3268aaffd78SIgor Ostapenko 	const char *cursor;
3278aaffd78SIgor Ostapenko 	bool parsed;
3288aaffd78SIgor Ostapenko 	struct rule rule;
3299f146a81SIgor Ostapenko 	bool eof = false;
3308aaffd78SIgor Ostapenko 
3318aaffd78SIgor Ostapenko 	DMB_RULES_SLOCK();
3328aaffd78SIgor Ostapenko 	cursor = V_dmb_rules;
3339f146a81SIgor Ostapenko 	while (!eof && (parsed = read_rule(&cursor, &rule, &eof))) {
3348aaffd78SIgor Ostapenko 		if (rule.pfil_type == pfil_type &&
3358aaffd78SIgor Ostapenko 		    rule.pfil_dir == (flags & rule.pfil_dir)  &&
3368aaffd78SIgor Ostapenko 		    strcmp(rule.ifname, ifp->if_xname) == 0) {
3378aaffd78SIgor Ostapenko 			m = rule.op(m, &rule);
3388aaffd78SIgor Ostapenko 			if (m == NULL) {
3399f146a81SIgor Ostapenko 				FEEDBACK_PFIL(pfil_type, flags, ifp, rule,
3408aaffd78SIgor Ostapenko 				    "mbuf operation failed");
3418aaffd78SIgor Ostapenko 				break;
3428aaffd78SIgor Ostapenko 			}
3438aaffd78SIgor Ostapenko 			counter_u64_add(V_dmb_hits, 1);
3448aaffd78SIgor Ostapenko 		}
3458aaffd78SIgor Ostapenko 	}
3468aaffd78SIgor Ostapenko 	if (!parsed) {
3479f146a81SIgor Ostapenko 		FEEDBACK_PFIL(pfil_type, flags, ifp, rule,
3489f146a81SIgor Ostapenko 		    "rule parsing failed");
3498aaffd78SIgor Ostapenko 		m_freem(m);
3508aaffd78SIgor Ostapenko 		m = NULL;
3518aaffd78SIgor Ostapenko 	}
3528aaffd78SIgor Ostapenko 	DMB_RULES_SUNLOCK();
3538aaffd78SIgor Ostapenko 
3548aaffd78SIgor Ostapenko 	if (m == NULL) {
3558aaffd78SIgor Ostapenko 		*mp = NULL;
3568aaffd78SIgor Ostapenko 		return (PFIL_DROPPED);
3578aaffd78SIgor Ostapenko 	}
3588aaffd78SIgor Ostapenko 	if (m != *mp) {
3598aaffd78SIgor Ostapenko 		*mp = m;
3608aaffd78SIgor Ostapenko 		return (PFIL_REALLOCED);
3618aaffd78SIgor Ostapenko 	}
3628aaffd78SIgor Ostapenko 
3638aaffd78SIgor Ostapenko 	return (PFIL_PASS);
3648aaffd78SIgor Ostapenko }
3658aaffd78SIgor Ostapenko 
366aa72c5baSGleb Smirnoff #ifdef INET
3678aaffd78SIgor Ostapenko static pfil_return_t
3688aaffd78SIgor Ostapenko dmb_pfil_inet_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
3698aaffd78SIgor Ostapenko     void *ruleset, struct inpcb *inp)
3708aaffd78SIgor Ostapenko {
3718aaffd78SIgor Ostapenko 	return (dmb_pfil_mbuf_chk(PFIL_TYPE_IP4, mp, ifp, flags,
3728aaffd78SIgor Ostapenko 	    ruleset, inp));
3738aaffd78SIgor Ostapenko }
374aa72c5baSGleb Smirnoff #endif
3758aaffd78SIgor Ostapenko 
376aa72c5baSGleb Smirnoff #ifdef INET6
3778aaffd78SIgor Ostapenko static pfil_return_t
3788aaffd78SIgor Ostapenko dmb_pfil_inet6_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
3798aaffd78SIgor Ostapenko     void *ruleset, struct inpcb *inp)
3808aaffd78SIgor Ostapenko {
3818aaffd78SIgor Ostapenko 	return (dmb_pfil_mbuf_chk(PFIL_TYPE_IP6, mp, ifp, flags,
3828aaffd78SIgor Ostapenko 	    ruleset, inp));
3838aaffd78SIgor Ostapenko }
384aa72c5baSGleb Smirnoff #endif
3858aaffd78SIgor Ostapenko 
3868aaffd78SIgor Ostapenko static pfil_return_t
3878aaffd78SIgor Ostapenko dmb_pfil_ethernet_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
3888aaffd78SIgor Ostapenko     void *ruleset, struct inpcb *inp)
3898aaffd78SIgor Ostapenko {
3908aaffd78SIgor Ostapenko 	return (dmb_pfil_mbuf_chk(PFIL_TYPE_ETHERNET, mp, ifp, flags,
3918aaffd78SIgor Ostapenko 	    ruleset, inp));
3928aaffd78SIgor Ostapenko }
3938aaffd78SIgor Ostapenko 
3948aaffd78SIgor Ostapenko static void
3958aaffd78SIgor Ostapenko dmb_pfil_init(void)
3968aaffd78SIgor Ostapenko {
3978aaffd78SIgor Ostapenko 	struct pfil_hook_args pha = {
3988aaffd78SIgor Ostapenko 		.pa_version = PFIL_VERSION,
3998aaffd78SIgor Ostapenko 		.pa_modname = "dummymbuf",
4008aaffd78SIgor Ostapenko 		.pa_flags = PFIL_IN | PFIL_OUT,
4018aaffd78SIgor Ostapenko 	};
4028aaffd78SIgor Ostapenko 
4038aaffd78SIgor Ostapenko #ifdef INET
4048aaffd78SIgor Ostapenko 	pha.pa_type = PFIL_TYPE_IP4;
4058aaffd78SIgor Ostapenko 	pha.pa_mbuf_chk = dmb_pfil_inet_mbuf_chk;
4068aaffd78SIgor Ostapenko 	pha.pa_rulname = "inet";
4078aaffd78SIgor Ostapenko 	V_dmb_pfil_inet_hook = pfil_add_hook(&pha);
4088aaffd78SIgor Ostapenko #endif
4098aaffd78SIgor Ostapenko 
4108aaffd78SIgor Ostapenko #ifdef INET6
4118aaffd78SIgor Ostapenko 	pha.pa_type = PFIL_TYPE_IP6;
4128aaffd78SIgor Ostapenko 	pha.pa_mbuf_chk = dmb_pfil_inet6_mbuf_chk;
4138aaffd78SIgor Ostapenko 	pha.pa_rulname = "inet6";
4148aaffd78SIgor Ostapenko 	V_dmb_pfil_inet6_hook = pfil_add_hook(&pha);
4158aaffd78SIgor Ostapenko #endif
4168aaffd78SIgor Ostapenko 
4178aaffd78SIgor Ostapenko 	pha.pa_type = PFIL_TYPE_ETHERNET;
4188aaffd78SIgor Ostapenko 	pha.pa_mbuf_chk = dmb_pfil_ethernet_mbuf_chk;
4198aaffd78SIgor Ostapenko 	pha.pa_rulname = "ethernet";
4208aaffd78SIgor Ostapenko 	V_dmb_pfil_ethernet_hook = pfil_add_hook(&pha);
4218aaffd78SIgor Ostapenko }
4228aaffd78SIgor Ostapenko 
4238aaffd78SIgor Ostapenko static void
4248aaffd78SIgor Ostapenko dmb_pfil_uninit(void)
4258aaffd78SIgor Ostapenko {
4268aaffd78SIgor Ostapenko #ifdef INET
4278aaffd78SIgor Ostapenko 	pfil_remove_hook(V_dmb_pfil_inet_hook);
4288aaffd78SIgor Ostapenko #endif
4298aaffd78SIgor Ostapenko 
4308aaffd78SIgor Ostapenko #ifdef INET6
4318aaffd78SIgor Ostapenko 	pfil_remove_hook(V_dmb_pfil_inet6_hook);
4328aaffd78SIgor Ostapenko #endif
4338aaffd78SIgor Ostapenko 
4348aaffd78SIgor Ostapenko 	pfil_remove_hook(V_dmb_pfil_ethernet_hook);
4358aaffd78SIgor Ostapenko }
4368aaffd78SIgor Ostapenko 
4378aaffd78SIgor Ostapenko static void
4388aaffd78SIgor Ostapenko vnet_dmb_init(void *unused __unused)
4398aaffd78SIgor Ostapenko {
4408aaffd78SIgor Ostapenko 	sx_init(&V_dmb_rules_lock, "dummymbuf rules");
4418aaffd78SIgor Ostapenko 	V_dmb_hits = counter_u64_alloc(M_WAITOK);
4428aaffd78SIgor Ostapenko 	dmb_pfil_init();
4438aaffd78SIgor Ostapenko }
4448aaffd78SIgor Ostapenko VNET_SYSINIT(vnet_dmb_init, SI_SUB_PROTO_PFIL, SI_ORDER_ANY,
4458aaffd78SIgor Ostapenko     vnet_dmb_init, NULL);
4468aaffd78SIgor Ostapenko 
4478aaffd78SIgor Ostapenko static void
4488aaffd78SIgor Ostapenko vnet_dmb_uninit(void *unused __unused)
4498aaffd78SIgor Ostapenko {
4508aaffd78SIgor Ostapenko 	dmb_pfil_uninit();
4518aaffd78SIgor Ostapenko 	counter_u64_free(V_dmb_hits);
4528aaffd78SIgor Ostapenko 	sx_destroy(&V_dmb_rules_lock);
4538aaffd78SIgor Ostapenko 	free(V_dmb_rules, M_DUMMYMBUF_RULES);
4548aaffd78SIgor Ostapenko }
4558aaffd78SIgor Ostapenko VNET_SYSUNINIT(vnet_dmb_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_ANY,
4568aaffd78SIgor Ostapenko     vnet_dmb_uninit, NULL);
4578aaffd78SIgor Ostapenko 
4588aaffd78SIgor Ostapenko static int
4598aaffd78SIgor Ostapenko dmb_modevent(module_t mod __unused, int event, void *arg __unused)
4608aaffd78SIgor Ostapenko {
4618aaffd78SIgor Ostapenko 	int error = 0;
4628aaffd78SIgor Ostapenko 
4638aaffd78SIgor Ostapenko 	switch (event) {
4648aaffd78SIgor Ostapenko 	case MOD_LOAD:
4658aaffd78SIgor Ostapenko 	case MOD_UNLOAD:
4668aaffd78SIgor Ostapenko 		break;
4678aaffd78SIgor Ostapenko 	default:
4688aaffd78SIgor Ostapenko 		error = EOPNOTSUPP;
4698aaffd78SIgor Ostapenko 		break;
4708aaffd78SIgor Ostapenko 	}
4718aaffd78SIgor Ostapenko 
4728aaffd78SIgor Ostapenko 	return (error);
4738aaffd78SIgor Ostapenko }
4748aaffd78SIgor Ostapenko 
4758aaffd78SIgor Ostapenko static moduledata_t dmb_mod = {
4768aaffd78SIgor Ostapenko 	"dummymbuf",
4778aaffd78SIgor Ostapenko 	dmb_modevent,
4788aaffd78SIgor Ostapenko 	NULL
4798aaffd78SIgor Ostapenko };
4808aaffd78SIgor Ostapenko 
4818aaffd78SIgor Ostapenko DECLARE_MODULE(dummymbuf, dmb_mod, SI_SUB_PROTO_PFIL, SI_ORDER_ANY);
4828aaffd78SIgor Ostapenko MODULE_VERSION(dummymbuf, 1);
483