130276ef1SKristof Provost#!/usr/bin/env python3 230276ef1SKristof Provost# 330276ef1SKristof Provost# SPDX-License-Identifier: BSD-2-Clause 430276ef1SKristof Provost# 530276ef1SKristof Provost# Copyright (c) 2021 Rubicon Communications, LLC (Netgate) 630276ef1SKristof Provost# 730276ef1SKristof Provost# Redistribution and use in source and binary forms, with or without 830276ef1SKristof Provost# modification, are permitted provided that the following conditions 930276ef1SKristof Provost# are met: 1030276ef1SKristof Provost# 1. Redistributions of source code must retain the above copyright 1130276ef1SKristof Provost# notice, this list of conditions and the following disclaimer. 1230276ef1SKristof Provost# 2. Redistributions in binary form must reproduce the above copyright 1330276ef1SKristof Provost# notice, this list of conditions and the following disclaimer in the 1430276ef1SKristof Provost# documentation and/or other materials provided with the distribution. 1530276ef1SKristof Provost# 1630276ef1SKristof Provost# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1730276ef1SKristof Provost# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1830276ef1SKristof Provost# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1930276ef1SKristof Provost# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2030276ef1SKristof Provost# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2130276ef1SKristof Provost# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2230276ef1SKristof Provost# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2330276ef1SKristof Provost# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2430276ef1SKristof Provost# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2530276ef1SKristof Provost# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2630276ef1SKristof Provost# SUCH DAMAGE. 2730276ef1SKristof Provost# 2830276ef1SKristof Provost 2930276ef1SKristof Provostimport argparse 3030276ef1SKristof Provostimport logging 3130276ef1SKristof Provostlogging.getLogger("scapy").setLevel(logging.CRITICAL) 3230276ef1SKristof Provostimport random 3330276ef1SKristof Provostimport scapy.all as sp 3430276ef1SKristof Provostimport socket 3530276ef1SKristof Provostimport sys 3630276ef1SKristof Provostfrom sniffer import Sniffer 3730276ef1SKristof Provost 3830276ef1SKristof ProvostPAYLOAD_MAGIC = bytes.fromhex('42c0ffee') 3930276ef1SKristof Provost 4030276ef1SKristof Provostdef ping(send_if, dst_ip, args): 4130276ef1SKristof Provost ether = sp.Ether() 4230276ef1SKristof Provost ip = sp.IP(dst=dst_ip, src=args.fromaddr[0]) 4330276ef1SKristof Provost icmp = sp.ICMP(type='echo-request') 4430276ef1SKristof Provost raw = sp.raw(PAYLOAD_MAGIC * 250) # We want 1000 bytes payload, -ish 4530276ef1SKristof Provost 4630276ef1SKristof Provost ip.flags = 2 # Don't fragment 4730276ef1SKristof Provost icmp.seq = random.randint(0, 65535) 4830276ef1SKristof Provost args.icmp_seq = icmp.seq 4930276ef1SKristof Provost 5030276ef1SKristof Provost req = ether / ip / icmp / raw 5130276ef1SKristof Provost sp.sendp(req, iface=send_if, verbose=False) 5230276ef1SKristof Provost 5330276ef1SKristof Provostdef check_icmp_too_big(args, packet): 5430276ef1SKristof Provost """ 5530276ef1SKristof Provost Verify that this is an ICMP packet too big error, and that the IP addresses 5630276ef1SKristof Provost in the payload packet match expectations. 5730276ef1SKristof Provost """ 5830276ef1SKristof Provost icmp = packet.getlayer(sp.ICMP) 5930276ef1SKristof Provost if not icmp: 6030276ef1SKristof Provost return False 6130276ef1SKristof Provost 6230276ef1SKristof Provost if not icmp.type == 3: 6330276ef1SKristof Provost return False 6430276ef1SKristof Provost ip = packet.getlayer(sp.IPerror) 6530276ef1SKristof Provost if not ip: 6630276ef1SKristof Provost return False 6730276ef1SKristof Provost 6830276ef1SKristof Provost if ip.src != args.fromaddr[0]: 6930276ef1SKristof Provost print("Incorrect src addr %s" % ip.src) 7030276ef1SKristof Provost return False 7130276ef1SKristof Provost if ip.dst != args.to[0]: 7230276ef1SKristof Provost print("Incorrect dst addr %s" % ip.dst) 7330276ef1SKristof Provost return False 7430276ef1SKristof Provost 7530276ef1SKristof Provost icmp2 = packet.getlayer(sp.ICMPerror) 7630276ef1SKristof Provost if not icmp2: 7730276ef1SKristof Provost print("IPerror doesn't contain ICMP") 7830276ef1SKristof Provost return False 7930276ef1SKristof Provost if icmp2.seq != args.icmp_seq: 8030276ef1SKristof Provost print("Incorrect icmp seq %d != %d" % (icmp2.seq, args.icmp_seq)) 8130276ef1SKristof Provost return False 8230276ef1SKristof Provost return True 8330276ef1SKristof Provost 8430276ef1SKristof Provostdef main(): 8530276ef1SKristof Provost parser = argparse.ArgumentParser("pft_icmp_check.py", 8630276ef1SKristof Provost description="ICMP error validation tool") 8730276ef1SKristof Provost parser.add_argument('--to', nargs=1, required=True, 8830276ef1SKristof Provost help='The destination IP address') 8930276ef1SKristof Provost parser.add_argument('--fromaddr', nargs=1, required=True, 9030276ef1SKristof Provost help='The source IP address') 9130276ef1SKristof Provost parser.add_argument('--sendif', nargs=1, required=True, 9230276ef1SKristof Provost help='The interface through which the packet(s) will be sent') 9330276ef1SKristof Provost parser.add_argument('--recvif', nargs=1, 9430276ef1SKristof Provost help='The interface on which to expect the ICMP error') 9530276ef1SKristof Provost 9630276ef1SKristof Provost args = parser.parse_args() 9730276ef1SKristof Provost sniffer = None 9830276ef1SKristof Provost if not args.recvif is None: 99*a39dedebSKajetan Staszkiewicz sniffer = Sniffer(args, check_icmp_too_big, args.recvif[0]) 10030276ef1SKristof Provost 10130276ef1SKristof Provost ping(args.sendif[0], args.to[0], args) 10230276ef1SKristof Provost 10330276ef1SKristof Provost if sniffer: 10430276ef1SKristof Provost sniffer.join() 10530276ef1SKristof Provost 106*a39dedebSKajetan Staszkiewicz if sniffer.correctPackets: 10730276ef1SKristof Provost sys.exit(0) 10830276ef1SKristof Provost else: 10930276ef1SKristof Provost sys.exit(1) 11030276ef1SKristof Provost 11130276ef1SKristof Provostif __name__ == '__main__': 11230276ef1SKristof Provost main() 113