From owner-dev-commits-src-branches@freebsd.org Wed Jun 2 18:51:05 2021 Return-Path: Delivered-To: dev-commits-src-branches@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id A21AF6498CA; Wed, 2 Jun 2021 18:51:05 +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 "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4FwJ5P4D5Gz4YM4; Wed, 2 Jun 2021 18:51:05 +0000 (UTC) (envelope-from git@FreeBSD.org) 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 79B9B1BCB3; Wed, 2 Jun 2021 18:51:05 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 152Ip5MO038213; Wed, 2 Jun 2021 18:51:05 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 152Ip5wB038211; Wed, 2 Jun 2021 18:51:05 GMT (envelope-from git) Date: Wed, 2 Jun 2021 18:51:05 GMT Message-Id: <202106021851.152Ip5wB038211@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kristof Provost Subject: git: cca9c2f51372 - stable/12 - pf tests: Test cases for fragment reassembly 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/stable/12 X-Git-Reftype: branch X-Git-Commit: cca9c2f51372f7a6c69b52617f58be4d725910f0 Auto-Submitted: auto-generated X-BeenThere: dev-commits-src-branches@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Commits to the stable branches of the FreeBSD src repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 02 Jun 2021 18:51:05 -0000 The branch stable/12 has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=cca9c2f51372f7a6c69b52617f58be4d725910f0 commit cca9c2f51372f7a6c69b52617f58be4d725910f0 Author: Kristof Provost AuthorDate: 2021-02-25 08:50:57 +0000 Commit: Kristof Provost CommitDate: 2021-06-02 18:40:07 +0000 pf tests: Test cases for fragment reassembly Obtained from: Alexander Bluhm, OpenBSD (cherry picked from commit d39d5ee2d67f61abc890b51973b5c4a0c81d6647) --- tests/sys/netpfil/pf/Makefile | 10 +++- 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, 397 insertions(+), 1 deletion(-) diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile index 34740ca9dca2..f67f510fbf29 100644 --- a/tests/sys/netpfil/pf/Makefile +++ b/tests/sys/netpfil/pf/Makefile @@ -29,9 +29,17 @@ ATF_TESTS_SH+= anchor \ ${PACKAGE}FILES+= utils.subr \ echo_inetd.conf \ CVE-2019-5597.py \ - CVE-2019-5598.py + CVE-2019-5598.py \ + fragcommon.py \ + frag-overindex.py \ + frag-overlimit.py \ + frag-overreplace.py ${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 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 +# +# 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 +# +# 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 +# +# 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 fe92a2ec88e3..de83f5d5c82b 100755 --- 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" }