git: 2d3fda5fa1dc - pf tests: Verify (tcp) checksum modification on unaligned options

Kristof Provost kp at FreeBSD.org
Wed Dec 23 12:03:45 UTC 2020


The branch main has been updated by kp:

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

commit 2d3fda5fa1dc99aa8788e5f8d8bb71e682101063
Author:     Kristof Provost <kp at FreeBSD.org>
AuthorDate: 2020-12-19 15:06:03 +0000
Commit:     Kristof Provost <kp at FreeBSD.org>
CommitDate: 2020-12-23 11:03:20 +0000

    pf tests: Verify (tcp) checksum modification on unaligned options
    
    It turns out pf incorrectly updates the TCP checksum if the TCP option
    we're modifying is not 2-byte algined with respect to the start of the
    packet.
    
    Create a TCP packet with such an option and throw it through a scrub
    rule, which will update timestamps and modify the packet.
    
    PR:             240416
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D27688
---
 tests/sys/netpfil/common/pft_ping.py | 69 +++++++++++++++++++++++++++--
 tests/sys/netpfil/pf/Makefile        |  1 +
 tests/sys/netpfil/pf/checksum.sh     | 85 ++++++++++++++++++++++++++++++++++++
 3 files changed, 151 insertions(+), 4 deletions(-)

diff --git a/tests/sys/netpfil/common/pft_ping.py b/tests/sys/netpfil/common/pft_ping.py
index 5d347adf8796..d960426e4b42 100644
--- a/tests/sys/netpfil/common/pft_ping.py
+++ b/tests/sys/netpfil/common/pft_ping.py
@@ -28,6 +28,7 @@
 
 import argparse
 import scapy.all as sp
+import socket
 import sys
 from sniffer import Sniffer
 
@@ -113,6 +114,53 @@ def ping6(send_if, dst_ip, args):
 	req = ether / ip6 / icmp
 	sp.sendp(req, iface=send_if, verbose=False)
 
+def check_tcpsyn(args, packet):
+	dst_ip = args.to[0]
+
+	ip = packet.getlayer(sp.IP)
+	if not ip:
+		return False
+	if ip.dst != dst_ip:
+		return False
+
+	tcp = packet.getlayer(sp.TCP)
+	if not tcp:
+		return False
+
+	# Verify IP checksum
+	chksum = ip.chksum
+	ip.chksum = None
+	new_chksum = sp.IP(sp.raw(ip)).chksum
+	if chksum != new_chksum:
+		print("Expected IP checksum %x but found %x\n" % (new_cshkum, chksum))
+		return False
+
+	# Verify TCP checksum
+	chksum = tcp.chksum
+	packet_raw = sp.raw(packet)
+	tcp.chksum = None
+	newpacket = sp.Ether(sp.raw(packet[sp.Ether]))
+	new_chksum = newpacket[sp.TCP].chksum
+	if chksum != new_chksum:
+		print("Expected TCP checksum %x but found %x\n" % (new_chksum, chksum))
+		return False
+
+	return True
+
+def tcpsyn(send_if, dst_ip, args):
+	opts=[('Timestamp', (1, 1)), ('MSS', 1280)]
+
+	if args.tcpopt_unaligned:
+		opts = [('NOP', 0 )] + opts
+
+	ether = sp.Ether()
+	ip = sp.IP(dst=dst_ip)
+	tcp = sp.TCP(dport=666, flags='S', options=opts)
+
+	req = ether / ip / tcp
+	sp.sendp(req, iface=send_if, verbose=False)
+
+
 def main():
 	parser = argparse.ArgumentParser("pft_ping.py",
 		description="Ping test tool")
@@ -127,6 +175,12 @@ def main():
 		required=True,
 		help='The destination IP address for the ICMP echo request')
 
+	# TCP options
+	parser.add_argument('--tcpsyn', action='store_true',
+			help='Send a TCP SYN packet')
+	parser.add_argument('--tcpopt_unaligned', action='store_true',
+			help='Include unaligned TCP options')
+
 	# Packet settings
 	parser.add_argument('--send-tos', nargs=1,
 		help='Set the ToS value for the transmitted packet')
@@ -142,12 +196,19 @@ def main():
 
 	sniffer = None
 	if not args.recvif is None:
-		sniffer = Sniffer(args, check_ping_request)
+		checkfn=check_ping_request
+		if args.tcpsyn:
+			checkfn=check_tcpsyn
 
-	if args.ip6:
-		ping6(args.sendif[0], args.to[0], args)
+		sniffer = Sniffer(args, checkfn)
+
+	if args.tcpsyn:
+		tcpsyn(args.sendif[0], args.to[0], args)
 	else:
-		ping(args.sendif[0], args.to[0], args)
+		if args.ip6:
+			ping6(args.sendif[0], args.to[0], args)
+		else:
+			ping(args.sendif[0], args.to[0], args)
 
 	if sniffer:
 		sniffer.join()
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
index 73c85320c013..68f54c801297 100644
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -6,6 +6,7 @@ TESTSDIR=       ${TESTSBASE}/sys/netpfil/pf
 TESTS_SUBDIRS+=	ioctl
 
 ATF_TESTS_SH+=	anchor \
+		checksum \
 		forward \
 		fragmentation \
 		icmp \
diff --git a/tests/sys/netpfil/pf/checksum.sh b/tests/sys/netpfil/pf/checksum.sh
new file mode 100644
index 000000000000..778ae1cd6e9f
--- /dev/null
+++ b/tests/sys/netpfil/pf/checksum.sh
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+#
+# Copyright (c) 2020 Kristof Provost <kp at FreeBSD.org>
+#
+# 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)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "unaligned" "cleanup"
+unaligned_head()
+{
+	atf_set descr 'Test unaligned checksum updates'
+	atf_set require.user root
+}
+
+unaligned_body()
+{
+	pft_init
+
+	epair_in=$(vnet_mkepair)
+	epair_out=$(vnet_mkepair)
+
+	vnet_mkjail alcatraz ${epair_in}b ${epair_out}a
+
+	ifconfig ${epair_in}a 192.0.2.2/24 up
+	route add -net 198.51.100.0/24 192.0.2.1
+
+	jexec alcatraz ifconfig ${epair_in}b 192.0.2.1/24 up
+	jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+	jexec alcatraz ifconfig ${epair_out}a 198.51.100.1/24 up
+	jexec alcatraz arp -s 198.51.100.2 00:01:02:03:04:05
+
+	ifconfig ${epair_out}b up
+
+	jexec alcatraz pfctl -e
+	pft_set_rules alcatraz \
+		"scrub on ${epair_in}b reassemble tcp max-mss 1200"
+
+	# Check aligned
+	atf_check -s exit:0 ${common_dir}/pft_ping.py \
+		--sendif ${epair_in}a \
+		--to 198.51.100.2 \
+		--recvif ${epair_out}b \
+		--tcpsyn
+
+	# And unaligned
+	atf_check -s exit:0 ${common_dir}/pft_ping.py \
+		--sendif ${epair_in}a \
+		--to 198.51.100.2 \
+		--recvif ${epair_out}b \
+		--tcpsyn \
+		--tcpopt_unaligned
+}
+
+unaligned_cleanup()
+{
+	pft_cleanup
+}
+
+atf_init_test_cases()
+{
+	atf_add_test_case "unaligned"
+}


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