1f7d24e3fSHanoh Haim#! /usr/bin/env python
2f7d24e3fSHanoh Haim#############################################################################
3f7d24e3fSHanoh Haim##                                                                         ##
4f7d24e3fSHanoh Haim## inet6.py --- IPv6 support for Scapy                                     ##
5f7d24e3fSHanoh Haim##              see http://natisbad.org/IPv6/                              ##
6f7d24e3fSHanoh Haim##              for more informations                                      ##
7f7d24e3fSHanoh Haim##                                                                         ##
8f7d24e3fSHanoh Haim## Copyright (C) 2005  Guillaume Valadon <guedou@hongo.wide.ad.jp>         ##
9f7d24e3fSHanoh Haim##                     Arnaud Ebalard <arnaud.ebalard@eads.net>            ##
10f7d24e3fSHanoh Haim##                                                                         ##
11f7d24e3fSHanoh Haim## This program is free software; you can redistribute it and/or modify it ##
12f7d24e3fSHanoh Haim## under the terms of the GNU General Public License version 2 as          ##
13f7d24e3fSHanoh Haim## published by the Free Software Foundation.                              ##
14f7d24e3fSHanoh Haim##                                                                         ##
15f7d24e3fSHanoh Haim## This program is distributed in the hope that it will be useful, but     ##
16f7d24e3fSHanoh Haim## WITHOUT ANY WARRANTY; without even the implied warranty of              ##
17f7d24e3fSHanoh Haim## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       ##
18f7d24e3fSHanoh Haim## General Public License for more details.                                ##
19f7d24e3fSHanoh Haim##                                                                         ##
20f7d24e3fSHanoh Haim#############################################################################
21f7d24e3fSHanoh Haim
22f7d24e3fSHanoh Haim"""
23f7d24e3fSHanoh HaimIPv6 (Internet Protocol v6).
24f7d24e3fSHanoh Haim"""
25f7d24e3fSHanoh Haim
26f7d24e3fSHanoh Haim
27f7d24e3fSHanoh Haimimport socket
28f7d24e3fSHanoh Haimif not socket.has_ipv6:
29f7d24e3fSHanoh Haim    raise socket.error("can't use AF_INET6, IPv6 is disabled")
30f7d24e3fSHanoh Haimif not hasattr(socket, "IPPROTO_IPV6"):
31f7d24e3fSHanoh Haim    # Workaround for http://bugs.python.org/issue6926
32f7d24e3fSHanoh Haim    socket.IPPROTO_IPV6 = 41
33f7d24e3fSHanoh Haim
34f7d24e3fSHanoh Haimif not ('IPPROTO_IPIP ' in globals()):
35f7d24e3fSHanoh Haim    IPPROTO_IPIP=4
36f7d24e3fSHanoh Haim
37f7d24e3fSHanoh Haim
38f7d24e3fSHanoh Haimfrom scapy.config import conf
39f7d24e3fSHanoh Haimfrom scapy.layers.l2 import *
40f7d24e3fSHanoh Haimfrom scapy.layers.inet import *
41f7d24e3fSHanoh Haimfrom scapy.fields import *
42f7d24e3fSHanoh Haimfrom scapy.packet import *
43f7d24e3fSHanoh Haimfrom scapy.volatile import *
44f7d24e3fSHanoh Haimfrom scapy.sendrecv import sr,sr1,srp1
45f7d24e3fSHanoh Haimfrom scapy.as_resolvers import AS_resolver_riswhois
46f7d24e3fSHanoh Haimfrom scapy.supersocket import SuperSocket,L3RawSocket
47f7d24e3fSHanoh Haimfrom scapy.arch import *
48f7d24e3fSHanoh Haimfrom scapy.utils6 import *
49f7d24e3fSHanoh Haim
50f7d24e3fSHanoh Haim
51f7d24e3fSHanoh Haim#############################################################################
52f7d24e3fSHanoh Haim# Helpers                                                                  ##
53f7d24e3fSHanoh Haim#############################################################################
54f7d24e3fSHanoh Haim
55f7d24e3fSHanoh Haimdef get_cls(name, fallback_cls):
56f7d24e3fSHanoh Haim    return globals().get(name, fallback_cls)
57f7d24e3fSHanoh Haim
58f7d24e3fSHanoh Haim
59f7d24e3fSHanoh Haim##########################
60f7d24e3fSHanoh Haim## Neighbor cache stuff ##
61f7d24e3fSHanoh Haim##########################
62f7d24e3fSHanoh Haim
63f7d24e3fSHanoh Haimconf.netcache.new_cache("in6_neighbor", 120)
64f7d24e3fSHanoh Haim
65f7d24e3fSHanoh Haimdef neighsol(addr, src, iface, timeout=1, chainCC=0):
66f7d24e3fSHanoh Haim    """
67f7d24e3fSHanoh Haim    Sends an ICMPv6 Neighbor Solicitation message to get the MAC address
68f7d24e3fSHanoh Haim    of the neighbor with specified IPv6 address addr. 'src' address is
69f7d24e3fSHanoh Haim    used as source of the message. Message is sent on iface. By default,
70f7d24e3fSHanoh Haim    timeout waiting for an answer is 1 second.
71f7d24e3fSHanoh Haim
72f7d24e3fSHanoh Haim    If no answer is gathered, None is returned. Else, the answer is
73f7d24e3fSHanoh Haim    returned (ethernet frame).
74f7d24e3fSHanoh Haim    """
75f7d24e3fSHanoh Haim
76f7d24e3fSHanoh Haim    nsma = in6_getnsma(inet_pton(socket.AF_INET6, addr))
77f7d24e3fSHanoh Haim    d = inet_ntop(socket.AF_INET6, nsma)
78f7d24e3fSHanoh Haim    dm = in6_getnsmac(nsma)
79f7d24e3fSHanoh Haim    p = Ether(dst=dm)/IPv6(dst=d, src=src, hlim=255)
80f7d24e3fSHanoh Haim    p /= ICMPv6ND_NS(tgt=addr)
81f7d24e3fSHanoh Haim    p /= ICMPv6NDOptSrcLLAddr(lladdr=get_if_hwaddr(iface))
82f7d24e3fSHanoh Haim    res = srp1(p,type=ETH_P_IPV6, iface=iface, timeout=1, verbose=0,
83f7d24e3fSHanoh Haim               chainCC=chainCC)
84f7d24e3fSHanoh Haim
85f7d24e3fSHanoh Haim    return res
86f7d24e3fSHanoh Haim
87f7d24e3fSHanoh Haimdef getmacbyip6(ip6, chainCC=0):
88f7d24e3fSHanoh Haim    """
89f7d24e3fSHanoh Haim    Returns the mac address to be used for provided 'ip6' peer.
90f7d24e3fSHanoh Haim    neighborCache.get() method is used on instantiated neighbor cache.
91f7d24e3fSHanoh Haim    Resolution mechanism is described in associated doc string.
92f7d24e3fSHanoh Haim
93f7d24e3fSHanoh Haim    (chainCC parameter value ends up being passed to sending function
94f7d24e3fSHanoh Haim     used to perform the resolution, if needed)
95f7d24e3fSHanoh Haim    """
96f7d24e3fSHanoh Haim
97f7d24e3fSHanoh Haim    if in6_ismaddr(ip6): # Multicast
98f7d24e3fSHanoh Haim        mac = in6_getnsmac(inet_pton(socket.AF_INET6, ip6))
99f7d24e3fSHanoh Haim        return mac
100f7d24e3fSHanoh Haim
101f7d24e3fSHanoh Haim    iff,a,nh = conf.route6.route(ip6, dev=conf.iface6)
102f7d24e3fSHanoh Haim
103f7d24e3fSHanoh Haim    if iff == LOOPBACK_NAME:
104f7d24e3fSHanoh Haim        return "ff:ff:ff:ff:ff:ff"
105f7d24e3fSHanoh Haim
106f7d24e3fSHanoh Haim    if nh != '::':
107f7d24e3fSHanoh Haim        ip6 = nh # Found next hop
108f7d24e3fSHanoh Haim
109f7d24e3fSHanoh Haim    mac = conf.netcache.in6_neighbor.get(ip6)
110f7d24e3fSHanoh Haim    if mac:
111f7d24e3fSHanoh Haim        return mac
112f7d24e3fSHanoh Haim
113f7d24e3fSHanoh Haim    res = neighsol(ip6, a, iff, chainCC=chainCC)
114f7d24e3fSHanoh Haim
115f7d24e3fSHanoh Haim    if res is not None:
116f7d24e3fSHanoh Haim        if ICMPv6NDOptDstLLAddr in res:
117f7d24e3fSHanoh Haim	  mac = res[ICMPv6NDOptDstLLAddr].lladdr
118f7d24e3fSHanoh Haim	else:
119f7d24e3fSHanoh Haim	  mac = res.src
120f7d24e3fSHanoh Haim        conf.netcache.in6_neighbor[ip6] = mac
121f7d24e3fSHanoh Haim        return mac
122f7d24e3fSHanoh Haim
123f7d24e3fSHanoh Haim    return None
124f7d24e3fSHanoh Haim
125f7d24e3fSHanoh Haim
126f7d24e3fSHanoh Haim#############################################################################
127f7d24e3fSHanoh Haim#############################################################################
128f7d24e3fSHanoh Haim###              IPv6 addresses manipulation routines                     ###
129f7d24e3fSHanoh Haim#############################################################################
130f7d24e3fSHanoh Haim#############################################################################
131f7d24e3fSHanoh Haim
132f7d24e3fSHanoh Haimclass Net6(Gen): # syntax ex. fec0::/126
133f7d24e3fSHanoh Haim    """Generate a list of IPv6s from a network address or a name"""
134f7d24e3fSHanoh Haim    name = "ipv6"
135f7d24e3fSHanoh Haim    ipaddress = re.compile(r"^([a-fA-F0-9:]+)(/[1]?[0-3]?[0-9])?$")
136f7d24e3fSHanoh Haim
137f7d24e3fSHanoh Haim    def __init__(self, net):
138f7d24e3fSHanoh Haim        self.repr = net
139f7d24e3fSHanoh Haim
140f7d24e3fSHanoh Haim        tmp = net.split('/')+["128"]
141f7d24e3fSHanoh Haim        if not self.ipaddress.match(net):
142f7d24e3fSHanoh Haim            tmp[0]=socket.getaddrinfo(tmp[0], None, socket.AF_INET6)[0][-1][0]
143f7d24e3fSHanoh Haim
144f7d24e3fSHanoh Haim        netmask = int(tmp[1])
145f7d24e3fSHanoh Haim        self.net = inet_pton(socket.AF_INET6, tmp[0])
146f7d24e3fSHanoh Haim        self.mask = in6_cidr2mask(netmask)
147f7d24e3fSHanoh Haim        self.plen = netmask
148f7d24e3fSHanoh Haim
149f7d24e3fSHanoh Haim    def __iter__(self):
150f7d24e3fSHanoh Haim        def m8(i):
151f7d24e3fSHanoh Haim            if i % 8 == 0:
152f7d24e3fSHanoh Haim                return i
153f7d24e3fSHanoh Haim        tuple = filter(lambda x: m8(x), xrange(8, 129))
154f7d24e3fSHanoh Haim
155f7d24e3fSHanoh Haim        a = in6_and(self.net, self.mask)
156f7d24e3fSHanoh Haim        tmp = map(lambda x:  x, struct.unpack('16B', a))
157f7d24e3fSHanoh Haim
158f7d24e3fSHanoh Haim        def parse_digit(a, netmask):
159f7d24e3fSHanoh Haim            netmask = min(8,max(netmask,0))
160f7d24e3fSHanoh Haim            a = (int(a) & (0xffL<<netmask),(int(a) | (0xffL>>(8-netmask)))+1)
161f7d24e3fSHanoh Haim            return a
162f7d24e3fSHanoh Haim        self.parsed = map(lambda x,y: parse_digit(x,y), tmp, map(lambda x,nm=self.plen: x-nm, tuple))
163f7d24e3fSHanoh Haim
164f7d24e3fSHanoh Haim        def rec(n, l):
165f7d24e3fSHanoh Haim            if n and  n % 2 == 0:
166f7d24e3fSHanoh Haim                sep = ':'
167f7d24e3fSHanoh Haim            else:
168f7d24e3fSHanoh Haim                sep = ''
169f7d24e3fSHanoh Haim            if n == 16:
170f7d24e3fSHanoh Haim                return l
171f7d24e3fSHanoh Haim            else:
172f7d24e3fSHanoh Haim                ll = []
173f7d24e3fSHanoh Haim                for i in xrange(*self.parsed[n]):
174f7d24e3fSHanoh Haim                    for y in l:
175f7d24e3fSHanoh Haim                        ll += [y+sep+'%.2x'%i]
176f7d24e3fSHanoh Haim                return rec(n+1, ll)
177f7d24e3fSHanoh Haim
178f7d24e3fSHanoh Haim        return iter(rec(0, ['']))
179f7d24e3fSHanoh Haim
180f7d24e3fSHanoh Haim    def __repr__(self):
181f7d24e3fSHanoh Haim        return "<Net6 %s>" % self.repr
182f7d24e3fSHanoh Haim
183f7d24e3fSHanoh Haim
184f7d24e3fSHanoh Haim
185f7d24e3fSHanoh Haim
186f7d24e3fSHanoh Haim
187f7d24e3fSHanoh Haim
188f7d24e3fSHanoh Haim#############################################################################
189f7d24e3fSHanoh Haim#############################################################################
190f7d24e3fSHanoh Haim###                              IPv6 Class                               ###
191f7d24e3fSHanoh Haim#############################################################################
192f7d24e3fSHanoh Haim#############################################################################
193f7d24e3fSHanoh Haim
194f7d24e3fSHanoh Haimclass IP6Field(Field):
195f7d24e3fSHanoh Haim    def __init__(self, name, default):
196f7d24e3fSHanoh Haim        Field.__init__(self, name, default, "16s")
197f7d24e3fSHanoh Haim    def h2i(self, pkt, x):
198f7d24e3fSHanoh Haim        if type(x) is str:
199f7d24e3fSHanoh Haim            try:
200f7d24e3fSHanoh Haim                x = in6_ptop(x)
201f7d24e3fSHanoh Haim            except socket.error:
202f7d24e3fSHanoh Haim                x = Net6(x)
203f7d24e3fSHanoh Haim        elif type(x) is list:
204f7d24e3fSHanoh Haim            x = map(Net6, x)
205f7d24e3fSHanoh Haim        return x
206f7d24e3fSHanoh Haim    def i2m(self, pkt, x):
207f7d24e3fSHanoh Haim        return inet_pton(socket.AF_INET6, x)
208f7d24e3fSHanoh Haim    def m2i(self, pkt, x):
209f7d24e3fSHanoh Haim        return inet_ntop(socket.AF_INET6, x)
210f7d24e3fSHanoh Haim    def any2i(self, pkt, x):
211f7d24e3fSHanoh Haim        return self.h2i(pkt,x)
212f7d24e3fSHanoh Haim    def i2repr(self, pkt, x):
213f7d24e3fSHanoh Haim        if x is None:
214f7d24e3fSHanoh Haim            return self.i2h(pkt,x)
215f7d24e3fSHanoh Haim        elif not isinstance(x, Net6) and not type(x) is list:
216f7d24e3fSHanoh Haim            if in6_isaddrTeredo(x):   # print Teredo info
217f7d24e3fSHanoh Haim                server, flag, maddr, mport = teredoAddrExtractInfo(x)
218f7d24e3fSHanoh Haim                return "%s [Teredo srv: %s cli: %s:%s]" % (self.i2h(pkt, x), server, maddr,mport)
219f7d24e3fSHanoh Haim            elif in6_isaddr6to4(x):   # print encapsulated address
220f7d24e3fSHanoh Haim                vaddr = in6_6to4ExtractAddr(x)
221f7d24e3fSHanoh Haim                return "%s [6to4 GW: %s]" % (self.i2h(pkt, x), vaddr)
222f7d24e3fSHanoh Haim        return self.i2h(pkt, x)       # No specific information to return
223f7d24e3fSHanoh Haim    def randval(self):
224f7d24e3fSHanoh Haim        return RandIP6()
225f7d24e3fSHanoh Haim
226f7d24e3fSHanoh Haimclass SourceIP6Field(IP6Field):
227f7d24e3fSHanoh Haim    def __init__(self, name, dstname):
228f7d24e3fSHanoh Haim        IP6Field.__init__(self, name, None)
229f7d24e3fSHanoh Haim        self.dstname = dstname
230f7d24e3fSHanoh Haim    def i2m(self, pkt, x):
231f7d24e3fSHanoh Haim        if x is None:
232f7d24e3fSHanoh Haim            dst=getattr(pkt,self.dstname)
233f7d24e3fSHanoh Haim            iff,x,nh = conf.route6.route(dst)
234f7d24e3fSHanoh Haim        return IP6Field.i2m(self, pkt, x)
235f7d24e3fSHanoh Haim    def i2h(self, pkt, x):
236f7d24e3fSHanoh Haim        if x is None:
237f7d24e3fSHanoh Haim            dst=getattr(pkt,self.dstname)
238f7d24e3fSHanoh Haim            if isinstance(dst,Gen):
239f7d24e3fSHanoh Haim                r = map(conf.route6.route, dst)
240f7d24e3fSHanoh Haim                r.sort()
241f7d24e3fSHanoh Haim                if r[0] == r[-1]:
242f7d24e3fSHanoh Haim                    x=r[0][1]
243f7d24e3fSHanoh Haim                else:
244f7d24e3fSHanoh Haim                    warning("More than one possible route for %s"%repr(dst))
245f7d24e3fSHanoh Haim                    return None
246f7d24e3fSHanoh Haim            else:
247f7d24e3fSHanoh Haim                iff,x,nh = conf.route6.route(dst)
248f7d24e3fSHanoh Haim        return IP6Field.i2h(self, pkt, x)
249f7d24e3fSHanoh Haim
250f7d24e3fSHanoh Haimipv6nh = { 0:"Hop-by-Hop Option Header",
251f7d24e3fSHanoh Haim           4:"IP",
252f7d24e3fSHanoh Haim           6:"TCP",
253f7d24e3fSHanoh Haim          17:"UDP",
254f7d24e3fSHanoh Haim          41:"IPv6",
255f7d24e3fSHanoh Haim          43:"Routing Header",
256f7d24e3fSHanoh Haim          44:"Fragment Header",
257f7d24e3fSHanoh Haim          47:"GRE",
258f7d24e3fSHanoh Haim          50:"ESP Header",
259f7d24e3fSHanoh Haim          51:"AH Header",
260f7d24e3fSHanoh Haim          58:"ICMPv6",
261f7d24e3fSHanoh Haim          59:"No Next Header",
262f7d24e3fSHanoh Haim          60:"Destination Option Header",
263f7d24e3fSHanoh Haim         135:"Mobility Header"}
264f7d24e3fSHanoh Haim
265f7d24e3fSHanoh Haimipv6nhcls = {  0: "IPv6ExtHdrHopByHop",
266f7d24e3fSHanoh Haim               4: "IP",
267f7d24e3fSHanoh Haim               6: "TCP",
268f7d24e3fSHanoh Haim               17: "UDP",
269f7d24e3fSHanoh Haim               43: "IPv6ExtHdrRouting",
270f7d24e3fSHanoh Haim               44: "IPv6ExtHdrFragment",
271f7d24e3fSHanoh Haim              #50: "IPv6ExtHrESP",
272f7d24e3fSHanoh Haim              #51: "IPv6ExtHdrAH",
273f7d24e3fSHanoh Haim               58: "ICMPv6Unknown",
274f7d24e3fSHanoh Haim               59: "Raw",
275f7d24e3fSHanoh Haim               60: "IPv6ExtHdrDestOpt" }
276f7d24e3fSHanoh Haim
277f7d24e3fSHanoh Haimclass IP6ListField(StrField):
278f7d24e3fSHanoh Haim    islist = 1
279f7d24e3fSHanoh Haim    def __init__(self, name, default, count_from=None, length_from=None):
280f7d24e3fSHanoh Haim        if default is None:
281f7d24e3fSHanoh Haim            default = []
282f7d24e3fSHanoh Haim        StrField.__init__(self, name, default)
283f7d24e3fSHanoh Haim        self.count_from = count_from
284f7d24e3fSHanoh Haim        self.length_from = length_from
285f7d24e3fSHanoh Haim
286f7d24e3fSHanoh Haim    def i2len(self, pkt, i):
287f7d24e3fSHanoh Haim        return 16*len(i)
288f7d24e3fSHanoh Haim
289f7d24e3fSHanoh Haim    def i2count(self, pkt, i):
290f7d24e3fSHanoh Haim        if type(i) is list:
291f7d24e3fSHanoh Haim            return len(i)
292f7d24e3fSHanoh Haim        return 0
293f7d24e3fSHanoh Haim
294f7d24e3fSHanoh Haim    def getfield(self, pkt, s):
295f7d24e3fSHanoh Haim        c = l = None
296f7d24e3fSHanoh Haim        if self.length_from is not None:
297f7d24e3fSHanoh Haim            l = self.length_from(pkt)
298f7d24e3fSHanoh Haim        elif self.count_from is not None:
299f7d24e3fSHanoh Haim            c = self.count_from(pkt)
300f7d24e3fSHanoh Haim
301f7d24e3fSHanoh Haim        lst = []
302f7d24e3fSHanoh Haim        ret = ""
303f7d24e3fSHanoh Haim        remain = s
304f7d24e3fSHanoh Haim        if l is not None:
305f7d24e3fSHanoh Haim            remain,ret = s[:l],s[l:]
306f7d24e3fSHanoh Haim        while remain:
307f7d24e3fSHanoh Haim            if c is not None:
308f7d24e3fSHanoh Haim                if c <= 0:
309f7d24e3fSHanoh Haim                    break
310f7d24e3fSHanoh Haim                c -= 1
311f7d24e3fSHanoh Haim            addr = inet_ntop(socket.AF_INET6, remain[:16])
312f7d24e3fSHanoh Haim            lst.append(addr)
313f7d24e3fSHanoh Haim            remain = remain[16:]
314f7d24e3fSHanoh Haim        return remain+ret,lst
315f7d24e3fSHanoh Haim
316f7d24e3fSHanoh Haim    def i2m(self, pkt, x):
317f7d24e3fSHanoh Haim        s = ''
318f7d24e3fSHanoh Haim        for y in x:
319f7d24e3fSHanoh Haim            try:
320f7d24e3fSHanoh Haim                y = inet_pton(socket.AF_INET6, y)
321f7d24e3fSHanoh Haim            except:
322f7d24e3fSHanoh Haim                y = socket.getaddrinfo(y, None, socket.AF_INET6)[0][-1][0]
323f7d24e3fSHanoh Haim                y = inet_pton(socket.AF_INET6, y)
324f7d24e3fSHanoh Haim            s += y
325f7d24e3fSHanoh Haim        return s
326f7d24e3fSHanoh Haim
327f7d24e3fSHanoh Haim    def i2repr(self,pkt,x):
328f7d24e3fSHanoh Haim        s = []
329f7d24e3fSHanoh Haim        if x == None:
330f7d24e3fSHanoh Haim            return "[]"
331f7d24e3fSHanoh Haim        for y in x:
332f7d24e3fSHanoh Haim            s.append('%s' % y)
333f7d24e3fSHanoh Haim        return "[ %s ]" % (", ".join(s))
334f7d24e3fSHanoh Haim
335f7d24e3fSHanoh Haimclass _IPv6GuessPayload:
336f7d24e3fSHanoh Haim    name = "Dummy class that implements guess_payload_class() for IPv6"
337f7d24e3fSHanoh Haim    def default_payload_class(self,p):
338f7d24e3fSHanoh Haim        if self.nh == 58: # ICMPv6
339f7d24e3fSHanoh Haim            t = ord(p[0])
340f7d24e3fSHanoh Haim            if len(p) > 2 and t == 139 or t == 140: # Node Info Query
341f7d24e3fSHanoh Haim                return _niquery_guesser(p)
342f7d24e3fSHanoh Haim            if len(p) >= icmp6typesminhdrlen.get(t, sys.maxint): # Other ICMPv6 messages
343f7d24e3fSHanoh Haim                return get_cls(icmp6typescls.get(t,"Raw"), "Raw")
344f7d24e3fSHanoh Haim            return Raw
345f7d24e3fSHanoh Haim        elif self.nh == 135 and len(p) > 3: # Mobile IPv6
346f7d24e3fSHanoh Haim            return _mip6_mhtype2cls.get(ord(p[2]), MIP6MH_Generic)
347f7d24e3fSHanoh Haim        else:
348f7d24e3fSHanoh Haim            return get_cls(ipv6nhcls.get(self.nh,"Raw"), "Raw")
349f7d24e3fSHanoh Haim
350f7d24e3fSHanoh Haimclass IPv6(_IPv6GuessPayload, Packet, IPTools):
351f7d24e3fSHanoh Haim    name = "IPv6"
352f7d24e3fSHanoh Haim    fields_desc = [ BitField("version" , 6 , 4),
353f7d24e3fSHanoh Haim                    BitField("tc", 0, 8), #TODO: IPv6, ByteField ?
354f7d24e3fSHanoh Haim                    BitField("fl", 0, 20),
355f7d24e3fSHanoh Haim                    ShortField("plen", None),
356f7d24e3fSHanoh Haim                    ByteEnumField("nh", 59, ipv6nh),
357f7d24e3fSHanoh Haim                    ByteField("hlim", 64),
358d5099029SHanoh Haim                    IP6Field("src", "::2"),
359f7d24e3fSHanoh Haim                    #SourceIP6Field("src", "dst"), # dst is for src @ selection
360d5099029SHanoh Haim                    IP6Field("dst", "::1") ]
361f7d24e3fSHanoh Haim
362f7d24e3fSHanoh Haim    def route(self):
363f7d24e3fSHanoh Haim        dst = self.dst
364f7d24e3fSHanoh Haim        if isinstance(dst,Gen):
365f7d24e3fSHanoh Haim            dst = iter(dst).next()
366f7d24e3fSHanoh Haim        return conf.route6.route(dst)
367f7d24e3fSHanoh Haim
368f7d24e3fSHanoh Haim    def mysummary(self):
369f7d24e3fSHanoh Haim        return "%s > %s (%i)" % (self.src,self.dst, self.nh)
370f7d24e3fSHanoh Haim
371f7d24e3fSHanoh Haim    def post_build(self, p, pay):
372f7d24e3fSHanoh Haim        p += pay
373f7d24e3fSHanoh Haim        if self.plen is None:
374f7d24e3fSHanoh Haim            l = len(p) - 40
375f7d24e3fSHanoh Haim            p = p[:4]+struct.pack("!H", l)+p[6:]
376f7d24e3fSHanoh Haim        return p
377f7d24e3fSHanoh Haim
378f7d24e3fSHanoh Haim    def extract_padding(self, s):
379f7d24e3fSHanoh Haim        l = self.plen
380f7d24e3fSHanoh Haim        return s[:l], s[l:]
381f7d24e3fSHanoh Haim
382f7d24e3fSHanoh Haim    def hashret(self):
383f7d24e3fSHanoh Haim        if self.nh == 58 and isinstance(self.payload, _ICMPv6):
384f7d24e3fSHanoh Haim            if self.payload.type < 128:
385f7d24e3fSHanoh Haim                return self.payload.payload.hashret()
386f7d24e3fSHanoh Haim            elif (self.payload.type in [133,134,135,136,144,145]):
387f7d24e3fSHanoh Haim                return struct.pack("B", self.nh)+self.payload.hashret()
388f7d24e3fSHanoh Haim
389f7d24e3fSHanoh Haim        nh = self.nh
390f7d24e3fSHanoh Haim        sd = self.dst
391f7d24e3fSHanoh Haim        ss = self.src
392f7d24e3fSHanoh Haim        if self.nh == 43 and isinstance(self.payload, IPv6ExtHdrRouting):
393f7d24e3fSHanoh Haim            # With routing header, the destination is the last
394f7d24e3fSHanoh Haim            # address of the IPv6 list if segleft > 0
395f7d24e3fSHanoh Haim            nh = self.payload.nh
396f7d24e3fSHanoh Haim            try:
397f7d24e3fSHanoh Haim                sd = self.addresses[-1]
398f7d24e3fSHanoh Haim            except IndexError:
399f7d24e3fSHanoh Haim                sd = '::1'
400f7d24e3fSHanoh Haim            # TODO: big bug with ICMPv6 error messages as the destination of IPerror6
401f7d24e3fSHanoh Haim            #       could be anything from the original list ...
402f7d24e3fSHanoh Haim            if 1:
403f7d24e3fSHanoh Haim                sd = inet_pton(socket.AF_INET6, sd)
404f7d24e3fSHanoh Haim                for a in self.addresses:
405f7d24e3fSHanoh Haim                    a = inet_pton(socket.AF_INET6, a)
406f7d24e3fSHanoh Haim                    sd = strxor(sd, a)
407f7d24e3fSHanoh Haim                sd = inet_ntop(socket.AF_INET6, sd)
408f7d24e3fSHanoh Haim
409f7d24e3fSHanoh Haim        if self.nh == 44 and isinstance(self.payload, IPv6ExtHdrFragment):
410f7d24e3fSHanoh Haim            nh = self.payload.nh
411f7d24e3fSHanoh Haim
412f7d24e3fSHanoh Haim        if self.nh == 0 and isinstance(self.payload, IPv6ExtHdrHopByHop):
413f7d24e3fSHanoh Haim            nh = self.payload.nh
414f7d24e3fSHanoh Haim
415f7d24e3fSHanoh Haim        if self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt):
416f7d24e3fSHanoh Haim            foundhao = None
417f7d24e3fSHanoh Haim            for o in self.payload.options:
418f7d24e3fSHanoh Haim                if isinstance(o, HAO):
419f7d24e3fSHanoh Haim                    foundhao = o
420f7d24e3fSHanoh Haim            if foundhao:
421f7d24e3fSHanoh Haim                nh = self.payload.nh # XXX what if another extension follows ?
422f7d24e3fSHanoh Haim                ss = foundhao.hoa
423f7d24e3fSHanoh Haim
424f7d24e3fSHanoh Haim        if conf.checkIPsrc and conf.checkIPaddr:
425f7d24e3fSHanoh Haim            sd = inet_pton(socket.AF_INET6, sd)
426f7d24e3fSHanoh Haim            ss = inet_pton(socket.AF_INET6, self.src)
427f7d24e3fSHanoh Haim            return struct.pack("B",nh)+self.payload.hashret()
428f7d24e3fSHanoh Haim        else:
429f7d24e3fSHanoh Haim            return struct.pack("B", nh)+self.payload.hashret()
430f7d24e3fSHanoh Haim
431f7d24e3fSHanoh Haim    def answers(self, other):
432f7d24e3fSHanoh Haim        if not isinstance(other, IPv6): # self is reply, other is request
433f7d24e3fSHanoh Haim            return False
434f7d24e3fSHanoh Haim        if conf.checkIPaddr:
435f7d24e3fSHanoh Haim            ss = inet_pton(socket.AF_INET6, self.src)
436f7d24e3fSHanoh Haim            sd = inet_pton(socket.AF_INET6, self.dst)
437f7d24e3fSHanoh Haim            os = inet_pton(socket.AF_INET6, other.src)
438f7d24e3fSHanoh Haim            od = inet_pton(socket.AF_INET6, other.dst)
439f7d24e3fSHanoh Haim            # request was sent to a multicast address (other.dst)
440f7d24e3fSHanoh Haim            # Check reply destination addr matches request source addr (i.e
441f7d24e3fSHanoh Haim            # sd == os) except when reply is multicasted too
442f7d24e3fSHanoh Haim            # XXX test mcast scope matching ?
443f7d24e3fSHanoh Haim            if in6_ismaddr(other.dst):
444f7d24e3fSHanoh Haim                if in6_ismaddr(self.dst):
445f7d24e3fSHanoh Haim                    if ((od == sd) or
446f7d24e3fSHanoh Haim                        (in6_isaddrllallnodes(self.dst) and in6_isaddrllallservers(other.dst))):
447f7d24e3fSHanoh Haim                         return self.payload.answers(other.payload)
448f7d24e3fSHanoh Haim                    return False
449f7d24e3fSHanoh Haim                if (os == sd):
450f7d24e3fSHanoh Haim                    return self.payload.answers(other.payload)
451f7d24e3fSHanoh Haim                return False
452f7d24e3fSHanoh Haim            elif (sd != os): # or ss != od): <- removed for ICMP errors
453f7d24e3fSHanoh Haim                return False
454f7d24e3fSHanoh Haim        if self.nh == 58 and isinstance(self.payload, _ICMPv6) and self.payload.type < 128:
455f7d24e3fSHanoh Haim            # ICMPv6 Error message -> generated by IPv6 packet
456f7d24e3fSHanoh Haim            # Note : at the moment, we jump the ICMPv6 specific class
457f7d24e3fSHanoh Haim            # to call answers() method of erroneous packet (over
458f7d24e3fSHanoh Haim            # initial packet). There can be cases where an ICMPv6 error
459f7d24e3fSHanoh Haim            # class could implement a specific answers method that perform
460f7d24e3fSHanoh Haim            # a specific task. Currently, don't see any use ...
461f7d24e3fSHanoh Haim            return self.payload.payload.answers(other)
462f7d24e3fSHanoh Haim        elif other.nh == 0 and isinstance(other.payload, IPv6ExtHdrHopByHop):
463f7d24e3fSHanoh Haim            return self.payload.answers(other.payload.payload)
464f7d24e3fSHanoh Haim        elif other.nh == 44 and isinstance(other.payload, IPv6ExtHdrFragment):
465f7d24e3fSHanoh Haim            return self.payload.answers(other.payload.payload)
466f7d24e3fSHanoh Haim        elif other.nh == 43 and isinstance(other.payload, IPv6ExtHdrRouting):
467f7d24e3fSHanoh Haim            return self.payload.answers(other.payload.payload) # Buggy if self.payload is a IPv6ExtHdrRouting
468f7d24e3fSHanoh Haim        elif other.nh == 60 and isinstance(other.payload, IPv6ExtHdrDestOpt):
469f7d24e3fSHanoh Haim            return self.payload.payload.answers(other.payload.payload)
470f7d24e3fSHanoh Haim        elif self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt): # BU in reply to BRR, for instance
471f7d24e3fSHanoh Haim            return self.payload.payload.answers(other.payload)
472f7d24e3fSHanoh Haim        else:
473f7d24e3fSHanoh Haim            if (self.nh != other.nh):
474f7d24e3fSHanoh Haim                return False
475f7d24e3fSHanoh Haim            return self.payload.answers(other.payload)
476f7d24e3fSHanoh Haim
477f7d24e3fSHanoh Haim
478f7d24e3fSHanoh Haimconf.neighbor.register_l3(Ether, IPv6, lambda l2,l3: getmacbyip6(l3.dst))
479f7d24e3fSHanoh Haim
480f7d24e3fSHanoh Haim
481f7d24e3fSHanoh Haimclass IPerror6(IPv6):
482f7d24e3fSHanoh Haim    name = "IPv6 in ICMPv6"
483f7d24e3fSHanoh Haim    def answers(self, other):
484f7d24e3fSHanoh Haim        if not isinstance(other, IPv6):
485f7d24e3fSHanoh Haim            return False
486f7d24e3fSHanoh Haim        sd = inet_pton(socket.AF_INET6, self.dst)
487f7d24e3fSHanoh Haim        ss = inet_pton(socket.AF_INET6, self.src)
488f7d24e3fSHanoh Haim        od = inet_pton(socket.AF_INET6, other.dst)
489f7d24e3fSHanoh Haim        os = inet_pton(socket.AF_INET6, other.src)
490f7d24e3fSHanoh Haim
491f7d24e3fSHanoh Haim        # Make sure that the ICMPv6 error is related to the packet scapy sent
492f7d24e3fSHanoh Haim        if isinstance(self.underlayer, _ICMPv6) and self.underlayer.type < 128:
493f7d24e3fSHanoh Haim
494f7d24e3fSHanoh Haim            # find upper layer for self (possible citation)
495f7d24e3fSHanoh Haim            selfup = self.payload
496f7d24e3fSHanoh Haim            while selfup is not None and isinstance(selfup, _IPv6ExtHdr):
497f7d24e3fSHanoh Haim                selfup = selfup.payload
498f7d24e3fSHanoh Haim
499f7d24e3fSHanoh Haim            # find upper layer for other (initial packet). Also look for RH
500f7d24e3fSHanoh Haim            otherup = other.payload
501f7d24e3fSHanoh Haim            request_has_rh = False
502f7d24e3fSHanoh Haim            while otherup is not None and isinstance(otherup, _IPv6ExtHdr):
503f7d24e3fSHanoh Haim                if isinstance(otherup, IPv6ExtHdrRouting):
504f7d24e3fSHanoh Haim                    request_has_rh = True
505f7d24e3fSHanoh Haim                otherup = otherup.payload
506f7d24e3fSHanoh Haim
507f7d24e3fSHanoh Haim            if ((ss == os and sd == od) or      # <- Basic case
508f7d24e3fSHanoh Haim                (ss == os and request_has_rh)): # <- Request has a RH :
509f7d24e3fSHanoh Haim                                                #    don't check dst address
510f7d24e3fSHanoh Haim
511f7d24e3fSHanoh Haim                # Let's deal with possible MSS Clamping
512f7d24e3fSHanoh Haim                if (isinstance(selfup, TCP) and
513f7d24e3fSHanoh Haim                    isinstance(otherup, TCP) and
514f7d24e3fSHanoh Haim                    selfup.options != otherup.options): # seems clamped
515f7d24e3fSHanoh Haim
516f7d24e3fSHanoh Haim                    # Save fields modified by MSS clamping
517f7d24e3fSHanoh Haim                    old_otherup_opts    = otherup.options
518f7d24e3fSHanoh Haim                    old_otherup_cksum   = otherup.chksum
519f7d24e3fSHanoh Haim                    old_otherup_dataofs = otherup.dataofs
520f7d24e3fSHanoh Haim                    old_selfup_opts     = selfup.options
521f7d24e3fSHanoh Haim                    old_selfup_cksum    = selfup.chksum
522f7d24e3fSHanoh Haim                    old_selfup_dataofs  = selfup.dataofs
523f7d24e3fSHanoh Haim
524f7d24e3fSHanoh Haim                    # Nullify them
525f7d24e3fSHanoh Haim                    otherup.options = []
526f7d24e3fSHanoh Haim                    otherup.chksum  = 0
527f7d24e3fSHanoh Haim                    otherup.dataofs = 0
528f7d24e3fSHanoh Haim                    selfup.options  = []
529f7d24e3fSHanoh Haim                    selfup.chksum   = 0
530f7d24e3fSHanoh Haim                    selfup.dataofs  = 0
531f7d24e3fSHanoh Haim
532f7d24e3fSHanoh Haim                    # Test it and save result
533f7d24e3fSHanoh Haim                    s1 = str(selfup)
534f7d24e3fSHanoh Haim                    s2 = str(otherup)
535f7d24e3fSHanoh Haim                    l = min(len(s1), len(s2))
536f7d24e3fSHanoh Haim                    res = s1[:l] == s2[:l]
537f7d24e3fSHanoh Haim
538f7d24e3fSHanoh Haim                    # recall saved values
539f7d24e3fSHanoh Haim                    otherup.options = old_otherup_opts
540f7d24e3fSHanoh Haim                    otherup.chksum  = old_otherup_cksum
541f7d24e3fSHanoh Haim                    otherup.dataofs = old_otherup_dataofs
542f7d24e3fSHanoh Haim                    selfup.options  = old_selfup_opts
543f7d24e3fSHanoh Haim                    selfup.chksum   = old_selfup_cksum
544f7d24e3fSHanoh Haim                    selfup.dataofs  = old_selfup_dataofs
545f7d24e3fSHanoh Haim
546f7d24e3fSHanoh Haim                    return res
547f7d24e3fSHanoh Haim
548f7d24e3fSHanoh Haim                s1 = str(selfup)
549f7d24e3fSHanoh Haim                s2 = str(otherup)
550f7d24e3fSHanoh Haim                l = min(len(s1), len(s2))
551f7d24e3fSHanoh Haim                return s1[:l] == s2[:l]
552f7d24e3fSHanoh Haim
553f7d24e3fSHanoh Haim        return False
554f7d24e3fSHanoh Haim
555f7d24e3fSHanoh Haim    def mysummary(self):
556f7d24e3fSHanoh Haim        return Packet.mysummary(self)
557f7d24e3fSHanoh Haim
558f7d24e3fSHanoh Haim
559f7d24e3fSHanoh Haim#############################################################################
560f7d24e3fSHanoh Haim#############################################################################
561f7d24e3fSHanoh Haim###                 Upper Layer Checksum computation                      ###
562f7d24e3fSHanoh Haim#############################################################################
563f7d24e3fSHanoh Haim#############################################################################
564f7d24e3fSHanoh Haim
565f7d24e3fSHanoh Haimclass PseudoIPv6(Packet): # IPv6 Pseudo-header for checksum computation
566f7d24e3fSHanoh Haim    name = "Pseudo IPv6 Header"
567f7d24e3fSHanoh Haim    fields_desc = [ IP6Field("src", "::"),
568f7d24e3fSHanoh Haim                    IP6Field("dst", "::"),
569f7d24e3fSHanoh Haim                    ShortField("uplen", None),
570f7d24e3fSHanoh Haim                    BitField("zero", 0, 24),
571f7d24e3fSHanoh Haim                    ByteField("nh", 0) ]
572f7d24e3fSHanoh Haim
573f7d24e3fSHanoh Haimdef in6_chksum(nh, u, p):
574f7d24e3fSHanoh Haim    """
575f7d24e3fSHanoh Haim    Performs IPv6 Upper Layer checksum computation. Provided parameters are:
576f7d24e3fSHanoh Haim
577f7d24e3fSHanoh Haim    - 'nh' : value of upper layer protocol
578f7d24e3fSHanoh Haim    - 'u'  : upper layer instance (TCP, UDP, ICMPv6*, ). Instance must be
579f7d24e3fSHanoh Haim             provided with all under layers (IPv6 and all extension headers,
580f7d24e3fSHanoh Haim             for example)
581f7d24e3fSHanoh Haim    - 'p'  : the payload of the upper layer provided as a string
582f7d24e3fSHanoh Haim
583f7d24e3fSHanoh Haim    Functions operate by filling a pseudo header class instance (PseudoIPv6)
584f7d24e3fSHanoh Haim    with
585f7d24e3fSHanoh Haim    - Next Header value
586f7d24e3fSHanoh Haim    - the address of _final_ destination (if some Routing Header with non
587f7d24e3fSHanoh Haim    segleft field is present in underlayer classes, last address is used.)
588f7d24e3fSHanoh Haim    - the address of _real_ source (basically the source address of an
589f7d24e3fSHanoh Haim    IPv6 class instance available in the underlayer or the source address
590f7d24e3fSHanoh Haim    in HAO option if some Destination Option header found in underlayer
591f7d24e3fSHanoh Haim    includes this option).
592f7d24e3fSHanoh Haim    - the length is the length of provided payload string ('p')
593f7d24e3fSHanoh Haim    """
594f7d24e3fSHanoh Haim
595f7d24e3fSHanoh Haim    ph6 = PseudoIPv6()
596f7d24e3fSHanoh Haim    ph6.nh = nh
597f7d24e3fSHanoh Haim    rthdr = 0
598f7d24e3fSHanoh Haim    hahdr = 0
599f7d24e3fSHanoh Haim    final_dest_addr_found = 0
600f7d24e3fSHanoh Haim    while u != None and not isinstance(u, IPv6):
601f7d24e3fSHanoh Haim        if (isinstance(u, IPv6ExtHdrRouting) and
602f7d24e3fSHanoh Haim            u.segleft != 0 and len(u.addresses) != 0 and
603f7d24e3fSHanoh Haim            final_dest_addr_found == 0):
604f7d24e3fSHanoh Haim            rthdr = u.addresses[-1]
605f7d24e3fSHanoh Haim            final_dest_addr_found = 1
606f7d24e3fSHanoh Haim        elif (isinstance(u, IPv6ExtHdrDestOpt) and (len(u.options) == 1) and
607f7d24e3fSHanoh Haim             isinstance(u.options[0], HAO)):
608f7d24e3fSHanoh Haim             hahdr  = u.options[0].hoa
609f7d24e3fSHanoh Haim        u = u.underlayer
610f7d24e3fSHanoh Haim    if u is None:
611f7d24e3fSHanoh Haim        warning("No IPv6 underlayer to compute checksum. Leaving null.")
612f7d24e3fSHanoh Haim        return 0
613f7d24e3fSHanoh Haim    if hahdr:
614f7d24e3fSHanoh Haim        ph6.src = hahdr
615f7d24e3fSHanoh Haim    else:
616f7d24e3fSHanoh Haim        ph6.src = u.src
617f7d24e3fSHanoh Haim    if rthdr:
618f7d24e3fSHanoh Haim        ph6.dst = rthdr
619f7d24e3fSHanoh Haim    else:
620f7d24e3fSHanoh Haim        ph6.dst = u.dst
621f7d24e3fSHanoh Haim    ph6.uplen = len(p)
622f7d24e3fSHanoh Haim    ph6s = str(ph6)
623f7d24e3fSHanoh Haim    return checksum(ph6s+p)
624f7d24e3fSHanoh Haim
625f7d24e3fSHanoh Haim
626f7d24e3fSHanoh Haim#############################################################################
627f7d24e3fSHanoh Haim#############################################################################
628f7d24e3fSHanoh Haim###                         Extension Headers                             ###
629f7d24e3fSHanoh Haim#############################################################################
630f7d24e3fSHanoh Haim#############################################################################
631f7d24e3fSHanoh Haim
632f7d24e3fSHanoh Haim
633f7d24e3fSHanoh Haim# Inherited by all extension header classes
634f7d24e3fSHanoh Haimclass _IPv6ExtHdr(_IPv6GuessPayload, Packet):
635f7d24e3fSHanoh Haim    name = 'Abstract IPV6 Option Header'
636f7d24e3fSHanoh Haim    aliastypes = [IPv6, IPerror6] # TODO ...
637f7d24e3fSHanoh Haim
638f7d24e3fSHanoh Haim
639f7d24e3fSHanoh Haim#################### IPv6 options for Extension Headers #####################
640f7d24e3fSHanoh Haim
641f7d24e3fSHanoh Haim_hbhopts = { 0x00: "Pad1",
642f7d24e3fSHanoh Haim             0x01: "PadN",
643f7d24e3fSHanoh Haim             0x04: "Tunnel Encapsulation Limit",
644f7d24e3fSHanoh Haim             0x05: "Router Alert",
645f7d24e3fSHanoh Haim             0x06: "Quick-Start",
646f7d24e3fSHanoh Haim             0xc2: "Jumbo Payload",
647f7d24e3fSHanoh Haim             0xc9: "Home Address Option" }
648f7d24e3fSHanoh Haim
649f7d24e3fSHanoh Haimclass _OTypeField(ByteEnumField):
650f7d24e3fSHanoh Haim    """
651f7d24e3fSHanoh Haim    Modified BytEnumField that displays information regarding the IPv6 option
652f7d24e3fSHanoh Haim    based on its option type value (What should be done by nodes that process
653f7d24e3fSHanoh Haim    the option if they do not understand it ...)
654f7d24e3fSHanoh Haim
655f7d24e3fSHanoh Haim    It is used by Jumbo, Pad1, PadN, RouterAlert, HAO options
656f7d24e3fSHanoh Haim    """
657f7d24e3fSHanoh Haim    pol = {0x00: "00: skip",
658f7d24e3fSHanoh Haim           0x40: "01: discard",
659f7d24e3fSHanoh Haim           0x80: "10: discard+ICMP",
660f7d24e3fSHanoh Haim           0xC0: "11: discard+ICMP not mcast"}
661f7d24e3fSHanoh Haim
662f7d24e3fSHanoh Haim    enroutechange = {0x00: "0: Don't change en-route",
663f7d24e3fSHanoh Haim                 0x20: "1: May change en-route" }
664f7d24e3fSHanoh Haim
665f7d24e3fSHanoh Haim    def i2repr(self, pkt, x):
666f7d24e3fSHanoh Haim        s = self.i2s.get(x, repr(x))
667f7d24e3fSHanoh Haim        polstr = self.pol[(x & 0xC0)]
668f7d24e3fSHanoh Haim        enroutechangestr = self.enroutechange[(x & 0x20)]
669f7d24e3fSHanoh Haim        return "%s [%s, %s]" % (s, polstr, enroutechangestr)
670f7d24e3fSHanoh Haim
671f7d24e3fSHanoh Haimclass HBHOptUnknown(Packet): # IPv6 Hop-By-Hop Option
672f7d24e3fSHanoh Haim    name = "Scapy6 Unknown Option"
673f7d24e3fSHanoh Haim    fields_desc = [_OTypeField("otype", 0x01, _hbhopts),
674f7d24e3fSHanoh Haim                   FieldLenField("optlen", None, length_of="optdata", fmt="B"),
675f7d24e3fSHanoh Haim                   StrLenField("optdata", "",
676f7d24e3fSHanoh Haim                               length_from = lambda pkt: pkt.optlen) ]
677f7d24e3fSHanoh Haim    def alignment_delta(self, curpos): # By default, no alignment requirement
678f7d24e3fSHanoh Haim        """
679f7d24e3fSHanoh Haim        As specified in section 4.2 of RFC 2460, every options has
680f7d24e3fSHanoh Haim        an alignment requirement ususally expressed xn+y, meaning
681f7d24e3fSHanoh Haim        the Option Type must appear at an integer multiple of x octest
682f7d24e3fSHanoh Haim        from the start of the header, plus y octet.
683f7d24e3fSHanoh Haim
684f7d24e3fSHanoh Haim        That function is provided the current position from the
685f7d24e3fSHanoh Haim        start of the header and returns required padding length.
686f7d24e3fSHanoh Haim        """
687f7d24e3fSHanoh Haim        return 0
688f7d24e3fSHanoh Haim
689f7d24e3fSHanoh Haimclass Pad1(Packet): # IPv6 Hop-By-Hop Option
690f7d24e3fSHanoh Haim    name = "Pad1"
691f7d24e3fSHanoh Haim    fields_desc = [ _OTypeField("otype", 0x00, _hbhopts) ]
692f7d24e3fSHanoh Haim    def alignment_delta(self, curpos): # No alignment requirement
693f7d24e3fSHanoh Haim        return 0
694f7d24e3fSHanoh Haim
695f7d24e3fSHanoh Haimclass PadN(Packet): # IPv6 Hop-By-Hop Option
696f7d24e3fSHanoh Haim    name = "PadN"
697f7d24e3fSHanoh Haim    fields_desc = [_OTypeField("otype", 0x01, _hbhopts),
698f7d24e3fSHanoh Haim                   FieldLenField("optlen", None, length_of="optdata", fmt="B"),
699f7d24e3fSHanoh Haim                   StrLenField("optdata", "",
700f7d24e3fSHanoh Haim                               length_from = lambda pkt: pkt.optlen)]
701f7d24e3fSHanoh Haim    def alignment_delta(self, curpos): # No alignment requirement
702f7d24e3fSHanoh Haim        return 0
703f7d24e3fSHanoh Haim
704f7d24e3fSHanoh Haimclass RouterAlert(Packet): # RFC 2711 - IPv6 Hop-By-Hop Option
705f7d24e3fSHanoh Haim    name = "Router Alert"
706f7d24e3fSHanoh Haim    fields_desc = [_OTypeField("otype", 0x05, _hbhopts),
707f7d24e3fSHanoh Haim                   ByteField("optlen", 2),
708f7d24e3fSHanoh Haim                   ShortEnumField("value", None,
709f7d24e3fSHanoh Haim                                  { 0: "Datagram contains a MLD message",
710f7d24e3fSHanoh Haim                                    1: "Datagram contains RSVP message",
711f7d24e3fSHanoh Haim                                    2: "Datagram contains an Active Network message" }) ]
712f7d24e3fSHanoh Haim    # TODO : Check IANA has not defined new values for value field of RouterAlertOption
713f7d24e3fSHanoh Haim    # TODO : now that we have that option, we should do something in MLD class that need it
714f7d24e3fSHanoh Haim    def alignment_delta(self, curpos): # alignment requirement : 2n+0
715f7d24e3fSHanoh Haim        x = 2 ; y = 0
716f7d24e3fSHanoh Haim        delta = x*((curpos - y + x - 1)/x) + y - curpos
717f7d24e3fSHanoh Haim        return delta
718f7d24e3fSHanoh Haim
719f7d24e3fSHanoh Haimclass Jumbo(Packet): # IPv6 Hop-By-Hop Option
720f7d24e3fSHanoh Haim    name = "Jumbo Payload"
721f7d24e3fSHanoh Haim    fields_desc = [_OTypeField("otype", 0xC2, _hbhopts),
722f7d24e3fSHanoh Haim                   ByteField("optlen", 4),
723f7d24e3fSHanoh Haim                   IntField("jumboplen", None) ]
724f7d24e3fSHanoh Haim    def alignment_delta(self, curpos): # alignment requirement : 4n+2
725f7d24e3fSHanoh Haim        x = 4 ; y = 2
726f7d24e3fSHanoh Haim        delta = x*((curpos - y + x - 1)/x) + y - curpos
727f7d24e3fSHanoh Haim        return delta
728f7d24e3fSHanoh Haim
729f7d24e3fSHanoh Haimclass HAO(Packet): # IPv6 Destination Options Header Option
730f7d24e3fSHanoh Haim    name = "Home Address Option"
731f7d24e3fSHanoh Haim    fields_desc = [_OTypeField("otype", 0xC9, _hbhopts),
732f7d24e3fSHanoh Haim                   ByteField("optlen", 16),
733f7d24e3fSHanoh Haim                   IP6Field("hoa", "::") ]
734f7d24e3fSHanoh Haim    def alignment_delta(self, curpos): # alignment requirement : 8n+6
735f7d24e3fSHanoh Haim        x = 8 ; y = 6
736f7d24e3fSHanoh Haim        delta = x*((curpos - y + x - 1)/x) + y - curpos
737f7d24e3fSHanoh Haim        return delta
738f7d24e3fSHanoh Haim
739f7d24e3fSHanoh Haim_hbhoptcls = { 0x00: Pad1,
740f7d24e3fSHanoh Haim               0x01: PadN,
741f7d24e3fSHanoh Haim               0x05: RouterAlert,
742f7d24e3fSHanoh Haim               0xC2: Jumbo,
743f7d24e3fSHanoh Haim               0xC9: HAO }
744f7d24e3fSHanoh Haim
745f7d24e3fSHanoh Haim
746f7d24e3fSHanoh Haim######################## Hop-by-Hop Extension Header ########################
747f7d24e3fSHanoh Haim
748f7d24e3fSHanoh Haimclass _HopByHopOptionsField(PacketListField):
749f7d24e3fSHanoh Haim    islist = 1
750f7d24e3fSHanoh Haim    holds_packet = 1
751f7d24e3fSHanoh Haim    def __init__(self, name, default, cls, curpos, count_from=None, length_from=None):
752f7d24e3fSHanoh Haim        self.curpos = curpos
753f7d24e3fSHanoh Haim        PacketListField.__init__(self, name, default, cls, count_from=count_from, length_from=length_from)
754f7d24e3fSHanoh Haim
755f7d24e3fSHanoh Haim    def i2len(self, pkt, i):
756f7d24e3fSHanoh Haim        l = len(self.i2m(pkt, i))
757f7d24e3fSHanoh Haim        return l
758f7d24e3fSHanoh Haim
759f7d24e3fSHanoh Haim    def i2count(self, pkt, i):
760f7d24e3fSHanoh Haim        if type(i) is list:
761f7d24e3fSHanoh Haim            return len(i)
762f7d24e3fSHanoh Haim        return 0
763f7d24e3fSHanoh Haim
764f7d24e3fSHanoh Haim    def getfield(self, pkt, s):
765f7d24e3fSHanoh Haim        c = l = None
766f7d24e3fSHanoh Haim        if self.length_from is not None:
767f7d24e3fSHanoh Haim            l = self.length_from(pkt)
768f7d24e3fSHanoh Haim        elif self.count_from is not None:
769f7d24e3fSHanoh Haim            c = self.count_from(pkt)
770f7d24e3fSHanoh Haim
771f7d24e3fSHanoh Haim        opt = []
772f7d24e3fSHanoh Haim        ret = ""
773f7d24e3fSHanoh Haim        x = s
774f7d24e3fSHanoh Haim        if l is not None:
775f7d24e3fSHanoh Haim            x,ret = s[:l],s[l:]
776f7d24e3fSHanoh Haim        while x:
777f7d24e3fSHanoh Haim            if c is not None:
778f7d24e3fSHanoh Haim                if c <= 0:
779f7d24e3fSHanoh Haim                    break
780f7d24e3fSHanoh Haim                c -= 1
781f7d24e3fSHanoh Haim            o = ord(x[0]) # Option type
782f7d24e3fSHanoh Haim            cls = self.cls
783f7d24e3fSHanoh Haim            if _hbhoptcls.has_key(o):
784f7d24e3fSHanoh Haim                cls = _hbhoptcls[o]
785f7d24e3fSHanoh Haim            try:
786f7d24e3fSHanoh Haim                op = cls(x)
787f7d24e3fSHanoh Haim            except:
788f7d24e3fSHanoh Haim                op = self.cls(x)
789f7d24e3fSHanoh Haim            opt.append(op)
790f7d24e3fSHanoh Haim            if isinstance(op.payload, conf.raw_layer):
791f7d24e3fSHanoh Haim                x = op.payload.load
792f7d24e3fSHanoh Haim                del(op.payload)
793f7d24e3fSHanoh Haim            else:
794f7d24e3fSHanoh Haim                x = ""
795f7d24e3fSHanoh Haim        return x+ret,opt
796f7d24e3fSHanoh Haim
797f7d24e3fSHanoh Haim    def i2m(self, pkt, x):
798f7d24e3fSHanoh Haim        autopad = None
799f7d24e3fSHanoh Haim        try:
800f7d24e3fSHanoh Haim            autopad = getattr(pkt, "autopad") # Hack : 'autopad' phantom field
801f7d24e3fSHanoh Haim        except:
802f7d24e3fSHanoh Haim            autopad = 1
803f7d24e3fSHanoh Haim
804f7d24e3fSHanoh Haim        if not autopad:
805f7d24e3fSHanoh Haim            return "".join(map(str, x))
806f7d24e3fSHanoh Haim
807f7d24e3fSHanoh Haim        curpos = self.curpos
808f7d24e3fSHanoh Haim        s = ""
809f7d24e3fSHanoh Haim        for p in x:
810f7d24e3fSHanoh Haim            d = p.alignment_delta(curpos)
811f7d24e3fSHanoh Haim            curpos += d
812f7d24e3fSHanoh Haim            if d == 1:
813f7d24e3fSHanoh Haim                s += str(Pad1())
814f7d24e3fSHanoh Haim            elif d != 0:
815f7d24e3fSHanoh Haim                s += str(PadN(optdata='\x00'*(d-2)))
816f7d24e3fSHanoh Haim            pstr = str(p)
817f7d24e3fSHanoh Haim            curpos += len(pstr)
818f7d24e3fSHanoh Haim            s += pstr
819f7d24e3fSHanoh Haim
820f7d24e3fSHanoh Haim        # Let's make the class including our option field
821f7d24e3fSHanoh Haim        # a multiple of 8 octets long
822f7d24e3fSHanoh Haim        d = curpos % 8
823f7d24e3fSHanoh Haim        if d == 0:
824f7d24e3fSHanoh Haim            return s
825f7d24e3fSHanoh Haim        d = 8 - d
826f7d24e3fSHanoh Haim        if d == 1:
827f7d24e3fSHanoh Haim            s += str(Pad1())
828f7d24e3fSHanoh Haim        elif d != 0:
829f7d24e3fSHanoh Haim            s += str(PadN(optdata='\x00'*(d-2)))
830f7d24e3fSHanoh Haim
831f7d24e3fSHanoh Haim        return s
832f7d24e3fSHanoh Haim
833f7d24e3fSHanoh Haim    def addfield(self, pkt, s, val):
834f7d24e3fSHanoh Haim        return s+self.i2m(pkt, val)
835f7d24e3fSHanoh Haim
836f7d24e3fSHanoh Haimclass _PhantomAutoPadField(ByteField):
837f7d24e3fSHanoh Haim    def addfield(self, pkt, s, val):
838f7d24e3fSHanoh Haim        return s
839f7d24e3fSHanoh Haim
840f7d24e3fSHanoh Haim    def getfield(self, pkt, s):
841f7d24e3fSHanoh Haim        return s, 1
842f7d24e3fSHanoh Haim
843f7d24e3fSHanoh Haim    def i2repr(self, pkt, x):
844f7d24e3fSHanoh Haim        if x:
845f7d24e3fSHanoh Haim            return "On"
846f7d24e3fSHanoh Haim        return "Off"
847f7d24e3fSHanoh Haim
848f7d24e3fSHanoh Haim
849f7d24e3fSHanoh Haimclass IPv6ExtHdrHopByHop(_IPv6ExtHdr):
850f7d24e3fSHanoh Haim    name = "IPv6 Extension Header - Hop-by-Hop Options Header"
851f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("nh", 59, ipv6nh),
852f7d24e3fSHanoh Haim                    FieldLenField("len", None, length_of="options", fmt="B",
853f7d24e3fSHanoh Haim                                  adjust = lambda pkt,x: (x+2+7)/8 - 1),
854f7d24e3fSHanoh Haim                    _PhantomAutoPadField("autopad", 1), # autopad activated by default
855f7d24e3fSHanoh Haim                    _HopByHopOptionsField("options", [], HBHOptUnknown, 2,
856f7d24e3fSHanoh Haim                                          length_from = lambda pkt: (8*(pkt.len+1))-2) ]
857f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 0 }}
858f7d24e3fSHanoh Haim
859f7d24e3fSHanoh Haim
860f7d24e3fSHanoh Haim######################## Destination Option Header ##########################
861f7d24e3fSHanoh Haim
862f7d24e3fSHanoh Haimclass IPv6ExtHdrDestOpt(_IPv6ExtHdr):
863f7d24e3fSHanoh Haim    name = "IPv6 Extension Header - Destination Options Header"
864f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("nh", 59, ipv6nh),
865f7d24e3fSHanoh Haim                    FieldLenField("len", None, length_of="options", fmt="B",
866f7d24e3fSHanoh Haim                                  adjust = lambda pkt,x: (x+2+7)/8 - 1),
867f7d24e3fSHanoh Haim                    _PhantomAutoPadField("autopad", 1), # autopad activated by default
868f7d24e3fSHanoh Haim                    _HopByHopOptionsField("options", [], HBHOptUnknown, 2,
869f7d24e3fSHanoh Haim                                          length_from = lambda pkt: (8*(pkt.len+1))-2) ]
870f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 60 }}
871f7d24e3fSHanoh Haim
872f7d24e3fSHanoh Haim
873f7d24e3fSHanoh Haim############################# Routing Header ################################
874f7d24e3fSHanoh Haim
875f7d24e3fSHanoh Haimclass IPv6ExtHdrRouting(_IPv6ExtHdr):
876f7d24e3fSHanoh Haim    name = "IPv6 Option Header Routing"
877f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("nh", 59, ipv6nh),
878f7d24e3fSHanoh Haim                    FieldLenField("len", None, count_of="addresses", fmt="B",
879f7d24e3fSHanoh Haim                                  adjust = lambda pkt,x:2*x), # in 8 bytes blocks
880f7d24e3fSHanoh Haim                    ByteField("type", 0),
881f7d24e3fSHanoh Haim                    ByteField("segleft", None),
882f7d24e3fSHanoh Haim                    BitField("reserved", 0, 32), # There is meaning in this field ...
883f7d24e3fSHanoh Haim                    IP6ListField("addresses", [],
884f7d24e3fSHanoh Haim                                 length_from = lambda pkt: 8*pkt.len)]
885f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 43 }}
886f7d24e3fSHanoh Haim
887f7d24e3fSHanoh Haim    def post_build(self, pkt, pay):
888f7d24e3fSHanoh Haim        if self.segleft is None:
889f7d24e3fSHanoh Haim            pkt = pkt[:3]+struct.pack("B", len(self.addresses))+pkt[4:]
890f7d24e3fSHanoh Haim        return _IPv6ExtHdr.post_build(self, pkt, pay)
891f7d24e3fSHanoh Haim
892f7d24e3fSHanoh Haim########################### Fragmentation Header ############################
893f7d24e3fSHanoh Haim
894f7d24e3fSHanoh Haimclass IPv6ExtHdrFragment(_IPv6ExtHdr):
895f7d24e3fSHanoh Haim    name = "IPv6 Extension Header - Fragmentation header"
896f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("nh", 59, ipv6nh),
897f7d24e3fSHanoh Haim                    BitField("res1", 0, 8),
898f7d24e3fSHanoh Haim                    BitField("offset", 0, 13),
899f7d24e3fSHanoh Haim                    BitField("res2", 0, 2),
900f7d24e3fSHanoh Haim                    BitField("m", 0, 1),
901f7d24e3fSHanoh Haim                    IntField("id", None) ]
902f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 44 }}
903f7d24e3fSHanoh Haim
904f7d24e3fSHanoh Haim
905f7d24e3fSHanoh Haimdef defragment6(pktlist):
906f7d24e3fSHanoh Haim    """
907f7d24e3fSHanoh Haim    Performs defragmentation of a list of IPv6 packets. Packets are reordered.
908f7d24e3fSHanoh Haim    Crap is dropped. What lacks is completed by 'X' characters.
909f7d24e3fSHanoh Haim    """
910f7d24e3fSHanoh Haim
911f7d24e3fSHanoh Haim    l = filter(lambda x: IPv6ExtHdrFragment in x, pktlist) # remove non fragments
912f7d24e3fSHanoh Haim    if not l:
913f7d24e3fSHanoh Haim        return []
914f7d24e3fSHanoh Haim
915f7d24e3fSHanoh Haim    id = l[0][IPv6ExtHdrFragment].id
916f7d24e3fSHanoh Haim
917f7d24e3fSHanoh Haim    llen = len(l)
918f7d24e3fSHanoh Haim    l = filter(lambda x: x[IPv6ExtHdrFragment].id == id, l)
919f7d24e3fSHanoh Haim    if len(l) != llen:
920f7d24e3fSHanoh Haim        warning("defragment6: some fragmented packets have been removed from list")
921f7d24e3fSHanoh Haim    llen = len(l)
922f7d24e3fSHanoh Haim
923f7d24e3fSHanoh Haim    # reorder fragments
924f7d24e3fSHanoh Haim    i = 0
925f7d24e3fSHanoh Haim    res = []
926f7d24e3fSHanoh Haim    while l:
927f7d24e3fSHanoh Haim        min_pos = 0
928f7d24e3fSHanoh Haim        min_offset  = l[0][IPv6ExtHdrFragment].offset
929f7d24e3fSHanoh Haim        for p in l:
930f7d24e3fSHanoh Haim            cur_offset = p[IPv6ExtHdrFragment].offset
931f7d24e3fSHanoh Haim            if cur_offset < min_offset:
932f7d24e3fSHanoh Haim                min_pos = 0
933f7d24e3fSHanoh Haim                min_offset  = cur_offset
934f7d24e3fSHanoh Haim        res.append(l[min_pos])
935f7d24e3fSHanoh Haim        del(l[min_pos])
936f7d24e3fSHanoh Haim
937f7d24e3fSHanoh Haim    # regenerate the fragmentable part
938f7d24e3fSHanoh Haim    fragmentable = ""
939f7d24e3fSHanoh Haim    for p in res:
940f7d24e3fSHanoh Haim        q=p[IPv6ExtHdrFragment]
941f7d24e3fSHanoh Haim        offset = 8*q.offset
942f7d24e3fSHanoh Haim        if offset != len(fragmentable):
943f7d24e3fSHanoh Haim            warning("Expected an offset of %d. Found %d. Padding with XXXX" % (len(fragmentable), offset))
944f7d24e3fSHanoh Haim        fragmentable += "X"*(offset - len(fragmentable))
945f7d24e3fSHanoh Haim        fragmentable += str(q.payload)
946f7d24e3fSHanoh Haim
947f7d24e3fSHanoh Haim    # Regenerate the unfragmentable part.
948f7d24e3fSHanoh Haim    q = res[0]
949f7d24e3fSHanoh Haim    nh = q[IPv6ExtHdrFragment].nh
950f7d24e3fSHanoh Haim    q[IPv6ExtHdrFragment].underlayer.nh = nh
951f7d24e3fSHanoh Haim    q[IPv6ExtHdrFragment].underlayer.payload = None
952f7d24e3fSHanoh Haim    q /= conf.raw_layer(load=fragmentable)
953f7d24e3fSHanoh Haim
954f7d24e3fSHanoh Haim    return IPv6(str(q))
955f7d24e3fSHanoh Haim
956f7d24e3fSHanoh Haim
957f7d24e3fSHanoh Haimdef fragment6(pkt, fragSize):
958f7d24e3fSHanoh Haim    """
959f7d24e3fSHanoh Haim    Performs fragmentation of an IPv6 packet. Provided packet ('pkt') must already
960f7d24e3fSHanoh Haim    contain an IPv6ExtHdrFragment() class. 'fragSize' argument is the expected
961f7d24e3fSHanoh Haim    maximum size of fragments (MTU). The list of packets is returned.
962f7d24e3fSHanoh Haim
963f7d24e3fSHanoh Haim    If packet does not contain an IPv6ExtHdrFragment class, it is returned in
964f7d24e3fSHanoh Haim    result list.
965f7d24e3fSHanoh Haim    """
966f7d24e3fSHanoh Haim
967f7d24e3fSHanoh Haim    pkt = pkt.copy()
968f7d24e3fSHanoh Haim
969f7d24e3fSHanoh Haim    if not IPv6ExtHdrFragment in pkt:
970f7d24e3fSHanoh Haim        # TODO : automatically add a fragment before upper Layer
971f7d24e3fSHanoh Haim        #        at the moment, we do nothing and return initial packet
972f7d24e3fSHanoh Haim        #        as single element of a list
973f7d24e3fSHanoh Haim        return [pkt]
974f7d24e3fSHanoh Haim
975f7d24e3fSHanoh Haim    # If the payload is bigger than 65535, a Jumbo payload must be used, as
976f7d24e3fSHanoh Haim    # an IPv6 packet can't be bigger than 65535 bytes.
977f7d24e3fSHanoh Haim    if len(str(pkt[IPv6ExtHdrFragment])) > 65535:
978f7d24e3fSHanoh Haim      warning("An IPv6 packet can'be bigger than 65535, please use a Jumbo payload.")
979f7d24e3fSHanoh Haim      return []
980f7d24e3fSHanoh Haim
981f7d24e3fSHanoh Haim    s = str(pkt) # for instantiation to get upper layer checksum right
982f7d24e3fSHanoh Haim
983f7d24e3fSHanoh Haim    if len(s) <= fragSize:
984f7d24e3fSHanoh Haim        return [pkt]
985f7d24e3fSHanoh Haim
986f7d24e3fSHanoh Haim    # Fragmentable part : fake IPv6 for Fragmentable part length computation
987f7d24e3fSHanoh Haim    fragPart = pkt[IPv6ExtHdrFragment].payload
988f7d24e3fSHanoh Haim    tmp = str(IPv6(src="::1", dst="::1")/fragPart)
989f7d24e3fSHanoh Haim    fragPartLen = len(tmp) - 40  # basic IPv6 header length
990f7d24e3fSHanoh Haim    fragPartStr = s[-fragPartLen:]
991f7d24e3fSHanoh Haim
992f7d24e3fSHanoh Haim    # Grab Next Header for use in Fragment Header
993f7d24e3fSHanoh Haim    nh = IPv6(tmp[:40]).nh
994f7d24e3fSHanoh Haim
995f7d24e3fSHanoh Haim    # Keep fragment header
996f7d24e3fSHanoh Haim    fragHeader = pkt[IPv6ExtHdrFragment]
997f7d24e3fSHanoh Haim    fragHeader.payload = None # detach payload
998f7d24e3fSHanoh Haim
999f7d24e3fSHanoh Haim    # Unfragmentable Part
1000f7d24e3fSHanoh Haim    unfragPartLen = len(s) - fragPartLen - 8
1001f7d24e3fSHanoh Haim    unfragPart = pkt
1002f7d24e3fSHanoh Haim    pkt[IPv6ExtHdrFragment].underlayer.payload = None # detach payload
1003f7d24e3fSHanoh Haim
1004f7d24e3fSHanoh Haim    # Cut the fragmentable part to fit fragSize. Inner fragments have
1005f7d24e3fSHanoh Haim    # a length that is an integer multiple of 8 octets. last Frag MTU
1006f7d24e3fSHanoh Haim    # can be anything below MTU
1007f7d24e3fSHanoh Haim    lastFragSize = fragSize - unfragPartLen - 8
1008f7d24e3fSHanoh Haim    innerFragSize = lastFragSize - (lastFragSize % 8)
1009f7d24e3fSHanoh Haim
1010f7d24e3fSHanoh Haim    if lastFragSize <= 0 or innerFragSize == 0:
1011f7d24e3fSHanoh Haim        warning("Provided fragment size value is too low. " +
1012f7d24e3fSHanoh Haim                "Should be more than %d" % (unfragPartLen + 8))
1013f7d24e3fSHanoh Haim        return [unfragPart/fragHeader/fragPart]
1014f7d24e3fSHanoh Haim
1015f7d24e3fSHanoh Haim    remain = fragPartStr
1016f7d24e3fSHanoh Haim    res = []
1017f7d24e3fSHanoh Haim    fragOffset = 0     # offset, incremeted during creation
1018f7d24e3fSHanoh Haim    fragId = random.randint(0,0xffffffff) # random id ...
1019f7d24e3fSHanoh Haim    if fragHeader.id is not None:  # ... except id provided by user
1020f7d24e3fSHanoh Haim        fragId = fragHeader.id
1021f7d24e3fSHanoh Haim    fragHeader.m = 1
1022f7d24e3fSHanoh Haim    fragHeader.id = fragId
1023f7d24e3fSHanoh Haim    fragHeader.nh = nh
1024f7d24e3fSHanoh Haim
1025f7d24e3fSHanoh Haim    # Main loop : cut, fit to FRAGSIZEs, fragOffset, Id ...
1026f7d24e3fSHanoh Haim    while True:
1027f7d24e3fSHanoh Haim        if (len(remain) > lastFragSize):
1028f7d24e3fSHanoh Haim            tmp = remain[:innerFragSize]
1029f7d24e3fSHanoh Haim            remain = remain[innerFragSize:]
1030f7d24e3fSHanoh Haim            fragHeader.offset = fragOffset    # update offset
1031f7d24e3fSHanoh Haim            fragOffset += (innerFragSize / 8)  # compute new one
1032f7d24e3fSHanoh Haim            if IPv6 in unfragPart:
1033f7d24e3fSHanoh Haim                unfragPart[IPv6].plen = None
1034f7d24e3fSHanoh Haim            tempo = unfragPart/fragHeader/conf.raw_layer(load=tmp)
1035f7d24e3fSHanoh Haim            res.append(tempo)
1036f7d24e3fSHanoh Haim        else:
1037f7d24e3fSHanoh Haim            fragHeader.offset = fragOffset    # update offSet
1038f7d24e3fSHanoh Haim            fragHeader.m = 0
1039f7d24e3fSHanoh Haim            if IPv6 in unfragPart:
1040f7d24e3fSHanoh Haim                unfragPart[IPv6].plen = None
1041f7d24e3fSHanoh Haim            tempo = unfragPart/fragHeader/conf.raw_layer(load=remain)
1042f7d24e3fSHanoh Haim            res.append(tempo)
1043f7d24e3fSHanoh Haim            break
1044f7d24e3fSHanoh Haim    return res
1045f7d24e3fSHanoh Haim
1046f7d24e3fSHanoh Haim
1047f7d24e3fSHanoh Haim############################### AH Header ###################################
1048f7d24e3fSHanoh Haim
1049f7d24e3fSHanoh Haim# class _AHFieldLenField(FieldLenField):
1050f7d24e3fSHanoh Haim#     def getfield(self, pkt, s):
1051f7d24e3fSHanoh Haim#         l = getattr(pkt, self.fld)
1052f7d24e3fSHanoh Haim#         l = (l*8)-self.shift
1053f7d24e3fSHanoh Haim#         i = self.m2i(pkt, s[:l])
1054f7d24e3fSHanoh Haim#         return s[l:],i
1055f7d24e3fSHanoh Haim
1056f7d24e3fSHanoh Haim# class _AHICVStrLenField(StrLenField):
1057f7d24e3fSHanoh Haim#     def i2len(self, pkt, x):
1058f7d24e3fSHanoh Haim
1059f7d24e3fSHanoh Haim
1060f7d24e3fSHanoh Haim
1061f7d24e3fSHanoh Haim# class IPv6ExtHdrAH(_IPv6ExtHdr):
1062f7d24e3fSHanoh Haim#     name = "IPv6 Extension Header - AH"
1063f7d24e3fSHanoh Haim#     fields_desc = [ ByteEnumField("nh", 59, ipv6nh),
1064f7d24e3fSHanoh Haim#                     _AHFieldLenField("len", None, "icv"),
1065f7d24e3fSHanoh Haim#                     ShortField("res", 0),
1066f7d24e3fSHanoh Haim#                     IntField("spi", 0),
1067f7d24e3fSHanoh Haim#                     IntField("sn", 0),
1068f7d24e3fSHanoh Haim#                     _AHICVStrLenField("icv", None, "len", shift=2) ]
1069f7d24e3fSHanoh Haim#     overload_fields = {IPv6: { "nh": 51 }}
1070f7d24e3fSHanoh Haim
1071f7d24e3fSHanoh Haim#     def post_build(self, pkt, pay):
1072f7d24e3fSHanoh Haim#         if self.len is None:
1073f7d24e3fSHanoh Haim#             pkt = pkt[0]+struct.pack("!B", 2*len(self.addresses))+pkt[2:]
1074f7d24e3fSHanoh Haim#         if self.segleft is None:
1075f7d24e3fSHanoh Haim#             pkt = pkt[:3]+struct.pack("!B", len(self.addresses))+pkt[4:]
1076f7d24e3fSHanoh Haim#         return _IPv6ExtHdr.post_build(self, pkt, pay)
1077f7d24e3fSHanoh Haim
1078f7d24e3fSHanoh Haim
1079f7d24e3fSHanoh Haim############################### ESP Header ##################################
1080f7d24e3fSHanoh Haim
1081f7d24e3fSHanoh Haim# class IPv6ExtHdrESP(_IPv6extHdr):
1082f7d24e3fSHanoh Haim#     name = "IPv6 Extension Header - ESP"
1083f7d24e3fSHanoh Haim#     fields_desc = [ IntField("spi", 0),
1084f7d24e3fSHanoh Haim#                     IntField("sn", 0),
1085f7d24e3fSHanoh Haim#                     # there is things to extract from IKE work
1086f7d24e3fSHanoh Haim#                     ]
1087f7d24e3fSHanoh Haim#     overloads_fields = {IPv6: { "nh": 50 }}
1088f7d24e3fSHanoh Haim
1089f7d24e3fSHanoh Haim
1090f7d24e3fSHanoh Haim
1091f7d24e3fSHanoh Haim#############################################################################
1092f7d24e3fSHanoh Haim#############################################################################
1093f7d24e3fSHanoh Haim###                           ICMPv6* Classes                             ###
1094f7d24e3fSHanoh Haim#############################################################################
1095f7d24e3fSHanoh Haim#############################################################################
1096f7d24e3fSHanoh Haim
1097f7d24e3fSHanoh Haimicmp6typescls = {    1: "ICMPv6DestUnreach",
1098f7d24e3fSHanoh Haim                     2: "ICMPv6PacketTooBig",
1099f7d24e3fSHanoh Haim                     3: "ICMPv6TimeExceeded",
1100f7d24e3fSHanoh Haim                     4: "ICMPv6ParamProblem",
1101f7d24e3fSHanoh Haim                   128: "ICMPv6EchoRequest",
1102f7d24e3fSHanoh Haim                   129: "ICMPv6EchoReply",
1103f7d24e3fSHanoh Haim                   130: "ICMPv6MLQuery",
1104f7d24e3fSHanoh Haim                   131: "ICMPv6MLReport",
1105f7d24e3fSHanoh Haim                   132: "ICMPv6MLDone",
1106f7d24e3fSHanoh Haim                   133: "ICMPv6ND_RS",
1107f7d24e3fSHanoh Haim                   134: "ICMPv6ND_RA",
1108f7d24e3fSHanoh Haim                   135: "ICMPv6ND_NS",
1109f7d24e3fSHanoh Haim                   136: "ICMPv6ND_NA",
1110f7d24e3fSHanoh Haim                   137: "ICMPv6ND_Redirect",
1111f7d24e3fSHanoh Haim                  #138: Do Me - RFC 2894 - Seems painful
1112f7d24e3fSHanoh Haim                   139: "ICMPv6NIQuery",
1113f7d24e3fSHanoh Haim                   140: "ICMPv6NIReply",
1114f7d24e3fSHanoh Haim                   141: "ICMPv6ND_INDSol",
1115f7d24e3fSHanoh Haim                   142: "ICMPv6ND_INDAdv",
111639000f46SYaroslav Brustinov                   143: "ICMPv6MLReportV2",
1117f7d24e3fSHanoh Haim                   144: "ICMPv6HAADRequest",
1118f7d24e3fSHanoh Haim                   145: "ICMPv6HAADReply",
1119f7d24e3fSHanoh Haim                   146: "ICMPv6MPSol",
1120f7d24e3fSHanoh Haim                   147: "ICMPv6MPAdv",
1121f7d24e3fSHanoh Haim                  #148: Do Me - SEND related - RFC 3971
1122f7d24e3fSHanoh Haim                  #149: Do Me - SEND related - RFC 3971
1123f7d24e3fSHanoh Haim                   151: "ICMPv6MRD_Advertisement",
1124f7d24e3fSHanoh Haim                   152: "ICMPv6MRD_Solicitation",
1125f7d24e3fSHanoh Haim                   153: "ICMPv6MRD_Termination",
1126f7d24e3fSHanoh Haim                   }
1127f7d24e3fSHanoh Haim
1128f7d24e3fSHanoh Haimicmp6typesminhdrlen = {    1: 8,
1129f7d24e3fSHanoh Haim                           2: 8,
1130f7d24e3fSHanoh Haim                           3: 8,
1131f7d24e3fSHanoh Haim                           4: 8,
1132f7d24e3fSHanoh Haim                         128: 8,
1133f7d24e3fSHanoh Haim                         129: 8,
1134f7d24e3fSHanoh Haim                         130: 24,
1135f7d24e3fSHanoh Haim                         131: 24,
1136f7d24e3fSHanoh Haim                         132: 24,
1137f7d24e3fSHanoh Haim                         133: 8,
1138f7d24e3fSHanoh Haim                         134: 16,
1139f7d24e3fSHanoh Haim                         135: 24,
1140f7d24e3fSHanoh Haim                         136: 24,
1141f7d24e3fSHanoh Haim                         137: 40,
1142f7d24e3fSHanoh Haim                         #139:
1143f7d24e3fSHanoh Haim                         #140
1144f7d24e3fSHanoh Haim                         141: 8,
1145f7d24e3fSHanoh Haim                         142: 8,
114639000f46SYaroslav Brustinov                         143: 16,
1147f7d24e3fSHanoh Haim                         144: 8,
1148f7d24e3fSHanoh Haim                         145: 8,
1149f7d24e3fSHanoh Haim                         146: 8,
1150f7d24e3fSHanoh Haim                         147: 8,
1151f7d24e3fSHanoh Haim                         151: 8,
1152f7d24e3fSHanoh Haim                         152: 4,
1153f7d24e3fSHanoh Haim                         153: 4
1154f7d24e3fSHanoh Haim                   }
1155f7d24e3fSHanoh Haim
1156f7d24e3fSHanoh Haimicmp6types = { 1 : "Destination unreachable",
1157f7d24e3fSHanoh Haim               2 : "Packet too big",
1158f7d24e3fSHanoh Haim               3 : "Time exceeded",
1159f7d24e3fSHanoh Haim               4 : "Parameter problem",
1160f7d24e3fSHanoh Haim             100 : "Private Experimentation",
1161f7d24e3fSHanoh Haim             101 : "Private Experimentation",
1162f7d24e3fSHanoh Haim             128 : "Echo Request",
1163f7d24e3fSHanoh Haim             129 : "Echo Reply",
1164f7d24e3fSHanoh Haim             130 : "MLD Query",
1165f7d24e3fSHanoh Haim             131 : "MLD Report",
1166f7d24e3fSHanoh Haim             132 : "MLD Done",
1167f7d24e3fSHanoh Haim             133 : "Router Solicitation",
1168f7d24e3fSHanoh Haim             134 : "Router Advertisement",
1169f7d24e3fSHanoh Haim             135 : "Neighbor Solicitation",
1170f7d24e3fSHanoh Haim             136 : "Neighbor Advertisement",
1171f7d24e3fSHanoh Haim             137 : "Redirect Message",
1172f7d24e3fSHanoh Haim             138 : "Router Renumbering",
1173f7d24e3fSHanoh Haim             139 : "ICMP Node Information Query",
1174f7d24e3fSHanoh Haim             140 : "ICMP Node Information Response",
1175f7d24e3fSHanoh Haim             141 : "Inverse Neighbor Discovery Solicitation Message",
1176f7d24e3fSHanoh Haim             142 : "Inverse Neighbor Discovery Advertisement Message",
1177f7d24e3fSHanoh Haim             143 : "Version 2 Multicast Listener Report",
1178f7d24e3fSHanoh Haim             144 : "Home Agent Address Discovery Request Message",
1179f7d24e3fSHanoh Haim             145 : "Home Agent Address Discovery Reply Message",
1180f7d24e3fSHanoh Haim             146 : "Mobile Prefix Solicitation",
1181f7d24e3fSHanoh Haim             147 : "Mobile Prefix Advertisement",
1182f7d24e3fSHanoh Haim             148 : "Certification Path Solicitation",
1183f7d24e3fSHanoh Haim             149 : "Certification Path Advertisement",
1184f7d24e3fSHanoh Haim             151 : "Multicast Router Advertisement",
1185f7d24e3fSHanoh Haim             152 : "Multicast Router Solicitation",
1186f7d24e3fSHanoh Haim             153 : "Multicast Router Termination",
1187f7d24e3fSHanoh Haim             200 : "Private Experimentation",
1188f7d24e3fSHanoh Haim             201 : "Private Experimentation" }
1189f7d24e3fSHanoh Haim
119039000f46SYaroslav Brustinovmldv2_group_types = {
119139000f46SYaroslav Brustinov    1: 'Mode is include (1)',
119239000f46SYaroslav Brustinov    2: 'Mode is exclude (2)',
119339000f46SYaroslav Brustinov    3: 'Change to include mode (3)',
119439000f46SYaroslav Brustinov    4: 'Change to exclude mode (4)',
119539000f46SYaroslav Brustinov    5: 'Alloc new sources (5)',
119639000f46SYaroslav Brustinov    6: 'Block old sources (6)',
119739000f46SYaroslav Brustinov    }
1198f7d24e3fSHanoh Haim
1199f7d24e3fSHanoh Haimclass _ICMPv6(Packet):
1200f7d24e3fSHanoh Haim    name = "ICMPv6 dummy class"
1201f7d24e3fSHanoh Haim    overload_fields = {IPv6: {"nh": 58}}
1202f7d24e3fSHanoh Haim    def post_build(self, p, pay):
1203f7d24e3fSHanoh Haim        p += pay
1204f7d24e3fSHanoh Haim        if self.cksum == None:
1205f7d24e3fSHanoh Haim            chksum = in6_chksum(58, self.underlayer, p)
1206f7d24e3fSHanoh Haim            p = p[:2]+struct.pack("!H", chksum)+p[4:]
1207f7d24e3fSHanoh Haim        return p
1208f7d24e3fSHanoh Haim
1209f7d24e3fSHanoh Haim    def hashret(self):
1210f7d24e3fSHanoh Haim        return self.payload.hashret()
1211f7d24e3fSHanoh Haim
1212f7d24e3fSHanoh Haim    def answers(self, other):
1213f7d24e3fSHanoh Haim        # isinstance(self.underlayer, _IPv6ExtHdr) may introduce a bug ...
1214f7d24e3fSHanoh Haim        if (isinstance(self.underlayer, IPerror6) or
1215f7d24e3fSHanoh Haim            isinstance(self.underlayer, _IPv6ExtHdr) and
1216f7d24e3fSHanoh Haim            isinstance(other, _ICMPv6)):
1217f7d24e3fSHanoh Haim            if not ((self.type == other.type) and
1218f7d24e3fSHanoh Haim                    (self.code == other.code)):
1219f7d24e3fSHanoh Haim                return 0
1220f7d24e3fSHanoh Haim            return 1
1221f7d24e3fSHanoh Haim        return 0
1222f7d24e3fSHanoh Haim
1223f7d24e3fSHanoh Haim
1224f7d24e3fSHanoh Haimclass _ICMPv6Error(_ICMPv6):
1225f7d24e3fSHanoh Haim    name = "ICMPv6 errors dummy class"
1226f7d24e3fSHanoh Haim    def guess_payload_class(self,p):
1227f7d24e3fSHanoh Haim        return IPerror6
1228f7d24e3fSHanoh Haim
1229f7d24e3fSHanoh Haimclass ICMPv6Unknown(_ICMPv6):
1230f7d24e3fSHanoh Haim    name = "Scapy6 ICMPv6 fallback class"
1231f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type",1, icmp6types),
1232f7d24e3fSHanoh Haim                    ByteField("code",0),
1233f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1234f7d24e3fSHanoh Haim                    StrField("msgbody", "")]
1235f7d24e3fSHanoh Haim
1236f7d24e3fSHanoh Haim
1237f7d24e3fSHanoh Haim################################## RFC 2460 #################################
1238f7d24e3fSHanoh Haim
1239f7d24e3fSHanoh Haimclass ICMPv6DestUnreach(_ICMPv6Error):
1240f7d24e3fSHanoh Haim    name = "ICMPv6 Destination Unreachable"
1241f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type",1, icmp6types),
1242f7d24e3fSHanoh Haim                    ByteEnumField("code",0, { 0: "No route to destination",
1243f7d24e3fSHanoh Haim                                              1: "Communication with destination administratively prohibited",
1244f7d24e3fSHanoh Haim                                              2: "Beyond scope of source address",
1245f7d24e3fSHanoh Haim                                              3: "Address unreachable",
1246f7d24e3fSHanoh Haim                                              4: "Port unreachable" }),
1247f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1248f7d24e3fSHanoh Haim                    XIntField("unused",0x00000000)]
1249f7d24e3fSHanoh Haim
1250f7d24e3fSHanoh Haimclass ICMPv6PacketTooBig(_ICMPv6Error):
1251f7d24e3fSHanoh Haim    name = "ICMPv6 Packet Too Big"
1252f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type",2, icmp6types),
1253f7d24e3fSHanoh Haim                    ByteField("code",0),
1254f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1255f7d24e3fSHanoh Haim                    IntField("mtu",1280)]
1256f7d24e3fSHanoh Haim
1257f7d24e3fSHanoh Haimclass ICMPv6TimeExceeded(_ICMPv6Error):
1258f7d24e3fSHanoh Haim    name = "ICMPv6 Time Exceeded"
1259f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type",3, icmp6types),
1260f7d24e3fSHanoh Haim                    ByteEnumField("code",0, { 0: "hop limit exceeded in transit",
1261f7d24e3fSHanoh Haim                                               1: "fragment reassembly time exceeded"}),
1262f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1263f7d24e3fSHanoh Haim                    XIntField("unused",0x00000000)]
1264f7d24e3fSHanoh Haim
1265f7d24e3fSHanoh Haim# The default pointer value is set to the next header field of
1266f7d24e3fSHanoh Haim# the encapsulated IPv6 packet
1267f7d24e3fSHanoh Haimclass ICMPv6ParamProblem(_ICMPv6Error):
1268f7d24e3fSHanoh Haim    name = "ICMPv6 Parameter Problem"
1269f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type",4, icmp6types),
1270f7d24e3fSHanoh Haim                    ByteEnumField("code",0, {0: "erroneous header field encountered",
1271f7d24e3fSHanoh Haim                                             1: "unrecognized Next Header type encountered",
1272f7d24e3fSHanoh Haim                                             2: "unrecognized IPv6 option encountered"}),
1273f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1274f7d24e3fSHanoh Haim                    IntField("ptr",6)]
1275f7d24e3fSHanoh Haim
1276f7d24e3fSHanoh Haimclass ICMPv6EchoRequest(_ICMPv6):
1277f7d24e3fSHanoh Haim    name = "ICMPv6 Echo Request"
1278f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type", 128, icmp6types),
1279f7d24e3fSHanoh Haim                    ByteField("code", 0),
1280f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1281f7d24e3fSHanoh Haim                    XShortField("id",0),
1282f7d24e3fSHanoh Haim                    XShortField("seq",0),
1283f7d24e3fSHanoh Haim                    StrField("data", "")]
1284f7d24e3fSHanoh Haim    def mysummary(self):
1285f7d24e3fSHanoh Haim        return self.sprintf("%name% (id: %id% seq: %seq%)")
1286f7d24e3fSHanoh Haim    def hashret(self):
1287f7d24e3fSHanoh Haim        return struct.pack("HH",self.id,self.seq)+self.payload.hashret()
1288f7d24e3fSHanoh Haim
1289f7d24e3fSHanoh Haim
1290f7d24e3fSHanoh Haimclass ICMPv6EchoReply(ICMPv6EchoRequest):
1291f7d24e3fSHanoh Haim    name = "ICMPv6 Echo Reply"
1292f7d24e3fSHanoh Haim    type = 129
1293f7d24e3fSHanoh Haim    def answers(self, other):
1294f7d24e3fSHanoh Haim        # We could match data content between request and reply.
1295f7d24e3fSHanoh Haim        return (isinstance(other, ICMPv6EchoRequest) and
1296f7d24e3fSHanoh Haim                self.id == other.id and self.seq == other.seq and
1297f7d24e3fSHanoh Haim                self.data == other.data)
1298f7d24e3fSHanoh Haim
1299f7d24e3fSHanoh Haim
1300f7d24e3fSHanoh Haim############ ICMPv6 Multicast Listener Discovery (RFC3810) ##################
1301f7d24e3fSHanoh Haim
1302f7d24e3fSHanoh Haim# tous les messages MLD sont emis avec une adresse source lien-locale
1303f7d24e3fSHanoh Haim# -> Y veiller dans le post_build si aucune n'est specifiee
1304f7d24e3fSHanoh Haim# La valeur de Hop-Limit doit etre de 1
1305f7d24e3fSHanoh Haim# "and an IPv6 Router Alert option in a Hop-by-Hop Options
1306f7d24e3fSHanoh Haim# header. (The router alert option is necessary to cause routers to
1307f7d24e3fSHanoh Haim# examine MLD messages sent to multicast addresses in which the router
1308f7d24e3fSHanoh Haim# itself has no interest"
1309f7d24e3fSHanoh Haimclass _ICMPv6ML(_ICMPv6):
1310f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type", 130, icmp6types),
1311f7d24e3fSHanoh Haim                    ByteField("code", 0),
1312f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1313f7d24e3fSHanoh Haim                    ShortField("mrd", 0),
1314f7d24e3fSHanoh Haim                    ShortField("reserved", 0),
1315f7d24e3fSHanoh Haim                    IP6Field("mladdr","::")]
1316f7d24e3fSHanoh Haim
1317f7d24e3fSHanoh Haim# general queries are sent to the link-scope all-nodes multicast
1318f7d24e3fSHanoh Haim# address ff02::1, with a multicast address field of 0 and a MRD of
1319f7d24e3fSHanoh Haim# [Query Response Interval]
1320f7d24e3fSHanoh Haim# Default value for mladdr is set to 0 for a General Query, and
1321f7d24e3fSHanoh Haim# overloaded by the user for a Multicast Address specific query
1322f7d24e3fSHanoh Haim# TODO : See what we can do to automatically include a Router Alert
1323f7d24e3fSHanoh Haim#        Option in a Destination Option Header.
1324f7d24e3fSHanoh Haimclass ICMPv6MLQuery(_ICMPv6ML): # RFC 2710
1325f7d24e3fSHanoh Haim    name = "MLD - Multicast Listener Query"
1326f7d24e3fSHanoh Haim    type   = 130
1327f7d24e3fSHanoh Haim    mrd    = 10000
1328f7d24e3fSHanoh Haim    mladdr = "::" # 10s for mrd
1329f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "dst": "ff02::1", "hlim": 1, "nh": 58 }}
1330f7d24e3fSHanoh Haim    def hashret(self):
1331f7d24e3fSHanoh Haim        if self.mladdr != "::":
1332f7d24e3fSHanoh Haim            return struct.pack("HH",self.mladdr)+self.payload.hashret()
1333f7d24e3fSHanoh Haim        else:
1334f7d24e3fSHanoh Haim            return self.payload.hashret()
1335f7d24e3fSHanoh Haim
1336f7d24e3fSHanoh Haim
1337f7d24e3fSHanoh Haim# TODO : See what we can do to automatically include a Router Alert
1338f7d24e3fSHanoh Haim#        Option in a Destination Option Header.
1339f7d24e3fSHanoh Haimclass ICMPv6MLReport(_ICMPv6ML): # RFC 2710
1340f7d24e3fSHanoh Haim    name = "MLD - Multicast Listener Report"
1341f7d24e3fSHanoh Haim    type = 131
1342f7d24e3fSHanoh Haim    overload_fields = {IPv6: {"hlim": 1, "nh": 58}}
1343f7d24e3fSHanoh Haim    # implementer le hashret et le answers
1344f7d24e3fSHanoh Haim
1345f7d24e3fSHanoh Haim# When a node ceases to listen to a multicast address on an interface,
1346f7d24e3fSHanoh Haim# it SHOULD send a single Done message to the link-scope all-routers
1347f7d24e3fSHanoh Haim# multicast address (FF02::2), carrying in its multicast address field
1348f7d24e3fSHanoh Haim# the address to which it is ceasing to listen
1349f7d24e3fSHanoh Haim# TODO : See what we can do to automatically include a Router Alert
1350f7d24e3fSHanoh Haim#        Option in a Destination Option Header.
1351f7d24e3fSHanoh Haimclass ICMPv6MLDone(_ICMPv6ML): # RFC 2710
1352f7d24e3fSHanoh Haim    name = "MLD - Multicast Listener Done"
1353f7d24e3fSHanoh Haim    type = 132
1354f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "dst": "ff02::2", "hlim": 1, "nh": 58}}
1355f7d24e3fSHanoh Haim
1356f7d24e3fSHanoh Haim
135739000f46SYaroslav Brustinovclass ICMPv6MLReportV2(_ICMPv6): # RFC 3810
135839000f46SYaroslav Brustinov    name = 'MLDv2 - Multicast Listener Report'
135939000f46SYaroslav Brustinov    fields_desc = [ ByteEnumField('type', 143, icmp6types),
136039000f46SYaroslav Brustinov                    ByteField('code', 0),
136139000f46SYaroslav Brustinov                    XShortField('cksum', None),
136239000f46SYaroslav Brustinov                    ShortField('reserved', 0),
136339000f46SYaroslav Brustinov                    ShortField('records_count', 1) ] # for now it's fixed 1 record
136439000f46SYaroslav Brustinov    overload_fields = {IPv6: { 'dst': 'ff02::16', 'hlim': 1, 'nh': 58 }}
136539000f46SYaroslav Brustinov
136639000f46SYaroslav Brustinov    def default_payload_class(self, p):
136739000f46SYaroslav Brustinov        return MLDv2Addr
136839000f46SYaroslav Brustinov
136939000f46SYaroslav Brustinov
137039000f46SYaroslav Brustinov# assumes empty aux
137139000f46SYaroslav Brustinovclass MLDv2Addr(Packet):
137239000f46SYaroslav Brustinov    name = 'MLDv2 - Address group'
137339000f46SYaroslav Brustinov    fields_desc = [
137439000f46SYaroslav Brustinov            ByteEnumField('type', 3, mldv2_group_types),
137539000f46SYaroslav Brustinov            ByteField('aux_len', 0),
137639000f46SYaroslav Brustinov            ShortField('len', 0),
137739000f46SYaroslav Brustinov            IP6Field('multicast_addr', '::'),
137839000f46SYaroslav Brustinov            IP6ListField('addrlist', [], count_from = lambda pkt:pkt.len)
137939000f46SYaroslav Brustinov            ]
138039000f46SYaroslav Brustinov
138139000f46SYaroslav Brustinov    def default_payload_class(self, p):
138239000f46SYaroslav Brustinov        return MLDv2Addr
138339000f46SYaroslav Brustinov
138439000f46SYaroslav Brustinov
138539000f46SYaroslav Brustinov
1386f7d24e3fSHanoh Haim########## ICMPv6 MRD - Multicast Router Discovery (RFC 4286) ###############
1387f7d24e3fSHanoh Haim
1388f7d24e3fSHanoh Haim# TODO:
1389f7d24e3fSHanoh Haim# - 04/09/06 troglocan : find a way to automatically add a router alert
1390f7d24e3fSHanoh Haim#            option for all MRD packets. This could be done in a specific
1391f7d24e3fSHanoh Haim#            way when IPv6 is the under layer with some specific keyword
1392f7d24e3fSHanoh Haim#            like 'exthdr'. This would allow to keep compatibility with
1393f7d24e3fSHanoh Haim#            providing IPv6 fields to be overloaded in fields_desc.
1394f7d24e3fSHanoh Haim#
1395f7d24e3fSHanoh Haim#            At the moment, if user inserts an IPv6 Router alert option
1396f7d24e3fSHanoh Haim#            none of the IPv6 default values of IPv6 layer will be set.
1397f7d24e3fSHanoh Haim
1398f7d24e3fSHanoh Haimclass ICMPv6MRD_Advertisement(_ICMPv6):
1399f7d24e3fSHanoh Haim    name = "ICMPv6 Multicast Router Discovery Advertisement"
1400f7d24e3fSHanoh Haim    fields_desc = [ByteEnumField("type", 151, icmp6types),
1401f7d24e3fSHanoh Haim                   ByteField("advinter", 20),
1402f7d24e3fSHanoh Haim                   XShortField("cksum", None),
1403f7d24e3fSHanoh Haim                   ShortField("queryint", 0),
1404f7d24e3fSHanoh Haim                   ShortField("robustness", 0)]
1405f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::2"}}
1406f7d24e3fSHanoh Haim                       # IPv6 Router Alert requires manual inclusion
1407f7d24e3fSHanoh Haim    def extract_padding(self, s):
1408f7d24e3fSHanoh Haim        return s[:8], s[8:]
1409f7d24e3fSHanoh Haim
1410f7d24e3fSHanoh Haimclass ICMPv6MRD_Solicitation(_ICMPv6):
1411f7d24e3fSHanoh Haim    name = "ICMPv6 Multicast Router Discovery Solicitation"
1412f7d24e3fSHanoh Haim    fields_desc = [ByteEnumField("type", 152, icmp6types),
1413f7d24e3fSHanoh Haim                   ByteField("res", 0),
1414f7d24e3fSHanoh Haim                   XShortField("cksum", None) ]
1415f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::2"}}
1416f7d24e3fSHanoh Haim                       # IPv6 Router Alert requires manual inclusion
1417f7d24e3fSHanoh Haim    def extract_padding(self, s):
1418f7d24e3fSHanoh Haim        return s[:4], s[4:]
1419f7d24e3fSHanoh Haim
1420f7d24e3fSHanoh Haimclass ICMPv6MRD_Termination(_ICMPv6):
1421f7d24e3fSHanoh Haim    name = "ICMPv6 Multicast Router Discovery Termination"
1422f7d24e3fSHanoh Haim    fields_desc = [ByteEnumField("type", 153, icmp6types),
1423f7d24e3fSHanoh Haim                   ByteField("res", 0),
1424f7d24e3fSHanoh Haim                   XShortField("cksum", None) ]
1425f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::6A"}}
1426f7d24e3fSHanoh Haim                       # IPv6 Router Alert requires manual inclusion
1427f7d24e3fSHanoh Haim    def extract_padding(self, s):
1428f7d24e3fSHanoh Haim        return s[:4], s[4:]
1429f7d24e3fSHanoh Haim
1430f7d24e3fSHanoh Haim
1431f7d24e3fSHanoh Haim################### ICMPv6 Neighbor Discovery (RFC 2461) ####################
1432f7d24e3fSHanoh Haim
1433f7d24e3fSHanoh Haimicmp6ndopts = { 1: "Source Link-Layer Address",
1434f7d24e3fSHanoh Haim                2: "Target Link-Layer Address",
1435f7d24e3fSHanoh Haim                3: "Prefix Information",
1436f7d24e3fSHanoh Haim                4: "Redirected Header",
1437f7d24e3fSHanoh Haim                5: "MTU",
1438f7d24e3fSHanoh Haim                6: "NBMA Shortcut Limit Option", # RFC2491
1439f7d24e3fSHanoh Haim                7: "Advertisement Interval Option",
1440f7d24e3fSHanoh Haim                8: "Home Agent Information Option",
1441f7d24e3fSHanoh Haim                9: "Source Address List",
1442f7d24e3fSHanoh Haim               10: "Target Address List",
1443f7d24e3fSHanoh Haim               11: "CGA Option",            # RFC 3971
1444f7d24e3fSHanoh Haim               12: "RSA Signature Option",  # RFC 3971
1445f7d24e3fSHanoh Haim               13: "Timestamp Option",      # RFC 3971
1446f7d24e3fSHanoh Haim               14: "Nonce option",          # RFC 3971
1447f7d24e3fSHanoh Haim               15: "Trust Anchor Option",   # RFC 3971
1448f7d24e3fSHanoh Haim               16: "Certificate Option",    # RFC 3971
1449f7d24e3fSHanoh Haim               17: "IP Address Option",                             # RFC 4068
1450f7d24e3fSHanoh Haim               18: "New Router Prefix Information Option",          # RFC 4068
1451f7d24e3fSHanoh Haim               19: "Link-layer Address Option",                     # RFC 4068
1452f7d24e3fSHanoh Haim               20: "Neighbor Advertisement Acknowledgement Option",
1453f7d24e3fSHanoh Haim               21: "CARD Request Option", # RFC 4065/4066/4067
1454f7d24e3fSHanoh Haim               22: "CARD Reply Option",   # RFC 4065/4066/4067
1455f7d24e3fSHanoh Haim               23: "MAP Option",          # RFC 4140
1456f7d24e3fSHanoh Haim               24: "Route Information Option",  # RFC 4191
1457f7d24e3fSHanoh Haim               25: "Recusive DNS Server Option",
1458f7d24e3fSHanoh Haim               26: "IPv6 Router Advertisement Flags Option"
1459f7d24e3fSHanoh Haim                }
1460f7d24e3fSHanoh Haim
1461f7d24e3fSHanoh Haimicmp6ndoptscls = { 1: "ICMPv6NDOptSrcLLAddr",
1462f7d24e3fSHanoh Haim                   2: "ICMPv6NDOptDstLLAddr",
1463f7d24e3fSHanoh Haim                   3: "ICMPv6NDOptPrefixInfo",
1464f7d24e3fSHanoh Haim                   4: "ICMPv6NDOptRedirectedHdr",
1465f7d24e3fSHanoh Haim                   5: "ICMPv6NDOptMTU",
1466f7d24e3fSHanoh Haim                   6: "ICMPv6NDOptShortcutLimit",
1467f7d24e3fSHanoh Haim                   7: "ICMPv6NDOptAdvInterval",
1468f7d24e3fSHanoh Haim                   8: "ICMPv6NDOptHAInfo",
1469f7d24e3fSHanoh Haim                   9: "ICMPv6NDOptSrcAddrList",
1470f7d24e3fSHanoh Haim                  10: "ICMPv6NDOptTgtAddrList",
1471f7d24e3fSHanoh Haim                  #11: Do Me,
1472f7d24e3fSHanoh Haim                  #12: Do Me,
1473f7d24e3fSHanoh Haim                  #13: Do Me,
1474f7d24e3fSHanoh Haim                  #14: Do Me,
1475f7d24e3fSHanoh Haim                  #15: Do Me,
1476f7d24e3fSHanoh Haim                  #16: Do Me,
1477f7d24e3fSHanoh Haim                  17: "ICMPv6NDOptIPAddr",
1478f7d24e3fSHanoh Haim                  18: "ICMPv6NDOptNewRtrPrefix",
1479f7d24e3fSHanoh Haim                  19: "ICMPv6NDOptLLA",
1480f7d24e3fSHanoh Haim                  #18: Do Me,
1481f7d24e3fSHanoh Haim                  #19: Do Me,
1482f7d24e3fSHanoh Haim                  #20: Do Me,
1483f7d24e3fSHanoh Haim                  #21: Do Me,
1484f7d24e3fSHanoh Haim                  #22: Do Me,
1485f7d24e3fSHanoh Haim                  23: "ICMPv6NDOptMAP",
1486f7d24e3fSHanoh Haim                  24: "ICMPv6NDOptRouteInfo",
1487f7d24e3fSHanoh Haim                  25: "ICMPv6NDOptRDNSS",
1488f7d24e3fSHanoh Haim                  26: "ICMPv6NDOptEFA"
1489f7d24e3fSHanoh Haim                  }
1490f7d24e3fSHanoh Haim
1491f7d24e3fSHanoh Haimclass _ICMPv6NDGuessPayload:
1492f7d24e3fSHanoh Haim    name = "Dummy ND class that implements guess_payload_class()"
1493f7d24e3fSHanoh Haim    def guess_payload_class(self,p):
1494f7d24e3fSHanoh Haim        if len(p) > 1:
1495f7d24e3fSHanoh Haim            return get_cls(icmp6ndoptscls.get(ord(p[0]),"Raw"), "Raw") # s/Raw/ICMPv6NDOptUnknown/g ?
1496f7d24e3fSHanoh Haim
1497f7d24e3fSHanoh Haim
1498f7d24e3fSHanoh Haim# Beginning of ICMPv6 Neighbor Discovery Options.
1499f7d24e3fSHanoh Haim
1500f7d24e3fSHanoh Haimclass ICMPv6NDOptUnknown(_ICMPv6NDGuessPayload, Packet):
1501f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery Option - Scapy Unimplemented"
1502f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type",None),
1503f7d24e3fSHanoh Haim                    FieldLenField("len",None,length_of="data",fmt="B",
1504f7d24e3fSHanoh Haim                                  adjust = lambda pkt,x: x+2),
1505f7d24e3fSHanoh Haim                    StrLenField("data","",
1506f7d24e3fSHanoh Haim                                length_from = lambda pkt: pkt.len-2) ]
1507f7d24e3fSHanoh Haim
1508f7d24e3fSHanoh Haim# NOTE: len includes type and len field. Expressed in unit of 8 bytes
1509f7d24e3fSHanoh Haim# TODO: Revoir le coup du ETHER_ANY
1510f7d24e3fSHanoh Haimclass ICMPv6NDOptSrcLLAddr(_ICMPv6NDGuessPayload, Packet):
1511f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery Option - Source Link-Layer Address"
1512f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type", 1),
1513f7d24e3fSHanoh Haim                    ByteField("len", 1),
1514f7d24e3fSHanoh Haim                    MACField("lladdr", ETHER_ANY) ]
1515f7d24e3fSHanoh Haim    def mysummary(self):
1516f7d24e3fSHanoh Haim        return self.sprintf("%name% %lladdr%")
1517f7d24e3fSHanoh Haim
1518f7d24e3fSHanoh Haimclass ICMPv6NDOptDstLLAddr(ICMPv6NDOptSrcLLAddr):
1519f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery Option - Destination Link-Layer Address"
1520f7d24e3fSHanoh Haim    type = 2
1521f7d24e3fSHanoh Haim
1522f7d24e3fSHanoh Haimclass ICMPv6NDOptPrefixInfo(_ICMPv6NDGuessPayload, Packet):
1523f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery Option - Prefix Information"
1524f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type",3),
1525f7d24e3fSHanoh Haim                    ByteField("len",4),
1526f7d24e3fSHanoh Haim                    ByteField("prefixlen",None),
1527f7d24e3fSHanoh Haim                    BitField("L",1,1),
1528f7d24e3fSHanoh Haim                    BitField("A",1,1),
1529f7d24e3fSHanoh Haim                    BitField("R",0,1),
1530f7d24e3fSHanoh Haim                    BitField("res1",0,5),
1531f7d24e3fSHanoh Haim                    XIntField("validlifetime",0xffffffffL),
1532f7d24e3fSHanoh Haim                    XIntField("preferredlifetime",0xffffffffL),
1533f7d24e3fSHanoh Haim                    XIntField("res2",0x00000000),
1534f7d24e3fSHanoh Haim                    IP6Field("prefix","::") ]
1535f7d24e3fSHanoh Haim    def mysummary(self):
1536f7d24e3fSHanoh Haim        return self.sprintf("%name% %prefix%")
1537f7d24e3fSHanoh Haim
1538f7d24e3fSHanoh Haim# TODO: We should also limit the size of included packet to something
1539f7d24e3fSHanoh Haim# like (initiallen - 40 - 2)
1540f7d24e3fSHanoh Haimclass TruncPktLenField(PacketLenField):
1541f7d24e3fSHanoh Haim
1542f7d24e3fSHanoh Haim    def __init__(self, name, default, cls, cur_shift, length_from=None, shift=0):
1543f7d24e3fSHanoh Haim        PacketLenField.__init__(self, name, default, cls, length_from=length_from)
1544f7d24e3fSHanoh Haim        self.cur_shift = cur_shift
1545f7d24e3fSHanoh Haim
1546f7d24e3fSHanoh Haim    def getfield(self, pkt, s):
1547f7d24e3fSHanoh Haim        l = self.length_from(pkt)
1548f7d24e3fSHanoh Haim        i = self.m2i(pkt, s[:l])
1549f7d24e3fSHanoh Haim        return s[l:],i
1550f7d24e3fSHanoh Haim
1551f7d24e3fSHanoh Haim    def m2i(self, pkt, m):
1552f7d24e3fSHanoh Haim        s = None
1553f7d24e3fSHanoh Haim        try: # It can happen we have sth shorter than 40 bytes
1554f7d24e3fSHanoh Haim            s = self.cls(m)
1555f7d24e3fSHanoh Haim        except:
1556f7d24e3fSHanoh Haim            return conf.raw_layer(m)
1557f7d24e3fSHanoh Haim        return s
1558f7d24e3fSHanoh Haim
1559f7d24e3fSHanoh Haim    def i2m(self, pkt, x):
1560f7d24e3fSHanoh Haim        s = str(x)
1561f7d24e3fSHanoh Haim        l = len(s)
1562f7d24e3fSHanoh Haim        r = (l + self.cur_shift) % 8
1563f7d24e3fSHanoh Haim        l = l - r
1564f7d24e3fSHanoh Haim        return s[:l]
1565f7d24e3fSHanoh Haim
1566f7d24e3fSHanoh Haim    def i2len(self, pkt, i):
1567f7d24e3fSHanoh Haim        return len(self.i2m(pkt, i))
1568f7d24e3fSHanoh Haim
1569f7d24e3fSHanoh Haim
1570f7d24e3fSHanoh Haim# Faire un post_build pour le recalcul de la taille (en multiple de 8 octets)
1571f7d24e3fSHanoh Haimclass ICMPv6NDOptRedirectedHdr(_ICMPv6NDGuessPayload, Packet):
1572f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery Option - Redirected Header"
1573f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type",4),
1574f7d24e3fSHanoh Haim                    FieldLenField("len", None, length_of="pkt", fmt="B",
1575f7d24e3fSHanoh Haim                                  adjust = lambda pkt,x:(x+8)/8),
1576f7d24e3fSHanoh Haim                    StrFixedLenField("res", "\x00"*6, 6),
1577f7d24e3fSHanoh Haim                    TruncPktLenField("pkt", "", IPv6, 8,
1578f7d24e3fSHanoh Haim                                     length_from = lambda pkt: 8*pkt.len-8) ]
1579f7d24e3fSHanoh Haim
1580f7d24e3fSHanoh Haim# See which value should be used for default MTU instead of 1280
1581f7d24e3fSHanoh Haimclass ICMPv6NDOptMTU(_ICMPv6NDGuessPayload, Packet):
1582f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery Option - MTU"
1583f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type",5),
1584f7d24e3fSHanoh Haim                    ByteField("len",1),
1585f7d24e3fSHanoh Haim                    XShortField("res",0),
1586f7d24e3fSHanoh Haim                    IntField("mtu",1280)]
1587f7d24e3fSHanoh Haim
1588f7d24e3fSHanoh Haimclass ICMPv6NDOptShortcutLimit(_ICMPv6NDGuessPayload, Packet): # RFC 2491
1589f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery Option - NBMA Shortcut Limit"
1590f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type", 6),
1591f7d24e3fSHanoh Haim                    ByteField("len", 1),
1592f7d24e3fSHanoh Haim                    ByteField("shortcutlim", 40), # XXX
1593f7d24e3fSHanoh Haim                    ByteField("res1", 0),
1594f7d24e3fSHanoh Haim                    IntField("res2", 0) ]
1595f7d24e3fSHanoh Haim
1596f7d24e3fSHanoh Haimclass ICMPv6NDOptAdvInterval(_ICMPv6NDGuessPayload, Packet):
1597f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - Interval Advertisement"
1598f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type",7),
1599f7d24e3fSHanoh Haim                    ByteField("len",1),
1600f7d24e3fSHanoh Haim                    ShortField("res", 0),
1601f7d24e3fSHanoh Haim                    IntField("advint", 0) ]
1602f7d24e3fSHanoh Haim    def mysummary(self):
1603f7d24e3fSHanoh Haim        return self.sprintf("%name% %advint% milliseconds")
1604f7d24e3fSHanoh Haim
1605f7d24e3fSHanoh Haimclass ICMPv6NDOptHAInfo(_ICMPv6NDGuessPayload, Packet):
1606f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - Home Agent Information"
1607f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type",8),
1608f7d24e3fSHanoh Haim                    ByteField("len",1),
1609f7d24e3fSHanoh Haim                    ShortField("res", 0),
1610f7d24e3fSHanoh Haim                    ShortField("pref", 0),
1611f7d24e3fSHanoh Haim                    ShortField("lifetime", 1)]
1612f7d24e3fSHanoh Haim    def mysummary(self):
1613f7d24e3fSHanoh Haim        return self.sprintf("%name% %pref% %lifetime% seconds")
1614f7d24e3fSHanoh Haim
1615f7d24e3fSHanoh Haim# type 9  : See ICMPv6NDOptSrcAddrList class below in IND (RFC 3122) support
1616f7d24e3fSHanoh Haim
1617f7d24e3fSHanoh Haim# type 10 : See ICMPv6NDOptTgtAddrList class below in IND (RFC 3122) support
1618f7d24e3fSHanoh Haim
1619f7d24e3fSHanoh Haimclass ICMPv6NDOptIPAddr(_ICMPv6NDGuessPayload, Packet):  # RFC 4068
1620f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - IP Address Option (FH for MIPv6)"
1621f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type",17),
1622f7d24e3fSHanoh Haim                    ByteField("len", 3),
1623f7d24e3fSHanoh Haim                    ByteEnumField("optcode", 1, {1: "Old Care-Of Address",
1624f7d24e3fSHanoh Haim                                                 2: "New Care-Of Address",
1625f7d24e3fSHanoh Haim                                                 3: "NAR's IP address" }),
1626f7d24e3fSHanoh Haim                    ByteField("plen", 64),
1627f7d24e3fSHanoh Haim                    IntField("res", 0),
1628f7d24e3fSHanoh Haim                    IP6Field("addr", "::") ]
1629f7d24e3fSHanoh Haim
1630f7d24e3fSHanoh Haimclass ICMPv6NDOptNewRtrPrefix(_ICMPv6NDGuessPayload, Packet): # RFC 4068
1631f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - New Router Prefix Information Option (FH for MIPv6)"
1632f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type",18),
1633f7d24e3fSHanoh Haim                    ByteField("len", 3),
1634f7d24e3fSHanoh Haim                    ByteField("optcode", 0),
1635f7d24e3fSHanoh Haim                    ByteField("plen", 64),
1636f7d24e3fSHanoh Haim                    IntField("res", 0),
1637f7d24e3fSHanoh Haim                    IP6Field("prefix", "::") ]
1638f7d24e3fSHanoh Haim
1639f7d24e3fSHanoh Haim_rfc4068_lla_optcode = {0: "Wildcard requesting resolution for all nearby AP",
1640f7d24e3fSHanoh Haim                        1: "LLA for the new AP",
1641f7d24e3fSHanoh Haim                        2: "LLA of the MN",
1642f7d24e3fSHanoh Haim                        3: "LLA of the NAR",
1643f7d24e3fSHanoh Haim                        4: "LLA of the src of TrSolPr or PrRtAdv msg",
1644f7d24e3fSHanoh Haim                        5: "AP identified by LLA belongs to current iface of router",
1645f7d24e3fSHanoh Haim                        6: "No preifx info available for AP identified by the LLA",
1646f7d24e3fSHanoh Haim                        7: "No fast handovers support for AP identified by the LLA" }
1647f7d24e3fSHanoh Haim
1648f7d24e3fSHanoh Haimclass ICMPv6NDOptLLA(_ICMPv6NDGuessPayload, Packet):     # RFC 4068
1649f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - Link-Layer Address (LLA) Option (FH for MIPv6)"
1650f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type", 19),
1651f7d24e3fSHanoh Haim                    ByteField("len", 1),
1652f7d24e3fSHanoh Haim                    ByteEnumField("optcode", 0, _rfc4068_lla_optcode),
1653f7d24e3fSHanoh Haim                    MACField("lla", ETHER_ANY) ] # We only support ethernet
1654f7d24e3fSHanoh Haim
1655f7d24e3fSHanoh Haimclass ICMPv6NDOptMAP(_ICMPv6NDGuessPayload, Packet):     # RFC 4140
1656f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - MAP Option"
1657f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type", 23),
1658f7d24e3fSHanoh Haim                    ByteField("len", 3),
1659f7d24e3fSHanoh Haim                    BitField("dist", 1, 4),
1660f7d24e3fSHanoh Haim                    BitField("pref", 15, 4), # highest availability
1661f7d24e3fSHanoh Haim                    BitField("R", 1, 1),
1662f7d24e3fSHanoh Haim                    BitField("res", 0, 7),
1663f7d24e3fSHanoh Haim                    IntField("validlifetime", 0xffffffff),
1664f7d24e3fSHanoh Haim                    IP6Field("addr", "::") ]
1665f7d24e3fSHanoh Haim
1666f7d24e3fSHanoh Haim
1667f7d24e3fSHanoh Haimclass IP6PrefixField(IP6Field):
1668f7d24e3fSHanoh Haim    def __init__(self, name, default):
1669f7d24e3fSHanoh Haim        IP6Field.__init__(self, name, default)
1670f7d24e3fSHanoh Haim        self.length_from = lambda pkt: 8*(pkt.len - 1)
1671f7d24e3fSHanoh Haim
1672f7d24e3fSHanoh Haim    def addfield(self, pkt, s, val):
1673f7d24e3fSHanoh Haim        return s + self.i2m(pkt, val)
1674f7d24e3fSHanoh Haim
1675f7d24e3fSHanoh Haim    def getfield(self, pkt, s):
1676f7d24e3fSHanoh Haim        l = self.length_from(pkt)
1677f7d24e3fSHanoh Haim        p = s[:l]
1678f7d24e3fSHanoh Haim        if l < 16:
1679f7d24e3fSHanoh Haim            p += '\x00'*(16-l)
1680f7d24e3fSHanoh Haim        return s[l:], self.m2i(pkt,p)
1681f7d24e3fSHanoh Haim
1682f7d24e3fSHanoh Haim    def i2len(self, pkt, x):
1683f7d24e3fSHanoh Haim        return len(self.i2m(pkt, x))
1684f7d24e3fSHanoh Haim
1685f7d24e3fSHanoh Haim    def i2m(self, pkt, x):
1686f7d24e3fSHanoh Haim        l = pkt.len
1687f7d24e3fSHanoh Haim
1688f7d24e3fSHanoh Haim        if x is None:
1689f7d24e3fSHanoh Haim            x = "::"
1690f7d24e3fSHanoh Haim            if l is None:
1691f7d24e3fSHanoh Haim                l = 1
1692f7d24e3fSHanoh Haim        x = inet_pton(socket.AF_INET6, x)
1693f7d24e3fSHanoh Haim
1694f7d24e3fSHanoh Haim        if l is None:
1695f7d24e3fSHanoh Haim            return x
1696f7d24e3fSHanoh Haim        if l in [0, 1]:
1697f7d24e3fSHanoh Haim            return ""
1698f7d24e3fSHanoh Haim        if l in [2, 3]:
1699f7d24e3fSHanoh Haim            return x[:8*(l-1)]
1700f7d24e3fSHanoh Haim
1701f7d24e3fSHanoh Haim        return x + '\x00'*8*(l-3)
1702f7d24e3fSHanoh Haim
1703f7d24e3fSHanoh Haimclass ICMPv6NDOptRouteInfo(_ICMPv6NDGuessPayload, Packet): # RFC 4191
1704f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery Option - Route Information Option"
1705f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type",24),
1706f7d24e3fSHanoh Haim                    FieldLenField("len", None, length_of="prefix", fmt="B",
1707f7d24e3fSHanoh Haim                                  adjust = lambda pkt,x: x/8 + 1),
1708f7d24e3fSHanoh Haim                    ByteField("plen", None),
1709f7d24e3fSHanoh Haim                    BitField("res1",0,3),
1710f7d24e3fSHanoh Haim                    BitField("prf",0,2),
1711f7d24e3fSHanoh Haim                    BitField("res2",0,3),
1712f7d24e3fSHanoh Haim                    IntField("rtlifetime", 0xffffffff),
1713f7d24e3fSHanoh Haim                    IP6PrefixField("prefix", None) ]
1714f7d24e3fSHanoh Haim
1715f7d24e3fSHanoh Haimclass ICMPv6NDOptRDNSS(_ICMPv6NDGuessPayload, Packet): # RFC 5006
1716f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery Option - Recursive DNS Server Option"
1717f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type", 25),
1718f7d24e3fSHanoh Haim                    FieldLenField("len", None, count_of="dns", fmt="B",
1719f7d24e3fSHanoh Haim                                  adjust = lambda pkt,x: 2*x+1),
1720f7d24e3fSHanoh Haim                    ShortField("res", None),
1721f7d24e3fSHanoh Haim                    IntField("lifetime", 0xffffffff),
1722f7d24e3fSHanoh Haim                    IP6ListField("dns", [],
1723f7d24e3fSHanoh Haim                                 length_from = lambda pkt: 8*(pkt.len-1)) ]
1724f7d24e3fSHanoh Haim
1725f7d24e3fSHanoh Haimclass ICMPv6NDOptEFA(_ICMPv6NDGuessPayload, Packet): # RFC 5175 (prev. 5075)
1726f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery Option - Expanded Flags Option"
1727f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type", 26),
1728f7d24e3fSHanoh Haim                    ByteField("len", 1),
1729f7d24e3fSHanoh Haim                    BitField("res", 0, 48) ]
1730f7d24e3fSHanoh Haim
1731f7d24e3fSHanoh Haim# End of ICMPv6 Neighbor Discovery Options.
1732f7d24e3fSHanoh Haim
1733f7d24e3fSHanoh Haimclass ICMPv6ND_RS(_ICMPv6NDGuessPayload, _ICMPv6):
1734f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - Router Solicitation"
1735f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type", 133, icmp6types),
1736f7d24e3fSHanoh Haim                    ByteField("code",0),
1737f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1738f7d24e3fSHanoh Haim                    IntField("res",0) ]
1739f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 58, "dst": "ff02::2", "hlim": 255 }}
1740f7d24e3fSHanoh Haim
1741f7d24e3fSHanoh Haimclass ICMPv6ND_RA(_ICMPv6NDGuessPayload, _ICMPv6):
1742f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - Router Advertisement"
1743f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type", 134, icmp6types),
1744f7d24e3fSHanoh Haim                    ByteField("code",0),
1745f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1746f7d24e3fSHanoh Haim                    ByteField("chlim",0),
1747f7d24e3fSHanoh Haim                    BitField("M",0,1),
1748f7d24e3fSHanoh Haim                    BitField("O",0,1),
1749f7d24e3fSHanoh Haim                    BitField("H",0,1),
1750f7d24e3fSHanoh Haim                    BitEnumField("prf",1,2, { 0: "Medium (default)",
1751f7d24e3fSHanoh Haim                                              1: "High",
1752f7d24e3fSHanoh Haim                                              2: "Reserved",
1753f7d24e3fSHanoh Haim                                              3: "Low" } ), # RFC 4191
1754f7d24e3fSHanoh Haim                    BitField("P",0,1),
1755f7d24e3fSHanoh Haim                    BitField("res",0,2),
1756f7d24e3fSHanoh Haim                    ShortField("routerlifetime",1800),
1757f7d24e3fSHanoh Haim                    IntField("reachabletime",0),
1758f7d24e3fSHanoh Haim                    IntField("retranstimer",0) ]
1759f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }}
1760f7d24e3fSHanoh Haim
1761f7d24e3fSHanoh Haim    def answers(self, other):
1762f7d24e3fSHanoh Haim        return isinstance(other, ICMPv6ND_RS)
1763f7d24e3fSHanoh Haim
1764f7d24e3fSHanoh Haimclass ICMPv6ND_NS(_ICMPv6NDGuessPayload, _ICMPv6, Packet):
1765f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - Neighbor Solicitation"
1766f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type",135, icmp6types),
1767f7d24e3fSHanoh Haim                    ByteField("code",0),
1768f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1769f7d24e3fSHanoh Haim                    IntField("res", 0),
1770f7d24e3fSHanoh Haim                    IP6Field("tgt","::") ]
1771f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }}
1772f7d24e3fSHanoh Haim
1773f7d24e3fSHanoh Haim    def mysummary(self):
1774f7d24e3fSHanoh Haim        return self.sprintf("%name% (tgt: %tgt%)")
1775f7d24e3fSHanoh Haim
1776f7d24e3fSHanoh Haim    def hashret(self):
1777f7d24e3fSHanoh Haim        return self.tgt+self.payload.hashret()
1778f7d24e3fSHanoh Haim
1779f7d24e3fSHanoh Haimclass ICMPv6ND_NA(_ICMPv6NDGuessPayload, _ICMPv6, Packet):
1780f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - Neighbor Advertisement"
1781f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type",136, icmp6types),
1782f7d24e3fSHanoh Haim                    ByteField("code",0),
1783f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1784f7d24e3fSHanoh Haim                    BitField("R",1,1),
1785f7d24e3fSHanoh Haim                    BitField("S",0,1),
1786f7d24e3fSHanoh Haim                    BitField("O",1,1),
1787f7d24e3fSHanoh Haim                    XBitField("res",0,29),
1788f7d24e3fSHanoh Haim                    IP6Field("tgt","::") ]
1789f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }}
1790f7d24e3fSHanoh Haim
1791f7d24e3fSHanoh Haim    def mysummary(self):
1792f7d24e3fSHanoh Haim        return self.sprintf("%name% (tgt: %tgt%)")
1793f7d24e3fSHanoh Haim
1794f7d24e3fSHanoh Haim    def hashret(self):
1795f7d24e3fSHanoh Haim        return self.tgt+self.payload.hashret()
1796f7d24e3fSHanoh Haim
1797f7d24e3fSHanoh Haim    def answers(self, other):
1798f7d24e3fSHanoh Haim        return isinstance(other, ICMPv6ND_NS) and self.tgt == other.tgt
1799f7d24e3fSHanoh Haim
1800f7d24e3fSHanoh Haim# associated possible options : target link-layer option, Redirected header
1801f7d24e3fSHanoh Haimclass ICMPv6ND_Redirect(_ICMPv6NDGuessPayload, _ICMPv6, Packet):
1802f7d24e3fSHanoh Haim    name = "ICMPv6 Neighbor Discovery - Redirect"
1803f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type",137, icmp6types),
1804f7d24e3fSHanoh Haim                    ByteField("code",0),
1805f7d24e3fSHanoh Haim                    XShortField("cksum", None),
1806f7d24e3fSHanoh Haim                    XIntField("res",0),
1807f7d24e3fSHanoh Haim                    IP6Field("tgt","::"),
1808f7d24e3fSHanoh Haim                    IP6Field("dst","::") ]
1809f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }}
1810f7d24e3fSHanoh Haim
1811f7d24e3fSHanoh Haim
1812f7d24e3fSHanoh Haim
1813f7d24e3fSHanoh Haim################ ICMPv6 Inverse Neighbor Discovery (RFC 3122) ###############
1814f7d24e3fSHanoh Haim
1815f7d24e3fSHanoh Haimclass ICMPv6NDOptSrcAddrList(_ICMPv6NDGuessPayload, Packet):
1816f7d24e3fSHanoh Haim    name = "ICMPv6 Inverse Neighbor Discovery Option - Source Address List"
1817f7d24e3fSHanoh Haim    fields_desc = [ ByteField("type",9),
1818f7d24e3fSHanoh Haim                    FieldLenField("len", None, count_of="addrlist", fmt="B",
1819f7d24e3fSHanoh Haim                                  adjust = lambda pkt,x: 2*x+1),
1820f7d24e3fSHanoh Haim                    StrFixedLenField("res", "\x00"*6, 6),
1821f7d24e3fSHanoh Haim                    IP6ListField("addrlist", [],
1822f7d24e3fSHanoh Haim                                length_from = lambda pkt: 8*(pkt.len-1)) ]
1823f7d24e3fSHanoh Haim
1824f7d24e3fSHanoh Haimclass ICMPv6NDOptTgtAddrList(ICMPv6NDOptSrcAddrList):
1825f7d24e3fSHanoh Haim    name = "ICMPv6 Inverse Neighbor Discovery Option - Target Address List"
1826f7d24e3fSHanoh Haim    type = 10
1827f7d24e3fSHanoh Haim
1828f7d24e3fSHanoh Haim
1829f7d24e3fSHanoh Haim# RFC3122
1830f7d24e3fSHanoh Haim# Options requises : source lladdr et target lladdr
1831f7d24e3fSHanoh Haim# Autres options valides : source address list, MTU
1832f7d24e3fSHanoh Haim# - Comme precise dans le document, il serait bien de prendre l'adresse L2
1833f7d24e3fSHanoh Haim#   demandee dans l'option requise target lladdr et l'utiliser au niveau
1834f7d24e3fSHanoh Haim#   de l'adresse destination ethernet si aucune adresse n'est precisee
1835f7d24e3fSHanoh Haim# - ca semble pas forcement pratique si l'utilisateur doit preciser toutes
1836f7d24e3fSHanoh Haim#   les options.
1837f7d24e3fSHanoh Haim# Ether() must use the target lladdr as destination
1838f7d24e3fSHanoh Haimclass ICMPv6ND_INDSol(_ICMPv6NDGuessPayload, _ICMPv6):
1839f7d24e3fSHanoh Haim    name = "ICMPv6 Inverse Neighbor Discovery Solicitation"
1840f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type",141, icmp6types),
1841f7d24e3fSHanoh Haim                    ByteField("code",0),
1842f7d24e3fSHanoh Haim                    XShortField("cksum",None),
1843f7d24e3fSHanoh Haim                    XIntField("reserved",0) ]
1844f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }}
1845f7d24e3fSHanoh Haim
1846f7d24e3fSHanoh Haim# Options requises :  target lladdr, target address list
1847f7d24e3fSHanoh Haim# Autres options valides : MTU
1848f7d24e3fSHanoh Haimclass ICMPv6ND_INDAdv(_ICMPv6NDGuessPayload, _ICMPv6):
1849f7d24e3fSHanoh Haim    name = "ICMPv6 Inverse Neighbor Discovery Advertisement"
1850f7d24e3fSHanoh Haim    fields_desc = [ ByteEnumField("type",142, icmp6types),
1851f7d24e3fSHanoh Haim                    ByteField("code",0),
1852f7d24e3fSHanoh Haim                    XShortField("cksum",None),
1853f7d24e3fSHanoh Haim                    XIntField("reserved",0) ]
1854f7d24e3fSHanoh Haim    overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }}
1855f7d24e3fSHanoh Haim
1856f7d24e3fSHanoh Haim
1857f7d24e3fSHanoh Haim###############################################################################
1858f7d24e3fSHanoh Haim# ICMPv6 Node Information Queries (RFC 4620)
1859f7d24e3fSHanoh Haim###############################################################################
1860f7d24e3fSHanoh Haim
1861f7d24e3fSHanoh Haim# [ ] Add automatic destination address computation using computeNIGroupAddr
1862f7d24e3fSHanoh Haim#     in IPv6 class (Scapy6 modification when integrated) if :
1863f7d24e3fSHanoh Haim#     - it is not provided
1864f7d24e3fSHanoh Haim#     - upper layer is ICMPv6NIQueryName() with a valid value
1865f7d24e3fSHanoh Haim# [ ] Try to be liberal in what we accept as internal values for _explicit_
1866f7d24e3fSHanoh Haim#     DNS elements provided by users. Any string should be considered
1867f7d24e3fSHanoh Haim#     valid and kept like it has been provided. At the moment, i2repr() will
1868f7d24e3fSHanoh Haim#     crash on many inputs
1869f7d24e3fSHanoh Haim# [ ] Do the documentation
1870f7d24e3fSHanoh Haim# [ ] Add regression tests
1871f7d24e3fSHanoh Haim# [ ] Perform test against real machines (NOOP reply is proof of implementation).
1872f7d24e3fSHanoh Haim# [ ] Check if there are differences between different stacks. Among *BSD,
1873f7d24e3fSHanoh Haim#     with others.
1874f7d24e3fSHanoh Haim# [ ] Deal with flags in a consistent way.
1875f7d24e3fSHanoh Haim# [ ] Implement compression in names2dnsrepr() and decompresiion in
1876f7d24e3fSHanoh Haim#     dnsrepr2names(). Should be deactivable.
1877f7d24e3fSHanoh Haim
1878f7d24e3fSHanoh Haimicmp6_niqtypes = { 0: "NOOP",
1879f7d24e3fSHanoh Haim                  2: "Node Name",
1880f7d24e3fSHanoh Haim                  3: "IPv6 Address",
1881f7d24e3fSHanoh Haim                  4: "IPv4 Address" }
1882f7d24e3fSHanoh Haim
1883f7d24e3fSHanoh Haim
1884f7d24e3fSHanoh Haimclass _ICMPv6NIHashret:
1885f7d24e3fSHanoh Haim    def hashret(self):
1886f7d24e3fSHanoh Haim        return self.nonce
1887f7d24e3fSHanoh Haim
1888f7d24e3fSHanoh Haimclass _ICMPv6NIAnswers:
1889f7d24e3fSHanoh Haim    def answers(self, other):
1890f7d24e3fSHanoh Haim        return self.nonce == other.nonce
1891f7d24e3fSHanoh Haim
1892f7d24e3fSHanoh Haim# Buggy; always returns the same value during a session
1893f7d24e3fSHanoh Haimclass NonceField(StrFixedLenField):
1894f7d24e3fSHanoh Haim    def __init__(self, name, default=None):
1895f7d24e3fSHanoh Haim        StrFixedLenField.__init__(self, name, default, 8)
1896f7d24e3fSHanoh Haim        if default is None:
1897f7d24e3fSHanoh Haim            self.default = self.randval()
1898f7d24e3fSHanoh Haim
1899f7d24e3fSHanoh Haim# Compute the NI group Address. Can take a FQDN as input parameter
1900f7d24e3fSHanoh Haimdef computeNIGroupAddr(name):
1901f7d24e3fSHanoh Haim    import md5
1902f7d24e3fSHanoh Haim    name = name.lower().split(".")[0]
1903f7d24e3fSHanoh Haim    record = chr(len(name))+name
1904f7d24e3fSHanoh Haim    h = md5.new(record)
1905f7d24e3fSHanoh Haim    h = h.digest()
1906f7d24e3fSHanoh Haim    addr = "ff02::2:%2x%2x:%2x%2x" % struct.unpack("BBBB", h[:4])
1907f7d24e3fSHanoh Haim    return addr
1908f7d24e3fSHanoh Haim
1909f7d24e3fSHanoh Haim
1910f7d24e3fSHanoh Haim# Here is the deal. First, that protocol is a piece of shit. Then, we
1911f7d24e3fSHanoh Haim# provide 4 classes for the different kinds of Requests (one for every
1912f7d24e3fSHanoh Haim# valid qtype: NOOP, Node Name, IPv6@, IPv4@). They all share the same
1913f7d24e3fSHanoh Haim# data field class that is made to be smart by guessing the specifc
1914f7d24e3fSHanoh Haim# type of value provided :
1915f7d24e3fSHanoh Haim#
1916f7d24e3fSHanoh Haim# - IPv6 if acceptable for inet_pton(AF_INET6, ): code is set to 0,
1917f7d24e3fSHanoh Haim#   if not overriden by user
1918f7d24e3fSHanoh Haim# - IPv4 if acceptable for inet_pton(AF_INET,  ): code is set to 2,
1919f7d24e3fSHanoh Haim#   if not overriden
1920f7d24e3fSHanoh Haim# - Name in the other cases: code is set to 0, if not overriden by user
1921f7d24e3fSHanoh Haim#
1922f7d24e3fSHanoh Haim# Internal storage, is not only the value, but the a pair providing
1923f7d24e3fSHanoh Haim# the type and the value (1 is IPv6@, 1 is Name or string, 2 is IPv4@)
1924f7d24e3fSHanoh Haim#
1925f7d24e3fSHanoh Haim# Note : I merged getfield() and m2i(). m2i() should not be called
1926f7d24e3fSHanoh Haim#        directly anyway. Same remark for addfield() and i2m()
1927f7d24e3fSHanoh Haim#
1928f7d24e3fSHanoh Haim# -- arno
1929f7d24e3fSHanoh Haim
1930f7d24e3fSHanoh Haim# "The type of information present in the Data field of a query is
1931f7d24e3fSHanoh Haim#  declared by the ICMP Code, whereas the type of information in a
1932f7d24e3fSHanoh Haim#  Reply is determined by the Qtype"
1933f7d24e3fSHanoh Haim
1934f7d24e3fSHanoh Haimdef names2dnsrepr(x):
1935f7d24e3fSHanoh Haim    """
1936f7d24e3fSHanoh Haim    Take as input a list of DNS names or a single DNS name
1937f7d24e3fSHanoh Haim    and encode it in DNS format (with possible compression)
1938f7d24e3fSHanoh Haim    If a string that is already a DNS name in DNS format
1939f7d24e3fSHanoh Haim    is passed, it is returned unmodified. Result is a string.
1940f7d24e3fSHanoh Haim    !!!  At the moment, compression is not implemented  !!!
1941f7d24e3fSHanoh Haim    """
1942f7d24e3fSHanoh Haim
1943f7d24e3fSHanoh Haim    if type(x) is str:
1944f7d24e3fSHanoh Haim        if x and x[-1] == '\x00': # stupid heuristic
1945f7d24e3fSHanoh Haim            return x
1946f7d24e3fSHanoh Haim        x = [x]
1947f7d24e3fSHanoh Haim
1948f7d24e3fSHanoh Haim    res = []
1949f7d24e3fSHanoh Haim    for n in x:
1950f7d24e3fSHanoh Haim        termin = "\x00"
1951f7d24e3fSHanoh Haim        if n.count('.') == 0: # single-component gets one more
1952f7d24e3fSHanoh Haim            termin += '\x00'
1953f7d24e3fSHanoh Haim        n = "".join(map(lambda y: chr(len(y))+y, n.split("."))) + termin
1954f7d24e3fSHanoh Haim        res.append(n)
1955f7d24e3fSHanoh Haim    return "".join(res)
1956f7d24e3fSHanoh Haim
1957f7d24e3fSHanoh Haim
1958f7d24e3fSHanoh Haimdef dnsrepr2names(x):
1959f7d24e3fSHanoh Haim    """
1960f7d24e3fSHanoh Haim    Take as input a DNS encoded string (possibly compressed)
1961f7d24e3fSHanoh Haim    and returns a list of DNS names contained in it.
1962f7d24e3fSHanoh Haim    If provided string is already in printable format
1963f7d24e3fSHanoh Haim    (does not end with a null character, a one element list
1964f7d24e3fSHanoh Haim    is returned). Result is a list.
1965f7d24e3fSHanoh Haim    """
1966f7d24e3fSHanoh Haim    res = []
1967f7d24e3fSHanoh Haim    cur = ""
1968f7d24e3fSHanoh Haim    while x:
1969f7d24e3fSHanoh Haim        l = ord(x[0])
1970f7d24e3fSHanoh Haim        x = x[1:]
1971f7d24e3fSHanoh Haim        if l == 0:
1972f7d24e3fSHanoh Haim            if cur and cur[-1] == '.':
1973f7d24e3fSHanoh Haim                cur = cur[:-1]
1974f7d24e3fSHanoh Haim            res.append(cur)
1975f7d24e3fSHanoh Haim            cur = ""
1976f7d24e3fSHanoh Haim            if x and ord(x[0]) == 0: # single component
1977f7d24e3fSHanoh Haim                x = x[1:]
1978f7d24e3fSHanoh Haim            continue
1979f7d24e3fSHanoh Haim        if l & 0xc0: # XXX TODO : work on that -- arno
1980f7d24e3fSHanoh Haim            raise Exception("DNS message can't be compressed at this point!")
1981f7d24e3fSHanoh Haim        else:
1982f7d24e3fSHanoh Haim            cur += x[:l]+"."
1983f7d24e3fSHanoh Haim            x = x[l:]
1984f7d24e3fSHanoh Haim    return res
1985f7d24e3fSHanoh Haim
1986f7d24e3fSHanoh Haim
1987f7d24e3fSHanoh Haimclass NIQueryDataField(StrField):
1988f7d24e3fSHanoh Haim    def __init__(self, name, default):
1989f7d24e3fSHanoh Haim        StrField.__init__(self, name, default)
1990f7d24e3fSHanoh Haim
1991f7d24e3fSHanoh Haim    def i2h(self, pkt, x):
1992f7d24e3fSHanoh Haim        if x is None:
1993f7d24e3fSHanoh Haim            return x
1994f7d24e3fSHanoh Haim        t,val = x
1995f7d24e3fSHanoh Haim        if t == 1:
1996f7d24e3fSHanoh Haim            val = dnsrepr2names(val)[0]
1997f7d24e3fSHanoh Haim        return val
1998f7d24e3fSHanoh Haim
1999f7d24e3fSHanoh Haim    def h2i(self, pkt, x):
2000f7d24e3fSHanoh Haim        if x is tuple and type(x[0]) is int:
2001f7d24e3fSHanoh Haim            return x
2002f7d24e3fSHanoh Haim
2003f7d24e3fSHanoh Haim        val = None
2004f7d24e3fSHanoh Haim        try: # Try IPv6
2005f7d24e3fSHanoh Haim            inet_pton(socket.AF_INET6, x)
2006f7d24e3fSHanoh Haim            val = (0, x)
2007f7d24e3fSHanoh Haim        except:
2008f7d24e3fSHanoh Haim            try: # Try IPv4
2009f7d24e3fSHanoh Haim                inet_pton(socket.AF_INET, x)
2010f7d24e3fSHanoh Haim                val = (2, x)
2011f7d24e3fSHanoh Haim            except: # Try DNS
2012f7d24e3fSHanoh Haim                if x is None:
2013f7d24e3fSHanoh Haim                    x = ""
2014f7d24e3fSHanoh Haim                x = names2dnsrepr(x)
2015f7d24e3fSHanoh Haim                val = (1, x)
2016f7d24e3fSHanoh Haim        return val
2017f7d24e3fSHanoh Haim
2018f7d24e3fSHanoh Haim    def i2repr(self, pkt, x):
2019f7d24e3fSHanoh Haim        t,val = x
2020f7d24e3fSHanoh Haim        if t == 1: # DNS Name
2021f7d24e3fSHanoh Haim            # we don't use dnsrepr2names() to deal with
2022f7d24e3fSHanoh Haim            # possible weird data extracted info
2023f7d24e3fSHanoh Haim            res = []
2024f7d24e3fSHanoh Haim            weird = None
2025f7d24e3fSHanoh Haim            while val:
2026f7d24e3fSHanoh Haim                l = ord(val[0])
2027f7d24e3fSHanoh Haim                val = val[1:]
2028f7d24e3fSHanoh Haim                if l == 0:
2029f7d24e3fSHanoh Haim                    if (len(res) > 1 and val): # fqdn with data behind
2030f7d24e3fSHanoh Haim                        weird = val
2031f7d24e3fSHanoh Haim                    elif len(val) > 1: # single label with data behind
2032f7d24e3fSHanoh Haim                        weird = val[1:]
2033f7d24e3fSHanoh Haim                    break
2034f7d24e3fSHanoh Haim                res.append(val[:l]+".")
2035f7d24e3fSHanoh Haim                val = val[l:]
2036f7d24e3fSHanoh Haim            tmp = "".join(res)
2037f7d24e3fSHanoh Haim            if tmp and tmp[-1] == '.':
2038f7d24e3fSHanoh Haim                tmp = tmp[:-1]
2039f7d24e3fSHanoh Haim            return tmp
2040f7d24e3fSHanoh Haim        return repr(val)
2041f7d24e3fSHanoh Haim
2042f7d24e3fSHanoh Haim    def getfield(self, pkt, s):
2043f7d24e3fSHanoh Haim        qtype = getattr(pkt, "qtype")
2044f7d24e3fSHanoh Haim        if qtype == 0: # NOOP
2045f7d24e3fSHanoh Haim            return s, (0, "")
2046f7d24e3fSHanoh Haim        else:
2047f7d24e3fSHanoh Haim            code = getattr(pkt, "code")
2048f7d24e3fSHanoh Haim            if code == 0:   # IPv6 Addr
2049f7d24e3fSHanoh Haim                return s[16:], (0, inet_ntop(socket.AF_INET6, s[:16]))
2050f7d24e3fSHanoh Haim            elif code == 2: # IPv4 Addr
2051f7d24e3fSHanoh Haim                return s[4:], (2, inet_ntop(socket.AF_INET, s[:4]))
2052f7d24e3fSHanoh Haim            else:           # Name or Unknown
2053f7d24e3fSHanoh Haim                return "", (1, s)
2054f7d24e3fSHanoh Haim
2055f7d24e3fSHanoh Haim    def addfield(self, pkt, s, val):
2056f7d24e3fSHanoh Haim        if ((type(