aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2021-02-25 08:50:57 +0000
committerKristof Provost <kp@FreeBSD.org>2021-06-02 18:40:07 +0000
commitcca9c2f51372f7a6c69b52617f58be4d725910f0 (patch)
tree006471a24644ae2ee8aeffa161ccf1375a7f2430
parent05b9673637feedb3ef0cf51fd9b7e914b08f7554 (diff)
downloadsrc-cca9c2f51372f7a6c69b52617f58be4d725910f0.tar.gz
src-cca9c2f51372f7a6c69b52617f58be4d725910f0.zip
pf tests: Test cases for fragment reassembly
Obtained from: Alexander Bluhm, OpenBSD (cherry picked from commit d39d5ee2d67f61abc890b51973b5c4a0c81d6647)
-rw-r--r--tests/sys/netpfil/pf/Makefile10
-rw-r--r--tests/sys/netpfil/pf/frag-overindex.py82
-rw-r--r--tests/sys/netpfil/pf/frag-overlimit.py87
-rw-r--r--tests/sys/netpfil/pf/frag-overreplace.py84
-rw-r--r--tests/sys/netpfil/pf/fragcommon.py52
-rwxr-xr-xtests/sys/netpfil/pf/fragmentation.sh83
6 files changed, 397 insertions, 1 deletions
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 <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 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"
}