1#!/usr/local/bin/python2.7
2# check wether path mtu to dst is as expected
3
4import os
5import threading
6from addr import *
7from scapy.all import *
8
9# usage: ping6_mtu src dst size icmp6-size
10
11# work around the broken sniffing of packages with bad checksum
12#a=srp1(eth, iface=SRC_IF, timeout=2)
13class Sniff1(threading.Thread):
14	filter = None
15	captured = None
16	packet = None
17	def run(self):
18		self.captured = sniff(iface=SRC_IF, filter=self.filter,
19		    count=1, timeout=3)
20		if self.captured:
21			self.packet = self.captured[0]
22
23srcaddr=sys.argv[1]
24dstaddr=sys.argv[2]
25size=int(sys.argv[3])
26expect=int(sys.argv[4])
27eid=os.getpid() & 0xffff
28hdr=IPv6(src=srcaddr, dst=dstaddr)/ICMPv6EchoRequest(id=eid)
29payload="a" * (size - len(str(hdr)))
30ip=hdr/payload
31iplen=IPv6(str(ip)).plen
32eth=Ether(src=SRC_MAC, dst=PF_MAC)/ip
33
34sniffer = Sniff1();
35# pcap cannot access icmp6, check for packet too big, avoid neighbor discovery
36sniffer.filter = "ip6 and dst %s and icmp6 and ip6[40] = 2 and ip6[41] = 0" \
37    % srcaddr
38sniffer.start()
39time.sleep(1)
40sendp(eth, iface=SRC_IF)
41sniffer.join(timeout=5)
42a = sniffer.packet
43
44if a is None:
45	print "no packet sniffed"
46	exit(2)
47if a and a.type == ETH_P_IPV6 and \
48    ipv6nh[a.payload.nh] == 'ICMPv6' and \
49    icmp6types[a.payload.payload.type] == 'Packet too big':
50	mtu=a.payload.payload.mtu
51	print "mtu=%d" % (mtu)
52	if mtu != expect:
53		print "MTU!=%d" % (expect)
54		exit(1)
55	iip=a.payload.payload.payload
56	iiplen=iip.plen
57	if iiplen != iplen:
58		print "inner IPv6 plen %d!=%d" % (iiplen, iplen)
59		exit(1)
60	isrc=iip.src
61	if isrc != srcaddr:
62		print "inner IPv6 src %d!=%d" % (isrc, srcaddr)
63		exit(1)
64	idst=iip.dst
65	if idst != dstaddr:
66		print "inner IPv6 dst %d!=%d" % (idst, dstaddr)
67		exit(1)
68	exit(0)
69print "MTU=UNKNOWN"
70exit(2)
71