xref: /openbsd-src/regress/sys/netinet/pmtu/udp_echo6.py (revision e7abe75af641bacab1663afa28052d03f1e67610)
1#!/usr/local/bin/python3
2
3import os
4import threading
5import string
6import random
7from addr import *
8from scapy.all import *
9
10class Sniff(threading.Thread):
11	filter = None
12	captured = None
13	packet = None
14	def __init__(self):
15		# clear packets buffered by scapy bpf
16		sniff(iface=LOCAL_IF, timeout=1)
17		super(Sniff, self).__init__()
18	def run(self):
19		self.captured = sniff(iface=LOCAL_IF, filter=self.filter,
20		    timeout=3)
21		if self.captured:
22			self.packet = self.captured[0]
23
24e=Ether(src=LOCAL_MAC, dst=REMOTE_MAC)
25ip6=IPv6(src=FAKE_NET_ADDR6, dst=REMOTE_ADDR6)
26uport=os.getpid() & 0xffff
27# inetd ignores UDP packets from privileged port or nfs
28if uport < 1024 or uport == 2049:
29	uport+=1024
30
31print("Send UDP packet with 1400 octets payload, receive echo.")
32data=''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase +
33    string.digits) for _ in range(1400))
34udp=UDP(sport=uport, dport='echo')/data
35echo=srp1(e/ip6/udp, iface=LOCAL_IF, timeout=5)
36
37if echo is None:
38	print("ERROR: No UDP answer from echo server received.")
39	exit(1)
40
41print("Send ICMP6 packet too big packet with MTU 1300.")
42icmp6=ICMPv6PacketTooBig(mtu=1300)/echo.payload
43sendp(e/IPv6(src=LOCAL_ADDR6, dst=REMOTE_ADDR6)/icmp6, iface=LOCAL_IF)
44
45print("Clear route cache at echo socket by sending from different address.")
46sendp(e/IPv6(src=LOCAL_ADDR6, dst=REMOTE_ADDR6)/udp, iface=LOCAL_IF)
47
48# srp1 cannot be used, fragment answer will not match on outgoing UDP packet
49sniffer = Sniff()
50sniffer.filter = \
51    "ip6 and src "+ip6.dst+" and dst "+ip6.src+" and proto ipv6-frag"
52sniffer.start()
53time.sleep(1)
54
55print("Send UDP packet with 1400 octets payload.")
56sendp(e/ip6/udp, iface=LOCAL_IF)
57
58print("Path MTU discovery will send UDP fragment with maximum length 1300.")
59sniffer.join(timeout=5)
60
61for a in sniffer.captured:
62	fh=a.payload.payload
63	if fh.offset != 0 or fh.nh != (ip6/udp).nh:
64		continue
65	uh=fh.payload
66	if uh.sport != udp.dport or uh.dport != udp.sport:
67		continue
68	frag=a
69	break
70else:
71	print("ERROR: No matching IPv6 fragment UDP answer found.")
72	exit(1)
73
74print("UDP echo has IPv6 and UDP header, so expected payload len is 1448.")
75elen = echo.plen + len(IPv6())
76print("elen=%d" % elen)
77if elen != 1448:
78	print("ERROR: UDP echo payload len is %d, expected 1448." % elen)
79	exit(1)
80
81print("Fragments contain multiple of 8 octets, so expected len is 1296.")
82flen = frag.plen + len(IPv6())
83print("flen=%d" % flen)
84if flen != 1296:
85	print("ERROR: UDP fragment len is %d, expected 1296." % flen)
86	exit(1)
87
88exit(0)
89