git: b4612f6a27b0 - stable/13 - pf tests: Test cases for fragment reassembly

Kristof Provost kp at FreeBSD.org
Wed Jun 2 18:51:05 UTC 2021


The branch stable/13 has been updated by kp:

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

commit b4612f6a27b05692119326ea6277d844a741fcf1
Author:     Kristof Provost <kp at FreeBSD.org>
AuthorDate: 2021-02-25 08:50:57 +0000
Commit:     Kristof Provost <kp at FreeBSD.org>
CommitDate: 2021-06-02 18:50:56 +0000

    pf tests: Test cases for fragment reassembly
    
    Obtained from:  Alexander Bluhm, OpenBSD
    
    (cherry picked from commit d39d5ee2d67f61abc890b51973b5c4a0c81d6647)
---
 tests/sys/netpfil/pf/Makefile            |  8 +++
 tests/sys/netpfil/pf/frag-overindex.py   | 82 ++++++++++++++++++++++++++++++
 tests/sys/netpfil/pf/frag-overlimit.py   | 87 ++++++++++++++++++++++++++++++++
 tests/sys/netpfil/pf/frag-overreplace.py | 84 ++++++++++++++++++++++++++++++
 tests/sys/netpfil/pf/fragcommon.py       | 52 +++++++++++++++++++
 tests/sys/netpfil/pf/fragmentation.sh    | 83 ++++++++++++++++++++++++++++++
 6 files changed, 396 insertions(+)

diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
index d2dbbcdd18f6..e21f78e761b6 100644
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -30,9 +30,17 @@ ATF_TESTS_SH+=	anchor \
 ${PACKAGE}FILES+=	CVE-2019-5597.py \
 			CVE-2019-5598.py \
 			echo_inetd.conf \
+			fragcommon.py \
+			frag-overindex.py \
+			frag-overlimit.py \
+			frag-overreplace.py \
 			utils.subr
 
 ${PACKAGE}FILESMODE_CVE-2019-5597.py=	0555
 ${PACKAGE}FILESMODE_CVE-2019-5598.py=	0555
+${PACKAGE}FILESMODE_fragcommon.py=	0555
+${PACKAGE}FILESMODE_frag-overindex.py=	0555
+${PACKAGE}FILESMODE_frag-overlimit.py=	0555
+${PACKAGE}FILESMODE_frag-overreplace.py=	0555
 
 .include <bsd.test.mk>
diff --git a/tests/sys/netpfil/pf/frag-overindex.py b/tests/sys/netpfil/pf/frag-overindex.py
new file mode 100644
index 000000000000..594eb9efe39d
--- /dev/null
+++ b/tests/sys/netpfil/pf/frag-overindex.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2012-2021 Alexander Bluhm <bluhm at openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from fragcommon import *
+
+#                               index boundary 4096 |
+# |--------------|
+#                 ....
+#                     |--------------|
+#                                              |XXXX-----|
+#                                    |--------------|
+#
+# this should trigger "frag index %d, new %d" log in kernel
+
+def send(src, dst, send_if, recv_if):
+	pid = os.getpid()
+	eid = pid & 0xffff
+	payload = b"ABCDEFGHIJKLMNOP"
+	dummy = b"01234567"
+	fragsize = 64
+	boundary = 4096
+	fragnum = int(boundary / fragsize)
+	packet = sp.IP(src=src, dst=dst)/ \
+			sp.ICMP(type='echo-request', id=eid)/ \
+			(int((boundary + 8) / len(payload)) * payload)
+	frag = []
+	fid = pid & 0xffff
+	for i in range(fragnum - 1):
+		frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+			frag=(i * fragsize) >> 3, flags='MF') /
+			bytes(packet)[20 + i * fragsize:20 + (i + 1) * fragsize])
+	frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+		frag=(boundary - 8) >> 3) /
+		(dummy + bytes(packet)[20 + boundary:20 + boundary + 8]))
+	frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+		frag=(boundary - fragsize) >> 3, flags='MF') /
+		bytes(packet)[20 + boundary - fragsize:20 + boundary])
+	eth = []
+	for f in frag:
+		eth.append(sp.Ether() / f)
+
+	if os.fork() == 0:
+		time.sleep(1)
+		for e in eth:
+			sp.sendp(e, iface=send_if)
+			time.sleep(0.001)
+		os._exit(0)
+
+	ans = sp.sniff(iface=recv_if, timeout=5)
+	print(ans)
+	for a in ans:
+		a.show()
+		if a and a.type == sp.ETH_P_IP and \
+				a.payload.proto == 1 and \
+				a.payload.frag == 0 and \
+				sp.icmptypes[a.payload.payload.type] == 'echo-reply':
+			id = a.payload.payload.id
+			print("id=%#x" % (id))
+			if id != eid:
+				print("WRONG ECHO REPLY ID")
+				sys.exit(2)
+			sys.exit(0)
+	print("NO ECHO REPLY")
+	exit(1)
+
+if __name__ == '__main__':
+	main(send)
diff --git a/tests/sys/netpfil/pf/frag-overlimit.py b/tests/sys/netpfil/pf/frag-overlimit.py
new file mode 100644
index 000000000000..e25ebf5b0dcd
--- /dev/null
+++ b/tests/sys/netpfil/pf/frag-overlimit.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2012-2021 Alexander Bluhm <bluhm at openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from fragcommon import *
+from itertools import chain
+
+#                               index boundary 4096 |
+# |--------------|
+#                 ....
+#                     |--------------|
+#                                                   |--------------|
+#                                                                   ....----|
+#                                              |XXXX-----|
+#                                    |--------------|
+
+# this should trigger "fragment requeue limit exceeded" log in kernel
+
+def send(src, dst, send_if, recv_if):
+	pid = os.getpid()
+	eid = pid & 0xffff
+	payload = b"ABCDEFGHIJKLMNOP"
+	dummy = b"01234567"
+	fragsize = 64
+	boundary = 4096
+	fragnum= int(boundary / fragsize)
+	packet = sp.IP(src=src, dst=dst)/ \
+			sp.ICMP(type='echo-request', id=eid)/ \
+			(int((boundary + boundary) / len(payload)) * payload)
+	frag = []
+	fid = pid & 0xffff
+	for i in chain(range(fragnum - 1), range(fragnum, fragnum + fragnum - 1)):
+		frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+			frag=(i * fragsize) >> 3, flags='MF') /
+			bytes(packet)[20 + i * fragsize:20 + (i + 1) * fragsize])
+	frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+		frag=(boundary + boundary - fragsize) >> 3) /
+		bytes(packet)[20 + boundary + boundary - fragsize:])
+	frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+		frag=(boundary - 8) >> 3, flags='MF')/
+		(dummy + bytes(packet)[20 + boundary:20 + boundary + 8]))
+	frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+		frag=(boundary - fragsize) >> 3, flags='MF') /
+		bytes(packet)[20 + boundary - fragsize:20 + boundary])
+	eth = []
+	for f in frag:
+		eth.append(sp.Ether() / f)
+
+	if os.fork() == 0:
+		time.sleep(1)
+		for e in eth:
+			sp.sendp(e, iface=send_if)
+			time.sleep(0.001)
+		os._exit(0)
+
+	ans = sp.sniff(iface=recv_if, timeout=10, filter=
+			"ip and src " + dst + " and dst " + src + " and icmp")
+	for a in ans:
+		if a and a.type == ETH_P_IP and \
+				a.payload.proto == 1 and \
+				a.payload.frag == 0 and \
+				sp.icmptypes[a.payload.payload.type] == 'echo-reply':
+			id = a.payload.payload.id
+			print("id=%#x" % (id))
+			if id != eid:
+				print("WRONG ECHO REPLY ID")
+				sys.exit(2)
+			print("ECHO REPLY")
+			sys.exit(1)
+	sys.exit(0)
+
+if __name__ == '__main__':
+	main(send)
diff --git a/tests/sys/netpfil/pf/frag-overreplace.py b/tests/sys/netpfil/pf/frag-overreplace.py
new file mode 100644
index 000000000000..ff9184243a1d
--- /dev/null
+++ b/tests/sys/netpfil/pf/frag-overreplace.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2012-2021 Alexander Bluhm <bluhm at openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from fragcommon import *
+
+#                               index boundary 4096 |
+# |--------------|
+#                 ....
+#                     |--------------|
+#                                              |XXXX-----|
+#                                    |--------------|
+#                                                   |--------------|
+
+# this should trigger "frag tail overlap %d" and "frag head overlap %d"
+
+def send(src, dst, send_if, recv_if):
+	pid = os.getpid()
+	eid = pid & 0xffff
+	payload = b"ABCDEFGHIJKLMNOP"
+	dummy = b"01234567"
+	fragsize = 1024
+	boundary = 4096
+	fragnum = int(boundary / fragsize)
+	packet = sp.IP(src=src, dst=dst)/ \
+			sp.ICMP(type='echo-request', id=eid)/ \
+			(int((boundary + fragsize) / len(payload)) * payload)
+	frag = []
+	fid = pid & 0xffff
+
+	for i in range(fragnum - 1):
+		frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+			frag=(i * fragsize) >> 3, flags='MF') /
+			bytes(packet)[20 + i * fragsize:20 + (i + 1) * fragsize])
+	frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+		frag=(boundary - 8) >> 3, flags='MF') /
+		(dummy + bytes(packet)[20 + boundary:20 + boundary + 8]))
+	frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+		frag=(boundary - fragsize) >> 3, flags='MF') /
+		bytes(packet)[20 + boundary - fragsize:20 + boundary])
+	frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+		frag=(boundary) >> 3)/bytes(packet)[20 + boundary:])
+
+	eth=[]
+	for f in frag:
+		eth.append(sp.Ether() / f)
+
+	if os.fork() == 0:
+		time.sleep(1)
+		for e in eth:
+			sp.sendp(e, iface=send_if)
+			time.sleep(0.001)
+		os._exit(0)
+
+	ans = sp.sniff(iface=recv_if, timeout=3, filter="")
+	for a in ans:
+		if a and a.type == sp.ETH_P_IP and \
+				a.payload.proto == 1 and \
+				a.payload.frag == 0 and \
+				sp.icmptypes[a.payload.payload.type] == 'echo-reply':
+			id=a.payload.payload.id
+			if id != eid:
+				print("WRONG ECHO REPLY ID")
+				sys.exit(2)
+			sys.exit(0)
+	print("NO ECHO REPLY")
+	sys.exit(1)
+
+if __name__ == '__main__':
+	main(send)
diff --git a/tests/sys/netpfil/pf/fragcommon.py b/tests/sys/netpfil/pf/fragcommon.py
new file mode 100644
index 000000000000..2bcd3989b420
--- /dev/null
+++ b/tests/sys/netpfil/pf/fragcommon.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate). All Rights Reserved.
+#
+# 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.
+
+import argparse
+import os
+import scapy.all as sp
+import sys
+import time
+
+def main(send):
+	parser = argparse.ArgumentParser("frag-overindex.py",
+		description="Fragmentation test tool")
+	parser.add_argument('--to', nargs=1,
+		required=True,
+		help='The address to send the fragmented packets to')
+	parser.add_argument('--fromaddr', nargs=1,
+		required=True,
+		help='The source address for the generated packets')
+	parser.add_argument('--sendif', nargs=1,
+		required=True,
+		help='The interface through which the packet(s) will be sent')
+	parser.add_argument('--recvif', nargs=1,
+		required=True,
+		help='The interface to expect the reply on')
+
+	args = parser.parse_args()
+
+	send(args.fromaddr[0], args.to[0], args.sendif[0], args.recvif[0])
diff --git a/tests/sys/netpfil/pf/fragmentation.sh b/tests/sys/netpfil/pf/fragmentation.sh
index 8b57bcf11487..8b16c9655d08 100644
--- a/tests/sys/netpfil/pf/fragmentation.sh
+++ b/tests/sys/netpfil/pf/fragmentation.sh
@@ -189,9 +189,92 @@ mtu_diff_cleanup()
 	pft_cleanup
 }
 
+frag_common()
+{
+	name=$1
+
+	pft_init
+
+	epair=$(vnet_mkepair)
+	vnet_mkjail alcatraz ${epair}a
+
+	ifconfig ${epair}b inet 192.0.2.1/24 up
+	jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
+
+	jexec alcatraz pfctl -e
+	pft_set_rules alcatraz \
+		"scrub all fragment reassemble"
+
+	# Sanity check
+	atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+	atf_check -s exit:0 -o ignore $(atf_get_srcdir)/frag-${1}.py \
+		--to 192.0.2.2 \
+		--fromaddr 192.0.2.1 \
+		--sendif ${epair}b \
+		--recvif ${epair}b
+}
+
+atf_test_case "overreplace" "cleanup"
+overreplace_head()
+{
+	atf_set descr 'ping fragment that overlaps fragment at index boundary and replace it'
+	atf_set require.user root
+	atf_set require.progs scapy
+}
+
+overreplace_body()
+{
+	frag_common overreplace
+}
+
+overreplace_cleanup()
+{
+	pft_cleanup
+}
+
+atf_test_case "overindex" "cleanup"
+overindex_head()
+{
+	atf_set descr 'ping fragment that overlaps the first fragment at index boundary'
+	atf_set require.user root
+	atf_set require.progs scapy
+}
+
+overindex_body()
+{
+	frag_common overindex
+}
+
+overindex_cleanup()
+{
+	pft_cleanup
+}
+
+atf_test_case "overlimit" "cleanup"
+overlimit_head()
+{
+	atf_set descr 'ping fragment at index boundary that cannot be requeued'
+	atf_set require.user root
+	atf_set require.progs scapy
+}
+
+overlimit_body()
+{
+	frag_common overlimit
+}
+
+overlimit_cleanup()
+{
+	pft_cleanup
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case "too_many_fragments"
 	atf_add_test_case "v6"
 	atf_add_test_case "mtu_diff"
+	atf_add_test_case "overreplace"
+	atf_add_test_case "overindex"
+	atf_add_test_case "overlimit"
 }


More information about the dev-commits-src-all mailing list