Index: head/ObsoleteFiles.inc =================================================================== --- head/ObsoleteFiles.inc +++ head/ObsoleteFiles.inc @@ -38,6 +38,9 @@ # xargs -n1 | sort | uniq -d; # done +# 20190817: pft_ping.py and sniffer.py moved to /usr/tests/sys/netpfil/common +OLD_FILES+=usr/tests/sys/netpfil/pf/sniffer.py +OLD_FILES+=usr/tests/sys/netpfil/pf/pft_ping.py # 20190816: dir.h removed from POSIX OLD_FILES+=usr/include/sys/dir.h # 20190729: gzip'ed a.out support removed Index: head/tests/sys/netpfil/common/Makefile =================================================================== --- head/tests/sys/netpfil/common/Makefile +++ head/tests/sys/netpfil/common/Makefile @@ -11,6 +11,10 @@ ${PACKAGE}FILES+= \ utils.subr \ - runner.subr + runner.subr \ + pft_ping.py \ + sniffer.py + +${PACKAGE}FILESMODE_pft_ping.py= 0555 .include Index: head/tests/sys/netpfil/common/pft_ping.py =================================================================== --- head/tests/sys/netpfil/common/pft_ping.py +++ head/tests/sys/netpfil/common/pft_ping.py @@ -0,0 +1,135 @@ +#!/usr/local/bin/python2.7 + +import argparse +import scapy.all as sp +import sys +from sniffer import Sniffer + +PAYLOAD_MAGIC = 0x42c0ffee + +def check_ping_request(args, packet): + if args.ip6: + return check_ping6_request(args, packet) + else: + return check_ping4_request(args, packet) + +def check_ping4_request(args, packet): + """ + Verify that the packet matches what we'd have sent + """ + dst_ip = args.to[0] + + ip = packet.getlayer(sp.IP) + if not ip: + return False + if ip.dst != dst_ip: + return False + + icmp = packet.getlayer(sp.ICMP) + if not icmp: + return False + if sp.icmptypes[icmp.type] != 'echo-request': + return False + + raw = packet.getlayer(sp.Raw) + if not raw: + return False + if raw.load != str(PAYLOAD_MAGIC): + return False + + # Wait to check expectations until we've established this is the packet we + # sent. + if args.expect_tos: + if ip.tos != int(args.expect_tos[0]): + print "Unexpected ToS value %d, expected %s" \ + % (ip.tos, args.expect_tos[0]) + return False + + return True + +def check_ping6_request(args, packet): + """ + Verify that the packet matches what we'd have sent + """ + dst_ip = args.to[0] + + ip = packet.getlayer(sp.IPv6) + if not ip: + return False + if ip.dst != dst_ip: + return False + + icmp = packet.getlayer(sp.ICMPv6EchoRequest) + if not icmp: + return False + if icmp.data != str(PAYLOAD_MAGIC): + return False + + return True + +def ping(send_if, dst_ip, args): + ether = sp.Ether() + ip = sp.IP(dst=dst_ip) + icmp = sp.ICMP(type='echo-request') + raw = sp.Raw(str(PAYLOAD_MAGIC)) + + if args.send_tos: + ip.tos = int(args.send_tos[0]) + + req = ether / ip / icmp / raw + sp.sendp(req, iface=send_if, verbose=False) + +def ping6(send_if, dst_ip, args): + ether = sp.Ether() + ip6 = sp.IPv6(dst=dst_ip) + icmp = sp.ICMPv6EchoRequest(data=PAYLOAD_MAGIC) + + req = ether / ip6 / icmp + sp.sendp(req, iface=send_if, verbose=False) + +def main(): + parser = argparse.ArgumentParser("pft_ping.py", + description="Ping test tool") + 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, + help='The interface on which to expect the ICMP echo response') + parser.add_argument('--ip6', action='store_true', + help='Use IPv6') + parser.add_argument('--to', nargs=1, + required=True, + help='The destination IP address for the ICMP echo request') + + # Packet settings + parser.add_argument('--send-tos', nargs=1, + help='Set the ToS value for the transmitted packet') + + # Expectations + parser.add_argument('--expect-tos', nargs=1, + help='The expected ToS value in the received packet') + + args = parser.parse_args() + + # We may not have a default route. Tell scapy where to start looking for routes + sp.conf.iface6 = args.sendif[0] + + sniffer = None + if not args.recvif is None: + sniffer = Sniffer(args, check_ping_request) + + if args.ip6: + ping6(args.sendif[0], args.to[0], args) + else: + ping(args.sendif[0], args.to[0], args) + + if sniffer: + sniffer.join() + + if sniffer.foundCorrectPacket: + sys.exit(0) + else: + sys.exit(1) + +if __name__ == '__main__': + main() Index: head/tests/sys/netpfil/common/sniffer.py =================================================================== --- head/tests/sys/netpfil/common/sniffer.py +++ head/tests/sys/netpfil/common/sniffer.py @@ -0,0 +1,25 @@ +# $FreeBSD$ + +import threading +import scapy.all as sp + +class Sniffer(threading.Thread): + def __init__(self, args, check_function): + threading.Thread.__init__(self) + + self._args = args + self._recvif = args.recvif[0] + self._check_function = check_function + self.foundCorrectPacket = False + + self.start() + + def _checkPacket(self, packet): + ret = self._check_function(self._args, packet) + if ret: + self.foundCorrectPacket = True + return ret + + def run(self): + self.packets = sp.sniff(iface=self._recvif, + stop_filter=self._checkPacket, timeout=3) Index: head/tests/sys/netpfil/pf/Makefile =================================================================== --- head/tests/sys/netpfil/pf/Makefile +++ head/tests/sys/netpfil/pf/Makefile @@ -21,12 +21,9 @@ ${PACKAGE}FILES+= utils.subr \ echo_inetd.conf \ - sniffer.py \ - pft_ping.py \ CVE-2019-5597.py \ CVE-2019-5598.py -${PACKAGE}FILESMODE_pft_ping.py= 0555 ${PACKAGE}FILESMODE_CVE-2019-5597.py= 0555 ${PACKAGE}FILESMODE_CVE-2019-5598.py= 0555 Index: head/tests/sys/netpfil/pf/forward.sh =================================================================== --- head/tests/sys/netpfil/pf/forward.sh +++ head/tests/sys/netpfil/pf/forward.sh @@ -2,6 +2,8 @@ . $(atf_get_srcdir)/utils.subr +common_dir=$(atf_get_srcdir)/../common + atf_test_case "v4" "cleanup" v4_head() { @@ -43,20 +45,20 @@ # Forward with pf enabled pft_set_rules alcatraz "block in" - atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:1 ${common_dir}/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a pft_set_rules alcatraz "block out" - atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:1 ${common_dir}/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recv ${epair_recv}a # Allow ICMP pft_set_rules alcatraz "block in" "pass in proto icmp" - atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:0 ${common_dir}/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a @@ -98,7 +100,7 @@ route add -6 2001:db8:43::/64 2001:db8:42::2 # Sanity check, can we forward ICMP echo requests without pf? - atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:0 ${common_dir}/pft_ping.py \ --ip6 \ --sendif ${epair_send}a \ --to 2001:db8:43::3 \ @@ -109,7 +111,7 @@ # Block incoming echo request packets pft_set_rules alcatraz \ "block in inet6 proto icmp6 icmp6-type echoreq" - atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:1 ${common_dir}/pft_ping.py \ --ip6 \ --sendif ${epair_send}a \ --to 2001:db8:43::3 \ @@ -118,7 +120,7 @@ # Block outgoing echo request packets pft_set_rules alcatraz \ "block out inet6 proto icmp6 icmp6-type echoreq" - atf_check -s exit:1 -e ignore $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:1 -e ignore ${common_dir}/pft_ping.py \ --ip6 \ --sendif ${epair_send}a \ --to 2001:db8:43::3 \ @@ -128,7 +130,7 @@ pft_set_rules alcatraz \ "block out" \ "pass out inet6 proto icmp6" - atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:0 ${common_dir}/pft_ping.py \ --ip6 \ --sendif ${epair_send}a \ --to 2001:db8:43::3 \ @@ -138,7 +140,7 @@ pft_set_rules alcatraz \ "block out inet6 proto icmp6 icmp6-type echoreq" \ "pass in proto icmp" - atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:1 ${common_dir}/pft_ping.py \ --ip6 \ --sendif ${epair_send}a \ --to 2001:db8:43::3 \ Index: head/tests/sys/netpfil/pf/pft_ping.py =================================================================== --- head/tests/sys/netpfil/pf/pft_ping.py +++ head/tests/sys/netpfil/pf/pft_ping.py @@ -1,135 +0,0 @@ -#!/usr/local/bin/python2.7 - -import argparse -import scapy.all as sp -import sys -from sniffer import Sniffer - -PAYLOAD_MAGIC = 0x42c0ffee - -def check_ping_request(args, packet): - if args.ip6: - return check_ping6_request(args, packet) - else: - return check_ping4_request(args, packet) - -def check_ping4_request(args, packet): - """ - Verify that the packet matches what we'd have sent - """ - dst_ip = args.to[0] - - ip = packet.getlayer(sp.IP) - if not ip: - return False - if ip.dst != dst_ip: - return False - - icmp = packet.getlayer(sp.ICMP) - if not icmp: - return False - if sp.icmptypes[icmp.type] != 'echo-request': - return False - - raw = packet.getlayer(sp.Raw) - if not raw: - return False - if raw.load != str(PAYLOAD_MAGIC): - return False - - # Wait to check expectations until we've established this is the packet we - # sent. - if args.expect_tos: - if ip.tos != int(args.expect_tos[0]): - print "Unexpected ToS value %d, expected %s" \ - % (ip.tos, args.expect_tos[0]) - return False - - return True - -def check_ping6_request(args, packet): - """ - Verify that the packet matches what we'd have sent - """ - dst_ip = args.to[0] - - ip = packet.getlayer(sp.IPv6) - if not ip: - return False - if ip.dst != dst_ip: - return False - - icmp = packet.getlayer(sp.ICMPv6EchoRequest) - if not icmp: - return False - if icmp.data != str(PAYLOAD_MAGIC): - return False - - return True - -def ping(send_if, dst_ip, args): - ether = sp.Ether() - ip = sp.IP(dst=dst_ip) - icmp = sp.ICMP(type='echo-request') - raw = sp.Raw(str(PAYLOAD_MAGIC)) - - if args.send_tos: - ip.tos = int(args.send_tos[0]) - - req = ether / ip / icmp / raw - sp.sendp(req, iface=send_if, verbose=False) - -def ping6(send_if, dst_ip, args): - ether = sp.Ether() - ip6 = sp.IPv6(dst=dst_ip) - icmp = sp.ICMPv6EchoRequest(data=PAYLOAD_MAGIC) - - req = ether / ip6 / icmp - sp.sendp(req, iface=send_if, verbose=False) - -def main(): - parser = argparse.ArgumentParser("pft_ping.py", - description="Ping test tool") - 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, - help='The interface on which to expect the ICMP echo response') - parser.add_argument('--ip6', action='store_true', - help='Use IPv6') - parser.add_argument('--to', nargs=1, - required=True, - help='The destination IP address for the ICMP echo request') - - # Packet settings - parser.add_argument('--send-tos', nargs=1, - help='Set the ToS value for the transmitted packet') - - # Expectations - parser.add_argument('--expect-tos', nargs=1, - help='The expected ToS value in the received packet') - - args = parser.parse_args() - - # We may not have a default route. Tell scapy where to start looking for routes - sp.conf.iface6 = args.sendif[0] - - sniffer = None - if not args.recvif is None: - sniffer = Sniffer(args, check_ping_request) - - if args.ip6: - ping6(args.sendif[0], args.to[0], args) - else: - ping(args.sendif[0], args.to[0], args) - - if sniffer: - sniffer.join() - - if sniffer.foundCorrectPacket: - sys.exit(0) - else: - sys.exit(1) - -if __name__ == '__main__': - main() Index: head/tests/sys/netpfil/pf/set_tos.sh =================================================================== --- head/tests/sys/netpfil/pf/set_tos.sh +++ head/tests/sys/netpfil/pf/set_tos.sh @@ -2,6 +2,8 @@ . $(atf_get_srcdir)/utils.subr +common_dir=$(atf_get_srcdir)/../common + atf_test_case "v4" "cleanup" v4_head() { @@ -37,7 +39,7 @@ # No change is done if not requested pft_set_rules alcatraz "scrub out proto icmp" - atf_check -s exit:1 -o ignore $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:1 -o ignore ${common_dir}/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a \ @@ -45,7 +47,7 @@ # The requested ToS is set pft_set_rules alcatraz "scrub out proto icmp set-tos 42" - atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:0 ${common_dir}/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a \ @@ -53,7 +55,7 @@ # ToS is not changed if the scrub rule does not match pft_set_rules alcatraz "scrub out proto tcp set-tos 42" - atf_check -s exit:1 -o ignore $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:1 -o ignore ${common_dir}/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a \ @@ -62,14 +64,14 @@ # Multiple scrub rules match as expected pft_set_rules alcatraz "scrub out proto tcp set-tos 13" \ "scrub out proto icmp set-tos 14" - atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:0 ${common_dir}/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a \ --expect-tos 14 # And this works even if the packet already has ToS values set - atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:0 ${common_dir}/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a \ @@ -78,7 +80,7 @@ # ToS values are unmolested if the packets do not match a scrub rule pft_set_rules alcatraz "scrub out proto tcp set-tos 13" - atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \ + atf_check -s exit:0 ${common_dir}/pft_ping.py \ --sendif ${epair_send}a \ --to 198.51.100.3 \ --recvif ${epair_recv}a \ Index: head/tests/sys/netpfil/pf/sniffer.py =================================================================== --- head/tests/sys/netpfil/pf/sniffer.py +++ head/tests/sys/netpfil/pf/sniffer.py @@ -1,25 +0,0 @@ -# $FreeBSD$ - -import threading -import scapy.all as sp - -class Sniffer(threading.Thread): - def __init__(self, args, check_function): - threading.Thread.__init__(self) - - self._args = args - self._recvif = args.recvif[0] - self._check_function = check_function - self.foundCorrectPacket = False - - self.start() - - def _checkPacket(self, packet): - ret = self._check_function(self._args, packet) - if ret: - self.foundCorrectPacket = True - return ret - - def run(self): - self.packets = sp.sniff(iface=self._recvif, - stop_filter=self._checkPacket, timeout=3)