ping.py – Python Implementation of the ping command

I’ve been looking for a pure python implementation of the ping command. Now that I found one, I am not sure if I want to use it, as it has a restriction: only privileged users can ping other hosts. I’ve used the ping command successfully as a normal user on all operating systems I have tried and never had an issue. Currently, I do not have the time to investigate this limitation, but, judging by the exception I get, it has to do with the creation of the socket through which the ICMP packet is sent. The normal user does not have the required privileges to create this socket.

[Update #1]: This limitation also exists on the ping command, which runs with setuid access rights. (Thanks Stephane)

[Update #2]: After Chris Hallman reported that the original code actually works on Windows only, I made some changes to it and publish the updated code below.

The following code is a pure Python implementation of the ping command. I originally found it in the source code tree of pylucid in a subdirectory where the developers keep various code snippets.

I had originally tested the code under Microsoft Windows as this was the OS I had available at the moment. Chris Hallman noticed that the code does not work under GNU/Linux. After spending some hours trying to figure out what was wrong it, I realized that the part of the code that caused the failure on Linux was the use of the time.clock() function instead of time.time(). The clock() method works differently under Windows and Linux.

I fixed the 2007 implementation and hereby publish the updated code(as python-ping). Feel free to test it and report any issues.

Needed: test on Solaris.

#!/usr/bin/env python
 
"""
    A pure python ping implementation using raw socket.
 
 
    Note that ICMP messages can only be sent from processes running as root.
 
 
    Derived from ping.c distributed in Linux's netkit. That code is
    copyright (c) 1989 by The Regents of the University of California.
    That code is in turn derived from code written by Mike Muuss of the
    US Army Ballistic Research Laboratory in December, 1983 and
    placed in the public domain. They have my thanks.
 
    Bugs are naturally mine. I'd be glad to hear about them. There are
    certainly word - size dependenceies here.
 
    Copyright (c) Matthew Dixon Cowles, <http://www.visi.com/~mdc/>.
    Distributable under the terms of the GNU General Public License
    version 2. Provided with no warranties of any sort.
 
    Original Version from Matthew Dixon Cowles:
      -> ftp://ftp.visi.com/users/mdc/ping.py
 
    Rewrite by Jens Diemer:
      -> http://www.python-forum.de/post-69122.html#69122
 
    Rewrite by George Notaras:
      -> http://www.g-loaded.eu/2009/10/30/python-ping/
 
    Revision history
    ~~~~~~~~~~~~~~~~
 
    November 8, 2009
    ----------------
    Improved compatibility with GNU/Linux systems.
 
    Fixes by:
     * George Notaras -- http://www.g-loaded.eu
    Reported by:
     * Chris Hallman -- http://cdhallman.blogspot.com
 
    Changes in this release:
     - Re-use time.time() instead of time.clock(). The 2007 implementation
       worked only under Microsoft Windows. Failed on GNU/Linux.
       time.clock() behaves differently under the two OSes[1].
 
    [1] http://docs.python.org/library/time.html#time.clock
 
    May 30, 2007
    ------------
    little rewrite by Jens Diemer:
     -  change socket asterisk import to a normal import
     -  replace time.time() with time.clock()
     -  delete "return None" (or change to "return" only)
     -  in checksum() rename "str" to "source_string"
 
    November 22, 1997
    -----------------
    Initial hack. Doesn't do much, but rather than try to guess
    what features I (or others) will want in the future, I've only
    put in what I need now.
 
    December 16, 1997
    -----------------
    For some reason, the checksum bytes are in the wrong order when
    this is run under Solaris 2.X for SPARC but it works right under
    Linux x86. Since I don't know just what's wrong, I'll swap the
    bytes always and then do an htons().
 
    December 4, 2000
    ----------------
    Changed the struct.pack() calls to pack the checksum and ID as
    unsigned. My thanks to Jerome Poincheval for the fix.
 
 
    Last commit info:
    ~~~~~~~~~~~~~~~~~
    $LastChangedDate: $
    $Rev: $
    $Author: $
"""
 
 
import os, sys, socket, struct, select, time
 
# From /usr/include/linux/icmp.h; your milage may vary.
ICMP_ECHO_REQUEST = 8 # Seems to be the same on Solaris.
 
 
def checksum(source_string):
    """
    I'm not too confident that this is right but testing seems
    to suggest that it gives the same answers as in_cksum in ping.c
    """
    sum = 0
    countTo = (len(source_string)/2)*2
    count = 0
    while count<countTo:
        thisVal = ord(source_string[count + 1])*256 + ord(source_string[count])
        sum = sum + thisVal
        sum = sum & 0xffffffff # Necessary?
        count = count + 2
 
    if countTo<len(source_string):
        sum = sum + ord(source_string[len(source_string) - 1])
        sum = sum & 0xffffffff # Necessary?
 
    sum = (sum >> 16)  +  (sum & 0xffff)
    sum = sum + (sum >> 16)
    answer = ~sum
    answer = answer & 0xffff
 
    # Swap bytes. Bugger me if I know why.
    answer = answer >> 8 | (answer << 8 & 0xff00)
 
    return answer
 
 
def receive_one_ping(my_socket, ID, timeout):
    """
    receive the ping from the socket.
    """
    timeLeft = timeout
    while True:
        startedSelect = time.time()
        whatReady = select.select([my_socket], [], [], timeLeft)
        howLongInSelect = (time.time() - startedSelect)
        if whatReady[0] == []: # Timeout
            return
 
        timeReceived = time.time()
        recPacket, addr = my_socket.recvfrom(1024)
        icmpHeader = recPacket[20:28]
        type, code, checksum, packetID, sequence = struct.unpack(
            "bbHHh", icmpHeader
        )
        if packetID == ID:
            bytesInDouble = struct.calcsize("d")
            timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
            return timeReceived - timeSent
 
        timeLeft = timeLeft - howLongInSelect
        if timeLeft <= 0:
            return
 
 
def send_one_ping(my_socket, dest_addr, ID):
    """
    Send one ping to the given >dest_addr<.
    """
    dest_addr  =  socket.gethostbyname(dest_addr)
 
    # Header is type (8), code (8), checksum (16), id (16), sequence (16)
    my_checksum = 0
 
    # Make a dummy heder with a 0 checksum.
    header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1)
    bytesInDouble = struct.calcsize("d")
    data = (192 - bytesInDouble) * "Q"
    data = struct.pack("d", time.time()) + data
 
    # Calculate the checksum on the data and the dummy header.
    my_checksum = checksum(header + data)
 
    # Now that we have the right checksum, we put that in. It's just easier
    # to make up a new header than to stuff it into the dummy.
    header = struct.pack(
        "bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1
    )
    packet = header + data
    my_socket.sendto(packet, (dest_addr, 1)) # Don't know about the 1
 
 
def do_one(dest_addr, timeout):
    """
    Returns either the delay (in seconds) or none on timeout.
    """
    icmp = socket.getprotobyname("icmp")
    try:
        my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
    except socket.error, (errno, msg):
        if errno == 1:
            # Operation not permitted
            msg = msg + (
                " - Note that ICMP messages can only be sent from processes"
                " running as root."
            )
            raise socket.error(msg)
        raise # raise the original error
 
    my_ID = os.getpid() & 0xFFFF
 
    send_one_ping(my_socket, dest_addr, my_ID)
    delay = receive_one_ping(my_socket, my_ID, timeout)
 
    my_socket.close()
    return delay
 
 
def verbose_ping(dest_addr, timeout = 2, count = 4):
    """
    Send >count< ping to >dest_addr< with the given >timeout< and display
    the result.
    """
    for i in xrange(count):
        print "ping %s..." % dest_addr,
        try:
            delay  =  do_one(dest_addr, timeout)
        except socket.gaierror, e:
            print "failed. (socket error: '%s')" % e[1]
            break
 
        if delay  ==  None:
            print "failed. (timeout within %ssec.)" % timeout
        else:
            delay  =  delay * 1000
            print "get ping in %0.4fms" % delay
    print
 
 
if __name__ == '__main__':
    verbose_ping("heise.de")
    verbose_ping("google.com")
    verbose_ping("a-test-url-taht-is-not-available.com")
    verbose_ping("192.168.1.1")

I decided not to publish the updated code on CodeTRAX, where I normally publish any programming-related stuff, but leave it on G-Loaded instead.

ping.py – Python Implementation of the ping command by George Notaras is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Copyright © 2009 - Some Rights Reserved

George Notaras avatar

About George Notaras

George Notaras is the editor of the G-Loaded Journal, a technical blog about Free and Open-Source Software. George, among other things, is an enthusiast self-taught GNU/Linux system administrator. He has created this web site to share the IT knowledge and experience he has gained over the years with other people. George primarily uses CentOS and Fedora. He has also developed some open-source software projects in his spare time.

48 responses on “ping.py – Python Implementation of the ping command

  1. Stephane Permalink →

    roadrunner /tmp $ ls -l `which ping`
    -rws–x–x 1 root root 30532 2009-07-25 19:03 /bin/ping

    yeah, ping is setuid root, so it runs with root privileges no matter who runs it.

    Stephane

  2. Lee Permalink →

    Hi, there’s an implementation of the ICMP ping via impacket at core security.

    #!/usr/bin/python
    # Copyright (c) 2003 CORE Security Technologies
    #
    # This software is provided under under a slightly modified version
    # of the Apache Software License. See the accompanying LICENSE file
    # for more information.
    #
    # $Id: ping.py,v 1.3 2003/10/27 17:36:56 jkohen Exp $
    #
    # Simple ICMP ping.
    #
    # This implementation of ping uses the ICMP echo and echo-reply packets
    # to check the status of a host. If the remote host is up, it should reply
    # to the echo probe with an echo-reply packet.
    # Note that this isn't a definite test, as in the case the remote host is up
    # but refuses to reply the probes.
    # Also note that the user must have special access to be able to open a raw
    # socket, which this program requires.
    #
    # Authors:
    #  Gerardo Richarte <gera@coresecurity.com>
    #  Javier Kohen <jkohen@coresecurity.com>
    #
    # Reference for:
    #  ImpactPacket: IP, ICMP, DATA.
    #  ImpactDecoder.
     
    import select
    import socket
    import time
    import sys
     
    from impacket import ImpactDecoder, ImpactPacket
     
    if len(sys.argv) < 3:
    	print "Use: %s <src ip> <dst ip>" % sys.argv[0]
    	sys.exit(1)
     
    src = sys.argv[1]
    dst = sys.argv[2]
     
    # Create a new IP packet and set its source and destination addresses.
     
    ip = ImpactPacket.IP()
    ip.set_ip_src(src)
    ip.set_ip_dst(dst)
     
    # Create a new ICMP packet of type ECHO.
     
    icmp = ImpactPacket.ICMP()
    icmp.set_icmp_type(icmp.ICMP_ECHO)
     
    # Include a 156-character long payload inside the ICMP packet.
    icmp.contains(ImpactPacket.Data("A"*156))
     
    # Have the IP packet contain the ICMP packet (along with its payload).
    ip.contains(icmp)
     
    # Open a raw socket. Special permissions are usually required.
    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
    s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
     
    seq_id = 0
    while 1:
    	# Give the ICMP packet the next ID in the sequence.
    	seq_id += 1
    	icmp.set_icmp_id(seq_id)
     
    	# Calculate its checksum.
    	icmp.set_icmp_cksum(0)
    	icmp.auto_checksum = 1
     
    	# Send it to the target host.
    	s.sendto(ip.get_packet(), (dst, 0))
     
    	# Wait for incoming replies.
    	if s in select.select([s],[],[],1)[0]:
    	   reply = s.recvfrom(2000)[0]
     
    	   # Use ImpactDecoder to reconstruct the packet hierarchy.
    	   rip = ImpactDecoder.IPDecoder().decode(reply)
    	   # Extract the ICMP packet from its container (the IP packet).
    	   ricmp = rip.child()
     
    	   # If the packet matches, report it to the user.
    	   if rip.get_ip_dst() == src and rip.get_ip_src() == dst and icmp.ICMP_ECHOREPLY == ricmp.get_icmp_type():
    		   print "Ping reply for sequence #%d" % ricmp.get_icmp_id()
     
    	   time.sleep(1)

    basically impacket does all of the header packing and can chain the different levels of the payload.

  3. George Notaras Post authorPermalink →

    @Stephane: Never thought to check the permissions of the actual ping command. Thanks for pointing this out.

    @Lee: Thanks for bringing this module and the ping implementation based on that into my attention. Looks great. I had to alter the pasted code in your comment in order to add the license info in the header and apply syntax highlighting. I hope this is OK.

  4. chris hallman Permalink →

    I tried this and the delay is always 0, but I know better:

    [root@panma021 python]# python icmp.py
    ping a46dcorr01… get ping in 0.0000ms
    ping a46dcorr01… get ping in 0.0000ms
    ping a46dcorr01… get ping in 0.0000ms
    ping a46dcorr01… get ping in 0.0000ms

    [root@panma021 python]# ping a46dcorr01
    PING a46dcorr01.example.org (123.123.123.123) 56(84) bytes of data.
    64 bytes from A46DCORR01.EXAMPLE.ORG (123.123.123.123): icmp_seq=1 ttl=249 time=17.0 ms
    64 bytes from A46DCORR01.EXAMPLE.ORG (123.123.123.123): icmp_seq=2 ttl=249 time=23.5 ms
    64 bytes from A46DCORR01.EXAMPLE.ORG (123.123.123.123): icmp_seq=3 ttl=249 time=17.1 ms
    64 bytes from A46DCORR01.EXAMPLE.ORG (123.123.123.123): icmp_seq=4 ttl=249 time=17.3 ms

    Is this an intended result or a bug?

  5. chris hallman Permalink →

    I ran this with tcpdump. I see echo requests, but no replies. Any idea why?

  6. George Notaras Post authorPermalink →

    @Chris: It worked here as expected. BTW, I had to remove the domain names and IP addresses from your first comment. No references to online stores are allowed. Sorry.

  7. chris hallman Permalink →

    It’s a brick and mortar store. Anyway, it seems to work on Windows, but not on Linux. I was trying it on Linux originally.

  8. George Notaras Post authorPermalink →

    @Chris: Interesting. I had tried it on Windows only, since that was the only thing I had available at that moment.

  9. chris hallman Permalink →

    I really would like to get this working on Linux also so I can do some ICMP testing. I’ve reviewed the packet captures from Linux & Windows and the only difference I can see is the identification field in the IP header. On Linux it’s 0, yet Windows has an incrementing value. I’ve tried modifying the code you posted, but can’t quite figure out how to add an incrementing ID. The ‘my_ID’ variable in the program is the identification field in the ICMP header, therefore it’s not related to what I’m trying to change.

  10. George Notaras Post authorPermalink →

    @Chris: The time.clock() function behaves differently under Windows and Linux:

    time.clock()

    On Unix, return the current processor time as a floating point number expressed in seconds. The precision, and in fact the very definition of the meaning of “processor time”, depends on that of the C function of the same name, but in any case, this is the function to use for benchmarking Python or timing algorithms.

    On Windows, this function returns wall-clock seconds elapsed since the first call to this function, as a floating point number, based on the Win32 function QueryPerformanceCounter(). The resolution is typically better than one microsecond.

    I also suspect the following block of code

    if packetID == ID:
        bytesInDouble = struct.calcsize("d")
        timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
        print "timeReceived:", timeReceived, " -- ", "timeSent:", timeSent     ## ADDED BY GNOT
        return timeReceived - timeSent

    I added the line that is marked with the “ADDED BY GNOT” comment. The output shows that under linux the precision of the printed times is not adequate to calculate the ping delay.

  11. George Notaras Post authorPermalink →

    The problem with the code was the use of the time.clock() function. I fixed it and corrected the code that was published in the post above.

  12. chris hallman Permalink →

    You sir, are brilliant!

    I understand the change, however I don’t understand how it corrected operation of the program. Initially, tcpdump showed packets were being sent but no responses were received and the resulting RTT was 0.0ms. Now, I see requests, responses and accurate RTT. How did your change correct all this?

  13. George Notaras Post authorPermalink →

    Hi Chris, I really do not have an explanation about what you describe. I suspect that this happened because the time.clock() function was also used when attaching the timestamp to the ICMP packet just before sending, which, when done on Linux, does not provide adequate time resolution in order to calculate the time difference. But that’s just a wild guess.

  14. James Permalink →

    Just so you know all ping commands have the restriction. If you do an ls -l on the ping command on any *nix system, you will find the following

    -rwsr-xr-x 1 root root 35108 Jun 15 2004 /bin/ping

    Note the s in the permissions. Ping is setUID root. This allows unprivileged users the ability to use ping. Why this is done is because ping (ICMP) require that the interface be placed into a listening mode, which requires root privileges to do. Additionally ICMP itself is a low level protocol requiring root privilege. With ping you are working at the hardware level. With something like a web browser you are working much higher in the food chain.

    Is this a security problem, yes, which is why your python ping requires root. This is also why systems using busybox usually don’t allow normal users to do ping as setting ping setUID root can set all of the busybox commands setUID root.

  15. George Notaras Post authorPermalink →

    @James: Your explanation was helpful for me and will probably be for all the readers of this post. Thanks for writing it.

  16. Emre Kazdagli Permalink →

    Hello,

    It seems you send every packet with the same id and sequence number.

    This results in incorrect response times when short timeouts are used since no packets are discarded.

    It would be a good idea to increment sequence numbers and also replace:

    my_ID = os.getpid() & 0xFFFF

    with something like:

    my_ID = int(time.time() * 100000) & 0xFFFF

    so that same ID is not produced each time.

  17. Jim Calfas Permalink →

    I found that I needed to make a change to the program to make it run on HP-UX – a change which may also be necessary for portability to other platforms. The line which reads:

    “bbHHh”, ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1

    had to be changed to:

    “bbHHh”, ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum)&0xffff, ID, 1

    The reason why the change is necessary is that Python doesn’t support unsigned integers, so you can sometimes end up with negative numbers when you rearrange bits. On Linux, this change has no effect.

  18. Jim B Permalink →

    For thread safety, the socket functions need to be mutexed (except for the select() call), at least on Win32.

  19. Ken Corey Permalink →

    That’s interesting code…

    Unfortunately, there seems to be an error on line 95:
    while count> 16) + (sum & 0xffff)

    -Ken

  20. Arthur Lee Permalink →

    @Ken Corey: Problem caused by pasting the code without first converting to valid HTML. Copy and Paste from HTML source instead.

  21. Zach Ware Permalink →

    Here’s a version that works under Python 3.2:

    #!/usr/bin/env python
     
    """
        A pure python ping implementation using raw socket.
     
        Note that ICMP messages can only be sent from processes running as root.
     
        Derived from ping.c distributed in Linux's netkit. That code is
        copyright (c) 1989 by The Regents of the University of California.
        That code is in turn derived from code written by Mike Muuss of the
        US Army Ballistic Research Laboratory in December, 1983 and
        placed in the public domain. They have my thanks.
     
        Bugs are naturally mine. I'd be glad to hear about them. There are
        certainly word - size dependenceies here.
     
        Copyright (c) Matthew Dixon Cowles, .
        Distributable under the terms of the GNU General Public License
        version 2. Provided with no warranties of any sort.
     
        Original Version from Matthew Dixon Cowles:
          -> ftp://ftp.visi.com/users/mdc/ping.py
     
        Rewrite by Jens Diemer:
          -> http://www.python-forum.de/post-69122.html#69122
     
        Rewrite by George Notaras:
          -> http://www.g-loaded.eu/2009/10/30/python-ping/
     
        Revision history
        ~~~~~~~~~~~~~~~~
     
        August 3, 2011
        --------------
        Ported to py3k by Zach Ware. Mostly done by 2to3; also minor changes to
        deal with bytes vs. string changes (no more ord() in checksum() because
        >source_string< is actually bytes, added .encode() to data in
        send_one_ping()).  That&#039;s about it.
     
        November 8, 2009
        ----------------
        Improved compatibility with GNU/Linux systems.
     
        Fixes by:
         * George Notaras -- http://www.g-loaded.eu
        Reported by:
         * Chris Hallman -- http://cdhallman.blogspot.com
     
        Changes in this release:
         - Re-use time.time() instead of time.clock(). The 2007 implementation
           worked only under Microsoft Windows. Failed on GNU/Linux.
           time.clock() behaves differently under the two OSes[1].
     
        [1] http://docs.python.org/library/time.html#time.clock
     
        May 30, 2007
        ------------
        little rewrite by Jens Diemer:
         -  change socket asterisk import to a normal import
         -  replace time.time() with time.clock()
         -  delete "return None" (or change to "return" only)
         -  in checksum() rename "str" to "source_string"
     
        November 22, 1997
        -----------------
        Initial hack. Doesn&#039;t do much, but rather than try to guess
        what features I (or others) will want in the future, I&#039;ve only
        put in what I need now.
     
        December 16, 1997
        -----------------
        For some reason, the checksum bytes are in the wrong order when
        this is run under Solaris 2.X for SPARC but it works right under
        Linux x86. Since I don&#039;t know just what&#039;s wrong, I&#039;ll swap the
        bytes always and then do an htons().
     
        December 4, 2000
        ----------------
        Changed the struct.pack() calls to pack the checksum and ID as
        unsigned. My thanks to Jerome Poincheval for the fix.
     
        Last commit info:
        ~~~~~~~~~~~~~~~~~
        $LastChangedDate: $
        $Rev: $
        $Author: $
    """
     
    import os, sys, socket, struct, select, time
     
    # From /usr/include/linux/icmp.h; your milage may vary.
    ICMP_ECHO_REQUEST = 8 # Seems to be the same on Solaris.
     
    def checksum(source_string):
        """
        I&#039;m not too confident that this is right but testing seems
        to suggest that it gives the same answers as in_cksum in ping.c
        """
        sum = 0
        countTo = (len(source_string)/2)*2
        count = 0
        while count<countTo:
            thisVal = source_string[count + 1]*256 + source_string[count]
            sum = sum + thisVal
            sum = sum & 0xffffffff # Necessary?
            count = count + 2
     
        if countTo> 16)  +  (sum & 0xffff)
        sum = sum + (sum >> 16)
        answer = ~sum
        answer = answer & 0xffff
     
        # Swap bytes. Bugger me if I know why.
        answer = answer >> 8 | (answer << 8 & 0xff00)
     
        return answer
     
    def receive_one_ping(my_socket, ID, timeout):
        """
        receive the ping from the socket.
        """
        timeLeft = timeout
        while True:
            startedSelect = time.time()
            whatReady = select.select([my_socket], [], [], timeLeft)
            howLongInSelect = (time.time() - startedSelect)
            if whatReady[0] == []: # Timeout
                return
     
            timeReceived = time.time()
            recPacket, addr = my_socket.recvfrom(1024)
            icmpHeader = recPacket[20:28]
            type, code, checksum, packetID, sequence = struct.unpack(
                "bbHHh", icmpHeader
            )
            if packetID == ID:
                bytesInDouble = struct.calcsize("d")
                timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
                return timeReceived - timeSent
     
            timeLeft = timeLeft - howLongInSelect
            if timeLeft dest_addrcountdest_addrtimeout< and display
        the result.
        """
        for i in range(count):
            print("ping %s..." % dest_addr, end=&#039; &#039;)
            try:
                delay  =  do_one(dest_addr, timeout)
            except socket.gaierror as e:
                print("failed. (socket error: &#039;%s&#039;)" % e.args[1])
                break
     
            if delay  ==  None:
                print("failed. (timeout within %ssec.)" % timeout)
            else:
                delay  =  delay * 1000
                print("get ping in %0.4fms" % delay)
        print()
     
    if __name__ == &#039;__main__&#039;:
        verbose_ping("heise.de")
        verbose_ping("google.com")
        verbose_ping("a-test-url-taht-is-not-available.com")
        verbose_ping("localhost")
  22. George Notaras Post authorPermalink →

    Received this email by Jim B, and I thought it would be useful to contribute it to the discussion:

    For thread safety, the socket functions need to be mutexed (except for the select() call), at least on Win32.

  23. tim Permalink →

    So how do I go about changing the packet size of the ping request? Is it this bit here –

    data = (192 - bytesInDouble) * "Q"

  24. Marty Falatic Permalink →

    @George: Thanks for the original post! This is what I wanted to do when I first dove into Python, only to find that pinging is surprisingly difficult to do permissions-wise (it seemed so basic… but writing a kluge to invoke the system ‘ping’ regularly wasn’t a reasonable option so I’m glad to see this in pure Python).

    @Zach: Particular thanks for the Python 3.x version, as I’m working that far more than 2.x for my own scripts.

  25. Marty Falatic Permalink →

    Ouch… looks like @Zach’s code comment may have been mangled by the XHTML parser, with syntax errors apparently due to angle brackets in the code (gt, lt, that sort of thing) being interpreted as tags here.

  26. Marty Falatic Permalink →

    Actually, the original post is also corrupted… for example, “while count\> 16) + (sum & 0xffff)” is apparently missing some code. Again, it’s an HTML conversion problem.

  27. George Notaras Post authorPermalink →

    I am sorry for the inconvenience. I had uninstalled a syntax highlighter plugin from wordpress and it seems that this has caused the issue with escaping.

    Please download the ZIP file from: http://www.codetrax.org/projects/python-ping/files

    I always hated wordpress for this.

    This post will corrected and the Python3 version will be added as soon as I find some free time to do this. Thanks for your understanding. ;)

  28. Marty Falatic Permalink →

    I managed to parse out the bits that were missing (between examining the two versions, viewing the page sources (which had some of the missing info), and looking at the original ping.c source code), and I got both the 3.x and 2.x versions working as a baseline.

    Then I did a whole lot of rework and updating last night and posted a new and improved Python 3.x version of this script on my site (with a link back to your page here):

    http://www.falatic.com/index.php/39/pinging-with-python

    While I focused on the Python 3 implementation, this should be pretty trivial to backport to 2.x. It seems to be quite robust, though I haven’t tested it on big-endian hardware (in theory it should work fine or at least require only minor tweaks for such a platform… I did my best to plan ahead for that use case).

    Note: As for WordPress and source code, I’ve been using the “SyntaxHighlighter Evolved” plugin with good results.

    1. George Notaras Post authorPermalink →

      Marty, thanks for your work. As for the syntax highlighter, I’d been using wp-syntax, but decided to get rid of it recently. Apparently this has not worked very well, so I might use a syntax highlighter again. BTW, “SyntaxHighlighter Evolved” looks quite nice. Thanks.

  29. George Notaras Post authorPermalink →

    Finally, I have re-activated the WP-Syntax plugin. I hope all issues with character escaping are now resolved.

  30. Jens Diemer Permalink →

    I merged your changes and martin’s changes and updated my repo: (mod: link is invalid)

  31. George Notaras Post authorPermalink →

    @Martin, @Jens: Thanks for your work and contributions to this code. However, I have two remarks:

    1) it seems you have focused on Python 3, which has not yet been adopted by the majority of users.

    2) several changes to the original code have been introduced by your published snippets.

    To be honest, despite the fact that Martin has built upon the source code of the ping utility that ships with cygwin, I’d rather stick with the old version for now (published in this post), which has been around for over a decade, until the new changes are reviewed by other users. Unfortunately, I do not have the time or the required expertise to evaluate them. Thanks for your understanding.

  32. Marty Falatic Permalink →

    @Jens: I see that, though you primarily just backported my script to Python 2.x and removed all the delimiting comments I added for clarity. The only actual bugfix seems to be the signal handling modification – I assume that SIGBREAK caused some problem on Ubuntu?

  33. Marty Falatic Permalink →

    @George: Just saw your note. I don’t expect you to merge in my modifications – that’s why I published them separately, though per GPL you’re welcome to whatever bits you think are helpful.

    Yes, there are certainly changes to the code, as well as a more rigorous analysis of the checksum routine. My changes were not Cygwin-specific, it was just a handy version I could readily test against. Other than adding in the “average” ping time what I turned this into is pretty much the same as what the original public domain ping.c does.

    Note that @Jens backport *is* the Python 2 version, for those using that (yes, I realize mine may have a more niche audience, though I think it’s always valuable to have a Python 3 version out there as people move forward… which is why I focused on the Python 3 port).

  34. George Notaras Post authorPermalink →

    @Marty: Thanks for your feedback and your work. As soon as I find some free time, I’ll go through your changes. They seem to be of high quality and I’m quite certain that studying your snippet will be quite educative about how pinging is performed.

    PS: Note to self: It is about time I get python 3 installed in my system :)

  35. Luiz Carvalho Permalink →

    George, I’m assuming this does not work with IPv6. Do you have any plans to port this code to support ICMPv6?

    1. George Notaras Post authorPermalink →

      Hi Luiz.

      Currently, I am out of free time and, also, I’m not sure I have the required qualifications for such a task. But it would be an interesting task for the future. Also, there is another python library I’d like to check out at some point, impacket, and also scapy. I am quite certain the latter has support for ICMPV6, but I’ve never checked it out.

  36. Jens Diemer Permalink →

    I created a separate GIT Repository for ping.py here: [link removed. see note]

    I found the existing fork [link removed. see note] and merged everything together. Would be great if everyone worked on the same repository.

    A idea is to implement a subprocess call ping with a parser of the output, so that no admin right is needed: [link removed. see note]

    [MODERATED: Please provide some explanation about where this code derives and what it has to do with the code that has been posted in this post. This is becoming really confusing and I cannot follow. Sorry.]

  37. Ravi Permalink →

    How can I use the code for sub-domains ? I have example.org/testing and It returns ‘failed’. (socket error: ‘getaddrinfo failed’)

    Kindly help me in this.

    1. George Notaras Post authorPermalink →

      @Ravi

      ping does not accept URLs as argument. You have to use a hostname or an IP address. In the case of your subdomain use: ping example.org

  38. bobmon Permalink →

    In checksum() —

    > # Swap bytes. Bugger me if I know why.
    > answer = answer >> 8 | (answer << 8 & 0xff00)

    It's because your checksum algorithm explicitly does a big-endian computation. You must have done this on x86 or x86-64 architectures, which are little-endian. So the swap just converts your value to little-endian.

    Testable: if this fails on a big-endian Sparc architecture (older Solaris…) but works without the swap, then there you go. I wish I had a Sparc to try it out on.

  39. Guillaume Permalink →

    Hello, does anyone know how to set a source address for the ping packets?

    I want to add to this ping implementation the equivalent to the -S option of the original ping. Anyone?

    Thanks

  40. john Permalink →

    thanks for the post, i wanted to make one observation. the way you calculate the rtt is not the same as ping. from what i can tell you the the get the send time when sending a ping and and the recive time when you recive the ping. you then subtract one from the other to get the rtt. however ping.c sends the send time as the data payload and uses the value it gets pack in the echo response (not the value it has in memory) to calculate the rtt.

    im still trying to work out if/why this bothers me :). however thought i would point it out regardless

  41. RK Permalink →

    Bobmon is correct WRT to endianness. Network numerical types are all big-endian. x86 and x86_64 are little endian architectures so you need to swap byte order for all numbers you send and receive on the network.

    In C, we handle these with the hton* and ntoh* family of functions. These are available in python socket library, but you can use the ‘!’ modifier on your integers in your struct.pack()s and unpack()s to explicitly use the network order, which is probably a little more pythonic.

    1. George Notaras Post authorPermalink →

      Hello RK,

      Thanks for your feedback. Currently it is impossible for me to further contribute to this project. Pierre Bourdon had taken over in 2010 and is currently maintaining the ping library in PyPI. Please contact Pierre regarding contributions.

      Kind Regards,
      George