Page MenuHomeFreeBSD

D37876.diff
No OneTemporary

D37876.diff

diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc
--- a/ObsoleteFiles.inc
+++ b/ObsoleteFiles.inc
@@ -52,6 +52,9 @@
# xargs -n1 | sort | uniq -d;
# done
+# 202XXXXX: remove injection.py test script
+OLD_FILES+=usr/tests/sbin/ping/injection.py
+
# 20221214: TCPDEBUG removed
OLD_FILES+=usr/include/netinet/tcp_debug.h
diff --git a/sbin/ping/tests/Makefile b/sbin/ping/tests/Makefile
--- a/sbin/ping/tests/Makefile
+++ b/sbin/ping/tests/Makefile
@@ -6,13 +6,13 @@
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
+${PACKAGE}FILES+= pinger.py
+
+${PACKAGE}FILESMODE_pinger.py= 0555
.include <bsd.test.mk>
diff --git a/sbin/ping/tests/injection.py b/sbin/ping/tests/injection.py
deleted file mode 100644
--- a/sbin/ping/tests/injection.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#! /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
--- a/sbin/ping/tests/ping_test.sh
+++ b/sbin/ping/tests/ping_test.sh
@@ -27,6 +27,8 @@
#
# $FreeBSD$
+. $(atf_get_srcdir)/../../../tests/sys/common/vnet.subr
+
require_ipv4()
{
if ! getaddrinfo -f inet localhost 1>/dev/null 2>&1; then
@@ -162,11 +164,22 @@
}
inject_opts_body()
{
- atf_check -s exit:0 -o match:"wrong total length" -o match:"NOP" python3 $(atf_get_srcdir)/injection.py opts
+ vnet_init
+ tun=$(vnet_mktun)
+ vnet_mkjail BRL $tun
+
+ atf_check -s exit:0 -o match:"wrong total length" -o match:"NOP" \
+ jexec BRL $(atf_get_srcdir)/pinger.py \
+ --iface $tun \
+ --src 192.0.2.1 \
+ --dst 192.0.2.2 \
+ --icmp_type 0 \
+ --icmp_code 0 \
+ --opts NOP-40
}
inject_opts_cleanup()
{
- ifconfig `cat tun.txt` destroy
+ vnet_cleanup
}
atf_test_case "inject_pip" "cleanup"
@@ -178,11 +191,23 @@
}
inject_pip_body()
{
- atf_check -s exit:2 -o match:"Destination Host Unreachable" -o not-match:"01010101" python3 $(atf_get_srcdir)/injection.py pip
+ vnet_init
+ tun=$(vnet_mktun)
+ vnet_mkjail BRL $tun
+
+ # XXX This test is wrong. It should match 40 NOPs (not not-match 4 NOPs).
+ atf_check -s exit:2 -o match:"Destination Host Unreachable" -o not-match:"01010101" \
+ jexec BRL $(atf_get_srcdir)/pinger.py \
+ --iface $tun \
+ --src 192.0.2.1 \
+ --dst 192.0.2.2 \
+ --icmp_type 3 \
+ --icmp_code 1 \
+ --opts NOP-40
}
inject_pip_cleanup()
{
- ifconfig `cat tun.txt` destroy
+ vnet_cleanup
}
# This is redundant with the ping_ tests, but it serves to ensure that scapy.py
@@ -196,11 +221,21 @@
}
inject_reply_body()
{
- atf_check -s exit:0 -o match:"1 packets transmitted, 1 packets received" python3 $(atf_get_srcdir)/injection.py reply
+ vnet_init
+ tun=$(vnet_mktun)
+ vnet_mkjail BRL $tun
+
+ atf_check -s exit:0 -o match:"1 packets transmitted, 1 packets received" \
+ jexec BRL $(atf_get_srcdir)/pinger.py \
+ --iface $tun \
+ --src 192.0.2.1 \
+ --dst 192.0.2.2 \
+ --icmp_type 0 \
+ --icmp_code 0
}
inject_reply_cleanup()
{
- ifconfig `cat tun.txt` destroy
+ vnet_cleanup
}
atf_init_test_cases()
diff --git a/sbin/ping/tests/pinger.py b/sbin/ping/tests/pinger.py
new file mode 100644
--- /dev/null
+++ b/sbin/ping/tests/pinger.py
@@ -0,0 +1,254 @@
+#!/usr/bin/env python3
+
+import sys
+import subprocess
+import argparse
+import logging
+
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+
+import scapy.all as sc
+
+routing_options = [
+ "RR",
+ "RR-same",
+ "RR-trunc",
+ "LSRR",
+ "LSRR-trunc",
+ "SSRR",
+ "SSRR-trunc",
+]
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(
+ prog="pinger.py",
+ description="P I N G E R",
+ epilog="This utility creates a tun interface, "
+ "sends an echo request, and forges the reply.",
+ )
+ # Required arguments
+ # Avoid setting defaults on these arguments,
+ # as we want to set them explicitly in the tests
+ parser.add_argument(
+ "--iface", type=str, required=True, help="Interface to send packet to"
+ )
+ parser.add_argument(
+ "--src", type=str, required=True, help="Source packet IP"
+ )
+ parser.add_argument(
+ "--dst", type=str, required=True, help="Destination packet IP"
+ )
+ parser.add_argument(
+ "--icmp_type", type=int, required=True, help="ICMP type"
+ )
+ parser.add_argument(
+ "--icmp_code", type=int, required=True, help="ICMP code"
+ )
+ # IP arguments
+ parser.add_argument(
+ "--flags", type=str, default="", choices=["DF", "MF"], help="IP flags"
+ )
+ parser.add_argument(
+ "--opts",
+ type=str,
+ default="",
+ choices=["EOL", "NOP", "NOP-40", "unk", "unk-40"] + routing_options,
+ help="Include IP options",
+ )
+ parser.add_argument(
+ "--special",
+ type=str,
+ default="",
+ choices=["tcp", "udp", "wrong", "warp"],
+ help="Send a special packet",
+ )
+ # ICMP arguments
+ # Match names with <netinet/ip_icmp.h>
+ parser.add_argument(
+ "--icmp_pptr", type=int, default=0, help="ICMP pointer"
+ )
+ parser.add_argument(
+ "--icmp_gwaddr",
+ type=str,
+ default="0.0.0.0",
+ help="ICMP gateway IP address",
+ )
+ parser.add_argument(
+ "--icmp_nextmtu", type=int, default=0, help="ICMP next MTU"
+ )
+ parser.add_argument(
+ "--icmp_otime", type=int, default=0, help="ICMP originate timestamp"
+ )
+ parser.add_argument(
+ "--icmp_rtime", type=int, default=0, help="ICMP receive timestamp"
+ )
+ parser.add_argument(
+ "--icmp_ttime", type=int, default=0, help="ICMP transmit timestamp"
+ )
+ parser.add_argument(
+ "--icmp_mask", type=str, default="0.0.0.0", help="ICMP address mask"
+ )
+ parser.add_argument(
+ "--request",
+ type=str,
+ default="",
+ choices=["mask", "timestamp"],
+ help="Request type",
+ )
+ # Miscellaneous arguments
+ parser.add_argument(
+ "--count", type=int, default=1, help="Number of packets to send"
+ )
+ parser.add_argument("--dup", action="store_true", help="Duplicate packets")
+ parser.add_argument("--version", action="version", version="%(prog)s 1.0")
+
+ return parser.parse_args()
+
+
+def construct_response_packet(echo, ip, icmp, special):
+ icmp_id_seq_types = [0, 8, 13, 14, 15, 16, 17, 18, 37, 38]
+ oip = echo[sc.IP]
+ oicmp = echo[sc.ICMP]
+ load = echo[sc.ICMP].payload
+ oip[sc.IP].remove_payload()
+ oicmp[sc.ICMP].remove_payload()
+ oicmp.type = 8
+
+ # As if the original IP packet had these set
+ oip.ihl = None
+ oip.len = None
+ oip.id = 1
+ oip.flags = ip.flags
+ oip.chksum = None
+ oip.options = ip.options
+
+ # Special options
+ if special == "tcp":
+ oip.proto = "tcp"
+ tcp = sc.TCP(sport=1234, dport=5678)
+ return ip / icmp / oip / tcp
+ if special == "udp":
+ oip.proto = "udp"
+ udp = sc.UDP(sport=1234, dport=5678)
+ return ip / icmp / oip / udp
+ if special == "warp":
+ # Build a package with a timestamp of INT_MAX
+ # (time-warped package)
+ payload_no_timestamp = sc.bytes_hex(load)[16:]
+ load = (b"\xff" * 8) + sc.hex_bytes(payload_no_timestamp)
+ if special == "wrong":
+ # Build a package with a wrong last byte
+ payload_no_last_byte = sc.bytes_hex(load)[:-2]
+ load = (sc.hex_bytes(payload_no_last_byte)) + b"\x00"
+
+ if icmp.type in icmp_id_seq_types:
+ pkt = ip / icmp / load
+ else:
+ ip.options = ""
+ pkt = ip / icmp / oip / oicmp / load
+
+ return pkt
+
+
+def generate_ip_options(opts):
+ routers = [
+ "192.0.2.10",
+ "192.0.2.20",
+ "192.0.2.30",
+ "192.0.2.40",
+ "192.0.2.50",
+ "192.0.2.60",
+ "192.0.2.70",
+ "192.0.2.80",
+ "192.0.2.90",
+ ]
+ routers_zero = [0, 0, 0, 0, 0, 0, 0, 0, 0]
+ if opts == "EOL":
+ options = sc.IPOption(b"\x00")
+ elif opts == "NOP":
+ options = sc.IPOption(b"\x01")
+ elif opts == "NOP-40":
+ options = sc.IPOption(b"\x01" * 40)
+ elif opts == "RR":
+ options = sc.IPOption_RR(pointer=40, routers=routers)
+ elif opts == "RR-same":
+ options = sc.IPOption_RR(pointer=3, routers=routers_zero)
+ elif opts == "RR-trunc":
+ options = sc.IPOption_RR(length=7, routers=routers_zero)
+ elif opts == "LSRR":
+ subprocess.run(["sysctl", "net.inet.ip.process_options=0"], check=True)
+ options = sc.IPOption_LSRR(routers=routers)
+ elif opts == "LSRR-trunc":
+ subprocess.run(["sysctl", "net.inet.ip.process_options=0"], check=True)
+ options = sc.IPOption_LSRR(length=3, routers=routers_zero)
+ elif opts == "SSRR":
+ subprocess.run(["sysctl", "net.inet.ip.process_options=0"], check=True)
+ options = sc.IPOption_SSRR(routers=routers)
+ elif opts == "SSRR-trunc":
+ subprocess.run(["sysctl", "net.inet.ip.process_options=0"], check=True)
+ options = sc.IPOption_SSRR(length=3, routers=routers_zero)
+ elif opts == "unk":
+ subprocess.run(["sysctl", "net.inet.ip.process_options=0"], check=True)
+ options = sc.IPOption(b"\x9f")
+ elif opts == "unk-40":
+ subprocess.run(["sysctl", "net.inet.ip.process_options=0"], check=True)
+ options = sc.IPOption(b"\x9f" * 40)
+ else:
+ options = ""
+
+ return options
+
+
+def main():
+ """P I N G E R"""
+ args = parse_args()
+ opts = generate_ip_options(args.opts)
+ ip = sc.IP(flags=args.flags, src=args.dst, dst=args.src, options=opts)
+ tun = sc.TunTapInterface(args.iface)
+ subprocess.run(["ifconfig", tun.iface, "up"], check=True)
+ subprocess.run(["ifconfig", tun.iface, args.src, args.dst], check=True)
+ command = [
+ "/sbin/ping",
+ "-c",
+ str(args.count),
+ "-t",
+ str(args.count),
+ "-v",
+ ]
+ if args.request == "mask":
+ command += ["-Mm"]
+ if args.request == "timestamp":
+ command += ["-Mt"]
+ if args.special != "":
+ command += ["-p1"]
+ if args.opts in routing_options:
+ command += ["-R"]
+ command += [args.dst]
+ with subprocess.Popen(args=command, text=True) as ping:
+ for dummy in range(args.count):
+ echo = tun.recv()
+ icmp = sc.ICMP(
+ type=args.icmp_type,
+ code=args.icmp_code,
+ id=echo[sc.ICMP].id,
+ seq=echo[sc.ICMP].seq,
+ ts_ori=args.icmp_otime,
+ ts_rx=args.icmp_rtime,
+ ts_tx=args.icmp_ttime,
+ gw=args.icmp_gwaddr,
+ ptr=args.icmp_pptr,
+ addr_mask=args.icmp_mask,
+ nexthopmtu=args.icmp_nextmtu,
+ )
+ pkt = construct_response_packet(echo, ip, icmp, args.special)
+ tun.send(pkt)
+ if args.dup is True:
+ tun.send(pkt)
+ ping.communicate()
+
+ sys.exit(ping.returncode)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/sys/common/vnet.subr b/tests/sys/common/vnet.subr
--- a/tests/sys/common/vnet.subr
+++ b/tests/sys/common/vnet.subr
@@ -47,6 +47,13 @@
echo ${ifname}
}
+vnet_mktun()
+{
+ ifname=$(ifconfig tun create)
+ list_interface $ifname
+ echo ${ifname}
+}
+
vnet_mkjail()
{
jailname=$1

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 26, 4:43 AM (16 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17798187
Default Alt Text
D37876.diff (13 KB)

Event Timeline