Sending ECHO_REQUEST (pinging) with python

Michael Bentley michael at jedimindworks.com
Mon Mar 26 10:37:42 CEST 2007


On Mar 26, 2007, at 1:30 AM, Thomas Dybdahl Ahle wrote:
> Hi, I've writing a python application in which I'd like to have a  
> small
> "ping label", to always tell the current ping time to the server.
>
> It seems however that I have to be root to send those imcp  
> packages, but
> I guess there must be a workaround since I can easily use the "ping"
> command as ordinary user.
>
> Do anybody know how to do this in python?
>

This won't solve your privileges issue, but this seems to get the  
ping time to server:

import socket
import os
import sys
import struct
import time
import select

# 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.

# Note that ICMP messages can only be sent from processes running
# as root.

# Revision history:
#
# 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.
#

# From /usr/include/linux/icmp.h; your milage may vary.
ICMP_ECHO_REQUEST = 8 # Seems to be the same on Solaris.

# 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
def checksum(str):
     csum = 0
     countTo = (len(str) / 2) * 2
     count = 0
     while count < countTo:
         thisVal = ord(str[count+1]) * 256 + ord(str[count])
         csum = csum + thisVal
         csum = csum & 0xffffffffL # Necessary?
         count = count + 2

     if countTo < len(str):
         csum = csum + ord(str[len(str) - 1])
         csum = csum & 0xffffffffL # Necessary?

     csum = (csum >> 16) + (csum & 0xffff)
     csum = csum + (csum >> 16)
     answer = ~csum
     answer = answer & 0xffff

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

     return answer

def receiveOnePing(mySocket, ID, timeout):
     timeLeft = timeout

     while 1:
         startedSelect = time.time()
         whatReady = select.select([mySocket], [], [], timeLeft)
         howLongInSelect = (time.time() - startedSelect)

         if whatReady[0] == []: # Timeout
             return -1

         timeReceived = time.time()
         recPacket, addr = mySocket.recvfrom(1024)
         icmpHeader = recPacket[20:28]
         typ, 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 -1

def sendOnePing(mySocket, destAddr, ID):
     # Header is type (8), code (8), checksum (16), id (16), sequence  
(16)
     myChecksum = 0

     # Make a dummy heder with a 0 checksum.
     header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum,  
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.
     myChecksum = 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.
     if sys.platform == 'darwin':
         myChecksum = socket.htons(myChecksum) & 0xffff
     else:
         myChecksum = socket.htons(myChecksum)

     header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0,
                         myChecksum, ID, 1)

     packet = header + data
     mySocket.sendto(packet, (destAddr, 1)) # Don't know about the 1

def doOne(destAddr, timeout=10):
     # Returns either the delay (in seconds) or none on timeout.
     icmp = socket.getprotobyname("icmp")
     mySocket = socket.socket(socket.AF_INET,socket.SOCK_RAW,icmp)
     myID = os.getpid() & 0xFFFF
     sendOnePing(mySocket, destAddr, myID)
     delay = receiveOnePing(mySocket, myID, timeout)
     mySocket.close()

     return delay


def ping(host, timeout=1):
     dest = socket.gethostbyname(host)
     delay = doOne(dest, timeout)
     return delay


Hope this helps,
Michael

---
The Rules of Optimization are simple.
Rule 1: Don't do it.
Rule 2 (for experts only): Don't do it yet.
-- Michael A. Jackson , "Principles of
Program Design", 1975.





More information about the Python-list mailing list