1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <phil@secdev.org>
4## This program is published under a GPLv2 license
5
6"""
7IPv4 (Internet Protocol v4).
8"""
9
10import os,time,struct,re,socket,new
11from select import select
12from collections import defaultdict
13from scapy.utils import checksum
14from scapy.layers.l2 import *
15from scapy.config import conf
16from scapy.fields import *
17from scapy.packet import *
18from scapy.volatile import *
19from scapy.sendrecv import sr,sr1,srp1
20from scapy.plist import PacketList,SndRcvList
21from scapy.automaton import Automaton,ATMT
22
23import scapy.as_resolvers
24
25
26####################
27## IP Tools class ##
28####################
29
30class IPTools:
31    """Add more powers to a class that have a "src" attribute."""
32    def whois(self):
33        os.system("whois %s" % self.src)
34    def ottl(self):
35        t = [32,64,128,255]+[self.ttl]
36        t.sort()
37        return t[t.index(self.ttl)+1]
38    def hops(self):
39        return self.ottl()-self.ttl-1
40
41
42_ip_options_names = { 0: "end_of_list",
43                      1: "nop",
44                      2: "security",
45                      3: "loose_source_route",
46                      4: "timestamp",
47                      5: "extended_security",
48                      6: "commercial_security",
49                      7: "record_route",
50                      8: "stream_id",
51                      9: "strict_source_route",
52                      10: "experimental_measurement",
53                      11: "mtu_probe",
54                      12: "mtu_reply",
55                      13: "flow_control",
56                      14: "access_control",
57                      15: "encode",
58                      16: "imi_traffic_descriptor",
59                      17: "extended_IP",
60                      18: "traceroute",
61                      19: "address_extension",
62                      20: "router_alert",
63                      21: "selective_directed_broadcast_mode",
64                      23: "dynamic_packet_state",
65                      24: "upstream_multicast_packet",
66                      25: "quick_start",
67                      30: "rfc4727_experiment",
68                      }
69
70
71class _IPOption_HDR(Packet):
72    fields_desc = [ BitField("copy_flag",0, 1),
73                    BitEnumField("optclass",0,2,{0:"control",2:"debug"}),
74                    BitEnumField("option",0,5, _ip_options_names) ]
75
76class IPOption(Packet):
77    name = "IP Option"
78    fields_desc = [ _IPOption_HDR,
79                    FieldLenField("length", None, fmt="B",  # Only option 0 and 1 have no length and value
80                                  length_of="value", adjust=lambda pkt,l:l+2),
81                    StrLenField("value", "",length_from=lambda pkt:pkt.length-2) ]
82
83    def extract_padding(self, p):
84        return "",p
85
86    registered_ip_options = {}
87    @classmethod
88    def register_variant(cls):
89        cls.registered_ip_options[cls.option.default] = cls
90    @classmethod
91    def dispatch_hook(cls, pkt=None, *args, **kargs):
92        if pkt:
93            opt = ord(pkt[0])&0x1f
94            if opt in cls.registered_ip_options:
95                return cls.registered_ip_options[opt]
96        return cls
97
98class IPOption_EOL(IPOption):
99    name = "IP Option End of Options List"
100    option = 0
101    fields_desc = [ _IPOption_HDR ]
102
103
104class IPOption_NOP(IPOption):
105    name = "IP Option No Operation"
106    option=1
107    fields_desc = [ _IPOption_HDR ]
108
109class IPOption_Security(IPOption):
110    name = "IP Option Security"
111    copy_flag = 1
112    option = 2
113    fields_desc = [ _IPOption_HDR,
114                    ByteField("length", 11),
115                    ShortField("security",0),
116                    ShortField("compartment",0),
117                    ShortField("handling_restrictions",0),
118                    StrFixedLenField("transmission_control_code","xxx",3),
119                    ]
120
121class IPOption_LSRR(IPOption):
122    name = "IP Option Loose Source and Record Route"
123    copy_flag = 1
124    option = 3
125    fields_desc = [ _IPOption_HDR,
126                    FieldLenField("length", None, fmt="B",
127                                  length_of="routers", adjust=lambda pkt,l:l+3),
128                    ByteField("pointer",4), # 4 is first IP
129                    FieldListField("routers",[],IPField("","0.0.0.0"),
130                                   length_from=lambda pkt:pkt.length-3)
131                    ]
132    def get_current_router(self):
133        return self.routers[self.pointer/4-1]
134
135class IPOption_RR(IPOption_LSRR):
136    name = "IP Option Record Route"
137    option = 7
138
139class IPOption_SSRR(IPOption_LSRR):
140    name = "IP Option Strict Source and Record Route"
141    option = 9
142
143class IPOption_Stream_Id(IPOption):
144    name = "IP Option Stream ID"
145    option = 8
146    fields_desc = [ _IPOption_HDR,
147                    ByteField("length", 4),
148                    ShortField("security",0), ]
149
150class IPOption_MTU_Probe(IPOption):
151    name = "IP Option MTU Probe"
152    option = 11
153    fields_desc = [ _IPOption_HDR,
154                    ByteField("length", 4),
155                    ShortField("mtu",0), ]
156
157class IPOption_MTU_Reply(IPOption_MTU_Probe):
158    name = "IP Option MTU Reply"
159    option = 12
160
161class IPOption_Traceroute(IPOption):
162    name = "IP Option Traceroute"
163    copy_flag = 1
164    option = 18
165    fields_desc = [ _IPOption_HDR,
166                    ByteField("length", 12),
167                    ShortField("id",0),
168                    ShortField("outbound_hops",0),
169                    ShortField("return_hops",0),
170                    IPField("originator_ip","0.0.0.0") ]
171
172class IPOption_Address_Extension(IPOption):
173    name = "IP Option Address Extension"
174    copy_flag = 1
175    option = 19
176    fields_desc = [ _IPOption_HDR,
177                    ByteField("length", 10),
178                    IPField("src_ext","0.0.0.0"),
179                    IPField("dst_ext","0.0.0.0") ]
180
181class IPOption_Router_Alert(IPOption):
182    name = "IP Option Router Alert"
183    copy_flag = 1
184    option = 20
185    fields_desc = [ _IPOption_HDR,
186                    ByteField("length", 4),
187                    ShortEnumField("alert",0, {0:"router_shall_examine_packet"}), ]
188
189
190class IPOption_SDBM(IPOption):
191    name = "IP Option Selective Directed Broadcast Mode"
192    copy_flag = 1
193    option = 21
194    fields_desc = [ _IPOption_HDR,
195                    FieldLenField("length", None, fmt="B",
196                                  length_of="addresses", adjust=lambda pkt,l:l+2),
197                    FieldListField("addresses",[],IPField("","0.0.0.0"),
198                                   length_from=lambda pkt:pkt.length-2)
199                    ]
200
201
202
203TCPOptions = (
204              { 0 : ("EOL",None),
205                1 : ("NOP",None),
206                2 : ("MSS","!H"),
207                3 : ("WScale","!B"),
208                4 : ("SAckOK",None),
209                5 : ("SAck","!"),
210                8 : ("Timestamp","!II"),
211                14 : ("AltChkSum","!BH"),
212                15 : ("AltChkSumOpt",None),
213                25 : ("Mood","!p")
214                },
215              { "EOL":0,
216                "NOP":1,
217                "MSS":2,
218                "WScale":3,
219                "SAckOK":4,
220                "SAck":5,
221                "Timestamp":8,
222                "AltChkSum":14,
223                "AltChkSumOpt":15,
224                "Mood":25
225                } )
226
227class TCPOptionsField(StrField):
228    islist=1
229    def getfield(self, pkt, s):
230        opsz = (pkt.dataofs-5)*4
231        if opsz < 0:
232            warning("bad dataofs (%i). Assuming dataofs=5"%pkt.dataofs)
233            opsz = 0
234        return s[opsz:],self.m2i(pkt,s[:opsz])
235    def m2i(self, pkt, x):
236        opt = []
237        while x:
238            onum = ord(x[0])
239            if onum == 0:
240                opt.append(("EOL",None))
241                x=x[1:]
242                break
243            if onum == 1:
244                opt.append(("NOP",None))
245                x=x[1:]
246                continue
247            olen = ord(x[1])
248            if olen < 2:
249                warning("Malformed TCP option (announced length is %i)" % olen)
250                olen = 2
251            oval = x[2:olen]
252            if TCPOptions[0].has_key(onum):
253                oname, ofmt = TCPOptions[0][onum]
254                if onum == 5: #SAck
255                    ofmt += "%iI" % (len(oval)/4)
256                if ofmt and struct.calcsize(ofmt) == len(oval):
257                    oval = struct.unpack(ofmt, oval)
258                    if len(oval) == 1:
259                        oval = oval[0]
260                opt.append((oname, oval))
261            else:
262                opt.append((onum, oval))
263            x = x[olen:]
264        return opt
265
266    def i2m(self, pkt, x):
267        opt = ""
268        for oname,oval in x:
269            if type(oname) is str:
270                if oname == "NOP":
271                    opt += "\x01"
272                    continue
273                elif oname == "EOL":
274                    opt += "\x00"
275                    continue
276                elif TCPOptions[1].has_key(oname):
277                    onum = TCPOptions[1][oname]
278                    ofmt = TCPOptions[0][onum][1]
279                    if onum == 5: #SAck
280                        ofmt += "%iI" % len(oval)
281                    if ofmt is not None and (type(oval) is not str or "s" in ofmt):
282                        if type(oval) is not tuple:
283                            oval = (oval,)
284                        oval = struct.pack(ofmt, *oval)
285                else:
286                    warning("option [%s] unknown. Skipped."%oname)
287                    continue
288            else:
289                onum = oname
290                if type(oval) is not str:
291                    warning("option [%i] is not string."%onum)
292                    continue
293            opt += chr(onum)+chr(2+len(oval))+oval
294        return opt+"\x00"*(3-((len(opt)+3)%4))
295    def randval(self):
296        return [] # XXX
297
298
299class ICMPTimeStampField(IntField):
300    re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$")
301    def i2repr(self, pkt, val):
302        if val is None:
303            return "--"
304        else:
305            sec, milli = divmod(val, 1000)
306            min, sec = divmod(sec, 60)
307            hour, min = divmod(min, 60)
308            return "%d:%d:%d.%d" %(hour, min, sec, int(milli))
309    def any2i(self, pkt, val):
310        if type(val) is str:
311            hmsms = self.re_hmsm.match(val)
312            if hmsms:
313                h,_,m,_,s,_,ms = hmsms = hmsms.groups()
314                ms = int(((ms or "")+"000")[:3])
315                val = ((int(h)*60+int(m or 0))*60+int(s or 0))*1000+ms
316            else:
317                val = 0
318        elif val is None:
319            val = int((time.time()%(24*60*60))*1000)
320        return val
321
322
323class IP(Packet, IPTools):
324    name = "IP"
325    fields_desc = [ BitField("version" , 4 , 4),
326                    BitField("ihl", None, 4),
327                    XByteField("tos", 0),
328                    ShortField("len", None),
329                    ShortField("id", 1),
330                    FlagsField("flags", 0, 3, ["MF","DF","evil"]),
331                    BitField("frag", 0, 13),
332                    ByteField("ttl", 64),
333                    ByteEnumField("proto", 0, IP_PROTOS),
334                    XShortField("chksum", None),
335                    #IPField("src", "127.0.0.1"),
336                    #Emph(SourceIPField("src","dst")),
337                    Emph(IPField("src", "16.0.0.1")),
338                    Emph(IPField("dst", "48.0.0.1")),
339                    PacketListField("options", [], IPOption, length_from=lambda p:p.ihl*4-20) ]
340    def post_build(self, p, pay):
341        ihl = self.ihl
342        p += "\0"*((-len(p))%4) # pad IP options if needed
343        if ihl is None:
344            ihl = len(p)/4
345            p = chr(((self.version&0xf)<<4) | ihl&0x0f)+p[1:]
346        if self.len is None:
347            l = len(p)+len(pay)
348            p = p[:2]+struct.pack("!H", l)+p[4:]
349        if self.chksum is None:
350            ck = checksum(p)
351            p = p[:10]+chr(ck>>8)+chr(ck&0xff)+p[12:]
352        return p+pay
353
354    def extract_padding(self, s):
355        l = self.len - (self.ihl << 2)
356        return s[:l],s[l:]
357
358    def send(self, s, slp=0):
359        for p in self:
360            try:
361                s.sendto(str(p), (p.dst,0))
362            except socket.error, msg:
363                log_runtime.error(msg)
364            if slp:
365                time.sleep(slp)
366    def route(self):
367        dst = self.dst
368        if isinstance(dst,Gen):
369            dst = iter(dst).next()
370        return conf.route.route(dst)
371    def hashret(self):
372        if ( (self.proto == socket.IPPROTO_ICMP)
373             and (isinstance(self.payload, ICMP))
374             and (self.payload.type in [3,4,5,11,12]) ):
375            return self.payload.payload.hashret()
376        else:
377            if conf.checkIPsrc and conf.checkIPaddr:
378                return strxor(inet_aton(self.src),inet_aton(self.dst))+struct.pack("B",self.proto)+self.payload.hashret()
379            else:
380                return struct.pack("B", self.proto)+self.payload.hashret()
381    def answers(self, other):
382        if not isinstance(other,IP):
383            return 0
384        if conf.checkIPaddr and (self.dst != other.src):
385            return 0
386        if ( (self.proto == socket.IPPROTO_ICMP) and
387             (isinstance(self.payload, ICMP)) and
388             (self.payload.type in [3,4,5,11,12]) ):
389            # ICMP error message
390            return self.payload.payload.answers(other)
391
392        else:
393            if ( (conf.checkIPaddr and (self.src != other.dst)) or
394                 (self.proto != other.proto) ):
395                return 0
396            return self.payload.answers(other.payload)
397    def mysummary(self):
398        s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%")
399        if self.frag:
400            s += " frag:%i" % self.frag
401        return s
402
403    def fragment(self, fragsize=1480):
404        """Fragment IP datagrams"""
405        fragsize = (fragsize+7)/8*8
406        lst = []
407        fnb = 0
408        fl = self
409        while fl.underlayer is not None:
410            fnb += 1
411            fl = fl.underlayer
412
413        for p in fl:
414            s = str(p[fnb].payload)
415            nb = (len(s)+fragsize-1)/fragsize
416            for i in range(nb):
417                q = p.copy()
418                del(q[fnb].payload)
419                del(q[fnb].chksum)
420                del(q[fnb].len)
421                if i == nb-1:
422                    q[IP].flags &= ~1
423                else:
424                    q[IP].flags |= 1
425                q[IP].frag = i*fragsize/8
426                r = conf.raw_layer(load=s[i*fragsize:(i+1)*fragsize])
427                r.overload_fields = p[IP].payload.overload_fields.copy()
428                q.add_payload(r)
429                lst.append(q)
430        return lst
431
432
433class TCP(Packet):
434    name = "TCP"
435    fields_desc = [ ShortEnumField("sport", 20, TCP_SERVICES),
436                    ShortEnumField("dport", 80, TCP_SERVICES),
437                    IntField("seq", 0),
438                    IntField("ack", 0),
439                    BitField("dataofs", None, 4),
440                    BitField("reserved", 0, 4),
441                    FlagsField("flags", 0x2, 8, "FSRPAUEC"),
442                    ShortField("window", 8192),
443                    XShortField("chksum", None),
444                    ShortField("urgptr", 0),
445                    TCPOptionsField("options", {}) ]
446    def post_build(self, p, pay):
447        p += pay
448        dataofs = self.dataofs
449        if dataofs is None:
450            dataofs = 5+((len(self.get_field("options").i2m(self,self.options))+3)/4)
451            p = p[:12]+chr((dataofs << 4) | ord(p[12])&0x0f)+p[13:]
452        if self.chksum is None:
453            if isinstance(self.underlayer, IP):
454                if self.underlayer.len is not None:
455                    ln = self.underlayer.len-20
456                else:
457                    ln = len(p)
458                psdhdr = struct.pack("!4s4sHH",
459                                     inet_aton(self.underlayer.src),
460                                     inet_aton(self.underlayer.dst),
461                                     self.underlayer.proto,
462                                     ln)
463                ck=checksum(psdhdr+p)
464                p = p[:16]+struct.pack("!H", ck)+p[18:]
465            elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr):
466                ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p)
467                p = p[:16]+struct.pack("!H", ck)+p[18:]
468            else:
469                warning("No IP underlayer to compute checksum. Leaving null.")
470        return p
471    def hashret(self):
472        if conf.checkIPsrc:
473            return struct.pack("H",self.sport ^ self.dport)+self.payload.hashret()
474        else:
475            return self.payload.hashret()
476    def answers(self, other):
477        if not isinstance(other, TCP):
478            return 0
479        if conf.checkIPsrc:
480            if not ((self.sport == other.dport) and
481                    (self.dport == other.sport)):
482                return 0
483        if (abs(other.seq-self.ack) > 2+len(other.payload)):
484            return 0
485        return 1
486    def mysummary(self):
487        if isinstance(self.underlayer, IP):
488            return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%")
489        elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6):
490            return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%")
491        else:
492            return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%")
493
494class UDP(Packet):
495    name = "UDP"
496    fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES),
497                    ShortEnumField("dport", 53, UDP_SERVICES),
498                    ShortField("len", None),
499                    XShortField("chksum", None), ]
500    def post_build(self, p, pay):
501        p += pay
502        l = self.len
503        if l is None:
504            l = len(p)
505            p = p[:4]+struct.pack("!H",l)+p[6:]
506        if self.chksum is None:
507            if isinstance(self.underlayer, IP):
508                if self.underlayer.len is not None:
509                    ln = self.underlayer.len-20
510                else:
511                    ln = len(p)
512                psdhdr = struct.pack("!4s4sHH",
513                                     inet_aton(self.underlayer.src),
514                                     inet_aton(self.underlayer.dst),
515                                     self.underlayer.proto,
516                                     ln)
517                ck=checksum(psdhdr+p)
518                p = p[:6]+struct.pack("!H", ck)+p[8:]
519            elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr):
520                ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p)
521                p = p[:6]+struct.pack("!H", ck)+p[8:]
522            else:
523                warning("No IP underlayer to compute checksum. Leaving null.")
524        return p
525    def extract_padding(self, s):
526        l = self.len - 8
527        return s[:l],s[l:]
528    def hashret(self):
529        return self.payload.hashret()
530    def answers(self, other):
531        if not isinstance(other, UDP):
532            return 0
533        if conf.checkIPsrc:
534            if self.dport != other.sport:
535                return 0
536        return self.payload.answers(other.payload)
537    def mysummary(self):
538        if isinstance(self.underlayer, IP):
539            return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%")
540        elif isinstance(self.underlayer, scapy.layers.inet6.IPv6):
541            return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%")
542        else:
543            return self.sprintf("UDP %UDP.sport% > %UDP.dport%")
544
545icmptypes = { 0 : "echo-reply",
546              3 : "dest-unreach",
547              4 : "source-quench",
548              5 : "redirect",
549              8 : "echo-request",
550              9 : "router-advertisement",
551              10 : "router-solicitation",
552              11 : "time-exceeded",
553              12 : "parameter-problem",
554              13 : "timestamp-request",
555              14 : "timestamp-reply",
556              15 : "information-request",
557              16 : "information-response",
558              17 : "address-mask-request",
559              18 : "address-mask-reply" }
560
561icmpcodes = { 3 : { 0  : "network-unreachable",
562                    1  : "host-unreachable",
563                    2  : "protocol-unreachable",
564                    3  : "port-unreachable",
565                    4  : "fragmentation-needed",
566                    5  : "source-route-failed",
567                    6  : "network-unknown",
568                    7  : "host-unknown",
569                    9  : "network-prohibited",
570                    10 : "host-prohibited",
571                    11 : "TOS-network-unreachable",
572                    12 : "TOS-host-unreachable",
573                    13 : "communication-prohibited",
574                    14 : "host-precedence-violation",
575                    15 : "precedence-cutoff", },
576              5 : { 0  : "network-redirect",
577                    1  : "host-redirect",
578                    2  : "TOS-network-redirect",
579                    3  : "TOS-host-redirect", },
580              11 : { 0 : "ttl-zero-during-transit",
581                     1 : "ttl-zero-during-reassembly", },
582              12 : { 0 : "ip-header-bad",
583                     1 : "required-option-missing", }, }
584
585
586
587
588class ICMP(Packet):
589    name = "ICMP"
590    fields_desc = [ ByteEnumField("type",8, icmptypes),
591                    MultiEnumField("code",0, icmpcodes, depends_on=lambda pkt:pkt.type,fmt="B"),
592                    XShortField("chksum", None),
593                    ConditionalField(XShortField("id",0),  lambda pkt:pkt.type in [0,8,13,14,15,16,17,18]),
594                    ConditionalField(XShortField("seq",0), lambda pkt:pkt.type in [0,8,13,14,15,16,17,18]),
595                    ConditionalField(ICMPTimeStampField("ts_ori", None), lambda pkt:pkt.type in [13,14]),
596                    ConditionalField(ICMPTimeStampField("ts_rx", None), lambda pkt:pkt.type in [13,14]),
597                    ConditionalField(ICMPTimeStampField("ts_tx", None), lambda pkt:pkt.type in [13,14]),
598                    ConditionalField(IPField("gw","0.0.0.0"),  lambda pkt:pkt.type==5),
599                    ConditionalField(ByteField("ptr",0),   lambda pkt:pkt.type==12),
600                    ConditionalField(X3BytesField("reserved",0), lambda pkt:pkt.type==12),
601                    ConditionalField(IPField("addr_mask","0.0.0.0"), lambda pkt:pkt.type in [17,18]),
602                    ConditionalField(IntField("unused",0), lambda pkt:pkt.type not in [0,5,8,12,13,14,15,16,17,18]),
603
604                    ]
605    def post_build(self, p, pay):
606        p += pay
607        if self.chksum is None:
608            ck = checksum(p)
609            p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:]
610        return p
611
612    def hashret(self):
613        if self.type in [0,8,13,14,15,16,17,18]:
614            return struct.pack("HH",self.id,self.seq)+self.payload.hashret()
615        return self.payload.hashret()
616    def answers(self, other):
617        if not isinstance(other,ICMP):
618            return 0
619        if ( (other.type,self.type) in [(8,0),(13,14),(15,16),(17,18)] and
620             self.id == other.id and
621             self.seq == other.seq ):
622            return 1
623        return 0
624
625    def guess_payload_class(self, payload):
626        if self.type in [3,4,5,11,12]:
627            return IPerror
628        else:
629            return None
630    def mysummary(self):
631        if isinstance(self.underlayer, IP):
632            return self.underlayer.sprintf("ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%")
633        else:
634            return self.sprintf("ICMP %ICMP.type% %ICMP.code%")
635
636
637
638
639
640class IPerror(IP):
641    name = "IP in ICMP"
642    def answers(self, other):
643        if not isinstance(other, IP):
644            return 0
645        if not ( ((conf.checkIPsrc == 0) or (self.dst == other.dst)) and
646                 (self.src == other.src) and
647                 ( ((conf.checkIPID == 0)
648                    or (self.id == other.id)
649                    or (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and
650                 (self.proto == other.proto) ):
651            return 0
652        return self.payload.answers(other.payload)
653    def mysummary(self):
654        return Packet.mysummary(self)
655
656
657class TCPerror(TCP):
658    name = "TCP in ICMP"
659    def answers(self, other):
660        if not isinstance(other, TCP):
661            return 0
662        if conf.checkIPsrc:
663            if not ((self.sport == other.sport) and
664                    (self.dport == other.dport)):
665                return 0
666        if conf.check_TCPerror_seqack:
667            if self.seq is not None:
668                if self.seq != other.seq:
669                    return 0
670            if self.ack is not None:
671                if self.ack != other.ack:
672                    return 0
673        return 1
674    def mysummary(self):
675        return Packet.mysummary(self)
676
677
678class UDPerror(UDP):
679    name = "UDP in ICMP"
680    def answers(self, other):
681        if not isinstance(other, UDP):
682            return 0
683        if conf.checkIPsrc:
684            if not ((self.sport == other.sport) and
685                    (self.dport == other.dport)):
686                return 0
687        return 1
688    def mysummary(self):
689        return Packet.mysummary(self)
690
691
692
693class ICMPerror(ICMP):
694    name = "ICMP in ICMP"
695    def answers(self, other):
696        if not isinstance(other,ICMP):
697            return 0
698        if not ((self.type == other.type) and
699                (self.code == other.code)):
700            return 0
701        if self.code in [0,8,13,14,17,18]:
702            if (self.id == other.id and
703                self.seq == other.seq):
704                return 1
705            else:
706                return 0
707        else:
708            return 1
709    def mysummary(self):
710        return Packet.mysummary(self)
711
712bind_layers( Ether,         IP,            type=2048)
713bind_layers( CookedLinux,   IP,            proto=2048)
714bind_layers( GRE,           IP,            proto=2048)
715bind_layers( SNAP,          IP,            code=2048)
716bind_layers( IPerror,       IPerror,       frag=0, proto=4)
717bind_layers( IPerror,       ICMPerror,     frag=0, proto=1)
718bind_layers( IPerror,       TCPerror,      frag=0, proto=6)
719bind_layers( IPerror,       UDPerror,      frag=0, proto=17)
720bind_layers( IP,            IP,            frag=0, proto=4)
721bind_layers( IP,            ICMP,          frag=0, proto=1)
722bind_layers( IP,            TCP,           frag=0, proto=6)
723bind_layers( IP,            UDP,           frag=0, proto=17)
724bind_layers( IP,            GRE,           frag=0, proto=47)
725
726conf.l2types.register(101, IP)
727conf.l2types.register_num2layer(12, IP)
728
729conf.l3types.register(ETH_P_IP, IP)
730conf.l3types.register_num2layer(ETH_P_ALL, IP)
731
732
733conf.neighbor.register_l3(Ether, IP, lambda l2,l3: getmacbyip(l3.dst))
734conf.neighbor.register_l3(Dot3, IP, lambda l2,l3: getmacbyip(l3.dst))
735
736
737###################
738## Fragmentation ##
739###################
740
741@conf.commands.register
742def fragment(pkt, fragsize=1480):
743    """Fragment a big IP datagram"""
744    fragsize = (fragsize+7)/8*8
745    lst = []
746    for p in pkt:
747        s = str(p[IP].payload)
748        nb = (len(s)+fragsize-1)/fragsize
749        for i in range(nb):
750            q = p.copy()
751            del(q[IP].payload)
752            del(q[IP].chksum)
753            del(q[IP].len)
754            if i == nb-1:
755                q[IP].flags &= ~1
756            else:
757                q[IP].flags |= 1
758            q[IP].frag = i*fragsize/8
759            r = conf.raw_layer(load=s[i*fragsize:(i+1)*fragsize])
760            r.overload_fields = p[IP].payload.overload_fields.copy()
761            q.add_payload(r)
762            lst.append(q)
763    return lst
764
765def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None):
766    if overlap_fragsize is None:
767        overlap_fragsize = fragsize
768    q = p.copy()
769    del(q[IP].payload)
770    q[IP].add_payload(overlap)
771
772    qfrag = fragment(q, overlap_fragsize)
773    qfrag[-1][IP].flags |= 1
774    return qfrag+fragment(p, fragsize)
775
776@conf.commands.register
777def defrag(plist):
778    """defrag(plist) -> ([not fragmented], [defragmented],
779                  [ [bad fragments], [bad fragments], ... ])"""
780    frags = defaultdict(PacketList)
781    nofrag = PacketList()
782    for p in plist:
783        ip = p[IP]
784        if IP not in p:
785            nofrag.append(p)
786            continue
787        if ip.frag == 0 and ip.flags & 1 == 0:
788            nofrag.append(p)
789            continue
790        uniq = (ip.id,ip.src,ip.dst,ip.proto)
791        frags[uniq].append(p)
792    defrag = []
793    missfrag = []
794    for lst in frags.itervalues():
795        lst.sort(key=lambda x: x.frag)
796        p = lst[0]
797        lastp = lst[-1]
798        if p.frag > 0 or lastp.flags & 1 != 0: # first or last fragment missing
799            missfrag.append(lst)
800            continue
801        p = p.copy()
802        if conf.padding_layer in p:
803            del(p[conf.padding_layer].underlayer.payload)
804        ip = p[IP]
805        if ip.len is None or ip.ihl is None:
806            clen = len(ip.payload)
807        else:
808            clen = ip.len - (ip.ihl<<2)
809        txt = conf.raw_layer()
810        for q in lst[1:]:
811            if clen != q.frag<<3: # Wrong fragmentation offset
812                if clen > q.frag<<3:
813                    warning("Fragment overlap (%i > %i) %r || %r ||  %r" % (clen, q.frag<<3, p,txt,q))
814                missfrag.append(lst)
815                break
816            if q[IP].len is None or q[IP].ihl is None:
817                clen += len(q[IP].payload)
818            else:
819                clen += q[IP].len - (q[IP].ihl<<2)
820            if conf.padding_layer in q:
821                del(q[conf.padding_layer].underlayer.payload)
822            txt.add_payload(q[IP].payload.copy())
823        else:
824            ip.flags &= ~1 # !MF
825            del(ip.chksum)
826            del(ip.len)
827            p = p/txt
828            defrag.append(p)
829    defrag2=PacketList()
830    for p in defrag:
831        defrag2.append(p.__class__(str(p)))
832    return nofrag,defrag2,missfrag
833
834@conf.commands.register
835def defragment(plist):
836    """defrag(plist) -> plist defragmented as much as possible """
837    frags = defaultdict(lambda:[])
838    final = []
839
840    pos = 0
841    for p in plist:
842        p._defrag_pos = pos
843        pos += 1
844        if IP in p:
845            ip = p[IP]
846            if ip.frag != 0 or ip.flags & 1:
847                ip = p[IP]
848                uniq = (ip.id,ip.src,ip.dst,ip.proto)
849                frags[uniq].append(p)
850                continue
851        final.append(p)
852
853    defrag = []
854    missfrag = []
855    for lst in frags.itervalues():
856        lst.sort(key=lambda x: x.frag)
857        p = lst[0]
858        lastp = lst[-1]
859        if p.frag > 0 or lastp.flags & 1 != 0: # first or last fragment missing
860            missfrag += lst
861            continue
862        p = p.copy()
863        if conf.padding_layer in p:
864            del(p[conf.padding_layer].underlayer.payload)
865        ip = p[IP]
866        if ip.len is None or ip.ihl is None:
867            clen = len(ip.payload)
868        else:
869            clen = ip.len - (ip.ihl<<2)
870        txt = conf.raw_layer()
871        for q in lst[1:]:
872            if clen != q.frag<<3: # Wrong fragmentation offset
873                if clen > q.frag<<3:
874                    warning("Fragment overlap (%i > %i) %r || %r ||  %r" % (clen, q.frag<<3, p,txt,q))
875                missfrag += lst
876                break
877            if q[IP].len is None or q[IP].ihl is None:
878                clen += len(q[IP].payload)
879            else:
880                clen += q[IP].len - (q[IP].ihl<<2)
881            if conf.padding_layer in q:
882                del(q[conf.padding_layer].underlayer.payload)
883            txt.add_payload(q[IP].payload.copy())
884        else:
885            ip.flags &= ~1 # !MF
886            del(ip.chksum)
887            del(ip.len)
888            p = p/txt
889            p._defrag_pos = max(x._defrag_pos for x in lst)
890            defrag.append(p)
891    defrag2=[]
892    for p in defrag:
893        q = p.__class__(str(p))
894        q._defrag_pos = p._defrag_pos
895        defrag2.append(q)
896    final += defrag2
897    final += missfrag
898    final.sort(key=lambda x: x._defrag_pos)
899    for p in final:
900        del(p._defrag_pos)
901
902    if hasattr(plist, "listname"):
903        name = "Defragmented %s" % plist.listname
904    else:
905        name = "Defragmented"
906
907    return PacketList(final, name=name)
908
909
910
911### Add timeskew_graph() method to PacketList
912def _packetlist_timeskew_graph(self, ip, **kargs):
913    """Tries to graph the timeskew between the timestamps and real time for a given ip"""
914    res = map(lambda x: self._elt2pkt(x), self.res)
915    b = filter(lambda x:x.haslayer(IP) and x.getlayer(IP).src == ip and x.haslayer(TCP), res)
916    c = []
917    for p in b:
918        opts = p.getlayer(TCP).options
919        for o in opts:
920            if o[0] == "Timestamp":
921                c.append((p.time,o[1][0]))
922    if not c:
923        warning("No timestamps found in packet list")
924        return
925    d = map(lambda (x,y): (x%2000,((x-c[0][0])-((y-c[0][1])/1000.0))),c)
926    g = Gnuplot.Gnuplot()
927    g.plot(Gnuplot.Data(d,**kargs))
928    return g
929
930PacketList.timeskew_graph = new.instancemethod(_packetlist_timeskew_graph, None, PacketList)
931
932
933### Create a new packet list
934class TracerouteResult(SndRcvList):
935    def __init__(self, res=None, name="Traceroute", stats=None):
936        PacketList.__init__(self, res, name, stats)
937        self.graphdef = None
938        self.graphASres = 0
939        self.padding = 0
940        self.hloc = None
941        self.nloc = None
942
943    def show(self):
944        return self.make_table(lambda (s,r): (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"),
945                                              s.ttl,
946                                              r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}")))
947
948
949    def get_trace(self):
950        trace = {}
951        for s,r in self.res:
952            if IP not in s:
953                continue
954            d = s[IP].dst
955            if d not in trace:
956                trace[d] = {}
957            trace[d][s[IP].ttl] = r[IP].src, ICMP not in r
958        for k in trace.values():
959            m = filter(lambda x:k[x][1], k.keys())
960            if not m:
961                continue
962            m = min(m)
963            for l in k.keys():
964                if l > m:
965                    del(k[l])
966        return trace
967
968    def trace3D(self):
969        """Give a 3D representation of the traceroute.
970        right button: rotate the scene
971        middle button: zoom
972        left button: move the scene
973        left button on a ball: toggle IP displaying
974        ctrl-left button on a ball: scan ports 21,22,23,25,80 and 443 and display the result"""
975        trace = self.get_trace()
976        import visual
977
978        class IPsphere(visual.sphere):
979            def __init__(self, ip, **kargs):
980                visual.sphere.__init__(self, **kargs)
981                self.ip=ip
982                self.label=None
983                self.setlabel(self.ip)
984            def setlabel(self, txt,visible=None):
985                if self.label is not None:
986                    if visible is None:
987                        visible = self.label.visible
988                    self.label.visible = 0
989                elif visible is None:
990                    visible=0
991                self.label=visual.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible)
992            def action(self):
993                self.label.visible ^= 1
994
995        visual.scene = visual.display()
996        visual.scene.exit = True
997        start = visual.box()
998        rings={}
999        tr3d = {}
1000        for i in trace:
1001            tr = trace[i]
1002            tr3d[i] = []
1003            ttl = tr.keys()
1004            for t in range(1,max(ttl)+1):
1005                if t not in rings:
1006                    rings[t] = []
1007                if t in tr:
1008                    if tr[t] not in rings[t]:
1009                        rings[t].append(tr[t])
1010                    tr3d[i].append(rings[t].index(tr[t]))
1011                else:
1012                    rings[t].append(("unk",-1))
1013                    tr3d[i].append(len(rings[t])-1)
1014        for t in rings:
1015            r = rings[t]
1016            l = len(r)
1017            for i in range(l):
1018                if r[i][1] == -1:
1019                    col = (0.75,0.75,0.75)
1020                elif r[i][1]:
1021                    col = visual.color.green
1022                else:
1023                    col = visual.color.blue
1024
1025                s = IPsphere(pos=((l-1)*visual.cos(2*i*visual.pi/l),(l-1)*visual.sin(2*i*visual.pi/l),2*t),
1026                             ip = r[i][0],
1027                             color = col)
1028                for trlst in tr3d.values():
1029                    if t <= len(trlst):
1030                        if trlst[t-1] == i:
1031                            trlst[t-1] = s
1032        forecol = colgen(0.625, 0.4375, 0.25, 0.125)
1033        for trlst in tr3d.values():
1034            col = forecol.next()
1035            start = (0,0,0)
1036            for ip in trlst:
1037                visual.cylinder(pos=start,axis=ip.pos-start,color=col,radius=0.2)
1038                start = ip.pos
1039
1040        movcenter=None
1041        while 1:
1042            visual.rate(50)
1043            if visual.scene.kb.keys:
1044                k = visual.scene.kb.getkey()
1045                if k == "esc" or k == "q":
1046                    break
1047            if visual.scene.mouse.events:
1048                ev = visual.scene.mouse.getevent()
1049                if ev.press == "left":
1050                    o = ev.pick
1051                    if o:
1052                        if ev.ctrl:
1053                            if o.ip == "unk":
1054                                continue
1055                            savcolor = o.color
1056                            o.color = (1,0,0)
1057                            a,b=sr(IP(dst=o.ip)/TCP(dport=[21,22,23,25,80,443]),timeout=2)
1058                            o.color = savcolor
1059                            if len(a) == 0:
1060                                txt = "%s:\nno results" % o.ip
1061                            else:
1062                                txt = "%s:\n" % o.ip
1063                                for s,r in a:
1064                                    txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n")
1065                            o.setlabel(txt, visible=1)
1066                        else:
1067                            if hasattr(o, "action"):
1068                                o.action()
1069                elif ev.drag == "left":
1070                    movcenter = ev.pos
1071                elif ev.drop == "left":
1072                    movcenter = None
1073            if movcenter:
1074                visual.scene.center -= visual.scene.mouse.pos-movcenter
1075                movcenter = visual.scene.mouse.pos
1076
1077
1078    def world_trace(self):
1079        from modules.geo import locate_ip
1080        ips = {}
1081        rt = {}
1082        ports_done = {}
1083        for s,r in self.res:
1084            ips[r.src] = None
1085            if s.haslayer(TCP) or s.haslayer(UDP):
1086                trace_id = (s.src,s.dst,s.proto,s.dport)
1087            elif s.haslayer(ICMP):
1088                trace_id = (s.src,s.dst,s.proto,s.type)
1089            else:
1090                trace_id = (s.src,s.dst,s.proto,0)
1091            trace = rt.get(trace_id,{})
1092            if not r.haslayer(ICMP) or r.type != 11:
1093                if ports_done.has_key(trace_id):
1094                    continue
1095                ports_done[trace_id] = None
1096            trace[s.ttl] = r.src
1097            rt[trace_id] = trace
1098
1099        trt = {}
1100        for trace_id in rt:
1101            trace = rt[trace_id]
1102            loctrace = []
1103            for i in range(max(trace.keys())):
1104                ip = trace.get(i,None)
1105                if ip is None:
1106                    continue
1107                loc = locate_ip(ip)
1108                if loc is None:
1109                    continue
1110#                loctrace.append((ip,loc)) # no labels yet
1111                loctrace.append(loc)
1112            if loctrace:
1113                trt[trace_id] = loctrace
1114
1115        tr = map(lambda x: Gnuplot.Data(x,with_="lines"), trt.values())
1116        g = Gnuplot.Gnuplot()
1117        world = Gnuplot.File(conf.gnuplot_world,with_="lines")
1118        g.plot(world,*tr)
1119        return g
1120
1121    def make_graph(self,ASres=None,padding=0):
1122        if ASres is None:
1123            ASres = conf.AS_resolver
1124        self.graphASres = ASres
1125        self.graphpadding = padding
1126        ips = {}
1127        rt = {}
1128        ports = {}
1129        ports_done = {}
1130        for s,r in self.res:
1131            r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r
1132            s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s
1133            ips[r.src] = None
1134            if TCP in s:
1135                trace_id = (s.src,s.dst,6,s.dport)
1136            elif UDP in s:
1137                trace_id = (s.src,s.dst,17,s.dport)
1138            elif ICMP in s:
1139                trace_id = (s.src,s.dst,1,s.type)
1140            else:
1141                trace_id = (s.src,s.dst,s.proto,0)
1142            trace = rt.get(trace_id,{})
1143            ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl
1144            if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and scapy.layers.inet6.IPv6 in r and scapy.layers.inet6.ICMPv6TimeExceeded in r):
1145                if trace_id in ports_done:
1146                    continue
1147                ports_done[trace_id] = None
1148                p = ports.get(r.src,[])
1149                if TCP in r:
1150                    p.append(r.sprintf("<T%ir,TCP.sport%> %TCP.sport% %TCP.flags%"))
1151                    trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%')
1152                elif UDP in r:
1153                    p.append(r.sprintf("<U%ir,UDP.sport%> %UDP.sport%"))
1154                    trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%')
1155                elif ICMP in r:
1156                    p.append(r.sprintf("<I%ir,ICMP.type%> ICMP %ICMP.type%"))
1157                    trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%')
1158                else:
1159                    p.append(r.sprintf("{IP:<P%ir,proto%> IP %proto%}{IPv6:<P%ir,nh%> IPv6 %nh%}"))
1160                    trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}')
1161                ports[r.src] = p
1162            else:
1163                trace[ttl] = r.sprintf('"%r,src%"')
1164            rt[trace_id] = trace
1165
1166        # Fill holes with unk%i nodes
1167        unknown_label = incremental_label("unk%i")
1168        blackholes = []
1169        bhip = {}
1170        for rtk in rt:
1171            trace = rt[rtk]
1172            k = trace.keys()
1173            for n in range(min(k), max(k)):
1174                if not trace.has_key(n):
1175                    trace[n] = unknown_label.next()
1176            if not ports_done.has_key(rtk):
1177                if rtk[2] == 1: #ICMP
1178                    bh = "%s %i/icmp" % (rtk[1],rtk[3])
1179                elif rtk[2] == 6: #TCP
1180                    bh = "%s %i/tcp" % (rtk[1],rtk[3])
1181                elif rtk[2] == 17: #UDP
1182                    bh = '%s %i/udp' % (rtk[1],rtk[3])
1183                else:
1184                    bh = '%s %i/proto' % (rtk[1],rtk[2])
1185                ips[bh] = None
1186                bhip[rtk[1]] = bh
1187                bh = '"%s"' % bh
1188                trace[max(k)+1] = bh
1189                blackholes.append(bh)
1190
1191        # Find AS numbers
1192        ASN_query_list = dict.fromkeys(map(lambda x:x.rsplit(" ",1)[0],ips)).keys()
1193        if ASres is None:
1194            ASNlist = []
1195        else:
1196            ASNlist = ASres.resolve(*ASN_query_list)
1197
1198        ASNs = {}
1199        ASDs = {}
1200        for ip,asn,desc, in ASNlist:
1201            if asn is None:
1202                continue
1203            iplist = ASNs.get(asn,[])
1204            if ip in bhip:
1205                if ip in ports:
1206                    iplist.append(ip)
1207                iplist.append(bhip[ip])
1208            else:
1209                iplist.append(ip)
1210            ASNs[asn] = iplist
1211            ASDs[asn] = desc
1212
1213
1214        backcolorlist=colgen("60","86","ba","ff")
1215        forecolorlist=colgen("a0","70","40","20")
1216
1217        s = "digraph trace {\n"
1218
1219        s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n"
1220
1221        s += "\n#ASN clustering\n"
1222        for asn in ASNs:
1223            s += '\tsubgraph cluster_%s {\n' % asn
1224            col = backcolorlist.next()
1225            s += '\t\tcolor="#%s%s%s";' % col
1226            s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col
1227            s += '\t\tfontsize = 10;'
1228            s += '\t\tlabel = "%s\\n[%s]"\n' % (asn,ASDs[asn])
1229            for ip in ASNs[asn]:
1230
1231                s += '\t\t"%s";\n'%ip
1232            s += "\t}\n"
1233
1234
1235
1236
1237        s += "#endpoints\n"
1238        for p in ports:
1239            s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p,p,"|".join(ports[p]))
1240
1241        s += "\n#Blackholes\n"
1242        for bh in blackholes:
1243            s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh
1244
1245        if padding:
1246            s += "\n#Padding\n"
1247            pad={}
1248            for snd,rcv in self.res:
1249                if rcv.src not in ports and rcv.haslayer(conf.padding_layer):
1250                    p = rcv.getlayer(conf.padding_layer).load
1251                    if p != "\x00"*len(p):
1252                        pad[rcv.src]=None
1253            for rcv in pad:
1254                s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv
1255
1256
1257
1258        s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n"
1259
1260
1261        for rtk in rt:
1262            s += "#---[%s\n" % `rtk`
1263            s += '\t\tedge [color="#%s%s%s"];\n' % forecolorlist.next()
1264            trace = rt[rtk]
1265            k = trace.keys()
1266            for n in range(min(k), max(k)):
1267                s += '\t%s ->\n' % trace[n]
1268            s += '\t%s;\n' % trace[max(k)]
1269
1270        s += "}\n";
1271        self.graphdef = s
1272
1273    def graph(self, ASres=None, padding=0, **kargs):
1274        """x.graph(ASres=conf.AS_resolver, other args):
1275        ASres=None          : no AS resolver => no clustering
1276        ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net)
1277        ASres=AS_resolver_cymru(): use whois.cymru.com whois database
1278        ASres=AS_resolver(server="whois.ra.net")
1279        type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option
1280        target: filename or redirect. Defaults pipe to Imagemagick's display program
1281        prog: which graphviz program to use"""
1282        if ASres is None:
1283            ASres = conf.AS_resolver
1284        if (self.graphdef is None or
1285            self.graphASres != ASres or
1286            self.graphpadding != padding):
1287            self.make_graph(ASres,padding)
1288
1289        return do_graph(self.graphdef, **kargs)
1290
1291
1292
1293@conf.commands.register
1294def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4 = None, filter=None, timeout=2, verbose=None, **kargs):
1295    """Instant TCP traceroute
1296traceroute(target, [maxttl=30,] [dport=80,] [sport=80,] [verbose=conf.verb]) -> None
1297"""
1298    if verbose is None:
1299        verbose = conf.verb
1300    if filter is None:
1301        # we only consider ICMP error packets and TCP packets with at
1302        # least the ACK flag set *and* either the SYN or the RST flag
1303        # set
1304        filter="(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))"
1305    if l4 is None:
1306        a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/TCP(seq=RandInt(),sport=sport, dport=dport),
1307                 timeout=timeout, filter=filter, verbose=verbose, **kargs)
1308    else:
1309        # this should always work
1310        filter="ip"
1311        a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/l4,
1312                 timeout=timeout, filter=filter, verbose=verbose, **kargs)
1313
1314    a = TracerouteResult(a.res)
1315    if verbose:
1316        a.show()
1317    return a,b
1318
1319
1320
1321#############################
1322## Simple TCP client stack ##
1323#############################
1324
1325class TCP_client(Automaton):
1326
1327    def parse_args(self, ip, port, *args, **kargs):
1328        self.dst = iter(Net(ip)).next()
1329        self.dport = port
1330        self.sport = random.randrange(0,2**16)
1331        self.l4 = IP(dst=ip)/TCP(sport=self.sport, dport=self.dport, flags=0,
1332                                 seq=random.randrange(0,2**32))
1333        self.src = self.l4.src
1334        self.swin=self.l4[TCP].window
1335        self.dwin=1
1336        self.rcvbuf=""
1337        bpf = "host %s  and host %s and port %i and port %i" % (self.src,
1338                                                                self.dst,
1339                                                                self.sport,
1340                                                                self.dport)
1341
1342#        bpf=None
1343        Automaton.parse_args(self, filter=bpf, **kargs)
1344
1345
1346    def master_filter(self, pkt):
1347        return (IP in pkt and
1348                pkt[IP].src == self.dst and
1349                pkt[IP].dst == self.src and
1350                TCP in pkt and
1351                pkt[TCP].sport == self.dport and
1352                pkt[TCP].dport == self.sport and
1353                self.l4[TCP].seq >= pkt[TCP].ack and # XXX: seq/ack 2^32 wrap up
1354                ((self.l4[TCP].ack == 0) or (self.l4[TCP].ack <= pkt[TCP].seq <= self.l4[TCP].ack+self.swin)) )
1355
1356
1357    @ATMT.state(initial=1)
1358    def START(self):
1359        pass
1360
1361    @ATMT.state()
1362    def SYN_SENT(self):
1363        pass
1364
1365    @ATMT.state()
1366    def ESTABLISHED(self):
1367        pass
1368
1369    @ATMT.state()
1370    def LAST_ACK(self):
1371        pass
1372
1373    @ATMT.state(final=1)
1374    def CLOSED(self):
1375        pass
1376
1377
1378    @ATMT.condition(START)
1379    def connect(self):
1380        raise self.SYN_SENT()
1381    @ATMT.action(connect)
1382    def send_syn(self):
1383        self.l4[TCP].flags = "S"
1384        self.send(self.l4)
1385        self.l4[TCP].seq += 1
1386
1387
1388    @ATMT.receive_condition(SYN_SENT)
1389    def synack_received(self, pkt):
1390        if pkt[TCP].flags & 0x3f == 0x12:
1391            raise self.ESTABLISHED().action_parameters(pkt)
1392    @ATMT.action(synack_received)
1393    def send_ack_of_synack(self, pkt):
1394        self.l4[TCP].ack = pkt[TCP].seq+1
1395        self.l4[TCP].flags = "A"
1396        self.send(self.l4)
1397
1398    @ATMT.receive_condition(ESTABLISHED)
1399    def incoming_data_received(self, pkt):
1400        if not isinstance(pkt[TCP].payload, NoPayload) and not isinstance(pkt[TCP].payload, conf.padding_layer):
1401            raise self.ESTABLISHED().action_parameters(pkt)
1402    @ATMT.action(incoming_data_received)
1403    def receive_data(self,pkt):
1404        data = str(pkt[TCP].payload)
1405        if data and self.l4[TCP].ack == pkt[TCP].seq:
1406            self.l4[TCP].ack += len(data)
1407            self.l4[TCP].flags = "A"
1408            self.send(self.l4)
1409            self.rcvbuf += data
1410            if pkt[TCP].flags & 8 != 0: #PUSH
1411                self.oi.tcp.send(self.rcvbuf)
1412                self.rcvbuf = ""
1413
1414    @ATMT.ioevent(ESTABLISHED,name="tcp", as_supersocket="tcplink")
1415    def outgoing_data_received(self, fd):
1416        raise self.ESTABLISHED().action_parameters(fd.recv())
1417    @ATMT.action(outgoing_data_received)
1418    def send_data(self, d):
1419        self.l4[TCP].flags = "PA"
1420        self.send(self.l4/d)
1421        self.l4[TCP].seq += len(d)
1422
1423
1424    @ATMT.receive_condition(ESTABLISHED)
1425    def reset_received(self, pkt):
1426        if pkt[TCP].flags & 4 != 0:
1427            raise self.CLOSED()
1428
1429    @ATMT.receive_condition(ESTABLISHED)
1430    def fin_received(self, pkt):
1431        if pkt[TCP].flags & 0x1 == 1:
1432            raise self.LAST_ACK().action_parameters(pkt)
1433    @ATMT.action(fin_received)
1434    def send_finack(self, pkt):
1435        self.l4[TCP].flags = "FA"
1436        self.l4[TCP].ack = pkt[TCP].seq+1
1437        self.send(self.l4)
1438        self.l4[TCP].seq += 1
1439
1440    @ATMT.receive_condition(LAST_ACK)
1441    def ack_of_fin_received(self, pkt):
1442        if pkt[TCP].flags & 0x3f == 0x10:
1443            raise self.CLOSED()
1444
1445
1446
1447
1448#####################
1449## Reporting stuff ##
1450#####################
1451
1452def report_ports(target, ports):
1453    """portscan a target and output a LaTeX table
1454report_ports(target, ports) -> string"""
1455    ans,unans = sr(IP(dst=target)/TCP(dport=ports),timeout=5)
1456    rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n"
1457    for s,r in ans:
1458        if not r.haslayer(ICMP):
1459            if r.payload.flags == 0x12:
1460                rep += r.sprintf("%TCP.sport% & open & SA \\\\\n")
1461    rep += "\\hline\n"
1462    for s,r in ans:
1463        if r.haslayer(ICMP):
1464            rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n")
1465        elif r.payload.flags != 0x12:
1466            rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n")
1467    rep += "\\hline\n"
1468    for i in unans:
1469        rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n")
1470    rep += "\\hline\n\\end{tabular}\n"
1471    return rep
1472
1473
1474
1475def IPID_count(lst, funcID=lambda x:x[1].id, funcpres=lambda x:x[1].summary()):
1476    idlst = map(funcID, lst)
1477    idlst.sort()
1478    classes = [idlst[0]]+map(lambda x:x[1],filter(lambda (x,y): abs(x-y)>50, map(lambda x,y: (x,y),idlst[:-1], idlst[1:])))
1479    lst = map(lambda x:(funcID(x), funcpres(x)), lst)
1480    lst.sort()
1481    print "Probably %i classes:" % len(classes), classes
1482    for id,pr in lst:
1483        print "%5i" % id, pr
1484
1485
1486def fragleak(target,sport=123, dport=123, timeout=0.2, onlyasc=0):
1487    load = "XXXXYYYYYYYYYY"
1488#    getmacbyip(target)
1489#    pkt = IP(dst=target, id=RandShort(), options="\x22"*40)/UDP()/load
1490    pkt = IP(dst=target, id=RandShort(), options="\x00"*40, flags=1)/UDP(sport=sport, dport=sport)/load
1491    s=conf.L3socket()
1492    intr=0
1493    found={}
1494    try:
1495        while 1:
1496            try:
1497                if not intr:
1498                    s.send(pkt)
1499                sin,sout,serr = select([s],[],[],timeout)
1500                if not sin:
1501                    continue
1502                ans=s.recv(1600)
1503                if not isinstance(ans, IP): #TODO: IPv6
1504                    continue
1505                if not isinstance(ans.payload, ICMP):
1506                    continue
1507                if not isinstance(ans.payload.payload, IPerror):
1508                    continue
1509                if ans.payload.payload.dst != target:
1510                    continue
1511                if ans.src  != target:
1512                    print "leak from", ans.src,
1513
1514
1515#                print repr(ans)
1516                if not ans.haslayer(conf.padding_layer):
1517                    continue
1518
1519
1520#                print repr(ans.payload.payload.payload.payload)
1521
1522#                if not isinstance(ans.payload.payload.payload.payload, conf.raw_layer):
1523#                    continue
1524#                leak = ans.payload.payload.payload.payload.load[len(load):]
1525                leak = ans.getlayer(conf.padding_layer).load
1526                if leak not in found:
1527                    found[leak]=None
1528                    linehexdump(leak, onlyasc=onlyasc)
1529            except KeyboardInterrupt:
1530                if intr:
1531                    raise
1532                intr=1
1533    except KeyboardInterrupt:
1534        pass
1535
1536def fragleak2(target, timeout=0.4, onlyasc=0):
1537    found={}
1538    try:
1539        while 1:
1540            p = sr1(IP(dst=target, options="\x00"*40, proto=200)/"XXXXYYYYYYYYYYYY",timeout=timeout,verbose=0)
1541            if not p:
1542                continue
1543            if conf.padding_layer in p:
1544                leak  = p[conf.padding_layer].load
1545                if leak not in found:
1546                    found[leak]=None
1547                    linehexdump(leak,onlyasc=onlyasc)
1548    except:
1549        pass
1550
1551
1552conf.stats_classic_protocols += [TCP,UDP,ICMP]
1553conf.stats_dot11_protocols += [TCP,UDP,ICMP]
1554
1555if conf.ipv6_enabled:
1556    import scapy.layers.inet6
1557