aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Somers <asomers@FreeBSD.org>2022-10-29 22:03:41 +0000
committerAlan Somers <asomers@FreeBSD.org>2023-01-27 19:27:47 +0000
commitd3deff397d7bc83abe9f1a236b7c7a57f457313d (patch)
tree447333f51446a0d20129f0b368ffeb801c38d043
parent6d28cb5486b83f16f0925c4fbb620ee96e9cb7db (diff)
downloadsrc-d3deff397d7bc83abe9f1a236b7c7a57f457313d.tar.gz
src-d3deff397d7bc83abe9f1a236b7c7a57f457313d.zip
Add test cases for ping with IP options in the response
Reviewed by: markj Differential Revision: https://reviews.freebsd.org/D37210 (cherry picked from commit e35cfc606a299ef40767e708362529c370f767f5)
-rw-r--r--sbin/ping/tests/Makefile4
-rw-r--r--sbin/ping/tests/injection.py83
-rw-r--r--sbin/ping/tests/ping_test.sh53
3 files changed, 140 insertions, 0 deletions
diff --git a/sbin/ping/tests/Makefile b/sbin/ping/tests/Makefile
index c89d522a0dee..c6845ac57e5c 100644
--- a/sbin/ping/tests/Makefile
+++ b/sbin/ping/tests/Makefile
@@ -6,9 +6,13 @@ SRCS.in_cksum_test= in_cksum_test.c ../utils.c
PACKAGE= tests
ATF_TESTS_SH+= ping_test
+# Exclusive because each injection test case uses the same IP addresses
+TEST_METADATA.ping_test+= is_exclusive="true"
+
${PACKAGE}FILES+= ping_c1_s56_t1.out
${PACKAGE}FILES+= ping_6_c1_s8_t1.out
${PACKAGE}FILES+= ping_c1_s56_t1_S127.out
${PACKAGE}FILES+= ping_c1_s8_t1_S1.out
+${PACKAGE}FILES+= injection.py
.include <bsd.test.mk>
diff --git a/sbin/ping/tests/injection.py b/sbin/ping/tests/injection.py
new file mode 100644
index 000000000000..d46359a0c9f7
--- /dev/null
+++ b/sbin/ping/tests/injection.py
@@ -0,0 +1,83 @@
+#! /usr/bin/env python3
+# Used to inject various malformed packets
+
+import errno
+import logging
+import subprocess
+import sys
+
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+
+from scapy.all import IP, ICMP, IPOption
+import scapy.layers.all
+from scapy.layers.inet import ICMPEcho_am
+from scapy.layers.tuntap import TunTapInterface
+
+SRC_ADDR = "192.0.2.14"
+DST_ADDR = "192.0.2.15"
+
+mode = sys.argv[1]
+ip = None
+
+# fill opts with nop (0x01)
+opts = b''
+for x in range(40):
+ opts += b'\x01'
+
+
+# Create and configure a tun interface with an RFC5737 nonrouteable address
+create_proc = subprocess.run(
+ args=["ifconfig", "tun", "create"],
+ capture_output=True,
+ check=True,
+ text=True)
+iface = create_proc.stdout.strip()
+tun = TunTapInterface(iface)
+with open("tun.txt", "w") as f:
+ f.write(iface)
+subprocess.run(["ifconfig", tun.iface, "up"])
+subprocess.run(["ifconfig", tun.iface, SRC_ADDR, DST_ADDR])
+
+ping = subprocess.Popen(
+ args=["/sbin/ping", "-v", "-c1", "-t1", DST_ADDR],
+ text=True
+)
+# Wait for /sbin/ping to ping us
+echo_req = tun.recv()
+
+# Construct the response packet
+if mode == "opts":
+ # Sending reply with IP options
+ echo_reply = IP(
+ dst=SRC_ADDR,
+ src=DST_ADDR,
+ options=IPOption(opts)
+ )/ICMP(type=0, code=0, id=echo_req.payload.id)/echo_req.payload.payload
+elif mode == "pip":
+ # packet in packet (inner has options)
+
+ inner = IP(
+ dst=SRC_ADDR,
+ src=DST_ADDR,
+ options=IPOption(opts)
+ )/ICMP(type=0, code=0, id=echo_req.payload.id)/echo_req.payload.payload
+ outer = IP(
+ dst=SRC_ADDR,
+ src=DST_ADDR
+ )/ICMP(type=3, code=1) # host unreach
+
+ echo_reply = outer/inner
+elif mode == "reply":
+ # Sending normal echo reply
+ echo_reply = IP(
+ dst=SRC_ADDR,
+ src=DST_ADDR,
+ )/ICMP(type=0, code=0, id=echo_req.payload.id)/echo_req.payload.payload
+else:
+ print("unknown mode {}".format(mode))
+ exit(1)
+
+tun.send(echo_reply)
+outs, errs = ping.communicate()
+
+sys.exit(ping.returncode)
diff --git a/sbin/ping/tests/ping_test.sh b/sbin/ping/tests/ping_test.sh
index c79a792d0eb0..9f821ed96360 100644
--- a/sbin/ping/tests/ping_test.sh
+++ b/sbin/ping/tests/ping_test.sh
@@ -153,6 +153,56 @@ ping6_46_body()
ping6 -4 -6 localhost
}
+atf_test_case "inject_opts" "cleanup"
+inject_opts_head()
+{
+ atf_set "descr" "Inject an ECHO REPLY with IP options"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "python3" "scapy"
+}
+inject_opts_body()
+{
+ atf_check -s exit:0 -o match:"wrong total length" -o match:"NOP" python3 $(atf_get_srcdir)/injection.py opts
+}
+inject_opts_cleanup()
+{
+ ifconfig `cat tun.txt` destroy
+}
+
+atf_test_case "inject_pip" "cleanup"
+inject_pip_head()
+{
+ atf_set "descr" "Inject an ICMP error with a quoted packet with IP options"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "python3" "scapy"
+}
+inject_pip_body()
+{
+ atf_check -s exit:2 -o match:"Destination Host Unreachable" -o not-match:"01010101" python3 $(atf_get_srcdir)/injection.py pip
+}
+inject_pip_cleanup()
+{
+ ifconfig `cat tun.txt` destroy
+}
+
+# This is redundant with the ping_ tests, but it serves to ensure that scapy.py
+# is working correctly.
+atf_test_case "inject_reply" "cleanup"
+inject_reply_head()
+{
+ atf_set "descr" "Basic ping test with packet injection"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "python3" "scapy"
+}
+inject_reply_body()
+{
+ atf_check -s exit:0 -o match:"1 packets transmitted, 1 packets received" python3 $(atf_get_srcdir)/injection.py reply
+}
+inject_reply_cleanup()
+{
+ ifconfig `cat tun.txt` destroy
+}
+
atf_init_test_cases()
{
atf_add_test_case ping_c1_s56_t1
@@ -164,6 +214,9 @@ atf_init_test_cases()
atf_add_test_case ping6_c1t4
atf_add_test_case ping_46
atf_add_test_case ping6_46
+ atf_add_test_case inject_opts
+ atf_add_test_case inject_pip
+ atf_add_test_case inject_reply
}
check_ping_statistics()