xref: /freebsd-src/tests/sys/common/sender.py (revision d0b2dbfa0ecf2bbc9709efc5e20baf8e4b44bbbf)
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