1619456bbSBjoern A. Zeeb#!/usr/bin/env python 2619456bbSBjoern A. Zeeb#- 3619456bbSBjoern A. Zeeb# SPDX-License-Identifier: BSD-2-Clause 4619456bbSBjoern A. Zeeb# 5619456bbSBjoern A. Zeeb# Copyright (c) 2019 Netflix, Inc. 6619456bbSBjoern A. Zeeb# 7619456bbSBjoern A. Zeeb# Redistribution and use in source and binary forms, with or without 8619456bbSBjoern A. Zeeb# modification, are permitted provided that the following conditions 9619456bbSBjoern A. Zeeb# are met: 10619456bbSBjoern A. Zeeb# 1. Redistributions of source code must retain the above copyright 11619456bbSBjoern A. Zeeb# notice, this list of conditions and the following disclaimer. 12619456bbSBjoern A. Zeeb# 2. Redistributions in binary form must reproduce the above copyright 13619456bbSBjoern A. Zeeb# notice, this list of conditions and the following disclaimer in the 14619456bbSBjoern A. Zeeb# documentation and/or other materials provided with the distribution. 15619456bbSBjoern A. Zeeb# 16619456bbSBjoern A. Zeeb# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17619456bbSBjoern A. Zeeb# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18619456bbSBjoern A. Zeeb# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19619456bbSBjoern A. Zeeb# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20619456bbSBjoern A. Zeeb# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21619456bbSBjoern A. Zeeb# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22619456bbSBjoern A. Zeeb# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23619456bbSBjoern A. Zeeb# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24619456bbSBjoern A. Zeeb# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25619456bbSBjoern A. Zeeb# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26619456bbSBjoern A. Zeeb# SUCH DAMAGE. 27619456bbSBjoern A. Zeeb# 28619456bbSBjoern A. Zeeb# 29619456bbSBjoern A. Zeeb 30619456bbSBjoern A. Zeebimport argparse 31a26e895fSKristof Provostimport logging 32a26e895fSKristof Provostlogging.getLogger("scapy").setLevel(logging.CRITICAL) 33619456bbSBjoern A. Zeebimport scapy.all as sp 34619456bbSBjoern A. Zeebimport socket 35619456bbSBjoern A. Zeebimport sys 36619456bbSBjoern A. Zeebfrom sniffer import Sniffer 37619456bbSBjoern A. Zeebfrom time import sleep 38619456bbSBjoern A. Zeeb 39619456bbSBjoern A. Zeebdef check_icmp6_error(args, packet): 40619456bbSBjoern A. Zeeb ip6 = packet.getlayer(sp.IPv6) 41619456bbSBjoern A. Zeeb if not ip6: 42619456bbSBjoern A. Zeeb return False 43619456bbSBjoern A. Zeeb oip6 = sp.IPv6(src=args.src[0], dst=args.to[0]) 44619456bbSBjoern A. Zeeb if ip6.dst != oip6.src: 45619456bbSBjoern A. Zeeb return False 46619456bbSBjoern A. Zeeb icmp6 = packet.getlayer(sp.ICMPv6TimeExceeded) 47619456bbSBjoern A. Zeeb if not icmp6: 48619456bbSBjoern A. Zeeb return False 49619456bbSBjoern A. Zeeb # ICMP6_TIME_EXCEED_REASSEMBLY 1 50619456bbSBjoern A. Zeeb if icmp6.code != 1: 51619456bbSBjoern A. Zeeb return False 52619456bbSBjoern A. Zeeb # Should we check the payload as well? 53619456bbSBjoern A. Zeeb # We are running in a very isolated environment and nothing else 54619456bbSBjoern A. Zeeb # should trigger an ICMPv6 Time Exceeded / Frag reassembly so leave it. 55619456bbSBjoern A. Zeeb #icmp6.display() 56619456bbSBjoern A. Zeeb return True 57619456bbSBjoern A. Zeeb 58619456bbSBjoern A. Zeeb 59619456bbSBjoern A. Zeebdef main(): 60619456bbSBjoern A. Zeeb parser = argparse.ArgumentParser("frag6.py", 61619456bbSBjoern A. Zeeb description="IPv6 fragementation test tool") 62619456bbSBjoern A. Zeeb parser.add_argument('--sendif', nargs=1, 63619456bbSBjoern A. Zeeb required=True, 64619456bbSBjoern A. Zeeb help='The interface through which the packet will be sent') 65619456bbSBjoern A. Zeeb parser.add_argument('--recvif', nargs=1, 66619456bbSBjoern A. Zeeb required=True, 67619456bbSBjoern A. Zeeb help='The interface on which to check for the packet') 68619456bbSBjoern A. Zeeb parser.add_argument('--src', nargs=1, 69619456bbSBjoern A. Zeeb required=True, 70619456bbSBjoern A. Zeeb help='The source IP address') 71619456bbSBjoern A. Zeeb parser.add_argument('--to', nargs=1, 72619456bbSBjoern A. Zeeb required=True, 73619456bbSBjoern A. Zeeb help='The destination IP address') 74619456bbSBjoern A. Zeeb parser.add_argument('--debug', 75619456bbSBjoern A. Zeeb required=False, action='store_true', 76619456bbSBjoern A. Zeeb help='Enable test debugging') 77619456bbSBjoern A. Zeeb 78619456bbSBjoern A. Zeeb args = parser.parse_args() 79619456bbSBjoern A. Zeeb 80619456bbSBjoern A. Zeeb 81619456bbSBjoern A. Zeeb # Start sniffing on recvif 82619456bbSBjoern A. Zeeb sniffer = Sniffer(args, check_icmp6_error) 83619456bbSBjoern A. Zeeb 84619456bbSBjoern A. Zeeb 85619456bbSBjoern A. Zeeb ######################################################################## 86619456bbSBjoern A. Zeeb # 87619456bbSBjoern A. Zeeb # Send a proper first fragment (off=0) and a second fragment which 88619456bbSBjoern A. Zeeb # just fits the 64k. The re-send the first fragment with an extra 89619456bbSBjoern A. Zeeb # unfragmentable part making the 64k to exceed the limit. 90619456bbSBjoern A. Zeeb # This is to make sure we don't allow to update meta-data for a 91619456bbSBjoern A. Zeeb # 1st fragmented packet should a second arrive but given the 92619456bbSBjoern A. Zeeb # fragmentable part is an exact duplicate only that fragment 93619456bbSBjoern A. Zeeb # will be silently discarded. 94619456bbSBjoern A. Zeeb # 95619456bbSBjoern A. Zeeb # A: Reassembly failure, timeout after 96619456bbSBjoern A. Zeeb # R: ICMPv6 time exceeded / statistics for the duplicate 97619456bbSBjoern A. Zeeb # 98619456bbSBjoern A. Zeeb data = "6" * 8 99619456bbSBjoern A. Zeeb ip6f00 = \ 100619456bbSBjoern A. Zeeb sp.Ether() / \ 101619456bbSBjoern A. Zeeb sp.IPv6(src=args.src[0], dst=args.to[0]) / \ 102619456bbSBjoern A. Zeeb sp.IPv6ExtHdrFragment(offset=0, m=1, id=20) / \ 103619456bbSBjoern A. Zeeb sp.UDP(dport=3456, sport=6543) / \ 104619456bbSBjoern A. Zeeb data 105619456bbSBjoern A. Zeeb data = "6" * 15 106619456bbSBjoern A. Zeeb ip6f01 = \ 107619456bbSBjoern A. Zeeb sp.Ether() / \ 108619456bbSBjoern A. Zeeb sp.IPv6(src=args.src[0], dst=args.to[0]) / \ 109619456bbSBjoern A. Zeeb sp.IPv6ExtHdrFragment(offset=0x1ffc, m=0, id=20) / \ 110619456bbSBjoern A. Zeeb sp.UDP(dport=3456, sport=6543) / \ 111619456bbSBjoern A. Zeeb data 112619456bbSBjoern A. Zeeb data = "6" * 8 113619456bbSBjoern A. Zeeb ip6f02 = \ 114619456bbSBjoern A. Zeeb sp.Ether() / \ 115619456bbSBjoern A. Zeeb sp.IPv6(src=args.src[0], dst=args.to[0]) / \ 116619456bbSBjoern A. Zeeb sp.IPv6ExtHdrDestOpt(options = \ 117619456bbSBjoern A. Zeeb sp.PadN(optdata="\x00\x00\x00\x00\x00\x00")) / \ 118619456bbSBjoern A. Zeeb sp.IPv6ExtHdrFragment(offset=0, m=1, id=20) / \ 119619456bbSBjoern A. Zeeb sp.UDP(dport=3456, sport=6543) / \ 120619456bbSBjoern A. Zeeb data 121619456bbSBjoern A. Zeeb if args.debug : 122619456bbSBjoern A. Zeeb ip6f00.display() 123619456bbSBjoern A. Zeeb ip6f01.display() 124619456bbSBjoern A. Zeeb ip6f02.display() 125619456bbSBjoern A. Zeeb sp.sendp(ip6f00, iface=args.sendif[0], verbose=False) 126619456bbSBjoern A. Zeeb sp.sendp(ip6f01, iface=args.sendif[0], verbose=False) 127619456bbSBjoern A. Zeeb sp.sendp(ip6f02, iface=args.sendif[0], verbose=False) 128619456bbSBjoern A. Zeeb 129*e32221a1SAlexander V. Chernikov sleep(3) 130619456bbSBjoern A. Zeeb sniffer.setEnd() 131619456bbSBjoern A. Zeeb sniffer.join() 132619456bbSBjoern A. Zeeb if not sniffer.foundCorrectPacket: 133619456bbSBjoern A. Zeeb sys.exit(1) 134619456bbSBjoern A. Zeeb 135619456bbSBjoern A. Zeeb sys.exit(0) 136619456bbSBjoern A. Zeeb 137619456bbSBjoern A. Zeebif __name__ == '__main__': 138619456bbSBjoern A. Zeeb main() 139