Date: Wed, 2 Jun 2021 18:51:05 GMT From: Kristof Provost <kp@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Subject: git: b4612f6a27b0 - stable/13 - pf tests: Test cases for fragment reassembly Message-ID: <202106021851.152Ip5JW038260@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=b4612f6a27b05692119326ea6277d844a741fcf1 commit b4612f6a27b05692119326ea6277d844a741fcf1 Author: Kristof Provost <kp@FreeBSD.org> AuthorDate: 2021-02-25 08:50:57 +0000 Commit: Kristof Provost <kp@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@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@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@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" }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202106021851.152Ip5JW038260>