1#!/usr/bin/env python
2
3import math, optparse, random, socket, sys, time
4import dpkt
5
6class Ping(object):
7    def __init__(self):
8        usage = '%prog [OPTIONS] <host>'
9        self.op = optparse.OptionParser(usage=usage)
10        self.op.add_option('-c', dest='count', type='int', default=sys.maxint,
11                           help='Total number of queries to send')
12        self.op.add_option('-i', dest='wait', type='float', default=1,
13                           help='Specify packet interval timeout in seconds')
14
15    def gen_ping(self, opts):
16        pass
17    def open_sock(self, opts):
18        pass
19    def print_header(self, opts):
20        pass
21    def print_reply(self, opts, buf):
22        pass
23
24    def main(self, argv=None):
25        if not argv:
26            argv = sys.argv[1:]
27        opts, args = self.op.parse_args(argv)
28
29        if not args:
30            self.op.error('missing host')
31        elif len(args) > 1:
32            self.op.error('only one host may be specified')
33
34        host = args[0]
35        opts.ip = socket.gethostbyname(host)
36        sock = self.open_sock(opts)
37
38        sent = rcvd = rtt_max = rtt_sum = rtt_sumsq = 0
39        rtt_min = 0xffff
40        try:
41            self.print_header(opts)
42            for ping in self.gen_ping(opts):
43                try:
44                    start = time.time()
45                    sock.send(ping)
46                    buf = sock.recv(0xffff)
47                    rtt = time.time() - start
48
49                    if rtt < rtt_min: rtt_min = rtt
50                    if rtt > rtt_max: rtt_max = rtt
51                    rtt_sum += rtt
52                    rtt_sumsq += rtt * rtt
53
54                    self.print_reply(opts, buf, rtt)
55                    rcvd += 1
56                except socket.timeout:
57                    pass
58                sent += 1
59                time.sleep(opts.wait)
60        except KeyboardInterrupt:
61            pass
62
63        print '\n--- %s ping statistics ---' % opts.ip
64        print '%d packets transmitted, %d packets received, %.1f%% packet loss' % \
65              (sent, rcvd, (float(sent - rcvd) / sent) * 100)
66        rtt_avg = rtt_sum / sent
67        if rtt_min == 0xffff: rtt_min = 0
68        print 'round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms' % \
69              (rtt_min * 1000, rtt_avg * 1000, rtt_max * 1000,
70               math.sqrt((rtt_sumsq / sent) - (rtt_avg * rtt_avg)) * 1000)
71
72class ICMPPing(Ping):
73    def __init__(self):
74        Ping.__init__(self)
75        self.op.add_option('-p', dest='payload', type='string',
76                           default='hello world!',
77                           help='Echo payload string')
78
79    def open_sock(self, opts):
80        sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, 1)
81        sock.connect((opts.ip, 1))
82        sock.settimeout(opts.wait)
83        return sock
84
85    def gen_ping(self, opts):
86        for i in xrange(opts.count):
87            icmp = dpkt.icmp.ICMP(
88                type=8, data=dpkt.icmp.ICMP.Echo(id=random.randint(0, 0xffff),
89                                                 seq=i, data=opts.payload))
90            yield str(icmp)
91
92    def print_header(self, opts):
93        print 'PING %s: %d data bytes' % (opts.ip, len(opts.payload))
94
95    def print_reply(self, opts, buf, rtt):
96        ip = dpkt.ip.IP(buf)
97        if sys.platform == 'darwin':
98            # XXX - work around raw socket bug on MacOS X
99            ip.data = ip.icmp = dpkt.icmp.ICMP(buf[20:])
100            ip.len = len(ip.data)
101        print '%d bytes from %s: icmp_seq=%d ip_id=%d ttl=%d time=%.3f ms' % \
102              (len(ip.icmp), opts.ip, ip.icmp.echo.seq, ip.id, ip.ttl,
103               rtt * 1000)
104
105if __name__ == '__main__':
106    p = ICMPPing()
107    p.main()
108