xref: /freebsd/sys/net/dummymbuf.c (revision 76e00c722bee123f902febce7b637ea7afa5e364)
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>
403f2eb1acSJustin Hibbits #include <net/if_private.h>
418aaffd78SIgor Ostapenko #include <net/vnet.h>
428aaffd78SIgor Ostapenko #include <net/pfil.h>
438aaffd78SIgor Ostapenko 
449f146a81SIgor Ostapenko static int validate_rules(const char *);
459f146a81SIgor Ostapenko 
468aaffd78SIgor Ostapenko /*
478aaffd78SIgor Ostapenko  * Separate sysctl sub-tree
488aaffd78SIgor Ostapenko  */
498aaffd78SIgor Ostapenko 
508aaffd78SIgor Ostapenko SYSCTL_NODE(_net, OID_AUTO, dummymbuf, 0, NULL,
518aaffd78SIgor Ostapenko     "Dummy mbuf sysctl");
528aaffd78SIgor Ostapenko 
538aaffd78SIgor Ostapenko /*
548aaffd78SIgor Ostapenko  * Rules
558aaffd78SIgor Ostapenko  */
568aaffd78SIgor Ostapenko 
578aaffd78SIgor Ostapenko static MALLOC_DEFINE(M_DUMMYMBUF_RULES, "dummymbuf_rules",
588aaffd78SIgor Ostapenko     "dummymbuf rules string buffer");
598aaffd78SIgor Ostapenko 
608aaffd78SIgor Ostapenko #define RULES_MAXLEN		1024
618aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(char *,	dmb_rules) = NULL;
628aaffd78SIgor Ostapenko #define V_dmb_rules		VNET(dmb_rules)
638aaffd78SIgor Ostapenko 
648aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(struct sx,	dmb_rules_lock);
658aaffd78SIgor Ostapenko #define V_dmb_rules_lock	VNET(dmb_rules_lock)
668aaffd78SIgor Ostapenko 
678aaffd78SIgor Ostapenko #define DMB_RULES_SLOCK()	sx_slock(&V_dmb_rules_lock)
688aaffd78SIgor Ostapenko #define DMB_RULES_SUNLOCK()	sx_sunlock(&V_dmb_rules_lock)
698aaffd78SIgor Ostapenko #define DMB_RULES_XLOCK()	sx_xlock(&V_dmb_rules_lock)
708aaffd78SIgor Ostapenko #define DMB_RULES_XUNLOCK()	sx_xunlock(&V_dmb_rules_lock)
719f146a81SIgor Ostapenko #define DMB_RULES_LOCK_ASSERT()	sx_assert(&V_dmb_rules_lock, SA_LOCKED)
728aaffd78SIgor Ostapenko 
738aaffd78SIgor Ostapenko static int
748aaffd78SIgor Ostapenko dmb_sysctl_handle_rules(SYSCTL_HANDLER_ARGS)
758aaffd78SIgor Ostapenko {
768aaffd78SIgor Ostapenko 	int error = 0;
778aaffd78SIgor Ostapenko 	char empty = '\0';
788aaffd78SIgor Ostapenko 	char **rulesp = (char **)arg1;
798aaffd78SIgor Ostapenko 
808aaffd78SIgor Ostapenko 	if (req->newptr == NULL) {
8161295e09SMark Johnston 		/* read only */
828aaffd78SIgor Ostapenko 		DMB_RULES_SLOCK();
838aaffd78SIgor Ostapenko 		arg1 = *rulesp;
848aaffd78SIgor Ostapenko 		if (arg1 == NULL) {
858aaffd78SIgor Ostapenko 			arg1 = &empty;
868aaffd78SIgor Ostapenko 			arg2 = 0;
878aaffd78SIgor Ostapenko 		}
888aaffd78SIgor Ostapenko 		error = sysctl_handle_string(oidp, arg1, arg2, req);
898aaffd78SIgor Ostapenko 		DMB_RULES_SUNLOCK();
908aaffd78SIgor Ostapenko 	} else {
9161295e09SMark Johnston 		/* read and write */
928aaffd78SIgor Ostapenko 		DMB_RULES_XLOCK();
939f146a81SIgor Ostapenko 		arg1 = malloc(arg2, M_DUMMYMBUF_RULES, M_WAITOK | M_ZERO);
948aaffd78SIgor Ostapenko 		error = sysctl_handle_string(oidp, arg1, arg2, req);
959f146a81SIgor Ostapenko 		if (error == 0 && (error = validate_rules(arg1)) == 0) {
969f146a81SIgor Ostapenko 			free(*rulesp, M_DUMMYMBUF_RULES);
979f146a81SIgor Ostapenko 			*rulesp = arg1;
989f146a81SIgor Ostapenko 			arg1 = NULL;
999f146a81SIgor Ostapenko 		}
1009f146a81SIgor Ostapenko 		free(arg1, M_DUMMYMBUF_RULES);
1018aaffd78SIgor Ostapenko 		DMB_RULES_XUNLOCK();
1028aaffd78SIgor Ostapenko 	}
1038aaffd78SIgor Ostapenko 
1048aaffd78SIgor Ostapenko 	return (error);
1058aaffd78SIgor Ostapenko }
1068aaffd78SIgor Ostapenko 
1078aaffd78SIgor Ostapenko SYSCTL_PROC(_net_dummymbuf, OID_AUTO, rules,
1088aaffd78SIgor Ostapenko     CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW | CTLFLAG_VNET,
1098aaffd78SIgor Ostapenko     &VNET_NAME(dmb_rules), RULES_MAXLEN, dmb_sysctl_handle_rules, "A",
11061295e09SMark Johnston     "{inet|inet6|ethernet} {in|out} <ifname> <opname>[ <opargs>]; ...;");
1118aaffd78SIgor Ostapenko 
1128aaffd78SIgor Ostapenko /*
1138aaffd78SIgor Ostapenko  * Statistics
1148aaffd78SIgor Ostapenko  */
1158aaffd78SIgor Ostapenko 
1168aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(counter_u64_t,	dmb_hits);
1178aaffd78SIgor Ostapenko #define V_dmb_hits			VNET(dmb_hits)
1188aaffd78SIgor Ostapenko SYSCTL_PROC(_net_dummymbuf, OID_AUTO, hits,
1198aaffd78SIgor Ostapenko     CTLTYPE_U64 | CTLFLAG_MPSAFE | CTLFLAG_STATS | CTLFLAG_RW | CTLFLAG_VNET,
1208aaffd78SIgor Ostapenko     &VNET_NAME(dmb_hits), 0, sysctl_handle_counter_u64,
1218aaffd78SIgor Ostapenko     "QU", "Number of times a rule has been applied");
1228aaffd78SIgor Ostapenko 
1238aaffd78SIgor Ostapenko /*
1248aaffd78SIgor Ostapenko  * pfil(9) context
1258aaffd78SIgor Ostapenko  */
1268aaffd78SIgor Ostapenko 
127aa72c5baSGleb Smirnoff #ifdef INET
1288aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(pfil_hook_t,		dmb_pfil_inet_hook);
1298aaffd78SIgor Ostapenko #define V_dmb_pfil_inet_hook		VNET(dmb_pfil_inet_hook)
130aa72c5baSGleb Smirnoff #endif
1318aaffd78SIgor Ostapenko 
132aa72c5baSGleb Smirnoff #ifdef INET6
1338aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(pfil_hook_t,		dmb_pfil_inet6_hook);
1348aaffd78SIgor Ostapenko #define V_dmb_pfil_inet6_hook		VNET(dmb_pfil_inet6_hook)
135aa72c5baSGleb Smirnoff #endif
1368aaffd78SIgor Ostapenko 
1378aaffd78SIgor Ostapenko VNET_DEFINE_STATIC(pfil_hook_t,		dmb_pfil_ethernet_hook);
1388aaffd78SIgor Ostapenko #define V_dmb_pfil_ethernet_hook	VNET(dmb_pfil_ethernet_hook)
1398aaffd78SIgor Ostapenko 
1408aaffd78SIgor Ostapenko /*
1418aaffd78SIgor Ostapenko  * Logging
1428aaffd78SIgor Ostapenko  */
1438aaffd78SIgor Ostapenko 
1449f146a81SIgor Ostapenko #define FEEDBACK_RULE(rule, msg)					\
1459f146a81SIgor Ostapenko 	printf("dummymbuf: %s: %.*s\n",					\
1469f146a81SIgor Ostapenko 	    (msg),							\
1479f146a81SIgor Ostapenko 	    (rule).syntax_len, (rule).syntax_begin			\
1489f146a81SIgor Ostapenko 	)
1499f146a81SIgor Ostapenko 
1509f146a81SIgor Ostapenko #define FEEDBACK_PFIL(pfil_type, pfil_flags, ifp, rule, msg)		\
1518aaffd78SIgor Ostapenko 	printf("dummymbuf: %s %b %s: %s: %.*s\n",			\
1526bd8d855SIgor Ostapenko 	    ((pfil_type) == PFIL_TYPE_IP4 ?	 "PFIL_TYPE_IP4" :	\
1536bd8d855SIgor Ostapenko 	     (pfil_type) == PFIL_TYPE_IP6 ?	 "PFIL_TYPE_IP6" :	\
1546bd8d855SIgor Ostapenko 	     (pfil_type) == PFIL_TYPE_ETHERNET ? "PFIL_TYPE_ETHERNET" :	\
1558aaffd78SIgor Ostapenko 						 "PFIL_TYPE_UNKNOWN"),	\
1568aaffd78SIgor Ostapenko 	    (pfil_flags), "\20\21PFIL_IN\22PFIL_OUT",			\
1578aaffd78SIgor Ostapenko 	    (ifp)->if_xname,						\
1588aaffd78SIgor Ostapenko 	    (msg),							\
1598aaffd78SIgor Ostapenko 	    (rule).syntax_len, (rule).syntax_begin			\
1608aaffd78SIgor Ostapenko 	)
1618aaffd78SIgor Ostapenko 
1628aaffd78SIgor Ostapenko /*
1638aaffd78SIgor Ostapenko  * Internals
1648aaffd78SIgor Ostapenko  */
1658aaffd78SIgor Ostapenko 
1668aaffd78SIgor Ostapenko struct rule;
1678aaffd78SIgor Ostapenko typedef struct mbuf * (*op_t)(struct mbuf *, struct rule *);
1688aaffd78SIgor Ostapenko struct rule {
1698aaffd78SIgor Ostapenko 	const char	*syntax_begin;
1708aaffd78SIgor Ostapenko 	int		 syntax_len;
1718aaffd78SIgor Ostapenko 	int		 pfil_type;
1728aaffd78SIgor Ostapenko 	int		 pfil_dir;
1738aaffd78SIgor Ostapenko 	char		 ifname[IFNAMSIZ];
1748aaffd78SIgor Ostapenko 	op_t		 op;
1758aaffd78SIgor Ostapenko 	const char	*opargs;
1768aaffd78SIgor Ostapenko };
1778aaffd78SIgor Ostapenko 
1788aaffd78SIgor Ostapenko static struct mbuf *
1798aaffd78SIgor Ostapenko dmb_m_pull_head(struct mbuf *m, struct rule *rule)
1808aaffd78SIgor Ostapenko {
1818aaffd78SIgor Ostapenko 	struct mbuf *n;
1828aaffd78SIgor Ostapenko 	int count;
1838aaffd78SIgor Ostapenko 
1848aaffd78SIgor Ostapenko 	count = (int)strtol(rule->opargs, NULL, 10);
1858aaffd78SIgor Ostapenko 	if (count < 0 || count > MCLBYTES)
1868aaffd78SIgor Ostapenko 		goto bad;
1878aaffd78SIgor Ostapenko 
1888aaffd78SIgor Ostapenko 	if (!(m->m_flags & M_PKTHDR))
1898aaffd78SIgor Ostapenko 		goto bad;
1908aaffd78SIgor Ostapenko 	if (m->m_pkthdr.len <= 0)
1918aaffd78SIgor Ostapenko 		return (m);
1928aaffd78SIgor Ostapenko 	if (count > m->m_pkthdr.len)
1938aaffd78SIgor Ostapenko 		count = m->m_pkthdr.len;
1948aaffd78SIgor Ostapenko 
1958aaffd78SIgor Ostapenko 	if ((n = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR)) == NULL)
1968aaffd78SIgor Ostapenko 		goto bad;
1978aaffd78SIgor Ostapenko 
1988aaffd78SIgor Ostapenko 	m_move_pkthdr(n, m);
1998aaffd78SIgor Ostapenko 	m_copydata(m, 0, count, n->m_ext.ext_buf);
2008aaffd78SIgor Ostapenko 	n->m_len = count;
2018aaffd78SIgor Ostapenko 
2028aaffd78SIgor Ostapenko 	m_adj(m, count);
2038aaffd78SIgor Ostapenko 	n->m_next = m;
2048aaffd78SIgor Ostapenko 
2058aaffd78SIgor Ostapenko 	return (n);
2068aaffd78SIgor Ostapenko 
2078aaffd78SIgor Ostapenko bad:
2088aaffd78SIgor Ostapenko 	m_freem(m);
2098aaffd78SIgor Ostapenko 	return (NULL);
2108aaffd78SIgor Ostapenko }
2118aaffd78SIgor Ostapenko 
212*76e00c72SKristof Provost static struct mbuf *
213*76e00c72SKristof Provost dmb_m_enlarge(struct mbuf *m, struct rule *rule)
214*76e00c72SKristof Provost {
215*76e00c72SKristof Provost 	struct mbuf *n;
216*76e00c72SKristof Provost 	int size;
217*76e00c72SKristof Provost 
218*76e00c72SKristof Provost 	size = (int)strtol(rule->opargs, NULL, 10);
219*76e00c72SKristof Provost 	if (size < 0 || size > MJUM16BYTES)
220*76e00c72SKristof Provost 		goto bad;
221*76e00c72SKristof Provost 
222*76e00c72SKristof Provost 	if (!(m->m_flags & M_PKTHDR))
223*76e00c72SKristof Provost 		goto bad;
224*76e00c72SKristof Provost 	if (m->m_pkthdr.len <= 0)
225*76e00c72SKristof Provost 		return (m);
226*76e00c72SKristof Provost 
227*76e00c72SKristof Provost 	if ((n = m_get3(size, M_NOWAIT, MT_DATA, M_PKTHDR)) == NULL)
228*76e00c72SKristof Provost 		goto bad;
229*76e00c72SKristof Provost 
230*76e00c72SKristof Provost 	m_move_pkthdr(n, m);
231*76e00c72SKristof Provost 	m_copydata(m, 0, m->m_pkthdr.len, n->m_ext.ext_buf);
232*76e00c72SKristof Provost 	n->m_len = m->m_pkthdr.len;
233*76e00c72SKristof Provost 
234*76e00c72SKristof Provost 	n->m_next = m;
235*76e00c72SKristof Provost 
236*76e00c72SKristof Provost 	return (n);
237*76e00c72SKristof Provost 
238*76e00c72SKristof Provost bad:
239*76e00c72SKristof Provost 	m_freem(m);
240*76e00c72SKristof Provost 	return (NULL);
241*76e00c72SKristof Provost }
242*76e00c72SKristof Provost 
2438aaffd78SIgor Ostapenko static bool
2449f146a81SIgor Ostapenko read_rule(const char **cur, struct rule *rule, bool *eof)
2458aaffd78SIgor Ostapenko {
2466bd8d855SIgor Ostapenko 	/* {inet|inet6|ethernet} {in|out} <ifname> <opname>[ <opargs>]; */
2478aaffd78SIgor Ostapenko 
2488aaffd78SIgor Ostapenko 	rule->syntax_begin = NULL;
2498aaffd78SIgor Ostapenko 	rule->syntax_len = 0;
2508aaffd78SIgor Ostapenko 
2518aaffd78SIgor Ostapenko 	if (*cur == NULL)
2528aaffd78SIgor Ostapenko 		return (false);
2538aaffd78SIgor Ostapenko 
2546bd8d855SIgor Ostapenko 	/* syntax_begin */
2558aaffd78SIgor Ostapenko 	while (**cur == ' ')
2568aaffd78SIgor Ostapenko 		(*cur)++;
2578aaffd78SIgor Ostapenko 	rule->syntax_begin = *cur;
258dfcb8de5SIgor Ostapenko 	rule->syntax_len = strlen(rule->syntax_begin);
2598aaffd78SIgor Ostapenko 
2606bd8d855SIgor Ostapenko 	/* syntax_len */
2618aaffd78SIgor Ostapenko 	char *delim = strchr(*cur, ';');
2628aaffd78SIgor Ostapenko 	if (delim == NULL)
2638aaffd78SIgor Ostapenko 		return (false);
2648aaffd78SIgor Ostapenko 	rule->syntax_len = (int)(delim - *cur + 1);
2658aaffd78SIgor Ostapenko 
2666bd8d855SIgor Ostapenko 	/* pfil_type */
2678aaffd78SIgor Ostapenko 	if (strstr(*cur, "inet6") == *cur) {
2688aaffd78SIgor Ostapenko 		rule->pfil_type = PFIL_TYPE_IP6;
2698aaffd78SIgor Ostapenko 		*cur += strlen("inet6");
2708aaffd78SIgor Ostapenko 	} else if (strstr(*cur, "inet") == *cur) {
2718aaffd78SIgor Ostapenko 		rule->pfil_type = PFIL_TYPE_IP4;
2728aaffd78SIgor Ostapenko 		*cur += strlen("inet");
2738aaffd78SIgor Ostapenko 	} else if (strstr(*cur, "ethernet")) {
2748aaffd78SIgor Ostapenko 		rule->pfil_type = PFIL_TYPE_ETHERNET;
2758aaffd78SIgor Ostapenko 		*cur += strlen("ethernet");
2768aaffd78SIgor Ostapenko 	} else {
2778aaffd78SIgor Ostapenko 		return (false);
2788aaffd78SIgor Ostapenko 	}
2798aaffd78SIgor Ostapenko 	while (**cur == ' ')
2808aaffd78SIgor Ostapenko 		(*cur)++;
2818aaffd78SIgor Ostapenko 
2826bd8d855SIgor Ostapenko 	/* pfil_dir */
2838aaffd78SIgor Ostapenko 	if (strstr(*cur, "in") == *cur) {
2848aaffd78SIgor Ostapenko 		rule->pfil_dir = PFIL_IN;
2858aaffd78SIgor Ostapenko 		*cur += strlen("in");
2868aaffd78SIgor Ostapenko 	} else if (strstr(*cur, "out") == *cur) {
2878aaffd78SIgor Ostapenko 		rule->pfil_dir = PFIL_OUT;
2888aaffd78SIgor Ostapenko 		*cur += strlen("out");
2898aaffd78SIgor Ostapenko 	} else {
2908aaffd78SIgor Ostapenko 		return (false);
2918aaffd78SIgor Ostapenko 	}
2928aaffd78SIgor Ostapenko 	while (**cur == ' ')
2938aaffd78SIgor Ostapenko 		(*cur)++;
2948aaffd78SIgor Ostapenko 
2956bd8d855SIgor Ostapenko 	/* ifname */
2968aaffd78SIgor Ostapenko 	char *sp = strchr(*cur, ' ');
2978aaffd78SIgor Ostapenko 	if (sp == NULL || sp > delim)
2988aaffd78SIgor Ostapenko 		return (false);
2998aaffd78SIgor Ostapenko 	size_t len = sp - *cur;
3008aaffd78SIgor Ostapenko 	if (len >= sizeof(rule->ifname))
3018aaffd78SIgor Ostapenko 		return (false);
3028aaffd78SIgor Ostapenko 	strncpy(rule->ifname, *cur, len);
3038aaffd78SIgor Ostapenko 	rule->ifname[len] = 0;
3048aaffd78SIgor Ostapenko 	*cur = sp;
3058aaffd78SIgor Ostapenko 	while (**cur == ' ')
3068aaffd78SIgor Ostapenko 		(*cur)++;
3078aaffd78SIgor Ostapenko 
3086bd8d855SIgor Ostapenko 	/* opname */
3098aaffd78SIgor Ostapenko 	if (strstr(*cur, "pull-head") == *cur) {
3108aaffd78SIgor Ostapenko 		rule->op = dmb_m_pull_head;
3118aaffd78SIgor Ostapenko 		*cur += strlen("pull-head");
312*76e00c72SKristof Provost 	} else if (strstr(*cur, "enlarge") == *cur) {
313*76e00c72SKristof Provost 		rule->op = dmb_m_enlarge;
314*76e00c72SKristof Provost 		*cur += strlen("enlarge");
3158aaffd78SIgor Ostapenko 	} else {
3168aaffd78SIgor Ostapenko 		return (false);
3178aaffd78SIgor Ostapenko 	}
3188aaffd78SIgor Ostapenko 	while (**cur == ' ')
3198aaffd78SIgor Ostapenko 		(*cur)++;
3208aaffd78SIgor Ostapenko 
3216bd8d855SIgor Ostapenko 	/* opargs */
3228aaffd78SIgor Ostapenko 	if (*cur > delim)
3238aaffd78SIgor Ostapenko 		return (false);
3248aaffd78SIgor Ostapenko 	rule->opargs = *cur;
3258aaffd78SIgor Ostapenko 
3266bd8d855SIgor Ostapenko 	/* the next rule & eof */
3278aaffd78SIgor Ostapenko 	*cur = delim + 1;
3289f146a81SIgor Ostapenko 	while (**cur == ' ')
3299f146a81SIgor Ostapenko 		(*cur)++;
3309f146a81SIgor Ostapenko 	*eof = strlen(*cur) == 0;
3318aaffd78SIgor Ostapenko 
3328aaffd78SIgor Ostapenko 	return (true);
3338aaffd78SIgor Ostapenko }
3348aaffd78SIgor Ostapenko 
3359f146a81SIgor Ostapenko static int
3369f146a81SIgor Ostapenko validate_rules(const char *rules)
3379f146a81SIgor Ostapenko {
3389f146a81SIgor Ostapenko 	const char *cursor = rules;
3399f146a81SIgor Ostapenko 	bool parsed;
3409f146a81SIgor Ostapenko 	struct rule rule;
3419f146a81SIgor Ostapenko 	bool eof = false;
3429f146a81SIgor Ostapenko 
3439f146a81SIgor Ostapenko 	DMB_RULES_LOCK_ASSERT();
3449f146a81SIgor Ostapenko 
3459f146a81SIgor Ostapenko 	while (!eof && (parsed = read_rule(&cursor, &rule, &eof))) {
3469f146a81SIgor Ostapenko 		/* noop */
3479f146a81SIgor Ostapenko 	}
3489f146a81SIgor Ostapenko 
3499f146a81SIgor Ostapenko 	if (!parsed) {
3509f146a81SIgor Ostapenko 		FEEDBACK_RULE(rule, "rule parsing failed");
3519f146a81SIgor Ostapenko 		return (EINVAL);
3529f146a81SIgor Ostapenko 	}
3539f146a81SIgor Ostapenko 
3549f146a81SIgor Ostapenko 	return (0);
3559f146a81SIgor Ostapenko }
3569f146a81SIgor Ostapenko 
3578aaffd78SIgor Ostapenko static pfil_return_t
3588aaffd78SIgor Ostapenko dmb_pfil_mbuf_chk(int pfil_type, struct mbuf **mp, struct ifnet *ifp,
3598aaffd78SIgor Ostapenko     int flags, void *ruleset, void *unused)
3608aaffd78SIgor Ostapenko {
3618aaffd78SIgor Ostapenko 	struct mbuf *m = *mp;
3628aaffd78SIgor Ostapenko 	const char *cursor;
3638aaffd78SIgor Ostapenko 	bool parsed;
3648aaffd78SIgor Ostapenko 	struct rule rule;
3659f146a81SIgor Ostapenko 	bool eof = false;
3668aaffd78SIgor Ostapenko 
3678aaffd78SIgor Ostapenko 	DMB_RULES_SLOCK();
3688aaffd78SIgor Ostapenko 	cursor = V_dmb_rules;
3699f146a81SIgor Ostapenko 	while (!eof && (parsed = read_rule(&cursor, &rule, &eof))) {
3708aaffd78SIgor Ostapenko 		if (rule.pfil_type == pfil_type &&
3718aaffd78SIgor Ostapenko 		    rule.pfil_dir == (flags & rule.pfil_dir)  &&
3728aaffd78SIgor Ostapenko 		    strcmp(rule.ifname, ifp->if_xname) == 0) {
3738aaffd78SIgor Ostapenko 			m = rule.op(m, &rule);
3748aaffd78SIgor Ostapenko 			if (m == NULL) {
3759f146a81SIgor Ostapenko 				FEEDBACK_PFIL(pfil_type, flags, ifp, rule,
3768aaffd78SIgor Ostapenko 				    "mbuf operation failed");
3778aaffd78SIgor Ostapenko 				break;
3788aaffd78SIgor Ostapenko 			}
3798aaffd78SIgor Ostapenko 			counter_u64_add(V_dmb_hits, 1);
3808aaffd78SIgor Ostapenko 		}
3818aaffd78SIgor Ostapenko 	}
3828aaffd78SIgor Ostapenko 	if (!parsed) {
3839f146a81SIgor Ostapenko 		FEEDBACK_PFIL(pfil_type, flags, ifp, rule,
3849f146a81SIgor Ostapenko 		    "rule parsing failed");
3858aaffd78SIgor Ostapenko 		m_freem(m);
3868aaffd78SIgor Ostapenko 		m = NULL;
3878aaffd78SIgor Ostapenko 	}
3888aaffd78SIgor Ostapenko 	DMB_RULES_SUNLOCK();
3898aaffd78SIgor Ostapenko 
3908aaffd78SIgor Ostapenko 	if (m == NULL) {
3918aaffd78SIgor Ostapenko 		*mp = NULL;
3928aaffd78SIgor Ostapenko 		return (PFIL_DROPPED);
3938aaffd78SIgor Ostapenko 	}
3948aaffd78SIgor Ostapenko 	if (m != *mp) {
3958aaffd78SIgor Ostapenko 		*mp = m;
3968aaffd78SIgor Ostapenko 		return (PFIL_REALLOCED);
3978aaffd78SIgor Ostapenko 	}
3988aaffd78SIgor Ostapenko 
3998aaffd78SIgor Ostapenko 	return (PFIL_PASS);
4008aaffd78SIgor Ostapenko }
4018aaffd78SIgor Ostapenko 
402aa72c5baSGleb Smirnoff #ifdef INET
4038aaffd78SIgor Ostapenko static pfil_return_t
4048aaffd78SIgor Ostapenko dmb_pfil_inet_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
4058aaffd78SIgor Ostapenko     void *ruleset, struct inpcb *inp)
4068aaffd78SIgor Ostapenko {
4078aaffd78SIgor Ostapenko 	return (dmb_pfil_mbuf_chk(PFIL_TYPE_IP4, mp, ifp, flags,
4088aaffd78SIgor Ostapenko 	    ruleset, inp));
4098aaffd78SIgor Ostapenko }
410aa72c5baSGleb Smirnoff #endif
4118aaffd78SIgor Ostapenko 
412aa72c5baSGleb Smirnoff #ifdef INET6
4138aaffd78SIgor Ostapenko static pfil_return_t
4148aaffd78SIgor Ostapenko dmb_pfil_inet6_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
4158aaffd78SIgor Ostapenko     void *ruleset, struct inpcb *inp)
4168aaffd78SIgor Ostapenko {
4178aaffd78SIgor Ostapenko 	return (dmb_pfil_mbuf_chk(PFIL_TYPE_IP6, mp, ifp, flags,
4188aaffd78SIgor Ostapenko 	    ruleset, inp));
4198aaffd78SIgor Ostapenko }
420aa72c5baSGleb Smirnoff #endif
4218aaffd78SIgor Ostapenko 
4228aaffd78SIgor Ostapenko static pfil_return_t
4238aaffd78SIgor Ostapenko dmb_pfil_ethernet_mbuf_chk(struct mbuf **mp, struct ifnet *ifp, int flags,
4248aaffd78SIgor Ostapenko     void *ruleset, struct inpcb *inp)
4258aaffd78SIgor Ostapenko {
4268aaffd78SIgor Ostapenko 	return (dmb_pfil_mbuf_chk(PFIL_TYPE_ETHERNET, mp, ifp, flags,
4278aaffd78SIgor Ostapenko 	    ruleset, inp));
4288aaffd78SIgor Ostapenko }
4298aaffd78SIgor Ostapenko 
4308aaffd78SIgor Ostapenko static void
4318aaffd78SIgor Ostapenko dmb_pfil_init(void)
4328aaffd78SIgor Ostapenko {
4338aaffd78SIgor Ostapenko 	struct pfil_hook_args pha = {
4348aaffd78SIgor Ostapenko 		.pa_version = PFIL_VERSION,
4358aaffd78SIgor Ostapenko 		.pa_modname = "dummymbuf",
4368aaffd78SIgor Ostapenko 		.pa_flags = PFIL_IN | PFIL_OUT,
4378aaffd78SIgor Ostapenko 	};
4388aaffd78SIgor Ostapenko 
4398aaffd78SIgor Ostapenko #ifdef INET
4408aaffd78SIgor Ostapenko 	pha.pa_type = PFIL_TYPE_IP4;
4418aaffd78SIgor Ostapenko 	pha.pa_mbuf_chk = dmb_pfil_inet_mbuf_chk;
4428aaffd78SIgor Ostapenko 	pha.pa_rulname = "inet";
4438aaffd78SIgor Ostapenko 	V_dmb_pfil_inet_hook = pfil_add_hook(&pha);
4448aaffd78SIgor Ostapenko #endif
4458aaffd78SIgor Ostapenko 
4468aaffd78SIgor Ostapenko #ifdef INET6
4478aaffd78SIgor Ostapenko 	pha.pa_type = PFIL_TYPE_IP6;
4488aaffd78SIgor Ostapenko 	pha.pa_mbuf_chk = dmb_pfil_inet6_mbuf_chk;
4498aaffd78SIgor Ostapenko 	pha.pa_rulname = "inet6";
4508aaffd78SIgor Ostapenko 	V_dmb_pfil_inet6_hook = pfil_add_hook(&pha);
4518aaffd78SIgor Ostapenko #endif
4528aaffd78SIgor Ostapenko 
4538aaffd78SIgor Ostapenko 	pha.pa_type = PFIL_TYPE_ETHERNET;
4548aaffd78SIgor Ostapenko 	pha.pa_mbuf_chk = dmb_pfil_ethernet_mbuf_chk;
4558aaffd78SIgor Ostapenko 	pha.pa_rulname = "ethernet";
4568aaffd78SIgor Ostapenko 	V_dmb_pfil_ethernet_hook = pfil_add_hook(&pha);
4578aaffd78SIgor Ostapenko }
4588aaffd78SIgor Ostapenko 
4598aaffd78SIgor Ostapenko static void
4608aaffd78SIgor Ostapenko dmb_pfil_uninit(void)
4618aaffd78SIgor Ostapenko {
4628aaffd78SIgor Ostapenko #ifdef INET
4638aaffd78SIgor Ostapenko 	pfil_remove_hook(V_dmb_pfil_inet_hook);
4648aaffd78SIgor Ostapenko #endif
4658aaffd78SIgor Ostapenko 
4668aaffd78SIgor Ostapenko #ifdef INET6
4678aaffd78SIgor Ostapenko 	pfil_remove_hook(V_dmb_pfil_inet6_hook);
4688aaffd78SIgor Ostapenko #endif
4698aaffd78SIgor Ostapenko 
4708aaffd78SIgor Ostapenko 	pfil_remove_hook(V_dmb_pfil_ethernet_hook);
4718aaffd78SIgor Ostapenko }
4728aaffd78SIgor Ostapenko 
4738aaffd78SIgor Ostapenko static void
4748be18c2dSSHENGYI HONG vnet_dmb_init(const void *unused __unused)
4758aaffd78SIgor Ostapenko {
4768aaffd78SIgor Ostapenko 	sx_init(&V_dmb_rules_lock, "dummymbuf rules");
4778aaffd78SIgor Ostapenko 	V_dmb_hits = counter_u64_alloc(M_WAITOK);
4788aaffd78SIgor Ostapenko 	dmb_pfil_init();
4798aaffd78SIgor Ostapenko }
4808aaffd78SIgor Ostapenko VNET_SYSINIT(vnet_dmb_init, SI_SUB_PROTO_PFIL, SI_ORDER_ANY,
4818aaffd78SIgor Ostapenko     vnet_dmb_init, NULL);
4828aaffd78SIgor Ostapenko 
4838aaffd78SIgor Ostapenko static void
4848be18c2dSSHENGYI HONG vnet_dmb_uninit(const void *unused __unused)
4858aaffd78SIgor Ostapenko {
4868aaffd78SIgor Ostapenko 	dmb_pfil_uninit();
4878aaffd78SIgor Ostapenko 	counter_u64_free(V_dmb_hits);
4888aaffd78SIgor Ostapenko 	sx_destroy(&V_dmb_rules_lock);
4898aaffd78SIgor Ostapenko 	free(V_dmb_rules, M_DUMMYMBUF_RULES);
4908aaffd78SIgor Ostapenko }
4918aaffd78SIgor Ostapenko VNET_SYSUNINIT(vnet_dmb_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_ANY,
4928aaffd78SIgor Ostapenko     vnet_dmb_uninit, NULL);
4938aaffd78SIgor Ostapenko 
4948aaffd78SIgor Ostapenko static int
4958aaffd78SIgor Ostapenko dmb_modevent(module_t mod __unused, int event, void *arg __unused)
4968aaffd78SIgor Ostapenko {
4978aaffd78SIgor Ostapenko 	int error = 0;
4988aaffd78SIgor Ostapenko 
4998aaffd78SIgor Ostapenko 	switch (event) {
5008aaffd78SIgor Ostapenko 	case MOD_LOAD:
5018aaffd78SIgor Ostapenko 	case MOD_UNLOAD:
5028aaffd78SIgor Ostapenko 		break;
5038aaffd78SIgor Ostapenko 	default:
5048aaffd78SIgor Ostapenko 		error = EOPNOTSUPP;
5058aaffd78SIgor Ostapenko 		break;
5068aaffd78SIgor Ostapenko 	}
5078aaffd78SIgor Ostapenko 
5088aaffd78SIgor Ostapenko 	return (error);
5098aaffd78SIgor Ostapenko }
5108aaffd78SIgor Ostapenko 
5118aaffd78SIgor Ostapenko static moduledata_t dmb_mod = {
5128aaffd78SIgor Ostapenko 	"dummymbuf",
5138aaffd78SIgor Ostapenko 	dmb_modevent,
5148aaffd78SIgor Ostapenko 	NULL
5158aaffd78SIgor Ostapenko };
5168aaffd78SIgor Ostapenko 
5178aaffd78SIgor Ostapenko DECLARE_MODULE(dummymbuf, dmb_mod, SI_SUB_PROTO_PFIL, SI_ORDER_ANY);
5188aaffd78SIgor Ostapenko MODULE_VERSION(dummymbuf, 1);
519