From nobody Wed Sep 23 12:47:47 2026 X-Original-To: dev-commits-src-main@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 4hqcFJ1b1sz6t10N for ; Wed, 23 Sep 2026 12:47:48 +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 "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hqcFJ0cSvz4Ny8 for ; Wed, 23 Sep 2026 12:47:48 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1790167668; 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=ns5HueCC5OvLH7IRD5F9mEnbGqWZxY1aGuR0AujBPsg=; b=tExsEL+TM27xxpLmKctPigKAegHf1/K5OljhdI/EW0TgV77HSzrYl+bh9uSCZ5z7/Asitx 7qVLz8e295y0oyKuphOwqjvWs9QC3CMpoH1szOQa86E882BLGnCyaFRstb25XZh0Wp8JjG clySYlgE0LONnD5mSbrGspfx9clc9UlUdewIT3B57Ef06NfqpSedkQZrO8QmTKN7dpB2Yt tga92qRpo+UqpMRBLskVnluJatoChO988ZzADEk6SnHXxwtYT9Me0hJ1BNa3fIepNLcpAL iep4WBP03DLyoZg0YPMyOYN0BBaR6T6fa6kvrJyT6DVmuQeO4kw1vhuBSPISzQ== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1790167668; b=BLl5XeI8odK5Rtz8HefsBd0OJ2NJ2P0I7Hn4axTyA1RrqouVQR2o/FyN+4iwsA945bOT/E kG2uyUZBXFFVmvhBmwH+b+5augY5fC5tHUc496tDrt6RUBlgP/oRx8iszCq6PeeiknYf8Q rLjr5iSZq/k64MKhb1BMi+t8CATkiBeUVHl4jJshCL85CKnnQuA+ufxPOovqvlfw9+qx2v rNIZjtDm7Sid3fW5TRBvG0fKv9vT6IIerkRjxVVNsIVEBNP38fOxQFwH5/IoIsn+0minmE 69RrEZJ392LD7Ig904VPdV6rypHgW0ERutA4NXQIFbTxhj1STUE8zeufTp/gjA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1790167668; 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=ns5HueCC5OvLH7IRD5F9mEnbGqWZxY1aGuR0AujBPsg=; b=Lsy5ELJy6sOjONC9noJvrixXZgAETp+39JoUXU/bkxxVJuMY6wIqD31hk48wUikOeuVIdg i542xF4vzjpu9uZP2e+gOECLdA4oucF0wAMLFq41TlYe2PPiQCFgjslJwt8AbGEuwqrP6u FhlIAoVaWXFDRriNp/c2UbB+SqKj2hIxzo2slTD6n0YYGPdJFQ0TxDheSlghXTaP7CKkdc Nyak1/uK9oZCkN0LQ++Iv+8hqis+9G6gxMkZS3PtxifhJ06KtOIV4Lis+t2Didia705/Zu i6PfbWhb2AtZAY19FDohA1Kh3gf2OwBO8PNHLQ+RMXvEwaE0P21pzh++C1X7MA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hqcFH6Wzvzv04 for ; Wed, 23 Sep 2026 12:47:47 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 1cb0b by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Wed, 23 Sep 2026 12:47:47 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Alexander Leidinger Subject: git: 044cae040488 - main - ipfw tests: cover layer-2 filtering of unmapped mbufs List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: netchild X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 044cae0404883d12f62f00d80896c6f81fb4c5ea Auto-Submitted: auto-generated Date: Wed, 23 Sep 2026 12:47:47 +0000 Message-Id: <6ab3ca73.1cb0b.3f7b3f34@gitrepo.freebsd.org> The branch main has been updated by netchild: URL: https://cgit.FreeBSD.org/src/commit/?id=044cae0404883d12f62f00d80896c6f81fb4c5ea commit 044cae0404883d12f62f00d80896c6f81fb4c5ea Author: Alexander Leidinger AuthorDate: 2026-09-04 07:36:05 +0000 Commit: Alexander Leidinger CommitDate: 2026-09-23 12:47:42 +0000 ipfw tests: cover layer-2 filtering of unmapped mbufs Test that ipfw's layer-2 hook copes with unmapped mbufs, on the pass and on the deny path. Reviewed by: glebius Assisted-by: Claude Code (Fable 5, Opus 5) Differential Revision: https://reviews.freebsd.org/D59390 --- tests/sys/netpfil/ipfw/Makefile | 3 +- tests/sys/netpfil/ipfw/unmapped.sh | 189 +++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 1 deletion(-) diff --git a/tests/sys/netpfil/ipfw/Makefile b/tests/sys/netpfil/ipfw/Makefile index 0be870945891..8dcb4bcbebfa 100644 --- a/tests/sys/netpfil/ipfw/Makefile +++ b/tests/sys/netpfil/ipfw/Makefile @@ -7,7 +7,8 @@ ATF_TESTS_SH+= fwd \ ipv6-flow-id \ log \ lookup \ - table + table \ + unmapped ${PACKAGE}FILES+= fwd_inetd.conf \ lookup_inetd.conf diff --git a/tests/sys/netpfil/ipfw/unmapped.sh b/tests/sys/netpfil/ipfw/unmapped.sh new file mode 100644 index 000000000000..b63b3f6fcad7 --- /dev/null +++ b/tests/sys/netpfil/ipfw/unmapped.sh @@ -0,0 +1,189 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2026 Alexander Leidinger +# +# 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. + +. $(atf_get_srcdir)/../common/utils.subr + +helper=$(atf_get_srcdir)/../../common/sendfile_helper + +# ipfw's layer-2 hook (net.link.ether.ipfw) runs ipfw_chk() on the raw +# Ethernet frame. A sender that advertises IFCAP_MEXTPG hands it an unmapped +# (M_EXTPG) sendfile(2)/KTLS chain; ipfw_chk() pulls the frame up to inspect +# it. Two preconditions decide whether this exercises anything: +# +# vnet_mkepair() clears IFCAP_MEXTPG, so it is restored on both ends. +# +# Without TCP options the mapped ether + IPv4 + TCP head is 54 bytes; a pull +# that reaches past it crosses into the unmapped payload. rfc1323 is disabled +# below so the head stays at 54. +ipfw_l2_setup() +{ + firewall_init ipfw + + epair=$(vnet_mkepair) + for iface in ${epair}a ${epair}b; do + ifconfig ${iface} mextpg + ifconfig ${iface} | grep -q MEXTPG || + atf_fail "MEXTPG unavailable on ${iface}" + done + + vnet_mkjail snd ${epair}a + vnet_mkjail rcv ${epair}b + + jexec snd ifconfig ${epair}a 192.0.2.1/24 up + jexec rcv ifconfig ${epair}b 192.0.2.2/24 up + jexec snd sysctl -q net.inet.tcp.rfc1323=0 + jexec rcv sysctl -q net.inet.tcp.rfc1323=0 + + dd if=/dev/random of=payload bs=1m count=8 status=none + payload=$(pwd)/payload + size=$(stat -f %z ${payload}) +} + +# Load a ruleset in the sender vnet and hook ipfw into its layer 2. The +# ruleset's first rule counts every layer-2 frame ipfw sees, which is what +# distinguishes "filtered and survived" from "never filtered". +ipfw_l2_enable() +{ + firewall_config snd ipfw ipfw "$@" + atf_check -s exit:0 -o ignore \ + jexec snd sysctl net.link.ether.ipfw=1 +} + +# Start the receiver, writing to $1, and the sender in the background. +ipfw_l2_sendfile_start() +{ + jexec rcv nc -l 5555 > $1 & + receiver=$! + sleep 1 + timeout 60 jexec snd ${helper} -c 192.0.2.2 -p 5555 ${payload} 0 ${size} 0 & + sender=$! +} + +# Wait for the sender to finish, then for the receiver to drain. +ipfw_l2_sendfile_wait() +{ + wait ${sender} + status=$? + + i=0 + while [ ${i} -lt 10 ] && kill -0 ${receiver} 2>/dev/null; do + sleep 1 + i=$((i + 1)) + done + kill ${receiver} 2>/dev/null + wait ${receiver} 2>/dev/null + return ${status} +} + +# Send the payload and leave what arrived in $1. +ipfw_l2_sendfile() +{ + ipfw_l2_sendfile_start $1 + ipfw_l2_sendfile_wait +} + +# Wait up to 10 s for rule $1 to have counted a packet. +ipfw_l2_wait_count() +{ + i=0 + while [ ${i} -lt 100 ]; do + count=$(jexec snd ipfw show $1 2>/dev/null | awk 'NR == 1 {print $2}') + [ "${count:-0}" -gt 0 ] && return 0 + sleep 0.1 + i=$((i + 1)) + done + return 1 +} + +# rule 100 counts layer-2 frames; without the hook it stays zero. +ipfw_l2_assert_filtered() +{ + ipfw_l2_wait_count 100 || + atf_fail "ipfw saw no frames on layer 2" +} + +atf_test_case "unmapped" "cleanup" +unmapped_head() +{ + atf_set descr 'Unmapped mbufs survive ipfw layer-2 filtering' + atf_set require.user root +} + +unmapped_body() +{ + ipfw_l2_setup + ipfw_l2_enable \ + "ipfw add 100 count ip from any to any layer2" \ + "ipfw add 200 allow ip from any to any" + + ipfw_l2_sendfile received || + atf_fail "sendfile through the ipfw layer-2 hook failed" + atf_check -s exit:0 cmp ${payload} received + ipfw_l2_assert_filtered +} + +unmapped_cleanup() +{ + firewall_cleanup ipfw +} + +# Let the handshake through and deny the data segments, so the deny path +# runs on the unmapped chain; rule 110 counts the first drop at once. +atf_test_case "unmapped_block" "cleanup" +unmapped_block_head() +{ + atf_set descr 'ipfw layer 2 drops unmapped data segments when told to' + atf_set require.user root +} + +unmapped_block_body() +{ + ipfw_l2_setup + ipfw_l2_enable \ + "ipfw add 100 count ip from any to any layer2" \ + "ipfw add 110 deny tcp from any to any dst-port 5555 tcpdatalen 1000-65535 layer2" \ + "ipfw add 200 allow ip from any to any" + + ipfw_l2_sendfile_start blocked + ipfw_l2_wait_count 110 || + atf_fail "the deny rule matched no data segment" + kill ${sender} ${receiver} 2>/dev/null + wait ${sender} ${receiver} 2>/dev/null + [ "$(stat -f %z blocked)" -lt ${size} ] || + atf_fail "the payload arrived although ipfw drops its data segments" + ipfw_l2_assert_filtered +} + +unmapped_block_cleanup() +{ + firewall_cleanup ipfw +} + +atf_init_test_cases() +{ + atf_add_test_case "unmapped" + atf_add_test_case "unmapped_block" +}