3
votes

I'm trying to use raw sockets in Python to send UDP packets to a host and then get the ICMP response back for the packet -- basically reimplementing traceroute.

I've managed to correctly construct my IP and UDP headers and send the packet. I can see it in Wireshark. I also see the ICMP response in Wireshark telling me that the TTL exceeded.

I have the following code:

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))

Then after the packet is sent I call another function to listen for incoming packets which includes this snippet:

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

I'm able to successfully unpack the IP header and check the protocol, but it is always either 6 or 17 (TCP or UDP). I never get the IP packet containing the ICMP payload even though it appears in Wireshark.

I've tried comparing the ICMP packet in Wireshark to other packets in Wireshark that my program does see and the IP headers are pretty much identical. I don't know what is wrong.

Thanks for the help

1
If I set TTL = 0 in the IP header the same piece of code does detect the ICMP response. Of course this is rather useless but figured it might help detect the problem...Rafael

1 Answers

0
votes

Judging from this answer, it looks like you need to pass the IPPROTO_ICMP option in when you create your socket.

You can do this like:

my_socket = socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.IPPROTO_ICMP)