def nmap_sig(target, oport=80, cport=81, ucport=1): res = {} tcpopt = [("WScale", 10), ("NOP", None), ("MSS", 256), ("Timestamp", (123, 0))] tests = [ IP(dst=target, id=1) / TCP(seq=1, sport=5001 + i, dport=oport if i < 4 else cport, options=tcpopt, flags=flags) for i, flags in enumerate(["CS", "", "SFUP", "A", "S", "A", "FPU"]) ] tests.append(IP(dst=target) / UDP(sport=5008, dport=ucport) / (300 * "i")) ans, unans = sr(tests, timeout=2) ans.extend((x, None) for x in unans) for snd, rcv in ans: if snd.sport == 5008: res["PU"] = (snd, rcv) else: test = "T%i" % (snd.sport - 5000) if rcv is not None and ICMP in rcv: warning("Test %s answered by an ICMP", test) rcv = None res[test] = rcv return nmap_probes2sig(res)
def nmap_sig(target, oport=80, cport=81, ucport=1): res = {} tcpopt = [ ("WScale", 10), ("NOP",None), ("MSS", 256), ("Timestamp",(123,0)) ] tests = [ IP(dst=target, id=1)/TCP(seq=1, sport=5001, dport=oport, options=tcpopt, flags="CS"), IP(dst=target, id=1)/TCP(seq=1, sport=5002, dport=oport, options=tcpopt, flags=0), IP(dst=target, id=1)/TCP(seq=1, sport=5003, dport=oport, options=tcpopt, flags="SFUP"), IP(dst=target, id=1)/TCP(seq=1, sport=5004, dport=oport, options=tcpopt, flags="A"), IP(dst=target, id=1)/TCP(seq=1, sport=5005, dport=cport, options=tcpopt, flags="S"), IP(dst=target, id=1)/TCP(seq=1, sport=5006, dport=cport, options=tcpopt, flags="A"), IP(dst=target, id=1)/TCP(seq=1, sport=5007, dport=cport, options=tcpopt, flags="FPU"), IP(str(IP(dst=target)/UDP(sport=5008,dport=ucport)/(300*"i"))) ] ans, unans = sr(tests, timeout=2) ans += map(lambda x: (x,None), unans) for S,T in ans: if S.sport == 5008: res["PU"] = nmap_udppacket_sig(S,T) else: t = "T%i" % (S.sport-5000) if T is not None and T.haslayer(ICMP): warning("Test %s answered by an ICMP" % t) T=None res[t] = nmap_tcppacket_sig(T) return res
def ikescan(ip): return sr( IP(dst=ip) / UDP() / ISAKMP(init_cookie=RandString(8), exch_type=2) / ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal()) )
def ikev2scan(ip): return sr( IP(dst=ip) / UDP() / IKEv2(init_SPI=RandString(8), exch_type=34) / IKEv2_payload_SA(prop=IKEv2_payload_Proposal()) )
def ikescan(ip): """Sends/receives a ISAMPK payload SA with payload proposal""" pkt = IP(dst=ip) pkt /= UDP() pkt /= ISAKMP(init_cookie=RandString(8), exch_type=2) pkt /= ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal()) return sr(pkt)
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
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
def queso_sig(target, dport=80, timeout=3): p = queso_kdb.get_base() ret = [] for flags in ["S", "SA", "F", "FA", "SF", "P", "SEC"]: ans, unans = sr(IP(dst=target)/TCP(dport=dport,flags=flags,seq=RandInt()), timeout=timeout, verbose=0) if len(ans) == 0: rs = "- - - -" else: s,r = ans[0] rs = "%i" % (r.seq != 0) if not r.ack: r += " 0" elif r.ack-s.seq > 666: rs += " R" % 0 else: rs += " +%i" % (r.ack-s.seq) rs += " %X" % r.window rs += " %x" % r.payload.flags ret.append(rs) return ret
def send_wait_for_message(step, exp_message): """ Block until the given message is (not) received. Parameter: new: (' new', optional): Only check the output printed since last time this step was used for this process. process_name ('<name> stderr'): Name of the process to check the output of. message ('message <message>'): Output (part) to wait for. """ world.cliopts = [] #clear options, always build new message, also possible make it in client_send_msg #debug.recv = [] conf.use_pcap = True # Uncomment this to get debug.recv filled with all received messages #conf.debug_match = True ans,unans = sr(world.climsg, iface=world.cfg["iface"], timeout=1, nofilter=1, verbose=99) expected_type_found = False received_names = "" world.srvmsg = [] for x in ans: a,b = x world.srvmsg.append(b) print("Debug: Received packet type = %s" % get_msg_type(b)) received_names = get_msg_type(b) + " " + received_names if (get_msg_type(b) == exp_message): expected_type_found = True for x in unans: print("Unmatched packet type = %s" % get_msg_type(x)) print("Received traffic (answered/unanswered): %d/%d packet(s)." % (len(ans), len(unans))) assert len(world.srvmsg) != 0, "No response received." assert expected_type_found, "Expected message " + exp_message + " not received (got " + received_names + ")"
def send_wait_for_message(condition_type, presence, exp_message): """ Block until the given message is (not) received. Parameter: new: (' new', optional): Only check the output printed since last time this step was used for this process. process_name ('<name> stderr'): Name of the process to check the output of. message ('message <message>'): Output (part) to wait for. """ world.cliopts = [ ] # clear options, always build new message, also possible make it in client_send_msg may_flag = False #debug.recv=[] if str(condition_type) in "MUST": pass elif str(condition_type) in "MAY": may_flag = True # we needs to get it operational # problem: break test with success. (for now we can break test only with fail) else: assert False, "Invalid expected behavior: %s." % str(condition_type) # Uncomment this to get debug.recv filled with all received messages conf.debug_match = True # checkIPsrc must be False so scapy can correctly match response to request conf.checkIPsrc = False apply_message_fields_changes() timeout = world.cfg["wait_interval"] if "HA" in os.environ.get('PYTEST_CURRENT_TEST').split("/"): timeout *= world.f_cfg.ha_packet_wait_interval_factor ans, unans = sr(world.climsg, iface=world.cfg["iface"], timeout=timeout, nofilter=1, verbose=world.scapy_verbose) if world.f_cfg.show_packets_from in ['both', 'client']: world.climsg[0].show() expected_type_found = False received_names = "" world.srvmsg = [] for x in ans: a, b = x world.srvmsg.append(b) if world.f_cfg.show_packets_from in ['both', 'server']: b.show() if not world.loops["active"]: log.info("Received packet type=%s" % get_msg_type(b)) received_names = get_msg_type(b) + " " + received_names if get_msg_type(b) == exp_message: expected_type_found = True if exp_message is not None: for x in unans: log.error(("Unanswered packet type=%s" % dhcp6.dhcp6_cls_by_type[x.msgtype])) if not world.loops["active"]: log.debug("Received traffic (answered/unanswered): %d/%d packet(s)." % (len(ans), len(unans))) if may_flag: if len(world.srvmsg) != 0: assert True, "Response received." if len(world.srvmsg) == 0: assert True, "Response not received." # stop the test... ?? elif presence: assert len(world.srvmsg) != 0, "No response received." assert expected_type_found, "Expected message " + exp_message + " not received (got " + received_names + ")" elif not presence: assert len(world.srvmsg) == 0, "Response received, not expected" return world.srvmsg
#print ("In UDP Flag") if (hostType == "singleHost"): #Add Table Headers to HTML htmlText = htmlText + "<h2>UDP Scan: " + str( hostIP ) + "<table><tr> <th>Host</th> <th>Port</th> <th>Status</th> </tr>" #build and send UDP packets for single host, multiple ports try: for port in ports: ip = IP(dst=host) udp = UDP(dport=int(port), sport=123) packet = ip / udp response = sr(packet, verbose=False, timeout=20) try: check = response[0][ICMP][0][1][ICMP] print("Port {} on {} is closed".format(port, host)) #Add host, port, and status to HTML htmlText = htmlText + "<tr> <td>" + hostIP + "</td> <td>" + str( port) + "</td> <td>Closed</td> </tr>" except IndexError: print("Port {} on {} is open|filtered".format( port, host)) #Add host, port, and status to HTML htmlText = htmlText + "<tr> <td>" + hostIP + "</td> <td>" + str( port) + "</td> <td>open|filtered</td> </tr>"
def send_wait_for_message(condition_type, presence, exp_message): """ Block until the given message is (not) received. Parameter: new: (' new', optional): Only check the output printed since last time this step was used for this process. process_name ('<name> stderr'): Name of the process to check the output of. message ('message <message>'): Output (part) to wait for. """ world.cliopts = [] # clear options, always build new message, also possible make it in client_send_msg may_flag = False #debug.recv=[] if str(condition_type) in "MUST": pass elif str(condition_type) in "MAY": may_flag = True # we needs to get it operational # problem: break test with success. (for now we can break test only with fail) else: assert False, "Invalid expected behavior: %s." % str(condition_type) # Uncomment this to get debug.recv filled with all received messages conf.debug_match = True # checkIPsrc must be False so scapy can correctly match response to request conf.checkIPsrc = False apply_message_fields_changes() ans, unans = sr(world.climsg, iface=world.cfg["iface"], timeout=world.cfg["wait_interval"], nofilter=1, verbose=world.scapy_verbose) if world.f_cfg.show_packets_from in ['both', 'client']: world.climsg[0].show() expected_type_found = False received_names = "" world.srvmsg = [] for x in ans: a, b = x world.srvmsg.append(b) if world.f_cfg.show_packets_from in ['both', 'server']: b.show() if not world.loops["active"]: log.info("Received packet type=%s" % get_msg_type(b)) received_names = get_msg_type(b) + " " + received_names if get_msg_type(b) == exp_message: expected_type_found = True for x in unans: log.error(("Unanswered packet type=%s" % dhcp6.dhcp6_cls_by_type[x.msgtype])) if not world.loops["active"]: log.debug("Received traffic (answered/unanswered): %d/%d packet(s)." % (len(ans), len(unans))) if may_flag: if len(world.srvmsg) != 0: assert True, "Response received." if len(world.srvmsg) == 0: assert True, "Response not received." # stop the test... ?? elif presence: assert len(world.srvmsg) != 0, "No response received." assert expected_type_found, "Expected message " + exp_message + " not received (got " + received_names + ")" elif not presence: assert len(world.srvmsg) == 0, "Response received, not expected" return world.srvmsg
def ikev2scan(ip): return sr( IP(dst=ip) / UDP() / IKEv2(init_SPI=RandString(8), exch_type=34) / IKEv2_payload_SA(prop=IKEv2_payload_Proposal()))
def ping(dst, **kwargs): """Function to execute a ping test. Args: dst (str): The destination address to ping. Can be either a FQDN or an IP address. **count (int): Keyword argument to optionally specify the number of ping packets to send in a single test. Defaults to 10. Max value of 20. **payload_size (int): Keyword argument to optionally specify the ICMP packet's payload size. Defaults to 56. Max value of 1472. Returns: dict: Returns a dictionary object with test results. """ rtt = [] comment = None count = kwargs.get("count", 10) if isinstance(count, str) and not count.isdigit(): raise TypeError(f"Provided 'count' of '{count}' must be an integer.") count = abs(int(count)) if not 1 <= count <= 20: comment = f"Provided count of '{count}' is not allowed. Defaulting to 10. Min: 1, Max: 20." count = 10 payload_size = kwargs.get("payload_size", constants.PACKET_PAYLOAD_SIZE) if isinstance(payload_size, str) and not payload_size.isdigit(): raise TypeError( f"Provided 'payload_size' of '{payload_size}' must be an integer.") payload_size = abs(int(payload_size)) if not 0 <= payload_size <= 1472: raise ValueError( f"Provided 'packet_size' of '{payload_size}' is not allowed. Min: 0, Max: 1472." ) result = { "dst": dst, "sent": count, "recv": None, "payload_size": payload_size, "packet_size": payload_size + 28, "loss": None, "rtt": { "min": None, "max": None, "avg": None }, "replies": [], "comment": comment, "failed": True, } dst = _resolve(dst) # Get the correct egress interface name for the provided destination. This is to solve # issues with testing via a VPN. iface = _get_route_dev(dst) # Tell Scapy to NOT ignore the inner packet source. This is to avoid issues with NAT. conf.checkIPsrc = False # After much testing within our network I found that if we don't limit the number of # packets that Scapy receives in a single sniff it will essentially clog up and never # return from its receive loop. Adding a custom BPF filter stops this from occurring. packet_filter = f"(src host {dst} or dst host {dst}) and icmp" for seq in range(0, count): packet = ICMP(id=os.getpid(), seq=seq) / Raw( RandString(size=payload_size)) ans = sr( IP(dst=dst, id=os.getpid()) / packet, iface=iface, filter=packet_filter, timeout=constants.PACKET_RECV_TIMEOUT, retry=constants.PACKET_SEND_RETRY, verbose=0, )[0] # Check if we got an echo-reply. if ans and ans[0][1][1].code == 0: rtt_ms = (ans[0][1].time - ans[0][0].sent_time) * 1000.0 rtt.append(rtt_ms) result["replies"].append({ "seq": ans[0][1].seq, "ttl": ans[0][1].ttl, "len": ans[0][1].len - 20, # Bytes received minus the IP header. "rtt_ms": rtt_ms, }) time.sleep(constants.PACKET_SEND_DELAY) # Calculate packet loss. result["loss"] = abs((100 * (len(rtt) - count) / count)) # Set RTT timings if packets were received. if rtt: result["rtt"]["min"] = format(min(rtt), ".3f") result["rtt"]["max"] = format(max(rtt), ".3f") result["rtt"]["avg"] = format(sum(rtt) / len(rtt), ".3f") result["failed"] = False return result
# copyright: Copyright (c) 2017-2020 qa_tech Co., Ltd. All rights reserved. # author: Jia Huang # create_time: 2020/7/15 15:42 # update_time: 2020/7/15 15:42 # description: function description """ scapy发送数据包有常用的如下几种方法: send(pkt) 发送三层数据包,但不会受到返回的结果。 sr(pkt) 发送三层数据包,返回两个结果,分别是接收到响应的数据包和未收到响应的数据包。 sr1(pkt) 发送三层数据包,仅仅返回接收到响应的数据包。 sendp(pkt) 发送二层数据包。 srp(pkt) 发送二层数据包,并等待响应。 srp1(pkt) 发送第二层数据包,并返回响应的数据包 """ from scapy.layers.inet import IP, TCP from scapy.sendrecv import sr # sr发送一个数据包,ans为响应的数据包,u_ans为未响应的数据包 pkt = IP(dst='114.114.114.114') / TCP(dport='http') ans, u_ans = sr(pkt, timeout=1) print(ans) print(u_ans) # 数据包来实现一个简单的SYN端口扫描,flags="S" 表示发送SYN数据包 port_scan = IP(dst='192.168.1.180') / TCP(dport=[22, 3389, 3306, 443, 445], flags='S') ans, u_ans = sr(port_scan, timeout=1) ans.summary()
def ikev2scan(ip, **kwargs): """Send a IKEv2 SA to an IP and wait for answers.""" return sr( IP(dst=ip) / UDP() / IKEv2(init_SPI=RandString(8), exch_type=34) / IKEv2_payload_SA(prop=IKEv2_payload_Proposal()), **kwargs) # noqa: E501
elif args.level == 'med': level = 100 elif args.level == 'low': level = 500 elif args.level == 'insane': level = 1000 print('scanning the crap out of {}...'.format(sys.argv[1])) for port in range(1,level): if args.icmp: # icmp scan network_layer = IP(dst=args.host) sr(network_layer/ICMP(), retry=0, timeout=0.001, verbose=False) print('icmp packet sent to host: counter {}'.format(port)) if args.syn: # synscan network_layer = IP(dst=args.host) transport_layer = TCP(dport=port, flags="S") send(network_layer/transport_layer, verbose=False) print('syn packet sent to port {}'.format(port)) if args.udp: # udp scan sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(.01) sock.sendto(bytes('rainbows', "utf-8"), (args.host, port)) print('udp packet sent to port {}'.format(port))
packet_tos = IP(dst=list_of_network_addresses[j]) / TCP(dport=22) packet_len = IP(dst=list_of_network_addresses[j]) / TCP(dport=22) packet_id = IP(dst=list_of_network_addresses[j]) / TCP(dport=22) packet_frag = IP(dst=list_of_network_addresses[j]) / TCP(dport=22) packet_ttl = IP(dst=list_of_network_addresses[j]) / TCP(dport=22) packet_proto = IP(dst=list_of_network_addresses[j]) / TCP(dport=22) packet_ihl.ihl = 18 packet_tos.tos = 18 packet_len.len = 18 packet_id.id = 18 packet_frag.frag = 18 packet_ttl.ttl = 18 packet_proto.proto = 18 answer_ihl = sr(packet_ihl, timeout=4) answer_tos = sr(packet_tos, timeout=4) answer_len = sr(packet_len, timeout=4) answer_id = sr(packet_id, timeout=4) answer_frag = sr(packet_frag, timeout=4) answer_ttl = sr(packet_ttl, timeout=4) answer_proto = sr(packet_proto, timeout=4) if len(answer_ihl[0]) != 0: if len( nx.dijkstra_path(graph_ihl, list_of_network_addresses[0], list_of_network_addresses[j])) == 2: graph_ihl.add_edge(str(list_of_network_addresses[0]), str(list_of_network_addresses[j]), color='r', weight=6)
def sendAndReceiveEthernetFrame(scapyFrame): return sr(scapyFrame, iface=managementServerNICName)
packet_tc = IPv6(dst=ipv6_addr) / TCP(dport=22) packet_fl = IPv6(dst=ipv6_addr) / TCP(dport=22) packet_plen = IPv6(dst=ipv6_addr) / TCP(dport=22) packet_nh = IPv6(dst=ipv6_addr) / TCP(dport=22) packet_hlim = IPv6(dst=ipv6_addr) / TCP(dport=22) packet_src = IPv6(dst=ipv6_addr) / TCP(dport=22) packet_tc.tc = 18 packet_fl.fl = 18 packet_plen.plen = 18 packet_nh.nh = 18 packet_hlim.hlim = 18 packet_src.src = 18 answer_tc = sr(packet_tc, timeout=4) answer_fl = sr(packet_fl, timeout=4) answer_plen = sr(packet_plen, timeout=4) answer_nh = sr(packet_nh, timeout=4) answer_hlim = sr(packet_hlim, timeout=4) answer_src = sr(packet_src, timeout=4) if len(answer_tc[0]) != 0 and not hasattr(answer_tc[0][0][1], 'type'): result_json["tc"] = "True" else: result_json["tc"] = "False" if len(answer_fl[0]) != 0 and not hasattr(answer_fl[0][0][1], 'type'): result_json["fl"] = "True" else: result_json["fl"] = "False"
# -*- coding: utf-8 -*- # copyright: Copyright (c) 2017-2020 qa_tech Co., Ltd. All rights reserved. # author: Jia Huang # create_time: 2020/7/16 11:18 # update_time: 2020/7/16 11:18 # description: function description from scapy.layers.inet import IP, TCP from scapy.sendrecv import sr from scapy.volatile import RandShort # tracert 114.114.114.114 windows每次发起3个icmp ans, u_ans = sr(IP(dst='114.114.114.114', ttl=(1, 20), id=RandShort()) / TCP(flags=0x2), timeout=3) for snd, rcv in ans: print(snd.ttl, rcv.src, isinstance(rcv.payload, TCP)) # 1 192.168.0.1 False ttl=1时,到第一个路由时,Time-to-live exceeded返回icmp # 2 192.168.1.1 False ttl=2时,到第二个路由时,Time-to-live exceeded返回icmp # 3 124.90.33.213 False # 4 123.157.192.1 False # 5 101.71.244.93 False # 6 219.158.12.133 False # 7 60.217.44.158 False # 8 114.114.114.114 True # 9 114.114.114.114 True # 10 114.114.114.114 True
def ikescan(ip): return sr( IP(dst=ip) / UDP() / ISAKMP(init_cookie=RandString(8), exch_type=2) / ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal()))
def ikev2scan(ip, **kwargs): """Send a IKEv2 SA to an IP and wait for answers.""" return sr(IP(dst=ip)/UDP()/IKEv2(init_SPI=RandString(8), exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal()), **kwargs)
def traceroute(dst, **kwargs): """Function to execute a traceroute. Args: dst (type): The destination address to trace to. Can be either a FQDN or an IP address. **proto (str) : Keyword argument to optionally specify the transport protocol to use in the traceroute. Defaults to ICMP. **dport (int) : Keyword argument to optionally specify the destination port. Defaults to 80 if `proto` is TCP, and None if ICMP. **payload_size (int) : Keyword argument to optionally specify the ICMP/TCP packet's payload size. Defaults to 56. Max value of 1472. **max_ttl (int) : Keyword argument to optionally specify the max time-to-live (max number of hops). Defaults to 32. Max value of 32. Returns: dict: Returns a dictionary object with test results. """ comment = None asn_mmdb_reader = geoip2.database.Reader("mmdb/GeoLite2-ASN.mmdb") proto = kwargs.get("proto", "ICMP").upper() if proto not in ("ICMP", "TCP"): comment = ( f"Provided 'proto' of '{proto}' is not supported. Defaulting to ICMP. ('ICMP', 'TCP')." ) proto = "ICMP" dport = kwargs.get("dport", 80) if isinstance(dport, str) and not dport.isdigit(): raise TypeError(f"Provided 'dport' of '{dport}' must be an integer.") dport = abs(int(dport)) if not 0 <= dport <= 65535: raise ValueError( f"Provided 'dport' of '{dport}' is not allowed. Min: 0, Max: 65535." ) payload_size = kwargs.get("payload_size", constants.PACKET_PAYLOAD_SIZE) if isinstance(payload_size, str) and not payload_size.isdigit(): raise TypeError( f"Provided 'payload_size' of '{payload_size}' must be an integer.") payload_size = abs(int(payload_size)) # ICMP has a maximum payload size of 1472 bytes which is significantly less than TCP's # supported maximum of 65535 bytes, but since we need to support both and I do not want # to exceed any MTUs; I'll leave 1472 as max. if not 0 <= payload_size <= 1472: raise ValueError( f"Provided 'packet_size' of '{payload_size}' is not allowed. Min: 0, Max: 1472." ) max_ttl = kwargs.get("max_ttl", constants.TRACE_MAX_TTL) if isinstance(max_ttl, str) and not max_ttl.isdigit(): raise TypeError( f"Provided 'max_ttl' of '{max_ttl}' must be an integer.") max_ttl = abs(int(max_ttl)) if not 0 <= max_ttl <= 32: raise ValueError(f"Provided 'max_ttl' of '{max_ttl}' is not allowed. " f"Min: 0, Max: {constants.TRACE_MAX_TTL}.") result = { "dst": dst, "proto": proto, "dport": dport, "payload_size": payload_size, "packet_size": payload_size + { "ICMP": 28, "TCP": 40 }[proto], "trace": [], "comment": comment, "failed": True, } dst = _resolve(dst) # Get the correct egress interface name for the provided destination. This is to solve # issues with testing via a VPN. iface = _get_route_dev(dst) packet = (TCP(dport=dport, flags="S") if proto == "TCP" else ICMP()) / Raw( RandString(size=payload_size)) # Tell Scapy to NOT ignore the inner packet source. This is to avoid issues with NAT. conf.checkIPsrc = False for ttl in range(constants.TRACE_MIN_TTL, max_ttl + 1): hop_data = { "asn": None, "ttl": ttl, "src": None, "hostname": None, "rtt_ms": None, "no_response": True, } ans = sr( IP(dst=dst, ttl=ttl, flags="DF", id=RandShort()) / packet, iface=iface, nofilter=0, timeout=constants.PACKET_RECV_TIMEOUT, retry=constants.PACKET_SEND_RETRY, verbose=0, )[0] if ans and ans[0][1].src not in [ hop["src"] for hop in result["trace"] ]: try: hop_data["asn"] = asn_mmdb_reader.asn( ans[0][1].src).autonomous_system_number except geoip2.errors.AddressNotFoundError: pass hop_data["src"] = ans[0][1].src hop_data["hostname"] = _resolve(ans[0][1].src, reverse=True) hop_data["rtt_ms"] = (ans[0][1].time - ans[0][0].sent_time) * 1000 hop_data["no_response"] = False result["trace"].append(hop_data) if ans[0][1].src == dst: result["failed"] = False break else: result["trace"].append(hop_data) return result