Python ICMP ping implementation when pinging multiple ips from threads?
Question: I've been using [jedie's python `ping`
implementation](https://github.com/jedie/python-ping/blob/master/ping.py) on
Windows. I could be wrong, but when pinging two computers (A and B) from
separate threads, ping will return the first ping it receives, regardless of
source.

Since it could be an issue with jedie's fork, I [reverted to the previous
version](http://www.g-loaded.eu/2009/10/30/python-ping/). (This is the version
I'm going to explore below)

I added in a line of code in `receive_one_ping`: (Line 134 or similar)

    
    
    recPacket, addr = my_socket.recvfrom(1024) # Existing line
    print "dest: {}, recv addr: {}.".format(dest_addr, addr) # New line
    

This allows us to see the address of the ping we're receiving. (Should be same
as the destination IP, right?)

**Testing:**

ping1() pings a known offline IP (1.2.3.4),  
ping2() pings a known online IP (192.168.1.1 - my router)

    
    
    >>> from ping import do_one
    
    >>> def ping1():
        print "Offline:", do_one("1.2.3.4",1)
    
    >>> ping1()
    Offline: None
    
    >>> def ping2():
        print "Online:", do_one("192.168.1.1",1)
    
    >>> ping2()
    Online: dest: 192.168.1.1, recv addr: ('192.168.1.1', 0).
    0.000403682590942
    

Now if we do them together: (Using Timer for simplicity)

    
    
    >>> from threading import Timer
    >>> t1 = Timer(1, ping1)
    >>> t2 = Timer(1, ping2)
    >>> t1.start(); t2.start()
    >>> Offline:Online: dest: 192.168.1.1, recv addr: ('192.168.1.1', 0).dest: 1.2.3.4, recv addr: ('192.168.1.1', 0).
    
    0.0004508952953870.000423517514093
    

It's a little mangled (due to print not working nicely with threading), so
here it is a bit clearer:

    
    
    >>> Online: dest: 192.168.1.1, recv addr: ('192.168.1.1', 0).
    Offline:dest: 1.2.3.4, recv addr: ('192.168.1.1', 0). # this is the issue - I assume dest should be the same as recv address?
    
    0.000450895295387
    0.000423517514093
    

**My questions:**

  1. Can anyone recreate this? 

  2. Should ping be behaving like this? I assume not. 

  3. Is there an existing ICMP ping for python that will not have this behaviour?  
Alternatively, can you think of an easy fix - ie polling `receive_one_ping`
until our destination matches our receive address?

**Edit:** I've created an issue on the [python-ping github
page](https://github.com/jedie/python-ping/issues/10)


Answer: This is happening because of the nature of ICMP. ICMP has no concept of ports,
so _all_ ICMP messages are received by _all_ listeners.

The usual way to disambiguate is to set a unique identifier in the ICMP ECHO
REQUEST payload, and look for it in the response. This code appears to do
that, but it uses the current process id to compose the ID. Since this is
multithreaded code, they will share a process id and all listeners in the
current process will think all ECHO REPLYs are ones they themselves sent!

You need to change the `ID` variable in `do_one()` so that it is per-thread
unique. You will need to change this line in `do_one()`:

    
    
    my_ID = os.getpid() & 0xFFFF
    

_Possibly_ this will work as an alternative, but ideally you should use a real
16-bit hashing function:

    
    
    # add to module header
    try:
        from thread import get_ident
    except ImportError:
        try:
            from _thread import get_ident
        except ImportError:
            def get_ident():
                return 0
    
    # now in do_one() body:
    my_ID = (get_ident() ^ os.getpid()) & 0xFFFF
    

I don't know if this module has any other thread issues, but it _seems_ to be
ok from a cursory examination.

Using the jedie implementation, you would make a similar change for the
`Ping()` `own_id` constructor argument. You can either pass in an id you know
to be unique (like above) and manage `Ping()` objects yourself, or you can
change this line (110) in the constructor:

    
    
    self.own_id = os.getpid() & 0xFFFF
    

Also see [this question and answer and answer comment
thread](http://stackoverflow.com/q/6904135/1002469) for more info.

