1d7cdd897SAlexander V. Chernikov#!/usr/bin/env python 2d7cdd897SAlexander V. Chernikov# - 3d7cdd897SAlexander V. Chernikov# SPDX-License-Identifier: BSD-2-Clause 4d7cdd897SAlexander V. Chernikov# 5d7cdd897SAlexander V. Chernikov# Copyright (c) 2020 Alexander V. Chernikov 6d7cdd897SAlexander V. Chernikov# 7d7cdd897SAlexander V. Chernikov# Redistribution and use in source and binary forms, with or without 8d7cdd897SAlexander V. Chernikov# modification, are permitted provided that the following conditions 9d7cdd897SAlexander V. Chernikov# are met: 10d7cdd897SAlexander V. Chernikov# 1. Redistributions of source code must retain the above copyright 11d7cdd897SAlexander V. Chernikov# notice, this list of conditions and the following disclaimer. 12d7cdd897SAlexander V. Chernikov# 2. Redistributions in binary form must reproduce the above copyright 13d7cdd897SAlexander V. Chernikov# notice, this list of conditions and the following disclaimer in the 14d7cdd897SAlexander V. Chernikov# documentation and/or other materials provided with the distribution. 15d7cdd897SAlexander V. Chernikov# 16d7cdd897SAlexander V. Chernikov# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17d7cdd897SAlexander V. Chernikov# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18d7cdd897SAlexander V. Chernikov# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19d7cdd897SAlexander V. Chernikov# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20d7cdd897SAlexander V. Chernikov# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21d7cdd897SAlexander V. Chernikov# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22d7cdd897SAlexander V. Chernikov# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23d7cdd897SAlexander V. Chernikov# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24d7cdd897SAlexander V. Chernikov# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25d7cdd897SAlexander V. Chernikov# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26d7cdd897SAlexander V. Chernikov# SUCH DAMAGE. 27d7cdd897SAlexander V. Chernikov# 28d7cdd897SAlexander V. Chernikov# 29d7cdd897SAlexander V. Chernikov 30d7cdd897SAlexander V. Chernikov 31d7cdd897SAlexander V. Chernikovfrom functools import partial 32d7cdd897SAlexander V. Chernikovimport socket 33*a26e895fSKristof Provostimport logging 34*a26e895fSKristof Provostlogging.getLogger("scapy").setLevel(logging.CRITICAL) 35d7cdd897SAlexander V. Chernikovimport scapy.all as sc 36d7cdd897SAlexander V. Chernikovimport argparse 37d7cdd897SAlexander V. Chernikovimport time 38d7cdd897SAlexander V. Chernikov 39d7cdd897SAlexander V. Chernikov 40d7cdd897SAlexander V. Chernikovdef parse_args(): 41d7cdd897SAlexander V. Chernikov parser = argparse.ArgumentParser(description='divert socket tester') 42d7cdd897SAlexander V. Chernikov parser.add_argument('--dip', type=str, help='destination packet IP') 43d7cdd897SAlexander V. Chernikov parser.add_argument('--sip', type=str, help='source packet IP') 44d7cdd897SAlexander V. Chernikov parser.add_argument('--dmac', type=str, help='packet dst mac') 45d7cdd897SAlexander V. Chernikov parser.add_argument('--smac', type=str, help='packet src mac') 46d7cdd897SAlexander V. Chernikov parser.add_argument('--iface', type=str, help='interface to use') 47d7cdd897SAlexander V. Chernikov parser.add_argument('--test_name', type=str, required=True, 48d7cdd897SAlexander V. Chernikov help='test name to run') 49d7cdd897SAlexander V. Chernikov return parser.parse_args() 50d7cdd897SAlexander V. Chernikov 51d7cdd897SAlexander V. Chernikov 52d7cdd897SAlexander V. Chernikovdef send_packet(args, pkt): 53d7cdd897SAlexander V. Chernikov sc.sendp(pkt, iface=args.iface, verbose=False) 54d7cdd897SAlexander V. Chernikov 55d7cdd897SAlexander V. Chernikov 56d7cdd897SAlexander V. Chernikovdef is_icmp6_echo_request(pkt): 57d7cdd897SAlexander V. Chernikov return pkt.type == 0x86DD and pkt.payload.nh == 58 and \ 58d7cdd897SAlexander V. Chernikov pkt.payload.payload.type == 128 59d7cdd897SAlexander V. Chernikov 60d7cdd897SAlexander V. Chernikov 61d7cdd897SAlexander V. Chernikovdef check_forwarded_ip_packet(orig_pkt, fwd_pkt): 62d7cdd897SAlexander V. Chernikov """ 63d7cdd897SAlexander V. Chernikov Checks that forwarded ICMP packet @fwd_ptk is the same as 64d7cdd897SAlexander V. Chernikov @orig_pkt. Assumes router-on-the-stick forwarding behaviour: 65d7cdd897SAlexander V. Chernikov * src/dst macs are swapped 66d7cdd897SAlexander V. Chernikov * TTL is decremented 67d7cdd897SAlexander V. Chernikov """ 68d7cdd897SAlexander V. Chernikov # Check ether fields 69d7cdd897SAlexander V. Chernikov assert orig_pkt.src == fwd_pkt.dst 70d7cdd897SAlexander V. Chernikov assert orig_pkt.dst == fwd_pkt.src 71d7cdd897SAlexander V. Chernikov assert len(orig_pkt) == len(fwd_pkt) 72d7cdd897SAlexander V. Chernikov # Check IP 73d7cdd897SAlexander V. Chernikov fwd_ip = fwd_pkt[sc.IP] 74d7cdd897SAlexander V. Chernikov orig_ip = orig_pkt[sc.IP] 75d7cdd897SAlexander V. Chernikov assert orig_ip.src == orig_ip.src 76d7cdd897SAlexander V. Chernikov assert orig_ip.dst == fwd_ip.dst 77d7cdd897SAlexander V. Chernikov assert orig_ip.ttl == fwd_ip.ttl + 1 78d7cdd897SAlexander V. Chernikov # Check ICMP 79d7cdd897SAlexander V. Chernikov fwd_icmp = fwd_ip[sc.ICMP] 80d7cdd897SAlexander V. Chernikov orig_icmp = orig_ip[sc.ICMP] 81d7cdd897SAlexander V. Chernikov assert bytes(orig_ip.payload) == bytes(fwd_ip.payload) 82d7cdd897SAlexander V. Chernikov 83d7cdd897SAlexander V. Chernikov 84d7cdd897SAlexander V. Chernikovdef fwd_ip_icmp_fast(args): 85d7cdd897SAlexander V. Chernikov """ 86d7cdd897SAlexander V. Chernikov Sends ICMP packet via args.iface interface. 87d7cdd897SAlexander V. Chernikov Receives and checks the forwarded packet. 88d7cdd897SAlexander V. Chernikov Assumes forwarding router decrements TTL 89d7cdd897SAlexander V. Chernikov """ 90d7cdd897SAlexander V. Chernikov 91d7cdd897SAlexander V. Chernikov def filter_f(x): 92d7cdd897SAlexander V. Chernikov return x.src == args.dmac and x.type == 0x0800 93d7cdd897SAlexander V. Chernikov 94d7cdd897SAlexander V. Chernikov e = sc.Ether(src=args.smac, dst=args.dmac) 95d7cdd897SAlexander V. Chernikov ip = sc.IP(src=args.sip, dst=args.dip) 96d7cdd897SAlexander V. Chernikov icmp = sc.ICMP(type='echo-request') 97d7cdd897SAlexander V. Chernikov pkt = e / ip / icmp 98d7cdd897SAlexander V. Chernikov 99d7cdd897SAlexander V. Chernikov send_cb = partial(send_packet, args, pkt) 100d7cdd897SAlexander V. Chernikov packets = sc.sniff(iface=args.iface, started_callback=send_cb, 101d7cdd897SAlexander V. Chernikov stop_filter=filter_f, lfilter=filter_f, timeout=5) 102d7cdd897SAlexander V. Chernikov assert len(packets) > 0 103d7cdd897SAlexander V. Chernikov fwd_pkt = packets[-1] 104d7cdd897SAlexander V. Chernikov try: 105d7cdd897SAlexander V. Chernikov check_forwarded_ip_packet(pkt, fwd_pkt) 106d7cdd897SAlexander V. Chernikov except Exception as e: 107d7cdd897SAlexander V. Chernikov print('Original packet:') 108d7cdd897SAlexander V. Chernikov pkt.show() 109d7cdd897SAlexander V. Chernikov print('Forwarded packet:') 110d7cdd897SAlexander V. Chernikov fwd_pkt.show() 111d7cdd897SAlexander V. Chernikov for a_packet in packets: 112d7cdd897SAlexander V. Chernikov a_packet.summary() 113d7cdd897SAlexander V. Chernikov raise Exception from e 114d7cdd897SAlexander V. Chernikov 115d7cdd897SAlexander V. Chernikov 116d7cdd897SAlexander V. Chernikovdef fwd_ip_icmp_slow(args): 117d7cdd897SAlexander V. Chernikov """ 118d7cdd897SAlexander V. Chernikov Sends ICMP packet via args.iface interface. 119d7cdd897SAlexander V. Chernikov Forces slow path processing by introducing IP option. 120d7cdd897SAlexander V. Chernikov Receives and checks the forwarded packet. 121d7cdd897SAlexander V. Chernikov Assumes forwarding router decrements TTL 122d7cdd897SAlexander V. Chernikov """ 123d7cdd897SAlexander V. Chernikov 124d7cdd897SAlexander V. Chernikov def filter_f(x): 125d7cdd897SAlexander V. Chernikov return x.src == args.dmac and x.type == 0x0800 126d7cdd897SAlexander V. Chernikov 127d7cdd897SAlexander V. Chernikov e = sc.Ether(src=args.smac, dst=args.dmac) 128d7cdd897SAlexander V. Chernikov # Add IP option to switch to 'normal' IP processing 129d7cdd897SAlexander V. Chernikov stream_id = sc.IPOption_Stream_Id(security=0xFFFF) 130d7cdd897SAlexander V. Chernikov ip = sc.IP(src=args.sip, dst=args.dip, 131d7cdd897SAlexander V. Chernikov options=[sc.IPOption_Stream_Id(security=0xFFFF)]) 132d7cdd897SAlexander V. Chernikov icmp = sc.ICMP(type='echo-request') 133d7cdd897SAlexander V. Chernikov pkt = e / ip / icmp 134d7cdd897SAlexander V. Chernikov 135d7cdd897SAlexander V. Chernikov send_cb = partial(send_packet, args, pkt) 136d7cdd897SAlexander V. Chernikov packets = sc.sniff(iface=args.iface, started_callback=send_cb, 137d7cdd897SAlexander V. Chernikov stop_filter=filter_f, lfilter=filter_f, timeout=5) 138d7cdd897SAlexander V. Chernikov assert len(packets) > 0 139d7cdd897SAlexander V. Chernikov check_forwarded_ip_packet(pkt, packets[-1]) 140d7cdd897SAlexander V. Chernikov 141d7cdd897SAlexander V. Chernikov 142d7cdd897SAlexander V. Chernikovdef check_forwarded_ip6_packet(orig_pkt, fwd_pkt): 143d7cdd897SAlexander V. Chernikov """ 144d7cdd897SAlexander V. Chernikov Checks that forwarded ICMP packet @fwd_ptk is the same as 145d7cdd897SAlexander V. Chernikov @orig_pkt. Assumes router-on-the-stick forwarding behaviour: 146d7cdd897SAlexander V. Chernikov * src/dst macs are swapped 147d7cdd897SAlexander V. Chernikov * TTL is decremented 148d7cdd897SAlexander V. Chernikov """ 149d7cdd897SAlexander V. Chernikov # Check ether fields 150d7cdd897SAlexander V. Chernikov assert orig_pkt.src == fwd_pkt.dst 151d7cdd897SAlexander V. Chernikov assert orig_pkt.dst == fwd_pkt.src 152d7cdd897SAlexander V. Chernikov assert len(orig_pkt) == len(fwd_pkt) 153d7cdd897SAlexander V. Chernikov # Check IP 154d7cdd897SAlexander V. Chernikov fwd_ip = fwd_pkt[sc.IPv6] 155d7cdd897SAlexander V. Chernikov orig_ip = orig_pkt[sc.IPv6] 156d7cdd897SAlexander V. Chernikov assert orig_ip.src == orig_ip.src 157d7cdd897SAlexander V. Chernikov assert orig_ip.dst == fwd_ip.dst 158d7cdd897SAlexander V. Chernikov assert orig_ip.hlim == fwd_ip.hlim + 1 159d7cdd897SAlexander V. Chernikov # Check ICMPv6 160d7cdd897SAlexander V. Chernikov assert bytes(orig_ip.payload) == bytes(fwd_ip.payload) 161d7cdd897SAlexander V. Chernikov 162d7cdd897SAlexander V. Chernikov 163d7cdd897SAlexander V. Chernikovdef fwd_ip6_icmp(args): 164d7cdd897SAlexander V. Chernikov """ 165d7cdd897SAlexander V. Chernikov Sends ICMPv6 packet via args.iface interface. 166d7cdd897SAlexander V. Chernikov Receives and checks the forwarded packet. 167d7cdd897SAlexander V. Chernikov Assumes forwarding router decrements TTL 168d7cdd897SAlexander V. Chernikov """ 169d7cdd897SAlexander V. Chernikov 170d7cdd897SAlexander V. Chernikov def filter_f(x): 171d7cdd897SAlexander V. Chernikov return x.src == args.dmac and is_icmp6_echo_request(x) 172d7cdd897SAlexander V. Chernikov 173d7cdd897SAlexander V. Chernikov e = sc.Ether(src=args.smac, dst=args.dmac) 174d7cdd897SAlexander V. Chernikov ip = sc.IPv6(src=args.sip, dst=args.dip) 175d7cdd897SAlexander V. Chernikov icmp = sc.ICMPv6EchoRequest() 176d7cdd897SAlexander V. Chernikov pkt = e / ip / icmp 177d7cdd897SAlexander V. Chernikov 178d7cdd897SAlexander V. Chernikov send_cb = partial(send_packet, args, pkt) 179d7cdd897SAlexander V. Chernikov packets = sc.sniff(iface=args.iface, started_callback=send_cb, 180d7cdd897SAlexander V. Chernikov stop_filter=filter_f, lfilter=filter_f, timeout=5) 181d7cdd897SAlexander V. Chernikov assert len(packets) > 0 182d7cdd897SAlexander V. Chernikov fwd_pkt = packets[-1] 183d7cdd897SAlexander V. Chernikov try: 184d7cdd897SAlexander V. Chernikov check_forwarded_ip6_packet(pkt, fwd_pkt) 185d7cdd897SAlexander V. Chernikov except Exception as e: 186d7cdd897SAlexander V. Chernikov print('Original packet:') 187d7cdd897SAlexander V. Chernikov pkt.show() 188d7cdd897SAlexander V. Chernikov print('Forwarded packet:') 189d7cdd897SAlexander V. Chernikov fwd_pkt.show() 190d7cdd897SAlexander V. Chernikov for idx, a_packet in enumerate(packets): 191d7cdd897SAlexander V. Chernikov print('{}: {}'.format(idx, a_packet.summary())) 192d7cdd897SAlexander V. Chernikov raise Exception from e 193d7cdd897SAlexander V. Chernikov 194d7cdd897SAlexander V. Chernikov 195d7cdd897SAlexander V. Chernikovdef main(): 196d7cdd897SAlexander V. Chernikov args = parse_args() 197d7cdd897SAlexander V. Chernikov test_ptr = globals()[args.test_name] 198d7cdd897SAlexander V. Chernikov test_ptr(args) 199d7cdd897SAlexander V. Chernikov 200d7cdd897SAlexander V. Chernikov 201d7cdd897SAlexander V. Chernikovif __name__ == '__main__': 202d7cdd897SAlexander V. Chernikov main() 203