- 论坛徽章:
- 0
|
从网上找的 ping 源代码
方便大家阅读我把代码复制过来了
- #!/usr/bin/env python
- # -*- coding: iso-8859-1 -*-
- """ping.py
-
- ping.py uses the ICMP protocol's mandatory ECHO_REQUEST
- datagram to elicit an ICMP ECHO_RESPONSE from a
- host or gateway.
- Copyright (C) 2004 - Lars Strand <lars strand at gnist org>;
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- Must be running as root, or write a suid-wrapper. Since newer *nix
- variants, the kernel ignores the set[ug]id flags on #! scripts for
- security reasons
- RFC792, echo/reply message:
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Code | Checksum |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Identifier | Sequence Number |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Data ...
- +-+-+-+-+-
- TODO:
- - do not create socket inside 'while' (but if not: ipv6 won't work)
- - add support for broadcast/multicast
- - add support for own payload string
- CHANGELOG:
- DONE -->; bugfix from Filip Van Raemdonck mechanix debian org
- DONE -->; add more support for modules (raise instead of sys.exit)
- DONE -->; locale func names
- DONE -->; package def
- DONE -->; some code cleanup
-
- """
- import sys
- import os
- import struct
- import array
- import time
- import select
- import binascii
- import math
- import getopt
- import string
- import socket
- # total size of data (payload)
- ICMP_DATA_STR = 56
- # initial values of header variables
- ICMP_TYPE = 8
- ICMP_TYPE_IP6 = 128
- ICMP_CODE = 0
- ICMP_CHECKSUM = 0
- ICMP_ID = 0
- ICMP_SEQ_NR = 0
- # Package definitions.
- __program__ = 'ping'
- __version__ = '0.5a'
- __date__ = '2004/15/12'
- __author__ = 'Lars Strand <lars at unik no>;'
- __licence__ = 'GPL'
- __copyright__ = 'Copyright (C) 2004 Lars Strand'
- def _construct(id, size, ipv6):
- """Constructs a ICMP echo packet of variable size
- """
- # size must be big enough to contain time sent
- if size < int(struct.calcsize("d")):
- _error("packetsize to small, must be at least %d" % int(struct.calcsize("d")))
-
- # construct header
- if ipv6:
- header = struct.pack('BbHHh', ICMP_TYPE_IP6, ICMP_CODE, ICMP_CHECKSUM, \
- ICMP_ID, ICMP_SEQ_NR+id)
- else:
- header = struct.pack('bbHHh', ICMP_TYPE, ICMP_CODE, ICMP_CHECKSUM, \
- ICMP_ID, ICMP_SEQ_NR+id)
- # if size big enough, embed this payload
- load = "-- IF YOU ARE READING THIS YOU ARE A NERD! --"
-
- # space for time
- size -= struct.calcsize("d")
- # construct payload based on size, may be omitted :)
- rest = ""
- if size >; len(load):
- rest = load
- size -= len(load)
- # pad the rest of payload
- rest += size * "X"
- # pack
- data = struct.pack("d", time.time()) + rest
- packet = header + data # ping packet without checksum
- checksum = _in_cksum(packet) # make checksum
- # construct header with correct checksum
- if ipv6:
- header = struct.pack('BbHHh', ICMP_TYPE_IP6, ICMP_CODE, checksum, \
- ICMP_ID, ICMP_SEQ_NR+id)
- else:
- header = struct.pack('bbHHh', ICMP_TYPE, ICMP_CODE, checksum, ICMP_ID, \
- ICMP_SEQ_NR+id)
- # ping packet *with* checksum
- packet = header + data
- # a perfectly formatted ICMP echo packet
- return packet
- def _in_cksum(packet):
- """THE RFC792 states: 'The 16 bit one's complement of
- the one's complement sum of all 16 bit words in the header.'
- Generates a checksum of a (ICMP) packet. Based on in_chksum found
- in ping.c on FreeBSD.
- """
- # add byte if not dividable by 2
- if len(packet) & 1:
- packet = packet + '\0'
- # split into 16-bit word and insert into a binary array
- words = array.array('h', packet)
- sum = 0
- # perform ones complement arithmetic on 16-bit words
- for word in words:
- sum += (word & 0xffff)
- hi = sum >;>; 16
- lo = sum & 0xffff
- sum = hi + lo
- sum = sum + (sum >;>; 16)
-
- return (~sum) & 0xffff # return ones complement
- def pingNode(alive=0, timeout=1.0, ipv6=0, number=sys.maxint, node=None, \
- flood=0, size=ICMP_DATA_STR):
- """Pings a node based on input given to the function.
- """
- # if no node, exit
- if not node:
- _error("")
- # if not a valid host, exit
- if ipv6:
- if socket.has_ipv6:
- try:
- info, port = socket.getaddrinfo(node, None)
- host = info[4][0]
- # do not print ipv6 twice if ipv6 address given as node
- if host == node:
- noPrintIPv6adr = 1
- except:
- _error("cannot resolve %s: Unknow host" % node)
- else:
- _error("No support for IPv6 on this plattform")
- else: # IPv4
- try:
- host = socket.gethostbyname(node)
- except:
- _error("cannot resolve %s: Unknow host" % node)
- # trying to ping a network?
- if not ipv6:
- if int(string.split(host, ".")[-1]) == 0:
- _error("no support for network ping")
- # do some sanity check
- if number == 0:
- _error("invalid count of packets to transmit: '%s'" % str(a))
- if alive:
- number = 1
- # Send the ping(s)
- start = 1; mint = 999; maxt = 0.0; avg = 0.0
- lost = 0; tsum = 0.0; tsumsq = 0.0
- # tell the user what we do
- if not alive:
- if ipv6:
- # do not print the ipv6 twice if ip adress given as node
- # (it can be to long in term window)
- if noPrintIPv6adr == 1:
- # add 40 (header) + 8 (icmp header) + payload
- print "PING %s : %d data bytes (40+8+%d)" % (str(node), \
- 40+8+size, size)
- else:
- # add 40 (header) + 8 (icmp header) + payload
- print "PING %s (%s): %d data bytes (40+8+%d)" % (str(node), \
- str(host), 40+8+size, size)
- else:
- # add 20 (header) + 8 (icmp header) + payload
- print "PING %s (%s): %d data bytes (20+8+%d)" % (str(node), str(host), \
- 20+8+size, size)
-
- # trap ctrl-d and ctrl-c
- try:
-
- # send the number of ping packets as given
- while start <= number:
- lost += 1 # in case user hit ctrl-c
-
- # create the IPv6/IPv4 socket
- if ipv6:
- # can not create a raw socket if not root or setuid to root
- try:
- pingSocket = socket.socket(socket.AF_INET6, socket.SOCK_RAW, \
- socket.getprotobyname("ipv6-icmp"))
- except socket.error, e:
- print "socket error: %s" % e
- _error("You must be root (uses raw sockets)" % os.path.basename(sys.argv[0]))
-
- # IPv4
- else:
- # can not create a raw socket if not root or setuid to root
- try:
- pingSocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, \
- socket.getprotobyname("icmp"))
- except socket.error, e:
- print "socket error: %s" % e
- _error("You must be root (%s uses raw sockets)" % os.path.basename(sys.argv[0]))
-
- packet = _construct(start, size, ipv6) # make a ping packet
- # send the ping
- try:
- pingSocket.sendto(packet,(node,1))
- except socket.error, e:
- _error("socket error: %s" % e)
- # reset values
- pong = ""; iwtd = []
- # wait until there is data in the socket
- while 1:
- # input, output, exceptional conditions
- iwtd, owtd, ewtd = select.select([pingSocket], [], [], timeout)
- break # no data and timout occurred
- # data on socket - this means we have an answer
- if iwtd: # ok, data on socket
- endtime = time.time() # time packet received
- # read data (we only need the header)
- pong, address = pingSocket.recvfrom(size+48)
- lost -= 1 # in case user hit ctrl-c
- # examine packet
- # fetch TTL from IP header
- if ipv6:
- # since IPv6 header and any extension header are never passed
- # to a raw socket, we can *not* get hoplimit field..
- # I hoped that a socket option would help, but it's not
- # supported:
- # pingSocket.setsockopt(IPPROTO_IPV6, IPV6_RECVHOPLIMIT, 1)
- # so we can't fetch hoplimit..
- # fetch hoplimit
- #rawPongHop = struct.unpack("c", pong[7])[0]
- # fetch pong header
- pongHeader = pong[0:8]
- pongType, pongCode, pongChksum, pongID, pongSeqnr = \
- struct.unpack("bbHHh", pongHeader)
- # fetch starttime from pong
- starttime = struct.unpack("d", pong[8:16])[0]
- # IPv4
- else:
- # time to live
- rawPongHop = struct.unpack("s", pong[8])[0]
- # convert TTL from 8 bit to 16 bit integer
- pongHop = int(binascii.hexlify(str(rawPongHop)), 16)
- # fetch pong header
- pongHeader = pong[20:28]
- pongType, pongCode, pongChksum, pongID, pongSeqnr = \
- struct.unpack("bbHHh", pongHeader)
- # fetch starttime from pong
- starttime = struct.unpack("d", pong[28:36])[0]
- # valid ping packet received?
- if not pongSeqnr == start:
- pong = None
- # NO data on socket - timeout waiting for answer
- if not pong:
- if alive:
- print "no reply from %s (%s)" % (str(node), str(host))
- else:
- print "ping timeout: %s (icmp_seq=%d) " % (host, start)
- # do not wait if just sending one packet
- if number != 1 and start < number:
- time.sleep(flood ^ 1)
- start += 1
- continue # lost a packet - try again
- triptime = endtime - starttime # compute RRT
- tsum += triptime # triptime for all packets (stddev)
- tsumsq += triptime * triptime # triptime^2 for all packets (stddev)
- # compute statistic
- maxt = max ((triptime, maxt))
- mint = min ((triptime, mint))
- if alive:
- print str(node) + " (" + str(host) +") is alive"
- else:
- if ipv6:
- # size + 8 = payload + header
- print "%d bytes from %s: icmp_seq=%d time=%.5f ms" % \
- (size+8, host, pongSeqnr, triptime*1000)
- else:
- print "%d bytes from %s: icmp_seq=%d ttl=%s time=%.5f ms" % \
- (size+8, host, pongSeqnr, pongHop, triptime*1000)
- # do not wait if just sending one packet
- if number != 1 and start < number:
- # if flood = 1; do not sleep - just ping
- time.sleep(flood ^ 1) # wait before send new packet
- # the last thing to do is update the counter - else the value
- # (can) get wrong when computing summary at the end (if user
- # hit ctrl-c when pinging)
- start += 1
- # end ping send/recv while
- # if user ctrl-d or ctrl-c
- except (EOFError, KeyboardInterrupt):
- # if user disrupts ping, it is most likly done before
- # the counter get updates - if do not update it here, the
- # summary get all wrong.
- start += 1
- pass
- # compute and print som stats
- # stddev computation based on ping.c from FreeBSD
- if start != 0 or lost >; 0: # do not print stats if 0 packet sent
- start -= 1 # since while is '<='
- avg = tsum / start # avg round trip
- vari = tsumsq / start - avg * avg
- # %-packet lost
- if start == lost:
- plost = 100
- else:
- plost = (lost/start)*100
- if not alive:
- print "\n--- %s ping statistics ---" % node
- print "%d packets transmitted, %d packets received, %d%% packet loss" % \
- (start, start-lost, plost)
- # don't display summary if 100% packet-loss
- if plost != 100:
- print "round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms" % \
- (mint*1000, (tsum/start)*1000, maxt*1000, math.sqrt(vari)*1000)
- pingSocket.close()
-
- def _error(err):
- """Exit if running standalone, else raise an exception
- """
- if __name__ == '__main__':
- print "%s: %s" % (os.path.basename(sys.argv[0]), str(err))
- print "Try `%s --help' for more information." % os.path.basename(sys.argv[0])
- sys.exit(1)
- else:
- raise Exception, str(err)
-
- def _usage():
- """Print usage if run as a standalone program
- """
- print """usage: %s [OPTIONS] HOST
- Send ICMP ECHO_REQUEST packets to network hosts.
- Mandatory arguments to long options are mandatory for short options too.
- -c, --count=N Stop after sending (and receiving) 'N' ECHO_RESPONSE
- packets.
- -s, --size=S Specify the number of data bytes to be sent. The default
- is 56, which translates into 64 ICMP data bytes when
- combined with the 8 bytes of ICMP header data.
- -f, --flood Flood ping. Outputs packets as fast as they come back. Use
- with caution!
- -6, --ipv6 Ping using IPv6.
- -t, --timeout=s Specify a timeout, in seconds, before a ping packet is
- considered 'lost'.
- -h, --help Display this help and exit
- Report bugs to lars [at] gnist org""" % os.path.basename(sys.argv[0])
- if __name__ == '__main__':
- """Main loop
- """
- # version control
- version = string.split(string.split(sys.version)[0][:3], ".")
- if map(int, version) < [2, 3]:
- _error("You need Python ver 2.3 or higher to run!")
- try:
- # opts = arguments recognized,
- # args = arguments NOT recognized (leftovers)
- opts, args = getopt.getopt(sys.argv[1:-1], "hat:6c:fs:", \
- ["help", "alive", "timeout=", "ipv6", \
- "count=", "flood", "packetsize="])
- except getopt.GetoptError:
- # print help information and exit:
- _error("illegal option(s) -- " + str(sys.argv[1:]))
- # test whether any host given
- if len(sys.argv) >;= 2:
- node = sys.argv[-1:][0] # host to be pinged
- if node[0] == '-' or node == '-h' or node == '--help' :
- _usage()
- else:
- _error("No arguments given")
- if args:
- _error("illegal option -- %s" % str(args))
-
- # default variables
- alive = 0; timeout = 1.0; ipv6 = 0; count = sys.maxint;
- flood = 0; size = ICMP_DATA_STR
- # run through arguments and set variables
- for o, a in opts:
- if o == "-h" or o == "--help": # display help and exit
- _usage()
- sys.exit(0)
- if o == "-t" or o == "--timeout": # timeout before "lost"
- try:
- timeout = float(a)
- except:
- _error("invalid timout: '%s'" % str(a))
- if o == "-6" or o == "--ipv6": # ping ipv6
- ipv6 = 1
- if o == "-c" or o == "--count": # how many pings?
- try:
- count = int(a)
- except:
- _error("invalid count of packets to transmit: '%s'" % str(a))
- if o == "-f" or o == "--flood": # no delay between ping send
- flood = 1
- if o == "-s" or o == "--packetsize": # set the ping payload size
- try:
- size = int(a)
- except:
- _error("invalid packet size: '%s'" % str(a))
- # just send one packet and say "it's alive"
- if o == "-a" or o == "--alive":
- alive = 1
- # here we send
- pingNode(alive=alive, timeout=timeout, ipv6=ipv6, number=count, \
- node=node, flood=flood, size=size)
- # if we made it this far, do a clean exit
- sys.exit(0)
- ### end
复制代码 |
|