git: 04535d6a572a - stable/13 - pf tests: Test unicast reverse path forwarding check

Kristof Provost kp at FreeBSD.org
Tue Feb 23 11:50:41 UTC 2021


The branch stable/13 has been updated by kp:

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

commit 04535d6a572ac63b448a5def4525947bd00d9f8a
Author:     Kristof Provost <kp at FreeBSD.org>
AuthorDate: 2021-02-15 21:16:36 +0000
Commit:     Kristof Provost <kp at FreeBSD.org>
CommitDate: 2021-02-23 11:46:25 +0000

    pf tests: Test unicast reverse path forwarding check
    
    Ensure that pf's urpf-failed keyword works as expected.
    
    PR:             253479
    MFC after:      1 week
    Reviewed by:    melifaro@
    Differential Revision:  https://reviews.freebsd.org/D28694
    
    (cherry picked from commit 6b52139eb8e8eda0ea263b24735556194f918642)
---
 tests/sys/netpfil/common/pft_ping.py | 52 ++++++++++++++++++++++++++++
 tests/sys/netpfil/pf/pass_block.sh   | 67 ++++++++++++++++++++++++++++++++++++
 2 files changed, 119 insertions(+)

diff --git a/tests/sys/netpfil/common/pft_ping.py b/tests/sys/netpfil/common/pft_ping.py
index 812250803309..957123e4f6f8 100644
--- a/tests/sys/netpfil/common/pft_ping.py
+++ b/tests/sys/netpfil/common/pft_ping.py
@@ -115,6 +115,35 @@ def check_ping6_request(args, packet):
 
 	return True
 
+def check_ping_reply(args, packet):
+	return check_ping4_reply(args, packet)
+
+def check_ping4_reply(args, packet):
+	"""
+	Check that this is a reply to the ping request we sent
+	"""
+	dst_ip = args.to[0]
+
+	ip = packet.getlayer(sp.IP)
+	if not ip:
+		return False
+	if ip.src != dst_ip:
+		return False
+
+	icmp = packet.getlayer(sp.ICMP)
+	if not icmp:
+		return False
+	if sp.icmptypes[icmp.type] != 'echo-reply':
+		return False
+
+	raw = packet.getlayer(sp.Raw)
+	if not raw:
+		return False
+	if raw.load != PAYLOAD_MAGIC:
+		return False
+
+	return True
+
 def ping(send_if, dst_ip, args):
 	ether = sp.Ether()
 	ip = sp.IP(dst=dst_ip)
@@ -124,6 +153,9 @@ def ping(send_if, dst_ip, args):
 	if args.send_tos:
 		ip.tos = int(args.send_tos[0])
 
+	if args.fromaddr:
+		ip.src = args.fromaddr[0]
+
 	req = ether / ip / icmp / raw
 	sp.sendp(req, iface=send_if, verbose=False)
 
@@ -132,6 +164,9 @@ def ping6(send_if, dst_ip, args):
 	ip6 = sp.IPv6(dst=dst_ip)
 	icmp = sp.ICMPv6EchoRequest(data=sp.raw(PAYLOAD_MAGIC))
 
+	if args.fromaddr:
+		ip.src = args.fromaddr[0]
+
 	req = ether / ip6 / icmp
 	sp.sendp(req, iface=send_if, verbose=False)
 
@@ -189,6 +224,8 @@ def main():
 		required=True,
 		help='The interface through which the packet(s) will be sent')
 	parser.add_argument('--recvif', nargs=1,
+		help='The interface on which to expect the ICMP echo request')
+	parser.add_argument('--replyif', nargs=1,
 		help='The interface on which to expect the ICMP echo response')
 	parser.add_argument('--checkdup', nargs=1,
 		help='The interface on which to expect the duplicated ICMP packets')
@@ -197,6 +234,8 @@ def main():
 	parser.add_argument('--to', nargs=1,
 		required=True,
 		help='The destination IP address for the ICMP echo request')
+	parser.add_argument('--fromaddr', nargs=1,
+		help='The source IP address for the ICMP echo request')
 
 	# TCP options
 	parser.add_argument('--tcpsyn', action='store_true',
@@ -225,6 +264,11 @@ def main():
 
 		sniffer = Sniffer(args, checkfn)
 
+	replysniffer = None
+	if not args.replyif is None:
+		checkfn=check_ping_reply
+		replysniffer = Sniffer(args, checkfn, recvif=args.replyif[0])
+
 	dupsniffer = None
 	if args.checkdup is not None:
 		dupsniffer = Sniffer(args, check_dup, recvif=args.checkdup[0])
@@ -250,5 +294,13 @@ def main():
 		else:
 			sys.exit(1)
 
+	if replysniffer:
+		replysniffer.join()
+
+		if replysniffer.foundCorrectPacket:
+			sys.exit(0)
+		else:
+			sys.exit(1)
+
 if __name__ == '__main__':
 	main()
diff --git a/tests/sys/netpfil/pf/pass_block.sh b/tests/sys/netpfil/pf/pass_block.sh
index 139adb43bddd..589b89891729 100644
--- a/tests/sys/netpfil/pf/pass_block.sh
+++ b/tests/sys/netpfil/pf/pass_block.sh
@@ -27,6 +27,8 @@
 
 . $(atf_get_srcdir)/utils.subr
 
+common_dir=$(atf_get_srcdir)/../common
+
 atf_test_case "v4" "cleanup"
 v4_head()
 {
@@ -189,10 +191,75 @@ nested_inline_cleanup()
 	pft_cleanup
 }
 
+atf_test_case "urpf" "cleanup"
+urpf_head()
+{
+	atf_set descr "Test unicast reverse path forwarding check"
+	atf_set require.user root
+	atf_set require.progs scapy
+}
+
+urpf_body()
+{
+	pft_init
+
+	epair_one=$(vnet_mkepair)
+	epair_two=$(vnet_mkepair)
+
+	vnet_mkjail alcatraz ${epair_one}b ${epair_two}b
+
+	ifconfig ${epair_one}a 192.0.2.2/24 up
+	ifconfig ${epair_two}a 198.51.100.2/24 up
+
+	jexec alcatraz ifconfig ${epair_one}b 192.0.2.1/24 up
+	jexec alcatraz ifconfig ${epair_two}b 198.51.100.1/24 up
+	jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+	# Sanity checks
+	atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1
+	atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.1
+	atf_check -s exit:0 ${common_dir}/pft_ping.py \
+		--sendif ${epair_one}a \
+		--to 192.0.2.1 \
+		--fromaddr 198.51.100.2 \
+		--replyif ${epair_two}a
+	atf_check -s exit:0 ${common_dir}/pft_ping.py \
+		--sendif ${epair_two}a \
+		--to 198.51.100.1 \
+		--fromaddr 192.0.2.2 \
+		--replyif ${epair_one}a
+
+	pft_set_rules alcatraz \
+		"block quick from urpf-failed"
+	jexec alcatraz pfctl -e
+
+	# Correct source still works
+	atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1
+	atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.1
+
+	# Unexpected source interface is blocked
+	atf_check -s exit:1 ${common_dir}/pft_ping.py \
+		--sendif ${epair_one}a \
+		--to 192.0.2.1 \
+		--fromaddr 198.51.100.2 \
+		--replyif ${epair_two}a
+	atf_check -s exit:1 ${common_dir}/pft_ping.py \
+		--sendif ${epair_two}a \
+		--to 198.51.100.1 \
+		--fromaddr 192.0.2.2 \
+		--replyif ${epair_one}a
+}
+
+urpf_cleanup()
+{
+	pft_cleanup
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case "v4"
 	atf_add_test_case "v6"
 	atf_add_test_case "noalias"
 	atf_add_test_case "nested_inline"
+	atf_add_test_case "urpf"
 }


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