From nobody Thu Aug 15 07:34:51 2024 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4Wkxj81NkQz5T7Lb; Thu, 15 Aug 2024 07:34:52 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Wkxj811pxz4Xv6; Thu, 15 Aug 2024 07:34:52 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1723707292; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=ZzptYA7X+uTUy3L35eBwT/wojm8ETZ8sLKSaHMjxOpU=; b=tUAO9ZXvOUvpLDTtSGSCqyE3ojUSzUohkQmm7J5akP/j5V6kruOOrUPnNSHt+MvccFgHI0 Wqg/mdgVVI7zctafMOZJ3K+fJU74JivMBXndh0UnPIR1Jcom0sV6dKO3zSKT99qBL3+d2Z 5C8iPin76AgVw5/kQH4Kye1TN950rcYeDjN6ZUMb61UXHzf+Y59GmaXbGB5Xx6R76TcQ0G V1q9jOdFCR7LjAi7h7IHO2NFtCrE2SNhI8ODaAp8u6osmNCNk0l97GTJ8/rOzQZMH+Tihn M1LycRWT+JsSUBfY1XWUsirHRoO9T8cFXgqzzJ2Qgkc/0E9UHppXqoWEuc2x6Q== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1723707292; a=rsa-sha256; cv=none; b=bt1Al/KKQZJVq0Wo3nZTye9rEqKSTg01dAdziAA4k9YQ4yYVDCz3QehKmNQCQ4DlfYr3xB 8RH9mPgIN600GoJ1e0x60pfoD9+ujAtcrvW8QC3kjkRSB9MEa6DZTPxfazoI2BhBEZ317W nkgmDcK0uk935YsOHCJ2uiQXAVde3JShLApNCK5QOOqZrmSW4Uv7sCbWrOMx50XvQAS/BL sctrbKsrqflZn4ozpeKcO8ddrVUuODk/tft1THVPwYK5ZHLDedQfbLZs9X61FCW1lRRIM6 cRBhTfUf+ufMYna5pWNF+GlBFQWZaNhRcS7Fry59/AC2BBfQg456H7YmYsMLqg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1723707292; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=ZzptYA7X+uTUy3L35eBwT/wojm8ETZ8sLKSaHMjxOpU=; b=yiRnnqCP1S296cJdn16ZxH60wn2dvLOxgGkJ9nI0ZkQjhWEAOGRR5Dbj/VwJl+vNZLld8t Cd1+Vj0eEIqOdFeYCgZYUEaMDboT5juluKRMubKITveC6FagL8lNg3e5RcACvvux54TvqF 60AKBqzEpuIEUzm7n1o1DbR4T7sXvH9nPCm2ByXClz6OxJO2uuAEegs061xO7bOIyIKQau BRX9zxeVoqowHEvA3crqYvuZWqw5mFRPR7WECX1/Y+UJ/pXLQZBrbXXLZVFGpGo2VssGgG JO/E+w1q8hfNCdx9pOtiLdtE1x+Izvn3N9hghDns6reF+oUJaL1EWcs54Bcnqg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4Wkxj80X2Fz1SMY; Thu, 15 Aug 2024 07:34:52 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 47F7Ypcf015271; Thu, 15 Aug 2024 07:34:52 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 47F7YpBA015268; Thu, 15 Aug 2024 07:34:51 GMT (envelope-from git) Date: Thu, 15 Aug 2024 07:34:51 GMT Message-Id: <202408150734.47F7YpBA015268@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: 8aaffd78c0f5 - main - Add dummymbuf module for testing purposes List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 8aaffd78c0f517985c12fd1e3cbceeb6c6b98ef5 Auto-Submitted: auto-generated The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=8aaffd78c0f517985c12fd1e3cbceeb6c6b98ef5 commit 8aaffd78c0f517985c12fd1e3cbceeb6c6b98ef5 Author: Igor Ostapenko AuthorDate: 2024-08-14 12:45:38 +0000 Commit: Kristof Provost 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 +.\" +.\" 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} [ ]; +.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 +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: : rule parsing failed: " +If everything looks fine then extra spaces removal may help due to the parser +is kept very simple. +.It "dummymbuf: : mbuf operation failed: " +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 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 + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * 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 = ∅ + 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} [ ];" + " ...;"); + +/* + * 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} [ ]; + + 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);