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