git: d7c9de2d68ca - main - pf tests: Add option to send fragmented packets
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 26 Oct 2023 14:16:53 UTC
The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=d7c9de2d68ca81c557e069c2b431529cf597886c commit d7c9de2d68ca81c557e069c2b431529cf597886c Author: Kajetan Staszkiewicz <vegeta@tuxpowered.net> AuthorDate: 2023-10-26 09:14:14 +0000 Commit: Kristof Provost <kp@FreeBSD.org> CommitDate: 2023-10-26 13:25:20 +0000 pf tests: Add option to send fragmented packets Add option to send fragmented packets and to properly sniff them by reassembling them by the sniffer itself. Reviewed by: kp Sponsored by: InnoGames GmbH Differential Revision: https://reviews.freebsd.org/D42354 --- tests/sys/netpfil/common/pft_ping.py | 39 +++++++++++++++++++++++++++--------- tests/sys/netpfil/common/sniffer.py | 18 +++++++++++++---- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/tests/sys/netpfil/common/pft_ping.py b/tests/sys/netpfil/common/pft_ping.py index af5b84e34c67..1abf4f609832 100644 --- a/tests/sys/netpfil/common/pft_ping.py +++ b/tests/sys/netpfil/common/pft_ping.py @@ -82,17 +82,29 @@ def prepare_ipv4(dst_address, send_params): def send_icmp_ping(dst_address, sendif, send_params): send_length = send_params['length'] + send_frag_length = send_params['frag_length'] + packets = [] ether = sp.Ether() if ':' in dst_address: ip6 = prepare_ipv6(dst_address, send_params) icmp = sp.ICMPv6EchoRequest(data=sp.raw(build_payload(send_length))) - req = ether / ip6 / icmp + if send_frag_length: + for packet in sp.fragment(ip6 / icmp, fragsize=send_frag_length): + packets.append(ether / packet) + else: + packets.append(ether / ip6 / icmp) + else: ip = prepare_ipv4(dst_address, send_params) icmp = sp.ICMP(type='echo-request') raw = sp.raw(build_payload(send_length)) - req = ether / ip / icmp / raw - sp.sendp(req, sendif, verbose=False) + if send_frag_length: + for packet in sp.fragment(ip / icmp / raw, fragsize=send_frag_length): + packets.append(ether / packet) + else: + packets.append(ether / ip / icmp / raw) + for packet in packets: + sp.sendp(packet, sendif, verbose=False) def send_tcp_syn(dst_address, sendif, send_params): @@ -372,7 +384,7 @@ def check_tcp_syn_reply(expect_params, packet): return check_tcp_syn_reply_4(expect_params, packet) -def setup_sniffer(recvif, ping_type, sniff_type, expect_params): +def setup_sniffer(recvif, ping_type, sniff_type, expect_params, defrag): if ping_type == 'icmp' and sniff_type == 'request': checkfn = check_ping_request elif ping_type == 'icmp' and sniff_type == 'reply': @@ -384,7 +396,7 @@ def setup_sniffer(recvif, ping_type, sniff_type, expect_params): else: raise Exception('Unspported ping or sniff type') - return Sniffer(expect_params, checkfn, recvif) + return Sniffer(expect_params, checkfn, recvif, defrag=defrag) def parse_args(): @@ -417,6 +429,8 @@ def parse_args(): parser_send = parser.add_argument_group('Values set in transmitted packets') parser_send.add_argument('--send-flags', nargs=1, type=str, help='IPv4 fragmentation flags') + parser_send.add_argument('--send-frag-length', nargs=1, type=int, + help='Force IP fragmentation with given fragment length') parser_send.add_argument('--send-hlim', nargs=1, type=int, help='IPv6 Hop Limit or IPv4 Time To Live') parser_send.add_argument('--send-mss', nargs=1, type=int, @@ -428,7 +442,7 @@ def parse_args(): parser_send.add_argument('--send-tc', nargs=1, type=int, help='IPv6 Traffic Class or IPv4 DiffServ / ToS') parser_send.add_argument('--send-tcpopt-unaligned', action='store_true', - help='Include unaligned TCP options') + help='Include unaligned TCP options') # Expectations parser_expect = parser.add_argument_group('Values expected in sniffed packets') @@ -467,7 +481,7 @@ def main(): # Standardize parameters which have nargs=1. send_params = {} expect_params = {} - for param_name in ('flags', 'hlim', 'length', 'mss', 'seq', 'tc'): + for param_name in ('flags', 'hlim', 'length', 'mss', 'seq', 'tc', 'frag_length'): param_arg = vars(args).get(f'send_{param_name}') send_params[param_name] = param_arg[0] if param_arg else None param_arg = vars(args).get(f'expect_{param_name}') @@ -488,6 +502,11 @@ def main(): sniffers = [] + if send_params['frag_length']: + defrag = True + else: + defrag = False + if recv_ifs: sniffer_params = copy(expect_params) sniffer_params['src_address'] = None @@ -495,7 +514,8 @@ def main(): for iface in recv_ifs: LOGGER.debug(f'Installing receive sniffer on {iface}') sniffers.append( - setup_sniffer(iface, args.ping_type, 'request', sniffer_params, + setup_sniffer(iface, args.ping_type, 'request', + sniffer_params, defrag, )) if reply_ifs: @@ -505,7 +525,8 @@ def main(): for iface in reply_ifs: LOGGER.debug(f'Installing reply sniffer on {iface}') sniffers.append( - setup_sniffer(iface, args.ping_type, 'reply', sniffer_params, + setup_sniffer(iface, args.ping_type, 'reply', + sniffer_params, defrag, )) LOGGER.debug(f'Installed {len(sniffers)} sniffers') diff --git a/tests/sys/netpfil/common/sniffer.py b/tests/sys/netpfil/common/sniffer.py index ab3ddc0aea3c..14305a37278c 100644 --- a/tests/sys/netpfil/common/sniffer.py +++ b/tests/sys/netpfil/common/sniffer.py @@ -30,7 +30,7 @@ import scapy.all as sp import sys class Sniffer(threading.Thread): - def __init__(self, args, check_function, recvif, timeout=3): + def __init__(self, args, check_function, recvif, timeout=3, defrag=False): threading.Thread.__init__(self) self._sem = threading.Semaphore(0) @@ -38,6 +38,7 @@ class Sniffer(threading.Thread): self._timeout = timeout self._recvif = recvif self._check_function = check_function + self._defrag = defrag self.correctPackets = 0 self.start() @@ -55,6 +56,15 @@ class Sniffer(threading.Thread): def run(self): self.packets = [] - self.packets = sp.sniff(iface=self._recvif, - stop_filter=self._checkPacket, timeout=self._timeout, - started_callback=self._startedCb) + if self._defrag: + # With fragment reassembly we can't stop the sniffer after catching + # the good packets, as those have not been reassembled. We must + # wait for sniffer to finish and check returned packets instead. + self.packets = sp.sniff(session=sp.IPSession, iface=self._recvif, + timeout=self._timeout, started_callback=self._startedCb) + for p in self.packets: + self._checkPacket(p) + else: + self.packets = sp.sniff(iface=self._recvif, + stop_filter=self._checkPacket, timeout=self._timeout, + started_callback=self._startedCb)