Python原始套接字无法接收ICMP数据包

3
我正在尝试使用Python中的原始套接字向主机发送UDP数据包,然后获取该数据包的ICMP响应,以重新实现traceroute。
我已经成功构建了IP和UDP标头,并发送了数据包。我可以在Wireshark中看到它。我还可以在Wireshark中看到ICMP响应,告诉我TTL已超过。
下面是代码:
me = gethostbyname(gethostname())
my_socket = socket(AF_INET, SOCK_RAW)
my_socket.setsockopt(IPPROTO_IP, IP_HDRINCL, 1)
my_socket.bind((me, 0))

hostname = 'www.google.com'
hostip = gethostbyname(hostname)

packet = create_packet(hostname)
send_socket.sendto(packet, (hostip , 0))

然后在数据包发送后,我调用另一个函数来监听传入的数据包,其中包括以下片段:
while True:
    ready = select.select([my_socket], [], [], time_left)
    if ready[0] == []:
        print "timeout"
    time_now = time.time()
    rec_packet, addr = my_socket.recvfrom(5120)

    unpacked_ip = unpack('!BBHHHBBH4s4s', rec_packet[0:20]) #0-20 is IP header
    prot = unpacked_ip[6] #gives the protocol id
    if prot == 1:
        #this is ICMP , let's do things

我能够成功地解包IP头并检查协议,但它总是6或17(TCP或UDP)。即使在Wireshark中显示ICMP负载,我也从未收到包含ICMP负载的IP数据包。
我尝试将Wireshark中的ICMP数据包与我的程序可以看到的其他数据包进行比较,发现IP头几乎完全相同。我不知道哪里出了问题。
谢谢您的帮助。

如果我在IP头中设置TTL = 0,同样的代码将会检测到ICMP响应。当然这是相当无用的,但想到它可能有助于检测问题... - Rafael
1个回答

1
这个答案来看,似乎你需要在创建套接字时传递IPPROTO_ICMP选项。
你可以这样做:
my_socket = socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.IPPROTO_ICMP)

我认为这只是过滤传入的数据包,因为添加它会使我的程序挂起(似乎在等待ICMP数据包到达,然后超时)。不过一旦我让它工作起来,它绝对看起来是一个很好的补充,这样我就不必像目前这样手动检查协议了。 - Rafael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接