docs: def mtr(target, dport=80, minttl=1, maxttl=30, stype="Random", srcport=50000, iface=None, l4=None, filter=None, timeout=2, verbose=None, gw=None, netproto="TCP", nquery=1, ptype=None, payload=b'', privaddr=0, rasn=1, **kargs):
      """A Multi-Traceroute (mtr) command:
           mtr(target, [maxttl=30,] [dport=80,] [sport=80,] [minttl=1,] [maxttl=1,] [iface=None]
               [l4=None,] [filter=None,] [nquery=1,] [privaddr=0,] [rasn=1,] [verbose=conf.verb])
  
                stype: Source Port Type: "Random" or "Increment".
              srcport: Source Port. Default: 50000.
                   gw: IPv4 Address of the Default Gateway.
             netproto: Network Protocol (One of: "TCP", "UDP" or "ICMP").
               nquery: Number of Traceroute queries to perform.
                ptype: Payload Type: "Disable", "RandStr", "RandStrTerm" or "Custom".
              payload: A byte object for each packet payload (e.g., b'\x01A\x0f\xff\x00') for ptype: 'Custom'.
             privaddr: 0 - Default: Normal display of all resolved AS numbers.
                       1 - Do not show an associated AS Number bound box (cluster) on graph for a private IPv4 Address.
                 rasn: 0 - Do not resolve AS Numbers - No graph clustering.
                       1 - Default: Resolve all AS numbers."""
      #
      # Initialize vars...
      trace = []			# Individual trace array
      #
      # Range check number of query traces
      if nquery < 1:
          nquery = 1
      #
      # Create instance of an MTR class...
      mtrc = MTR(nquery=nquery, target=target)
      #
      # Default to network protocol: "TCP" if not found in list...
      plist = ["TCP", "UDP", "ICMP"]
      netproto = netproto.upper()
      if netproto not in plist:
          netproto = "TCP"
      mtrc._netprotocol = netproto
      #
      # Default to source type: "Random" if not found in list...
      slist = ["Random", "Increment"]
      stype = stype.title()
      if stype not in slist:
          stype = "Random"
      if stype == "Random":
          sport = RandShort()  # Random
      elif stype == "Increment":
          if srcport != None:
              sport = IncrementalValue(start=(srcport - 1), step=1, restart=65535)  # Increment
      #
      # Default to payload type to it's default network protocol value if not found in list...
      pllist = ["Disabled", "RandStr", "RandStrTerm", "Custom"]
      if ptype is None or (not ptype in pllist):
          if netproto == "ICMP":
              ptype = "RandStr"		# ICMP: A random string payload to fill out the minimum packet size
          elif netproto == "UDP":
              ptype = "RandStrTerm"  # UDP: A random string terminated payload to fill out the minimum packet size
          elif netproto == "TCP":
              ptype = "Disabled"		# TCP: Disabled -> The minimum packet size satisfied - no payload required
      #
      # Set trace interface...
      if not iface is None:
          mtrc._iface = iface
      else:
          mtrc._iface = conf.iface
      #
      # Set Default Gateway...
      if not gw is None:
          mtrc._gw = gw
      #
      # Set default verbosity if no override...
      if verbose is None:
          verbose = conf.verb
      #
      # Only consider ICMP error packets and TCP packets with at
      # least the ACK flag set *and* either the SYN or the RST flag set...
      filterundefined = False
      if filter is None:
          filterundefined = True
          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))"
      #
      # Resolve and expand each target...
      ntraces = 0		# Total trace count
      exptrg = []		# Expanded targets
      for t in target:
          #
          # Use kamene's 'Net' function to expand target...
          et = [ip for ip in iter(Net(t))]
          exptrg.extend(et)
          #
          # Map Host Names to IP Addresses and store...
          if t in mtrc._host2ip:
              mtrc._host2ip[t].extend(et)
          else:
              mtrc._host2ip[t] = et
          #
          # Map IP Addresses to Host Names and store...
          for a in et:
              mtrc._ip2host[a] = t
      #
      # Store resolved and expanded targets...
      mtrc._exptrg = exptrg
      #
      # Traceroute each expanded target value...
      if l4 is None:
          #
          # Standard Layer: 3 ('TCP', 'UDP' or 'ICMP') tracing...
          for n in range(0, nquery):
              for t in exptrg:
                  #
                  # Execute a traceroute based on network protocol setting...
                  if netproto == "ICMP":
                      #
                      # MTR Network Protocol: 'ICMP'
                      tid = 8				        # Use a 'Type: 8 - Echo Request' packet for the trace:
                      id = 0x8888					# MTR ICMP identifier: '0x8888'
                      seq = IncrementalValue(start=(minttl - 2), step=1, restart=-10)  # Use a Sequence number in step with TTL value
                      if filterundefined:
                          #
                          # Update Filter -> Allow for ICMP echo-request (8) and ICMP echo-reply (0) packet to be processed...
                          filter = "(icmp and (icmp[0]=8 or icmp[0]=0 or icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12))"
                      #
                      # Check payload types:
                      if ptype == 'Disabled':
                          a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / ICMP(type=tid, id=id, seq=seq),
                                    timeout=timeout, filter=filter, verbose=verbose, **kargs)
                      else:
                          if ptype == 'RandStr':
                              #
                              # Use a random payload string to full out a minimum size PDU of 46 bytes for each ICMP packet:
                              # Length of 'IP()/ICMP()' = 28, Minimum Protocol Data Unit (PDU) is = 46 -> Therefore a
                              # payload of 18 octets is required.
                              pload = RandString(size=18)
                          elif ptype == 'RandStrTerm':
                              pload = RandStringTerm(size=17, term=b'\n')  # Random string terminated
                          elif ptype == 'Custom':
                              pload = payload
                          #
                          # ICMP trace with payload...
                          a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / ICMP(type=tid, id=id, seq=seq) / Raw(load=pload),
                                    timeout=timeout, filter=filter, verbose=verbose, **kargs)
                  elif netproto == "UDP":
                      #
                      # MTR Network Protocol: 'UDP'
                      if filterundefined:
                          filter += " or udp"			# Update Filter -> Allow for processing UDP packets
                      #
                      # Check payload types:
                      if ptype == 'Disabled':
                          a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / UDP(sport=sport, dport=dport),
                                    timeout=timeout, filter=filter, verbose=verbose, **kargs)
                      else:
                          if ptype == 'RandStr':
                              #
                              # Use a random payload string to full out a minimum size PDU of 46 bytes for each UDP packet:
                              # Length of 'IP()/UDP()' = 28, Minimum PDU is = 46 -> Therefore a payload of 18 octets is required.
                              pload = RandString(size=18)
                          elif ptype == 'RandStrTerm':
                              pload = RandStringTerm(size=17, term=b'\n')  # Random string terminated
                          elif ptype == 'Custom':
                              pload = payload
                          #
                          # UDP trace with payload...
                          a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / UDP(sport=sport, dport=dport) / Raw(load=pload),
                                    timeout=timeout, filter=filter, verbose=verbose, **kargs)
                  else:
                      #
                      # Default MTR Network Protocol: 'TCP'
                      #
                      # Use some TCP options for the trace. Some firewalls will filter
                      # TCP/IP packets without the 'Timestamp' option set.
                      #
                      # Note: The minimum PDU size of 46 is statisfied with the use of TCP options.
                      #
                      # Use an integer encoded microsecond timestamp for the TCP option timestamp for each trace sequence.
                      uts = IntAutoMicroTime()
                      opts = [('MSS', 1460), ('NOP', None), ('NOP', None), ('Timestamp', (uts, 0)), ('NOP', None), ('WScale', 7)]
                      seq = RandInt()		# Use a random TCP sequence number
                      #
                      # Check payload types:
                      if ptype == 'Disabled':
                          a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / TCP(seq=seq, sport=sport, dport=dport, options=opts),
                                    timeout=timeout, filter=filter, verbose=verbose, **kargs)
                      else:
                          if ptype == 'RandStr':
                              pload = RandString(size=32)			# Use a 32 byte random string
                          elif ptype == 'RandStrTerm':
                              pload = RandStringTerm(size=32, term=b'\n')  # Use a 32 byte random string terminated
                          elif ptype == 'Custom':
                              pload = payload
                          #
                          # TCP trace with payload...
                          a, b = sr(IP(dst=[t], id=RandShort(),
                                       ttl=(minttl, maxttl)) / TCP(seq=seq, sport=sport, dport=dport, options=opts) / Raw(load=pload),
                                    timeout=timeout, filter=filter, verbose=verbose, **kargs)
                  #
                  # Create an 'MTracerouteResult' instance for each result packets...
                  trace.append(MTracerouteResult(res=a.res))
                  mtrc._res.append(a)		# Store Response packets
                  mtrc._ures.append(b)		# Store Unresponse packets
                  if verbose:
                      trace[ntraces].show(ntrace=(ntraces + 1))
                      print()
                  ntraces += 1
      else:
          #
          # Custom Layer: 4 tracing...
          filter = "ip"
          for n in range(0, nquery):
              for t in exptrg:
                  #
                  # Run traceroute...
                  a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / l4,
                            timeout=timeout, filter=filter, verbose=verbose, **kargs)
                  trace.append(MTracerouteResult(res=a.res))
                  mtrc._res.append(a)
                  mtrc._ures.append(b)
                  if verbose:
                      trace[ntraces].show(ntrace=(ntraces + 1))
                      print()
                  ntraces += 1
      #
      # Store total trace run count...
      mtrc._ntraces = ntraces
      #
      # Get the trace components...
      # for n in range(0, ntraces):
      for n in range(0, mtrc._ntraces):
          trace[n].get_trace_components(mtrc, n)
      #
      # Compute any Black Holes...
      mtrc.get_black_holes()
      #
      # Compute Trace Hop Ranges...
      mtrc.compute_hop_ranges()
      #
      # Resolve AS Numbers...
      if rasn:
          mtrc.get_asns(privaddr)
          #
          # Try to guess ASNs for Traceroute 'Unkown Hops'...
          mtrc.guess_unk_asns()
      #
      # Debug: Print object vars at verbose level 8...
      if verbose == 8:
          print("mtrc._target (User Target(s)):")
          print("=======================================================")
          print(mtrc._target)
          print("\nmtrc._exptrg (Resolved and Expanded Target(s)):")
          print("=======================================================")
          print(mtrc._exptrg)
          print("\nmtrc._host2ip (Target Host Name to IP Address):")
          print("=======================================================")
          print(mtrc._host2ip)
          print("\nmtrc._ip2host (Target IP Address to Host Name):")
          print("=======================================================")
          print(mtrc._ip2host)
          print("\nmtrc._res (Trace Response Packets):")
          print("=======================================================")
          print(mtrc._res)
          print("\nmtrc._ures (Trace Unresponse Packets):")
          print("=======================================================")
          print(mtrc._ures)
          print("\nmtrc._ips (Trace Unique IPv4 Addresses):")
          print("=======================================================")
          print(mtrc._ips)
          print("\nmtrc._rt (Individual Route Traces):")
          print("=======================================================")
          print(mtrc._rt)
          print("\nmtrc._rtt (Round Trip Times (msecs) for Trace Nodes):")
          print("=======================================================")
          print(mtrc._rtt)
          print("\nmtrc._hops (Traceroute Hop Ranges):")
          print("=======================================================")
          print(mtrc._hops)
          print("\nmtrc._tlblid (Target Trace Label IDs):")
          print("=======================================================")
          print(mtrc._tlblid)
          print("\nmtrc._ports (Completed Targets & Ports):")
          print("=======================================================")
          print(mtrc._ports)
          print("\nmtrc._portsdone (Completed Trace Routes & Ports):")
          print("=======================================================")
          print(mtrc._portsdone)
          print("\nconf.L3socket (Layer 3 Socket Method):")
          print("=======================================================")
          print(conf.L3socket)
          print("\nconf.AS_resolver Resolver (AS Resolver Method):")
          print("=======================================================")
          print(conf.AS_resolver)
          print("\nmtrc._asns (AS Numbers):")
          print("=======================================================")
          print(mtrc._asns)
          print("\nmtrc._asds (AS Descriptions):")
          print("=======================================================")
          print(mtrc._asds)
          print("\nmtrc._unks (Unknown Hops IP Boundary for AS Numbers):")
          print("=======================================================")
          print(mtrc._unks)
          print("\nmtrc._iface (Trace Interface):")
          print("=======================================================")
          print(mtrc._iface)
          print("\nmtrc._gw (Trace Default Gateway IPv4 Address):")
          print("=======================================================")
          print(mtrc._gw)
  
      return mtrc