首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >python中的跟踪路由实现

python中的跟踪路由实现
EN

Stack Overflow用户
提问于 2020-11-22 20:52:04
回答 1查看 1.7K关注 0票数 0

我一直试图为python编写一个简单的跟踪路由实现;请找到下面的代码。

代码语言:javascript
复制
import socket

def main (HostName):
    dest_addr = socket.gethostbyname(HostName)
    TTL = 1    #Define the time to live as 1. Will be incremented by one after each UDP message has been sent
    Max = 30



while True:
    ICMP_socket = socket.socket(socket.AF_INET,socket.SOCK_RAW, socket.IPPROTO_ICMP) #Create socket that can receive ICMP 
    UDP = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) #Create socket that will send UDP messages 
    UDP.setsockopt(0,4,TTL)
    
    
    
    ICMP_socket.bind(("",33434)) #33434 is the default port used for traceroute 
    message = "Hi" #The message we send is an empty string 
    UDP.sendto(message.encode(),(dest_addr, 33434))
    
   
    
    Receiver = (0,"")
    Router_addr = None
    
    
    try:
        Receiver = ICMP_socket.recvfrom(1024)
        Router_addr = Receiver[1]
        if Router_addr == None:
            print("*")
        else:
            print(Router_addr)
    except:
        pass
    finally:
        ICMP_socket.close()
        UDP.close()
    
    TTL = TTL + 1
    
    if Router_addr == dest_addr or TTL == Max:
        break
    
    
if __name__ =="__main__":
    main('google.com')

我使用sudo在我的macOS终端上运行它,因为它需要根权限。然而,我得到的输出似乎是错误的。第一个ip是我的本地IP,第二个是我不知道的IP,其余的都是google的IP。它没有输出中间It,只有在达到TTL限制为30时才会中断。

这是输出(我删除了第一个Ip地址,即本地IP地址):

代码语言:javascript
复制
('172.30.224.110', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)
('172.217.19.46', 0)

有人能帮忙吗?我似乎找不到这个问题。

谢谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-11-23 11:20:34

为什么程序不停止

当程序“到达”目的地时,程序不会停止,因为您忽略了address (您称之为Router_addr ),因为AF_INET是一个tuple (参见文档中的“套接字家族”)。问题中包含的输出(这是print(Router_addr)的结果)也可以看出这一点。

如果您将if Router_addr == dest_addr or TTL == Max:更新为如下所示,您的程序将停止。

代码语言:javascript
复制
if Router_addr[0] == dest_addr or TTL == Max:

输出显示了什么

我不知道你的网络拓扑结构,你是如何到达互联网的,特别是谷歌。从您在输出中所描述和看到的内容来看。

  • 172.30.224.110是提供商的路由器的内部接口(很可能)的私有IP地址;请记住,响应是从与您“更近”的接口发送到您的计算机的,而不是“更接近”因特网的响应(正如您提到的,您不承认此IP地址属于您的主机、大学路由器/网络)。
  • 在google IP地址(172.217.19.46)和上面的路由器之间可能没有多个跳;例如,如果google的服务器位于提供商的网络上(对于大型内容提供商和CDN来说,实现高性能的大眼球提供商的高性能的一种非常常见的做法);在我的机器上,我看到多个跳到google运行您的程序(也许您想要检查另一个目的地)
  • 最不清楚的是为什么您在第一行看到计算机的IP地址;这可能是因为您正在使用Mac作为路由器(可能用于因特网共享)或采用涉及转发(因此第一个路由器就是您的机器)的“奇怪”配置;我在这里假设,当您谈论本地IP地址时,您并不是指回送IP地址(否则请阅读下面)。

为什么输出中的回送地址

如果您在路径中看到的某些网络主机(可能包括您的第一跳)是回送IP地址,而不是您发送数据包的地址,那么原因是下面的片段(以及后台发生了什么):

代码语言:javascript
复制
if Router_addr == None:
    print("*")

当网络节点没有响应时,socket.recvfrom()返回('127.0.0.1', 0),而不是None。这是因为对于UDPICMP Destination Unreachable (类型3) Port Unreachable (代码3)是由localhost生成的,而在目标端口没有监听的情况下。顺便说一句,这就是为什么ICMP_socket.recvfrom(1024)没有被阻塞的原因。

您修改过的程序,稍加修改,以打印TTLICMP类型和代码,给出(见 )。

代码语言:javascript
复制
$ sudo python3 trorig.py
('192.168.178.1', 0) TTL: [1] type: [11] code: [0]
('62.245.142.131', 0) TTL: [2] type: [11] code: [0]
('62.245.142.130', 0) TTL: [3] type: [11] code: [0]
('82.135.16.226', 0) TTL: [4] type: [11] code: [0]
('142.250.161.214', 0) TTL: [5] type: [11] code: [0]
('127.0.0.1', 0) TTL: [6] type: [3] code: [3]       
('172.253.75.128', 0) TTL: [7] type: [11] code: [0]
('172.253.75.143', 0) TTL: [8] type: [11] code: [0]
('74.125.244.81', 0) TTL: [9] type: [11] code: [0]
('172.253.75.141', 0) TTL: [10] type: [11] code: [0]
('172.217.23.78', 0) TTL: [11] type: [3] code: [3]

相应的tcpdump输出为(注意,无响应的网络主机没有行,142.250.161.214后面跟着172.253.75.128,):

代码语言:javascript
复制
$ sudo tcpdump -i en0 -nnn icmp      
Password:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on en0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:30:45.720700 IP 192.168.178.1 > 192.168.178.20: ICMP time exceeded in-transit, length 38
14:30:45.740808 IP 62.245.142.131 > 192.168.178.20: ICMP time exceeded in-transit, length 38
14:30:45.761857 IP 62.245.142.130 > 192.168.178.20: ICMP time exceeded in-transit, length 36
14:30:45.782603 IP 82.135.16.226 > 192.168.178.20: ICMP time exceeded in-transit, length 36
14:30:45.803874 IP 142.250.161.214 > 192.168.178.20: ICMP time exceeded in-transit, length 38
14:30:53.454002 IP 172.253.75.128 > 192.168.178.20: ICMP time exceeded in-transit, length 76
14:30:53.476512 IP 172.253.75.143 > 192.168.178.20: ICMP time exceeded in-transit, length 38
14:30:53.496455 IP 74.125.244.81 > 192.168.178.20: ICMP time exceeded in-transit, length 38
14:30:53.519329 IP 172.253.75.141 > 192.168.178.20: ICMP time exceeded in-transit, length 38
14:30:53.541726 IP 172.217.23.78 > 192.168.178.20: ICMP 172.217.23.78 udp port 33434 unreachable, length 36

相应的traceroute输出是(与上面的路径相比有一些不同,因为没有同时运行它,但问题仍然是,TTL: 6的网络主机是没有响应的):

代码语言:javascript
复制
$ traceroute -n google.com
traceroute to google.com (172.217.23.78), 64 hops max, 52 byte packets
 1  192.168.178.1  8.694 ms  2.893 ms  3.095 ms
 2  62.245.142.131  17.545 ms  17.361 ms  19.010 ms
 3  62.245.142.130  25.948 ms  20.302 ms  26.077 ms
 4  82.135.16.226  20.418 ms  19.967 ms  21.411 ms
 5  142.250.161.214  29.145 ms  25.573 ms  19.979 ms
 6  * * *
 7  108.170.247.113  26.875 ms
    172.253.75.146  21.736 ms
    108.170.247.113  21.987 ms
 8  172.253.75.143  22.435 ms
    172.253.75.141  20.617 ms
    108.170.247.120  21.670 ms
 9  172.217.23.78  19.538 ms
    74.125.244.97  18.503 ms
    74.125.244.81  18.907 ms

因此,您检查无响应网络主机的方式是不正确的。您可能希望将其更新为如下所示:

代码语言:javascript
复制
if Router_addr == ('127.0.0.1', 0) :
    print("*")

最后两点您可能需要考虑:

  • 如上文所示,并不是每个主机(或网络)都会返回响应,响应可能会丢失或丢弃。您可能希望在放弃并声明网络主机没有响应之前,在ICMP_socket.recvfrom(1024)中等待多长时间的超时时间。你可以看看socket.SO_RCVTIMEO
  • 如上文所示,在一个给定的TTL上,可能有多个网络主机远离您的计算机到达目的地。您可能需要考虑发送多个数据包来发现它们(如果它们是可发现的)。

编辑(回答下面的评论):原始代码需要一些小的修改才能没有问题地运行;下面是更新和增强的代码

代码语言:javascript
复制
import socket
import struct

def main (HostName):
    dest_addr = socket.gethostbyname(HostName)
    TTL = 1    
    Max = 30

    while True:
        ICMP_socket = socket.socket(socket.AF_INET,socket.SOCK_RAW, socket.IPPROTO_ICMP) 
        ICMP_socket.bind(("",33434))

        UDP = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)    
        UDP.setsockopt(0,4,TTL)
        message = "Hi" 
        UDP.sendto(message.encode(),(dest_addr, 33434))

        Router_addr = None
        try:
            Data, Router_addr = ICMP_socket.recvfrom(1024)
            ICMP_header = Data[20:28] 
            type_, code, *_ = struct.unpack('bbHHh', ICMP_header)                      

            if Router_addr == ('127.0.0.1', 0) :
                print(f"* TTL: [{TTL}] type: [{type_}] code: [{code}]")
            else:
                print(f"{Router_addr} TTL: [{TTL}] type: [{type_}] code: [{code}]")
        except Exception as e:
            print(e)
        finally:
            ICMP_socket.close()
            UDP.close()
        
        TTL = TTL + 1
        
        if Router_addr[0] == dest_addr or TTL == Max:
            break
    
if __name__ =="__main__":
    main('google.com')
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64959414

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档