@@ -447,7 +447,8 @@ class IP(Packet, IPTools):
         if self.dst == "224.0.0.251":  # mDNS
             return struct.pack("B", self.proto) + self.payload.hashret()
         if conf.checkIPsrc and conf.checkIPaddr:
-            return (strxor(inet_aton(self.src), inet_aton(self.dst))
+            return (strxor(inet_pton(socket.AF_INET, self.src),
+                           inet_pton(socket.AF_INET, self.dst))
                     + struct.pack("B", self.proto) + self.payload.hashret())
         return struct.pack("B", self.proto) + self.payload.hashret()
 
# This file is part of Scapy
# See http://www.secdev.org/projects/scapy for more informations
# Copyright (C) Philippe Biondi <phil@secdev.org>
# This program is published under a GPLv2 license

"""
IPv4 (Internet Protocol v4).
"""

from __future__ import absolute_import
from __future__ import print_function
import os
import time
import struct
import re
import socket
import types
from select import select
from collections import defaultdict

from scapy.utils import checksum, inet_aton, inet_ntoa
from scapy.base_classes import Gen
from scapy.data import *
from scapy.layers.l2 import *
from scapy.compat import *
from scapy.config import conf
from scapy.arch import WINDOWS
from scapy.extlib import plt, MATPLOTLIB, MATPLOTLIB_INLINED, MATPLOTLIB_DEFAULT_PLOT_KARGS
from scapy.fields import *
from scapy.packet import *
from scapy.volatile import *
from scapy.sendrecv import sr, sr1, srp1
from scapy.plist import PacketList, SndRcvList
from scapy.automaton import Automaton, ATMT
from scapy.error import warning
from scapy.utils import whois

import scapy.as_resolvers

import scapy.modules.six as six
from scapy.modules.six.moves import range

####################
#  IP Tools class  #
####################


class IPTools(object):
    """Add more powers to a class with an "src" attribute."""
    __slots__ = []

    def whois(self):
        """whois the source and print the output"""
        print(whois(self.src).decode("utf8", "ignore"))

    def _ttl(self):
        """Returns ttl or hlim, depending on the IP version"""
        return self.hlim if isinstance(self, scapy.layers.inet6.IPv6) else self.ttl

    def ottl(self):
        t = sorted([32, 64, 128, 255] + [self._ttl()])
        return t[t.index(self._ttl()) + 1]

    def hops(self):
        return self.ottl() - self._ttl()


_ip_options_names = {0: "end_of_list",
                     1: "nop",
                     2: "security",
                     3: "loose_source_route",
                     4: "timestamp",
                     5: "extended_security",
                     6: "commercial_security",
                     7: "record_route",
                     8: "stream_id",
                     9: "strict_source_route",
                     10: "experimental_measurement",
                     11: "mtu_probe",
                     12: "mtu_reply",
                     13: "flow_control",
                     14: "access_control",
                     15: "encode",
                     16: "imi_traffic_descriptor",
                     17: "extended_IP",
                     18: "traceroute",
                     19: "address_extension",
                     20: "router_alert",
                     21: "selective_directed_broadcast_mode",
                     23: "dynamic_packet_state",
                     24: "upstream_multicast_packet",
                     25: "quick_start",
                     30: "rfc4727_experiment",
                     }


class _IPOption_HDR(Packet):
    fields_desc = [BitField("copy_flag", 0, 1),
                   BitEnumField("optclass", 0, 2, {0: "control", 2: "debug"}),
                   BitEnumField("option", 0, 5, _ip_options_names)]


class IPOption(Packet):
    name = "IP Option"
    fields_desc = [_IPOption_HDR,
                   FieldLenField("length", None, fmt="B",  # Only option 0 and 1 have no length and value
                                 length_of="value", adjust=lambda pkt, l:l + 2),
                   StrLenField("value", "", length_from=lambda pkt:pkt.length - 2)]

    def extract_padding(self, p):
        return b"", p

    registered_ip_options = {}

    @classmethod
    def register_variant(cls):
        cls.registered_ip_options[cls.option.default] = cls

    @classmethod
    def dispatch_hook(cls, pkt=None, *args, **kargs):
        if pkt:
            opt = orb(pkt[0]) & 0x1f
            if opt in cls.registered_ip_options:
                return cls.registered_ip_options[opt]
        return cls


class IPOption_EOL(IPOption):
    name = "IP Option End of Options List"
    option = 0
    fields_desc = [_IPOption_HDR]


class IPOption_NOP(IPOption):
    name = "IP Option No Operation"
    option = 1
    fields_desc = [_IPOption_HDR]


class IPOption_Security(IPOption):
    name = "IP Option Security"
    copy_flag = 1
    option = 2
    fields_desc = [_IPOption_HDR,
                   ByteField("length", 11),
                   ShortField("security", 0),
                   ShortField("compartment", 0),
                   ShortField("handling_restrictions", 0),
                   StrFixedLenField("transmission_control_code", "xxx", 3),
                   ]


class IPOption_RR(IPOption):
    name = "IP Option Record Route"
    option = 7
    fields_desc = [_IPOption_HDR,
                   FieldLenField("length", None, fmt="B",
                                 length_of="routers", adjust=lambda pkt, l:l + 3),
                   ByteField("pointer", 4),  # 4 is first IP
                   FieldListField("routers", [], IPField("", "0.0.0.0"),
                                  length_from=lambda pkt:pkt.length - 3)
                   ]

    def get_current_router(self):
        return self.routers[self.pointer // 4 - 1]


class IPOption_LSRR(IPOption_RR):
    name = "IP Option Loose Source and Record Route"
    copy_flag = 1
    option = 3


class IPOption_SSRR(IPOption_RR):
    name = "IP Option Strict Source and Record Route"
    copy_flag = 1
    option = 9


class IPOption_Stream_Id(IPOption):
    name = "IP Option Stream ID"
    copy_flag = 1
    option = 8
    fields_desc = [_IPOption_HDR,
                   ByteField("length", 4),
                   ShortField("security", 0), ]


class IPOption_MTU_Probe(IPOption):
    name = "IP Option MTU Probe"
    option = 11
    fields_desc = [_IPOption_HDR,
                   ByteField("length", 4),
                   ShortField("mtu", 0), ]


class IPOption_MTU_Reply(IPOption_MTU_Probe):
    name = "IP Option MTU Reply"
    option = 12


class IPOption_Traceroute(IPOption):
    name = "IP Option Traceroute"
    option = 18
    fields_desc = [_IPOption_HDR,
                   ByteField("length", 12),
                   ShortField("id", 0),
                   ShortField("outbound_hops", 0),
                   ShortField("return_hops", 0),
                   IPField("originator_ip", "0.0.0.0")]


class IPOption_Address_Extension(IPOption):
    name = "IP Option Address Extension"
    copy_flag = 1
    option = 19
    fields_desc = [_IPOption_HDR,
                   ByteField("length", 10),
                   IPField("src_ext", "0.0.0.0"),
                   IPField("dst_ext", "0.0.0.0")]


class IPOption_Router_Alert(IPOption):
    name = "IP Option Router Alert"
    copy_flag = 1
    option = 20
    fields_desc = [_IPOption_HDR,
                   ByteField("length", 4),
                   ShortEnumField("alert", 0, {0: "router_shall_examine_packet"}), ]


class IPOption_SDBM(IPOption):
    name = "IP Option Selective Directed Broadcast Mode"
    copy_flag = 1
    option = 21
    fields_desc = [_IPOption_HDR,
                   FieldLenField("length", None, fmt="B",
                                 length_of="addresses", adjust=lambda pkt, l:l + 2),
                   FieldListField("addresses", [], IPField("", "0.0.0.0"),
                                  length_from=lambda pkt:pkt.length - 2)
                   ]


TCPOptions = (
    {0: ("EOL", None),
     1: ("NOP", None),
     2: ("MSS", "!H"),
     3: ("WScale", "!B"),
     4: ("SAckOK", None),
     5: ("SAck", "!"),
     8: ("Timestamp", "!II"),
     14: ("AltChkSum", "!BH"),
     15: ("AltChkSumOpt", None),
     25: ("Mood", "!p"),
     28: ("UTO", "!H"),
     34: ("TFO", "!II"),
     # RFC 3692
     253: ("Experiment", "!HHHH"),
     254: ("Experiment", "!HHHH"),
     },
    {"EOL": 0,
     "NOP": 1,
     "MSS": 2,
     "WScale": 3,
     "SAckOK": 4,
     "SAck": 5,
     "Timestamp": 8,
     "AltChkSum": 14,
     "AltChkSumOpt": 15,
     "Mood": 25,
     "UTO": 28,
     "TFO": 34,
     })


class TCPOptionsField(StrField):
    islist = 1

    def getfield(self, pkt, s):
        opsz = (pkt.dataofs - 5) * 4
        if opsz < 0:
            warning("bad dataofs (%i). Assuming dataofs=5" % pkt.dataofs)
            opsz = 0
        return s[opsz:], self.m2i(pkt, s[:opsz])

    def m2i(self, pkt, x):
        opt = []
        while x:
            onum = orb(x[0])
            if onum == 0:
                opt.append(("EOL", None))
                x = x[1:]
                break
            if onum == 1:
                opt.append(("NOP", None))
                x = x[1:]
                continue
            olen = orb(x[1])
            if olen < 2:
                warning("Malformed TCP option (announced length is %i)" % olen)
                olen = 2
            oval = x[2:olen]
            if onum in TCPOptions[0]:
                oname, ofmt = TCPOptions[0][onum]
                if onum == 5:  # SAck
                    ofmt += "%iI" % (len(oval) // 4)
                if ofmt and struct.calcsize(ofmt) == len(oval):
                    oval = struct.unpack(ofmt, oval)
                    if len(oval) == 1:
                        oval = oval[0]
                opt.append((oname, oval))
            else:
                opt.append((onum, oval))
            x = x[olen:]
        return opt

    def i2m(self, pkt, x):
        opt = b""
        for oname, oval in x:
            if isinstance(oname, str):
                if oname == "NOP":
                    opt += b"\x01"
                    continue
                elif oname == "EOL":
                    opt += b"\x00"
                    continue
                elif oname in TCPOptions[1]:
                    onum = TCPOptions[1][oname]
                    ofmt = TCPOptions[0][onum][1]
                    if onum == 5:  # SAck
                        ofmt += "%iI" % len(oval)
                    if ofmt is not None and (not isinstance(oval, str) or "s" in ofmt):
                        if not isinstance(oval, tuple):
                            oval = (oval,)
                        oval = struct.pack(ofmt, *oval)
                else:
                    warning("option [%s] unknown. Skipped.", oname)
                    continue
            else:
                onum = oname
                if not isinstance(oval, str):
                    warning("option [%i] is not string." % onum)
                    continue
            opt += chb(onum) + chb(2 + len(oval)) + raw(oval)
        return opt + b"\x00" * (3 - ((len(opt) + 3) % 4))

    def randval(self):
        return []  # XXX


class ICMPTimeStampField(IntField):
    re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$")

    def i2repr(self, pkt, val):
        if val is None:
            return "--"
        else:
            sec, milli = divmod(val, 1000)
            min, sec = divmod(sec, 60)
            hour, min = divmod(min, 60)
            return "%d:%d:%d.%d" % (hour, min, sec, int(milli))

    def any2i(self, pkt, val):
        if isinstance(val, str):
            hmsms = self.re_hmsm.match(val)
            if hmsms:
                h, _, m, _, s, _, ms = hmsms = hmsms.groups()
                ms = int(((ms or "") + "000")[:3])
                val = ((int(h) * 60 + int(m or 0)) * 60 + int(s or 0)) * 1000 + ms
            else:
                val = 0
        elif val is None:
            val = int((time.time() % (24 * 60 * 60)) * 1000)
        return val


class DestIPField(IPField, DestField):
    bindings = {}

    def __init__(self, name, default):
        IPField.__init__(self, name, None)
        DestField.__init__(self, name, default)

    def i2m(self, pkt, x):
        if x is None:
            x = self.dst_from_pkt(pkt)
        return IPField.i2m(self, pkt, x)

    def i2h(self, pkt, x):
        if x is None:
            x = self.dst_from_pkt(pkt)
        return IPField.i2h(self, pkt, x)


class IP(Packet, IPTools):
    __slots__ = ["_defrag_pos"]
    name = "IP"
    fields_desc = [BitField("version", 4, 4),
                   BitField("ihl", None, 4),
                   XByteField("tos", 0),
                   ShortField("len", None),
                   ShortField("id", 1),
                   FlagsField("flags", 0, 3, ["MF", "DF", "evil"]),
                   BitField("frag", 0, 13),
                   ByteField("ttl", 64),
                   ByteEnumField("proto", 0, IP_PROTOS),
                   XShortField("chksum", None),
                   # IPField("src", "127.0.0.1"),
                   Emph(SourceIPField("src", "dst")),
                   Emph(DestIPField("dst", "127.0.0.1")),
                   PacketListField("options", [], IPOption, length_from=lambda p:p.ihl * 4 - 20)]

    def post_build(self, p, pay):
        ihl = self.ihl
        p += b"\0" * ((-len(p)) % 4)  # pad IP options if needed
        if ihl is None:
            ihl = len(p) // 4
            p = chb(((self.version & 0xf) << 4) | ihl & 0x0f) + p[1:]
        if self.len is None:
            l = len(p) + len(pay)
            p = p[:2] + struct.pack("!H", l) + p[4:]
        if self.chksum is None:
            ck = checksum(p)
            p = p[:10] + chb(ck >> 8) + chb(ck & 0xff) + p[12:]
        return p + pay

    def extract_padding(self, s):
        l = self.len - (self.ihl << 2)
        return s[:l], s[l:]

    def route(self):
        dst = self.dst
        if isinstance(dst, Gen):
            dst = next(iter(dst))
        if conf.route is None:
            # unused import, only to initialize conf.route
            import scapy.route
        return conf.route.route(dst)

    def hashret(self):
        if ((self.proto == socket.IPPROTO_ICMP)
            and (isinstance(self.payload, ICMP))
                and (self.payload.type in [3, 4, 5, 11, 12])):
            return self.payload.payload.hashret()
        if not conf.checkIPinIP and self.proto in [4, 41]:  # IP, IPv6
            return self.payload.hashret()
        if self.dst == "224.0.0.251":  # mDNS
            return struct.pack("B", self.proto) + self.payload.hashret()
        if conf.checkIPsrc and conf.checkIPaddr:
            return (strxor(inet_aton(self.src), inet_aton(self.dst))
                    + struct.pack("B", self.proto) + self.payload.hashret())
        return struct.pack("B", self.proto) + self.payload.hashret()

    def answers(self, other):
        if not conf.checkIPinIP:  # skip IP in IP and IPv6 in IP
            if self.proto in [4, 41]:
                return self.payload.answers(other)
            if isinstance(other, IP) and other.proto in [4, 41]:
                return self.answers(other.payload)
            if conf.ipv6_enabled \
               and isinstance(other, scapy.layers.inet6.IPv6) \
               and other.nh in [4, 41]:
                return self.answers(other.payload)
        if not isinstance(other, IP):
            return 0
        if conf.checkIPaddr:
            if other.dst == "224.0.0.251" and self.dst == "224.0.0.251":  # mDNS
                return self.payload.answers(other.payload)
            elif (self.dst != other.src):
                return 0
        if ((self.proto == socket.IPPROTO_ICMP) and
            (isinstance(self.payload, ICMP)) and
                (self.payload.type in [3, 4, 5, 11, 12])):
            # ICMP error message
            return self.payload.payload.answers(other)

        else:
            if ((conf.checkIPaddr and (self.src != other.dst)) or
                    (self.proto != other.proto)):
                return 0
            return self.payload.answers(other.payload)

    def mysummary(self):
        s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%")
        if self.frag:
            s += " frag:%i" % self.frag
        return s

    def fragment(self, fragsize=1480):
        """Fragment IP datagrams"""
        fragsize = (fragsize + 7) // 8 * 8
        lst = []
        fnb = 0
        fl = self
        while fl.underlayer is not None:
            fnb += 1
            fl = fl.underlayer

        for p in fl:
            s = raw(p[fnb].payload)
            nb = (len(s) + fragsize - 1) // fragsize
            for i in range(nb):
                q = p.copy()
                del(q[fnb].payload)
                del(q[fnb].chksum)
                del(q[fnb].len)
                if i != nb - 1:
                    q[fnb].flags |= 1
                q[fnb].frag += i * fragsize // 8
                r = conf.raw_layer(load=s[i * fragsize:(i + 1) * fragsize])
                r.overload_fields = p[fnb].payload.overload_fields.copy()
                q.add_payload(r)
                lst.append(q)
        return lst


def in4_chksum(proto, u, p):
    """
    As Specified in RFC 2460 - 8.1 Upper-Layer Checksums

    Performs IPv4 Upper Layer checksum computation. Provided parameters are:
    - 'proto' : value of upper layer protocol
    - 'u'  : IP upper layer instance
    - 'p'  : the payload of the upper layer provided as a string
    """
    if not isinstance(u, IP):
        warning("No IP underlayer to compute checksum. Leaving null.")
        return 0
    if u.len is not None:
        if u.ihl is None:
            olen = sum(len(x) for x in u.options)
            ihl = 5 + olen // 4 + (1 if olen % 4 else 0)
        else:
            ihl = u.ihl
        ln = u.len - 4 * ihl
    else:
        ln = len(p)
    psdhdr = struct.pack("!4s4sHH",
                         inet_aton(u.src),
                         inet_aton(u.dst),
                         proto,
                         ln)
    return checksum(psdhdr + p)


class TCP(Packet):
    name = "TCP"
    fields_desc = [ShortEnumField("sport", 20, TCP_SERVICES),
                   ShortEnumField("dport", 80, TCP_SERVICES),
                   IntField("seq", 0),
                   IntField("ack", 0),
                   BitField("dataofs", None, 4),
                   BitField("reserved", 0, 3),
                   FlagsField("flags", 0x2, 9, "FSRPAUECN"),
                   ShortField("window", 8192),
                   XShortField("chksum", None),
                   ShortField("urgptr", 0),
                   TCPOptionsField("options", [])]

    def post_build(self, p, pay):
        p += pay
        dataofs = self.dataofs
        if dataofs is None:
            dataofs = 5 + ((len(self.get_field("options").i2m(self, self.options)) + 3) // 4)
            p = p[:12] + chb((dataofs << 4) | orb(p[12]) & 0x0f) + p[13:]
        if self.chksum is None:
            if isinstance(self.underlayer, IP):
                ck = in4_chksum(socket.IPPROTO_TCP, self.underlayer, p)
                p = p[:16] + struct.pack("!H", ck) + p[18:]
            elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr):
                ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p)
                p = p[:16] + struct.pack("!H", ck) + p[18:]
            else:
                warning("No IP underlayer to compute checksum. Leaving null.")
        return p

    def hashret(self):
        if conf.checkIPsrc:
            return struct.pack("H", self.sport ^ self.dport) + self.payload.hashret()
        else:
            return self.payload.hashret()

    def answers(self, other):
        if not isinstance(other, TCP):
            return 0
        # RST packets don't get answers
        if other.flags.R:
            return 0
        # We do not support the four-way handshakes with the SYN+ACK
        # answer split in two packets (one ACK and one SYN): in that
        # case the ACK will be seen as an answer, but not the SYN.
        if self.flags.S:
            # SYN packets without ACK are not answers
            if not self.flags.A:
                return 0
            # SYN+ACK packets answer SYN packets
            if not other.flags.S:
                return 0
        if conf.checkIPsrc:
            if not ((self.sport == other.dport) and
                    (self.dport == other.sport)):
                return 0
        # Do not check ack value for SYN packets without ACK
        if not (other.flags.S and not other.flags.A) \
           and abs(other.ack - self.seq) > 2:
            return 0
        # Do not check ack value for RST packets without ACK
        if self.flags.R and not self.flags.A:
            return 1
        if abs(other.seq - self.ack) > 2 + len(other.payload):
            return 0
        return 1

    def mysummary(self):
        if isinstance(self.underlayer, IP):
            return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%")
        elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6):
            return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%")
        else:
            return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%")


class UDP(Packet):
    name = "UDP"
    fields_desc = [ShortEnumField("sport", 53, UDP_SERVICES),
                   ShortEnumField("dport", 53, UDP_SERVICES),
                   ShortField("len", None),
                   XShortField("chksum", None), ]

    def post_build(self, p, pay):
        p += pay
        l = self.len
        if l is None:
            l = len(p)
            p = p[:4] + struct.pack("!H", l) + p[6:]
        if self.chksum is None:
            if isinstance(self.underlayer, IP):
                ck = in4_chksum(socket.IPPROTO_UDP, self.underlayer, p)
                # According to RFC768 if the result checksum is 0, it should be set to 0xFFFF
                if ck == 0:
                    ck = 0xFFFF
                p = p[:6] + struct.pack("!H", ck) + p[8:]
            elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr):
                ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p)
                # According to RFC2460 if the result checksum is 0, it should be set to 0xFFFF
                if ck == 0:
                    ck = 0xFFFF
                p = p[:6] + struct.pack("!H", ck) + p[8:]
            else:
                warning("No IP underlayer to compute checksum. Leaving null.")
        return p

    def extract_padding(self, s):
        l = self.len - 8
        return s[:l], s[l:]

    def hashret(self):
        return self.payload.hashret()

    def answers(self, other):
        if not isinstance(other, UDP):
            return 0
        if conf.checkIPsrc:
            if self.dport != other.sport:
                return 0
        return self.payload.answers(other.payload)

    def mysummary(self):
        if isinstance(self.underlayer, IP):
            return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%")
        elif isinstance(self.underlayer, scapy.layers.inet6.IPv6):
            return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%")
        else:
            return self.sprintf("UDP %UDP.sport% > %UDP.dport%")


icmptypes = {0: "echo-reply",
             3: "dest-unreach",
             4: "source-quench",
             5: "redirect",
             8: "echo-request",
             9: "router-advertisement",
             10: "router-solicitation",
             11: "time-exceeded",
             12: "parameter-problem",
             13: "timestamp-request",
             14: "timestamp-reply",
             15: "information-request",
             16: "information-response",
             17: "address-mask-request",
             18: "address-mask-reply"}

icmpcodes = {3: {0: "network-unreachable",
                    1: "host-unreachable",
                    2: "protocol-unreachable",
                    3: "port-unreachable",
                    4: "fragmentation-needed",
                    5: "source-route-failed",
                    6: "network-unknown",
                    7: "host-unknown",
                    9: "network-prohibited",
                    10: "host-prohibited",
                    11: "TOS-network-unreachable",
                    12: "TOS-host-unreachable",
                    13: "communication-prohibited",
                    14: "host-precedence-violation",
                    15: "precedence-cutoff", },
             5: {0: "network-redirect",
                 1: "host-redirect",
                 2: "TOS-network-redirect",
                 3: "TOS-host-redirect", },
             11: {0: "ttl-zero-during-transit",
                  1: "ttl-zero-during-reassembly", },
             12: {0: "ip-header-bad",
                  1: "required-option-missing", }, }


class ICMP(Packet):
    name = "ICMP"
    fields_desc = [ByteEnumField("type", 8, icmptypes),
                   MultiEnumField("code", 0, icmpcodes, depends_on=lambda pkt:pkt.type, fmt="B"),
                   XShortField("chksum", None),
                   ConditionalField(XShortField("id", 0), lambda pkt:pkt.type in [0, 8, 13, 14, 15, 16, 17, 18]),
                   ConditionalField(XShortField("seq", 0), lambda pkt:pkt.type in [0, 8, 13, 14, 15, 16, 17, 18]),
                   ConditionalField(ICMPTimeStampField("ts_ori", None), lambda pkt:pkt.type in [13, 14]),
                   ConditionalField(ICMPTimeStampField("ts_rx", None), lambda pkt:pkt.type in [13, 14]),
                   ConditionalField(ICMPTimeStampField("ts_tx", None), lambda pkt:pkt.type in [13, 14]),
                   ConditionalField(IPField("gw", "0.0.0.0"), lambda pkt:pkt.type == 5),
                   ConditionalField(ByteField("ptr", 0), lambda pkt:pkt.type == 12),
                   ConditionalField(ByteField("reserved", 0), lambda pkt:pkt.type in [3, 11]),
                   ConditionalField(ByteField("length", 0), lambda pkt:pkt.type in [3, 11, 12]),
                   ConditionalField(IPField("addr_mask", "0.0.0.0"), lambda pkt:pkt.type in [17, 18]),
                   ConditionalField(ShortField("nexthopmtu", 0), lambda pkt:pkt.type == 3),
                   ConditionalField(ShortField("unused", 0), lambda pkt:pkt.type in [11, 12]),
                   ConditionalField(IntField("unused", 0), lambda pkt:pkt.type not in [0, 3, 5, 8, 11, 12, 13, 14, 15, 16, 17, 18])
                   ]

    def post_build(self, p, pay):
        p += pay
        if self.chksum is None:
            ck = checksum(p)
            p = p[:2] + chb(ck >> 8) + chb(ck & 0xff) + p[4:]
        return p

    def hashret(self):
        if self.type in [0, 8, 13, 14, 15, 16, 17, 18]:
            return struct.pack("HH", self.id, self.seq) + self.payload.hashret()
        return self.payload.hashret()

    def answers(self, other):
        if not isinstance(other, ICMP):
            return 0
        if ((other.type, self.type) in [(8, 0), (13, 14), (15, 16), (17, 18)] and
            self.id == other.id and
                self.seq == other.seq):
            return 1
        return 0

    def guess_payload_class(self, payload):
        if self.type in [3, 4, 5, 11, 12]:
            return IPerror
        else:
            return None

    def mysummary(self):
        if isinstance(self.underlayer, IP):
            return self.underlayer.sprintf("ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%")
        else:
            return self.sprintf("ICMP %ICMP.type% %ICMP.code%")


class IPerror(IP):
    name = "IP in ICMP"

    def answers(self, other):
        if not isinstance(other, IP):
            return 0
        if not (((conf.checkIPsrc == 0) or (self.dst == other.dst)) and
                (self.src == other.src) and
                (((conf.checkIPID == 0)
                  or (self.id == other.id)
                  or (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and
                (self.proto == other.proto)):
            return 0
        return self.payload.answers(other.payload)

    def mysummary(self):
        return Packet.mysummary(self)


class TCPerror(TCP):
    name = "TCP in ICMP"

    def answers(self, other):
        if not isinstance(other, TCP):
            return 0
        if conf.checkIPsrc:
            if not ((self.sport == other.sport) and
                    (self.dport == other.dport)):
                return 0
        if conf.check_TCPerror_seqack:
            if self.seq is not None:
                if self.seq != other.seq:
                    return 0
            if self.ack is not None:
                if self.ack != other.ack:
                    return 0
        return 1

    def mysummary(self):
        return Packet.mysummary(self)


class UDPerror(UDP):
    name = "UDP in ICMP"

    def answers(self, other):
        if not isinstance(other, UDP):
            return 0
        if conf.checkIPsrc:
            if not ((self.sport == other.sport) and
                    (self.dport == other.dport)):
                return 0
        return 1

    def mysummary(self):
        return Packet.mysummary(self)


class ICMPerror(ICMP):
    name = "ICMP in ICMP"

    def answers(self, other):
        if not isinstance(other, ICMP):
            return 0
        if not ((self.type == other.type) and
                (self.code == other.code)):
            return 0
        if self.code in [0, 8, 13, 14, 17, 18]:
            if (self.id == other.id and
                    self.seq == other.seq):
                return 1
            else:
                return 0
        else:
            return 1

    def mysummary(self):
        return Packet.mysummary(self)


bind_layers(Ether, IP, type=2048)
bind_layers(CookedLinux, IP, proto=2048)
bind_layers(GRE, IP, proto=2048)
bind_layers(SNAP, IP, code=2048)
bind_layers(Loopback, IP, type=0)
bind_layers(Loopback, IP, type=2)
bind_layers(IPerror, IPerror, frag=0, proto=4)
bind_layers(IPerror, ICMPerror, frag=0, proto=1)
bind_layers(IPerror, TCPerror, frag=0, proto=6)
bind_layers(IPerror, UDPerror, frag=0, proto=17)
bind_layers(IP, IP, frag=0, proto=4)
bind_layers(IP, ICMP, frag=0, proto=1)
bind_layers(IP, TCP, frag=0, proto=6)
bind_layers(IP, UDP, frag=0, proto=17)
bind_layers(IP, GRE, frag=0, proto=47)

conf.l2types.register(DLT_RAW, IP)
conf.l2types.register_num2layer(DLT_RAW_ALT, IP)
conf.l2types.register(DLT_IPV4, IP)

conf.l3types.register(ETH_P_IP, IP)
conf.l3types.register_num2layer(ETH_P_ALL, IP)


def inet_register_l3(l2, l3):
    return getmacbyip(l3.dst)


conf.neighbor.register_l3(Ether, IP, inet_register_l3)
conf.neighbor.register_l3(Dot3, IP, inet_register_l3)


###################
#  Fragmentation  #
###################

@conf.commands.register
def fragment(pkt, fragsize=1480):
    """Fragment a big IP datagram"""
    fragsize = (fragsize + 7) // 8 * 8
    lst = []
    for p in pkt:
        s = raw(p[IP].payload)
        nb = (len(s) + fragsize - 1) // fragsize
        for i in range(nb):
            q = p.copy()
            del(q[IP].payload)
            del(q[IP].chksum)
            del(q[IP].len)
            if i != nb - 1:
                q[IP].flags |= 1
            q[IP].frag += i * fragsize // 8
            r = conf.raw_layer(load=s[i * fragsize:(i + 1) * fragsize])
            r.overload_fields = p[IP].payload.overload_fields.copy()
            q.add_payload(r)
            lst.append(q)
    return lst


@conf.commands.register
def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None):
    """Build overlapping fragments to bypass NIPS

p:                the original packet
overlap:          the overlapping data
fragsize:         the fragment size of the packet
overlap_fragsize: the fragment size of the overlapping packet"""

    if overlap_fragsize is None:
        overlap_fragsize = fragsize
    q = p.copy()
    del(q[IP].payload)
    q[IP].add_payload(overlap)

    qfrag = fragment(q, overlap_fragsize)
    qfrag[-1][IP].flags |= 1
    return qfrag + fragment(p, fragsize)


@conf.commands.register
def defrag(plist):
    """defrag(plist) -> ([not fragmented], [defragmented],
                  [ [bad fragments], [bad fragments], ... ])"""
    frags = defaultdict(PacketList)
    nofrag = PacketList()
    for p in plist:
        if IP not in p:
            nofrag.append(p)
            continue
        ip = p[IP]
        if ip.frag == 0 and ip.flags & 1 == 0:
            nofrag.append(p)
            continue
        uniq = (ip.id, ip.src, ip.dst, ip.proto)
        frags[uniq].append(p)
    defrag = []
    missfrag = []
    for lst in six.itervalues(frags):
        lst.sort(key=lambda x: x.frag)
        p = lst[0]
        lastp = lst[-1]
        if p.frag > 0 or lastp.flags & 1 != 0:  # first or last fragment missing
            missfrag.append(lst)
            continue
        p = p.copy()
        if conf.padding_layer in p:
            del(p[conf.padding_layer].underlayer.payload)
        ip = p[IP]
        if ip.len is None or ip.ihl is None:
            clen = len(ip.payload)
        else:
            clen = ip.len - (ip.ihl << 2)
        txt = conf.raw_layer()
        for q in lst[1:]:
            if clen != q.frag << 3:  # Wrong fragmentation offset
                if clen > q.frag << 3:
                    warning("Fragment overlap (%i > %i) %r || %r ||  %r" % (clen, q.frag << 3, p, txt, q))
                missfrag.append(lst)
                break
            if q[IP].len is None or q[IP].ihl is None:
                clen += len(q[IP].payload)
            else:
                clen += q[IP].len - (q[IP].ihl << 2)
            if conf.padding_layer in q:
                del(q[conf.padding_layer].underlayer.payload)
            txt.add_payload(q[IP].payload.copy())
        else:
            ip.flags &= ~1  # !MF
            del(ip.chksum)
            del(ip.len)
            p = p / txt
            defrag.append(p)
    defrag2 = PacketList()
    for p in defrag:
        defrag2.append(p.__class__(raw(p)))
    return nofrag, defrag2, missfrag


@conf.commands.register
def defragment(plist):
    """defrag(plist) -> plist defragmented as much as possible """
    frags = defaultdict(lambda: [])
    final = []

    pos = 0
    for p in plist:
        p._defrag_pos = pos
        pos += 1
        if IP in p:
            ip = p[IP]
            if ip.frag != 0 or ip.flags & 1:
                ip = p[IP]
                uniq = (ip.id, ip.src, ip.dst, ip.proto)
                frags[uniq].append(p)
                continue
        final.append(p)

    defrag = []
    missfrag = []
    for lst in six.itervalues(frags):
        lst.sort(key=lambda x: x.frag)
        p = lst[0]
        lastp = lst[-1]
        if p.frag > 0 or lastp.flags & 1 != 0:  # first or last fragment missing
            missfrag += lst
            continue
        p = p.copy()
        if conf.padding_layer in p:
            del(p[conf.padding_layer].underlayer.payload)
        ip = p[IP]
        if ip.len is None or ip.ihl is None:
            clen = len(ip.payload)
        else:
            clen = ip.len - (ip.ihl << 2)
        txt = conf.raw_layer()
        for q in lst[1:]:
            if clen != q.frag << 3:  # Wrong fragmentation offset
                if clen > q.frag << 3:
                    warning("Fragment overlap (%i > %i) %r || %r ||  %r" % (clen, q.frag << 3, p, txt, q))
                missfrag += lst
                break
            if q[IP].len is None or q[IP].ihl is None:
                clen += len(q[IP].payload)
            else:
                clen += q[IP].len - (q[IP].ihl << 2)
            if conf.padding_layer in q:
                del(q[conf.padding_layer].underlayer.payload)
            txt.add_payload(q[IP].payload.copy())
        else:
            ip.flags &= ~1  # !MF
            del(ip.chksum)
            del(ip.len)
            p = p / txt
            p._defrag_pos = max(x._defrag_pos for x in lst)
            defrag.append(p)
    defrag2 = []
    for p in defrag:
        q = p.__class__(raw(p))
        q._defrag_pos = p._defrag_pos
        defrag2.append(q)
    final += defrag2
    final += missfrag
    final.sort(key=lambda x: x._defrag_pos)
    for p in final:
        del(p._defrag_pos)

    if hasattr(plist, "listname"):
        name = "Defragmented %s" % plist.listname
    else:
        name = "Defragmented"

    return PacketList(final, name=name)


# Add timeskew_graph() method to PacketList
def _packetlist_timeskew_graph(self, ip, **kargs):
    """Tries to graph the timeskew between the timestamps and real time for a given ip"""

    # Filter TCP segments which source address is 'ip'
    tmp = (self._elt2pkt(x) for x in self.res)
    b = (x for x in tmp if IP in x and x[IP].src == ip and TCP in x)

    # Build a list of tuples (creation_time, replied_timestamp)
    c = []
    tsf = ICMPTimeStampField("", None)
    for p in b:
        opts = p.getlayer(TCP).options
        for o in opts:
            if o[0] == "Timestamp":
                c.append((p.time, tsf.any2i("", o[1][0])))

    # Stop if the list is empty
    if not c:
        warning("No timestamps found in packet list")
        return []

    # Prepare the data that will be plotted
    first_creation_time = c[0][0]
    first_replied_timestamp = c[0][1]

    def _wrap_data(ts_tuple, wrap_seconds=2000):
        """Wrap the list of tuples."""

        ct, rt = ts_tuple  # (creation_time, replied_timestamp)
        X = ct % wrap_seconds
        Y = ((ct - first_creation_time) - ((rt - first_replied_timestamp) / 1000.0))

        return X, Y

    data = [_wrap_data(e) for e in c]

    # Mimic the default gnuplot output
    if kargs == {}:
        kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS
    lines = plt.plot(data, **kargs)

    # Call show() if matplotlib is not inlined
    if not MATPLOTLIB_INLINED:
        plt.show()

    return lines


PacketList.timeskew_graph = _packetlist_timeskew_graph


# Create a new packet list
class TracerouteResult(SndRcvList):
    __slots__ = ["graphdef", "graphpadding", "graphASres", "padding", "hloc",
                 "nloc"]

    def __init__(self, res=None, name="Traceroute", stats=None):
        PacketList.__init__(self, res, name, stats)
        self.graphdef = None
        self.graphASres = 0
        self.padding = 0
        self.hloc = None
        self.nloc = None

    def show(self):
        return self.make_table(lambda s, r: (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"),
                                             s.ttl,
                                             r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}")))

    def get_trace(self):
        trace = {}
        for s, r in self.res:
            if IP not in s:
                continue
            d = s[IP].dst
            if d not in trace:
                trace[d] = {}
            trace[d][s[IP].ttl] = r[IP].src, ICMP not in r
        for k in six.itervalues(trace):
            try:
                m = min(x for x, y in six.itervalues(k) if y)
            except ValueError:
                continue
            for l in list(k):  # use list(): k is modified in the loop
                if l > m:
                    del k[l]
        return trace

    def trace3D(self):
        """Give a 3D representation of the traceroute.
        right button: rotate the scene
        middle button: zoom
        left button: move the scene
        left button on a ball: toggle IP displaying
        ctrl-left button on a ball: scan ports 21,22,23,25,80 and 443 and display the result"""
        trace = self.get_trace()
        import visual

        class IPsphere(visual.sphere):
            def __init__(self, ip, **kargs):
                visual.sphere.__init__(self, **kargs)
                self.ip = ip
                self.label = None
                self.setlabel(self.ip)

            def setlabel(self, txt, visible=None):
                if self.label is not None:
                    if visible is None:
                        visible = self.label.visible
                    self.label.visible = 0
                elif visible is None:
                    visible = 0
                self.label = visual.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible)

            def action(self):
                self.label.visible ^= 1

        visual.scene = visual.display()
        visual.scene.exit = True
        start = visual.box()
        rings = {}
        tr3d = {}
        for i in trace:
            tr = trace[i]
            tr3d[i] = []
            for t in range(1, max(tr) + 1):
                if t not in rings:
                    rings[t] = []
                if t in tr:
                    if tr[t] not in rings[t]:
                        rings[t].append(tr[t])
                    tr3d[i].append(rings[t].index(tr[t]))
                else:
                    rings[t].append(("unk", -1))
                    tr3d[i].append(len(rings[t]) - 1)
        for t in rings:
            r = rings[t]
            l = len(r)
            for i in range(l):
                if r[i][1] == -1:
                    col = (0.75, 0.75, 0.75)
                elif r[i][1]:
                    col = visual.color.green
                else:
                    col = visual.color.blue

                s = IPsphere(pos=((l - 1) * visual.cos(2 * i * visual.pi / l), (l - 1) * visual.sin(2 * i * visual.pi / l), 2 * t),
                             ip=r[i][0],
                             color=col)
                for trlst in six.itervalues(tr3d):
                    if t <= len(trlst):
                        if trlst[t - 1] == i:
                            trlst[t - 1] = s
        forecol = colgen(0.625, 0.4375, 0.25, 0.125)
        for trlst in six.itervalues(tr3d):
            col = next(forecol)
            start = (0, 0, 0)
            for ip in trlst:
                visual.cylinder(pos=start, axis=ip.pos - start, color=col, radius=0.2)
                start = ip.pos

        movcenter = None
        while True:
            visual.rate(50)
            if visual.scene.kb.keys:
                k = visual.scene.kb.getkey()
                if k == "esc" or k == "q":
                    break
            if visual.scene.mouse.events:
                ev = visual.scene.mouse.getevent()
                if ev.press == "left":
                    o = ev.pick
                    if o:
                        if ev.ctrl:
                            if o.ip == "unk":
                                continue
                            savcolor = o.color
                            o.color = (1, 0, 0)
                            a, b = sr(IP(dst=o.ip) / TCP(dport=[21, 22, 23, 25, 80, 443]), timeout=2)
                            o.color = savcolor
                            if len(a) == 0:
                                txt = "%s:\nno results" % o.ip
                            else:
                                txt = "%s:\n" % o.ip
                                for s, r in a:
                                    txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n")
                            o.setlabel(txt, visible=1)
                        else:
                            if hasattr(o, "action"):
                                o.action()
                elif ev.drag == "left":
                    movcenter = ev.pos
                elif ev.drop == "left":
                    movcenter = None
            if movcenter:
                visual.scene.center -= visual.scene.mouse.pos - movcenter
                movcenter = visual.scene.mouse.pos

    def world_trace(self):
        """Display traceroute results on a world map."""

        # Check that the geoip2 module can be imported
        # Doc: http://geoip2.readthedocs.io/en/latest/
        try:
            # GeoIP2 modules need to be imported as below
            import geoip2.database
            import geoip2.errors
        except ImportError:
            warning("Can't import geoip2. Won't be able to plot the world.")
            return []
        # Check availability of database
        if not conf.geoip_city:
            warning("Cannot import the geolite2 CITY database !\n"
                    "Download it from http://dev.maxmind.com/geoip/geoip2/geolite2/"
                    "then set its path to conf.geoip_city")
            return []
        # Check availability of plotting devices
        try:
            import cartopy.crs as ccrs
        except ImportError:
            warning("Cannot import cartopy.\n"
                    "More infos on http://scitools.org.uk/cartopy/docs/latest/installing.html")
            return []
        if not MATPLOTLIB:
            warning("Matplotlib is not installed. Won't be able to plot the world.")
            return []

        # Open & read the GeoListIP2 database
        try:
            db = geoip2.database.Reader(conf.geoip_city)
        except:
            warning("Can't open geoip2 database at %s", conf.geoip_city)
            return []

        # Regroup results per trace
        ips = {}
        rt = {}
        ports_done = {}
        for s, r in self.res:
            ips[r.src] = None
            if s.haslayer(TCP) or s.haslayer(UDP):
                trace_id = (s.src, s.dst, s.proto, s.dport)
            elif s.haslayer(ICMP):
                trace_id = (s.src, s.dst, s.proto, s.type)
            else:
                trace_id = (s.src, s.dst, s.proto, 0)
            trace = rt.get(trace_id, {})
            if not r.haslayer(ICMP) or r.type != 11:
                if trace_id in ports_done:
                    continue
                ports_done[trace_id] = None
            trace[s.ttl] = r.src
            rt[trace_id] = trace

        # Get the addresses locations
        trt = {}
        for trace_id in rt:
            trace = rt[trace_id]
            loctrace = []
            for i in range(max(trace)):
                ip = trace.get(i, None)
                if ip is None:
                    continue
                # Fetch database
                try:
                    sresult = db.city(ip)
                except geoip2.errors.AddressNotFoundError:
                    continue
                loctrace.append((sresult.location.longitude, sresult.location.latitude))
            if loctrace:
                trt[trace_id] = loctrace

        # Load the map renderer
        plt.figure(num='Scapy')
        ax = plt.axes(projection=ccrs.PlateCarree())
        # Draw countries
        ax.coastlines()
        ax.stock_img()
        # Set normal size
        ax.set_global()
        # Add title
        plt.title("Scapy traceroute results")

        from matplotlib.collections import LineCollection
        from matplotlib import colors as mcolors
        colors_cycle = iter(mcolors.BASE_COLORS)
        lines = []

        # Split traceroute measurement
        for key, trc in six.iteritems(trt):
            # Get next color
            color = next(colors_cycle)
            # Gather mesurments data
            data_lines = [(trc[i], trc[i + 1]) for i in range(len(trc) - 1)]
            # Create line collection
            line_col = LineCollection(data_lines, linewidths=2,
                                      label=key[1],
                                      color=color)
            lines.append(line_col)
            ax.add_collection(line_col)
            # Create map points
            lines.extend([ax.plot(*x, marker='.', color=color) for x in trc])

        # Generate legend
        ax.legend()

        # Call show() if matplotlib is not inlined
        if not MATPLOTLIB_INLINED:
            plt.show()

        # Clean
        ax.remove()

        # Return the drawn lines
        return lines

    def make_graph(self, ASres=None, padding=0):
        if ASres is None:
            ASres = conf.AS_resolver
        self.graphASres = ASres
        self.graphpadding = padding
        ips = {}
        rt = {}
        ports = {}
        ports_done = {}
        for s, r in self.res:
            r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r
            s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s
            ips[r.src] = None
            if TCP in s:
                trace_id = (s.src, s.dst, 6, s.dport)
            elif UDP in s:
                trace_id = (s.src, s.dst, 17, s.dport)
            elif ICMP in s:
                trace_id = (s.src, s.dst, 1, s.type)
            else:
                trace_id = (s.src, s.dst, s.proto, 0)
            trace = rt.get(trace_id, {})
            ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl
            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):
                if trace_id in ports_done:
                    continue
                ports_done[trace_id] = None
                p = ports.get(r.src, [])
                if TCP in r:
                    p.append(r.sprintf("<T%ir,TCP.sport%> %TCP.sport% %TCP.flags%"))
                    trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%')
                elif UDP in r:
                    p.append(r.sprintf("<U%ir,UDP.sport%> %UDP.sport%"))
                    trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%')
                elif ICMP in r:
                    p.append(r.sprintf("<I%ir,ICMP.type%> ICMP %ICMP.type%"))
                    trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%')
                else:
                    p.append(r.sprintf("{IP:<P%ir,proto%> IP %proto%}{IPv6:<P%ir,nh%> IPv6 %nh%}"))
                    trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}')
                ports[r.src] = p
            else:
                trace[ttl] = r.sprintf('"%r,src%"')
            rt[trace_id] = trace

        # Fill holes with unk%i nodes
        unknown_label = incremental_label("unk%i")
        blackholes = []
        bhip = {}
        for rtk in rt:
            trace = rt[rtk]
            max_trace = max(trace)
            for n in range(min(trace), max_trace):
                if n not in trace:
                    trace[n] = next(unknown_label)
            if rtk not in ports_done:
                if rtk[2] == 1:  # ICMP
                    bh = "%s %i/icmp" % (rtk[1], rtk[3])
                elif rtk[2] == 6:  # TCP
                    bh = "%s %i/tcp" % (rtk[1], rtk[3])
                elif rtk[2] == 17:  # UDP
                    bh = '%s %i/udp' % (rtk[1], rtk[3])
                else:
                    bh = '%s %i/proto' % (rtk[1], rtk[2])
                ips[bh] = None
                bhip[rtk[1]] = bh
                bh = '"%s"' % bh
                trace[max_trace + 1] = bh
                blackholes.append(bh)

        # Find AS numbers
        ASN_query_list = set(x.rsplit(" ", 1)[0] for x in ips)
        if ASres is None:
            ASNlist = []
        else:
            ASNlist = ASres.resolve(*ASN_query_list)

        ASNs = {}
        ASDs = {}
        for ip, asn, desc, in ASNlist:
            if asn is None:
                continue
            iplist = ASNs.get(asn, [])
            if ip in bhip:
                if ip in ports:
                    iplist.append(ip)
                iplist.append(bhip[ip])
            else:
                iplist.append(ip)
            ASNs[asn] = iplist
            ASDs[asn] = desc

        backcolorlist = colgen("60", "86", "ba", "ff")
        forecolorlist = colgen("a0", "70", "40", "20")

        s = "digraph trace {\n"

        s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n"

        s += "\n#ASN clustering\n"
        for asn in ASNs:
            s += '\tsubgraph cluster_%s {\n' % asn
            col = next(backcolorlist)
            s += '\t\tcolor="#%s%s%s";' % col
            s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col
            s += '\t\tfontsize = 10;'
            s += '\t\tlabel = "%s\\n[%s]"\n' % (asn, ASDs[asn])
            for ip in ASNs[asn]:

                s += '\t\t"%s";\n' % ip
            s += "\t}\n"

        s += "#endpoints\n"
        for p in ports:
            s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p, p, "|".join(ports[p]))

        s += "\n#Blackholes\n"
        for bh in blackholes:
            s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh

        if padding:
            s += "\n#Padding\n"
            pad = {}
            for snd, rcv in self.res:
                if rcv.src not in ports and rcv.haslayer(conf.padding_layer):
                    p = rcv.getlayer(conf.padding_layer).load
                    if p != b"\x00" * len(p):
                        pad[rcv.src] = None
            for rcv in pad:
                s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv

        s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n"

        for rtk in rt:
            s += "#---[%s\n" % repr(rtk)
            s += '\t\tedge [color="#%s%s%s"];\n' % next(forecolorlist)
            trace = rt[rtk]
            maxtrace = max(trace)
            for n in range(min(trace), maxtrace):
                s += '\t%s ->\n' % trace[n]
            s += '\t%s;\n' % trace[maxtrace]

        s += "}\n"
        self.graphdef = s

    def graph(self, ASres=None, padding=0, **kargs):
        """x.graph(ASres=conf.AS_resolver, other args):
        ASres=None          : no AS resolver => no clustering
        ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net)
        ASres=AS_resolver_cymru(): use whois.cymru.com whois database
        ASres=AS_resolver(server="whois.ra.net")
        type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option
        target: filename or redirect. Defaults pipe to Imagemagick's display program
        prog: which graphviz program to use"""
        if ASres is None:
            ASres = conf.AS_resolver
        if (self.graphdef is None or
            self.graphASres != ASres or
                self.graphpadding != padding):
            self.make_graph(ASres, padding)

        return do_graph(self.graphdef, **kargs)


@conf.commands.register
def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4=None, filter=None, timeout=2, verbose=None, **kargs):
    """Instant TCP traceroute
traceroute(target, [maxttl=30,] [dport=80,] [sport=80,] [verbose=conf.verb]) -> None
"""
    if verbose is None:
        verbose = conf.verb
    if filter is None:
        # we only consider ICMP error packets and TCP packets with at
        # least the ACK flag set *and* either the SYN or the RST flag
        # set
        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))"
    if l4 is None:
        a, b = sr(IP(dst=target, id=RandShort(), ttl=(minttl, maxttl)) / TCP(seq=RandInt(), sport=sport, dport=dport),
                  timeout=timeout, filter=filter, verbose=verbose, **kargs)
    else:
        # this should always work
        filter = "ip"
        a, b = sr(IP(dst=target, id=RandShort(), ttl=(minttl, maxttl)) / l4,
                  timeout=timeout, filter=filter, verbose=verbose, **kargs)

    a = TracerouteResult(a.res)
    if verbose:
        a.show()
    return a, b


@conf.commands.register
def traceroute_map(*args, **kargs):
    """Util function to call traceroute on multiple targets, then
    show the different paths on a map.
    params:
     - *args: IPs on which traceroute will be called"""
    kargs.setdefault("verbose", 0)
    res = []
    for target in args:
        res += traceroute(target, **kargs)[0].res
    return TracerouteResult(res).world_trace()

#############################
#  Simple TCP client stack  #
#############################


class TCP_client(Automaton):

    def parse_args(self, ip, port, *args, **kargs):
        self.dst = str(Net(ip))
        self.dport = port
        self.sport = random.randrange(0, 2**16)
        self.l4 = IP(dst=ip) / TCP(sport=self.sport, dport=self.dport, flags=0,
                                   seq=random.randrange(0, 2**32))
        self.src = self.l4.src
        self.swin = self.l4[TCP].window
        self.dwin = 1
        self.rcvbuf = b""
        bpf = "host %s  and host %s and port %i and port %i" % (self.src,
                                                                self.dst,
                                                                self.sport,
                                                                self.dport)

#        bpf=None
        Automaton.parse_args(self, filter=bpf, **kargs)

    def master_filter(self, pkt):
        return (IP in pkt and
                pkt[IP].src == self.dst and
                pkt[IP].dst == self.src and
                TCP in pkt and
                pkt[TCP].sport == self.dport and
                pkt[TCP].dport == self.sport and
                self.l4[TCP].seq >= pkt[TCP].ack and  # XXX: seq/ack 2^32 wrap up
                ((self.l4[TCP].ack == 0) or (self.l4[TCP].ack <= pkt[TCP].seq <= self.l4[TCP].ack + self.swin)))

    @ATMT.state(initial=1)
    def START(self):
        pass

    @ATMT.state()
    def SYN_SENT(self):
        pass

    @ATMT.state()
    def ESTABLISHED(self):
        pass

    @ATMT.state()
    def LAST_ACK(self):
        pass

    @ATMT.state(final=1)
    def CLOSED(self):
        pass

    @ATMT.condition(START)
    def connect(self):
        raise self.SYN_SENT()

    @ATMT.action(connect)
    def send_syn(self):
        self.l4[TCP].flags = "S"
        self.send(self.l4)
        self.l4[TCP].seq += 1

    @ATMT.receive_condition(SYN_SENT)
    def synack_received(self, pkt):
        if pkt[TCP].flags & 0x3f == 0x12:
            raise self.ESTABLISHED().action_parameters(pkt)

    @ATMT.action(synack_received)
    def send_ack_of_synack(self, pkt):
        self.l4[TCP].ack = pkt[TCP].seq + 1
        self.l4[TCP].flags = "A"
        self.send(self.l4)

    @ATMT.receive_condition(ESTABLISHED)
    def incoming_data_received(self, pkt):
        if not isinstance(pkt[TCP].payload, NoPayload) and not isinstance(pkt[TCP].payload, conf.padding_layer):
            raise self.ESTABLISHED().action_parameters(pkt)

    @ATMT.action(incoming_data_received)
    def receive_data(self, pkt):
        data = raw(pkt[TCP].payload)
        if data and self.l4[TCP].ack == pkt[TCP].seq:
            self.l4[TCP].ack += len(data)
            self.l4[TCP].flags = "A"
            self.send(self.l4)
            self.rcvbuf += data
            if pkt[TCP].flags.P:
                self.oi.tcp.send(self.rcvbuf)
                self.rcvbuf = b""

    @ATMT.ioevent(ESTABLISHED, name="tcp", as_supersocket="tcplink")
    def outgoing_data_received(self, fd):
        raise self.ESTABLISHED().action_parameters(fd.recv())

    @ATMT.action(outgoing_data_received)
    def send_data(self, d):
        self.l4[TCP].flags = "PA"
        self.send(self.l4 / d)
        self.l4[TCP].seq += len(d)

    @ATMT.receive_condition(ESTABLISHED)
    def reset_received(self, pkt):
        if pkt[TCP].flags & 4 != 0:
            raise self.CLOSED()

    @ATMT.receive_condition(ESTABLISHED)
    def fin_received(self, pkt):
        if pkt[TCP].flags & 0x1 == 1:
            raise self.LAST_ACK().action_parameters(pkt)

    @ATMT.action(fin_received)
    def send_finack(self, pkt):
        self.l4[TCP].flags = "FA"
        self.l4[TCP].ack = pkt[TCP].seq + 1
        self.send(self.l4)
        self.l4[TCP].seq += 1

    @ATMT.receive_condition(LAST_ACK)
    def ack_of_fin_received(self, pkt):
        if pkt[TCP].flags & 0x3f == 0x10:
            raise self.CLOSED()


#####################
#  Reporting stuff  #
#####################


@conf.commands.register
def report_ports(target, ports):
    """portscan a target and output a LaTeX table
report_ports(target, ports) -> string"""
    ans, unans = sr(IP(dst=target) / TCP(dport=ports), timeout=5)
    rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n"
    for s, r in ans:
        if not r.haslayer(ICMP):
            if r.payload.flags == 0x12:
                rep += r.sprintf("%TCP.sport% & open & SA \\\\\n")
    rep += "\\hline\n"
    for s, r in ans:
        if r.haslayer(ICMP):
            rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n")
        elif r.payload.flags != 0x12:
            rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n")
    rep += "\\hline\n"
    for i in unans:
        rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n")
    rep += "\\hline\n\\end{tabular}\n"
    return rep


@conf.commands.register
def IPID_count(lst, funcID=lambda x: x[1].id, funcpres=lambda x: x[1].summary()):
    """Identify IP id values classes in a list of packets

lst:      a list of packets
funcID:   a function that returns IP id values
funcpres: a function used to summarize packets"""
    idlst = [funcID(e) for e in lst]
    idlst.sort()
    classes = [idlst[0]]
    classes += [t[1] for t in zip(idlst[:-1], idlst[1:]) if abs(t[0] - t[1]) > 50]
    lst = [(funcID(x), funcpres(x)) for x in lst]
    lst.sort()
    print("Probably %i classes:" % len(classes), classes)
    for id, pr in lst:
        print("%5i" % id, pr)


@conf.commands.register
def fragleak(target, sport=123, dport=123, timeout=0.2, onlyasc=0):
    load = "XXXXYYYYYYYYYY"
#    getmacbyip(target)
#    pkt = IP(dst=target, id=RandShort(), options=b"\x22"*40)/UDP()/load
    pkt = IP(dst=target, id=RandShort(), options=b"\x00" * 40, flags=1) / UDP(sport=sport, dport=sport) / load
    s = conf.L3socket()
    intr = 0
    found = {}
    try:
        while True:
            try:
                if not intr:
                    s.send(pkt)
                sin, sout, serr = select([s], [], [], timeout)
                if not sin:
                    continue
                ans = s.recv(1600)
                if not isinstance(ans, IP):  # TODO: IPv6
                    continue
                if not isinstance(ans.payload, ICMP):
                    continue
                if not isinstance(ans.payload.payload, IPerror):
                    continue
                if ans.payload.payload.dst != target:
                    continue
                if ans.src != target:
                    print("leak from", ans.src, end=' ')


#                print repr(ans)
                if not ans.haslayer(conf.padding_layer):
                    continue


#                print repr(ans.payload.payload.payload.payload)

#                if not isinstance(ans.payload.payload.payload.payload, conf.raw_layer):
#                    continue
#                leak = ans.payload.payload.payload.payload.load[len(load):]
                leak = ans.getlayer(conf.padding_layer).load
                if leak not in found:
                    found[leak] = None
                    linehexdump(leak, onlyasc=onlyasc)
            except KeyboardInterrupt:
                if intr:
                    raise
                intr = 1
    except KeyboardInterrupt:
        pass


@conf.commands.register
def fragleak2(target, timeout=0.4, onlyasc=0):
    found = {}
    try:
        while True:
            p = sr1(IP(dst=target, options=b"\x00" * 40, proto=200) / "XXXXYYYYYYYYYYYY", timeout=timeout, verbose=0)
            if not p:
                continue
            if conf.padding_layer in p:
                leak = p[conf.padding_layer].load
                if leak not in found:
                    found[leak] = None
                    linehexdump(leak, onlyasc=onlyasc)
    except:
        pass


conf.stats_classic_protocols += [TCP, UDP, ICMP]
conf.stats_dot11_protocols += [TCP, UDP, ICMP]

if conf.ipv6_enabled:
    import scapy.layers.inet6

Are the changes to `inet.py` necessary?