ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-FastPing/FastPing.pm
Revision: 1.12
Committed: Tue Feb 1 04:06:24 2011 UTC (13 years, 3 months ago) by root
Branch: MAIN
Changes since 1.11: +18 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     AnyEvent::FastPing - quickly ping a large number of hosts
4    
5     =head1 SYNOPSIS
6    
7     use AnyEvent::FastPing;
8    
9     =head1 DESCRIPTION
10    
11 root 1.2 This module was written for a single purpose only: sending ICMP ECHO
12 root 1.1 REQUEST packets as quickly as possible to a large number of hosts
13     (thousands to millions).
14    
15     It employs a sending thread and is fully event-driven (using AnyEvent), so
16     you have to run an event model supported by AnyEvent to use this module.
17    
18     =head1 FUNCTIONS
19    
20     =over 4
21    
22     =cut
23    
24     package AnyEvent::FastPing;
25    
26 root 1.9 use common::sense;
27 root 1.1
28     use AnyEvent;
29    
30     BEGIN {
31 root 1.11 our $VERSION = '2.0';
32 root 1.1 our @ISA = qw(Exporter);
33    
34     require Exporter;
35     #Exporter::export_ok_tags (keys %EXPORT_TAGS);
36    
37     require XSLoader;
38     XSLoader::load (__PACKAGE__, $VERSION);
39     }
40    
41 root 1.11 our ($THR_RES_FD, $ICMP4_FD, $ICMP6_FD);
42 root 1.1
43     our $THR_RES_FH; open $THR_RES_FH, "<&=$THR_RES_FD" or die "FATAL: cannot fdopen";
44    
45 root 1.11 our $ICMP4_FH; our $ICMP4_W = $ICMP4_FD >= 0 && (open $ICMP4_FH, "<&=$ICMP4_FD") && AE::io $ICMP4_FH, 0, \&_recv_icmp4;
46     our $ICMP6_FH; our $ICMP6_W = $ICMP6_FD >= 0 && (open $ICMP6_FH, "<&=$ICMP6_FD") && AE::io $ICMP6_FH, 0, \&_recv_icmp6;
47 root 1.1
48     =item AnyEvent::FastPing::ipv4_supported
49    
50     Returns true if IPv4 is supported in this module and on this system.
51    
52     =item AnyEvent::FastPing::ipv6_supported
53    
54     Returns true if IPv6 is supported in this module and on this system.
55    
56     =item AnyEvent::FastPing::icmp4_pktsize
57    
58     Returns the number of bytes each IPv4 ping packet has.
59    
60     =item AnyEvent::FastPing::icmp6_pktsize
61    
62     Returns the number of bytes each IPv4 ping packet has.
63    
64 root 1.11 =cut
65    
66     sub new {
67     my ($klass) = @_;
68    
69     _new $klass, (rand 65536), (rand 65536), (rand 65536)
70     }
71    
72     our @IDLE_CB;
73    
74     sub DESTROY {
75     undef $IDLE_CB[ &id ];
76     &_free;
77     }
78    
79     sub on_idle {
80     $IDLE_CB[ &id ] = $_[1];
81     }
82    
83     our $THR_RES_W = AE::io $THR_RES_FH, 0, sub {
84     sysread $THR_RES_FH, my $buf, 8;
85    
86     for my $id (unpack "S*", $buf) {
87     _stop_id $id;
88     ($IDLE_CB[$id] || sub { })->();
89     }
90     };
91    
92     for(1..10) {
93     my $p = new AnyEvent::FastPing;#d#
94     $p->interval (0);
95     $p->max_rtt (0.5);
96     #$p->add_range (v127.0.0.1, v127.255.255.254, 0);
97 root 1.12 $p->add_range (v127.0.0.1, v127.0.0.1, 0);
98     #$p->add_range (v1.0.0.1, v1.255.255.254, 0);
99 root 1.11 $p->on_idle (my $cv = AE::cv);
100     my $cnt;
101     $p->on_recv (sub {
102     $cnt++;
103     });
104     $p->start;
105 root 1.12
106     {
107     my $p = new AnyEvent::FastPing;#d#
108     $p->interval (0);
109     $p->max_rtt (0.5);
110     $p->add_hosts ([v0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2, (v0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1)x8, v0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.3], 0);
111     my $cnt;
112     $p->on_recv (sub {
113     use Data::Dump; ddx \@_;
114     });
115     $p->on_idle (sub {
116     undef $p;
117     });
118     $p->start;
119     }
120    
121 root 1.11 $cv->recv;
122     warn $cnt;
123     }
124    
125 root 1.1 =item AnyEvent::FastPing::icmp_ping [ranges...], $send_interval, $payload, \&callback
126    
127     Ping the given IPv4 address ranges. Each range is an arrayref of the
128     form C<[lo, hi, interval]>, where C<lo> and C<hi> are octet strings with
129     either 4 octets (for IPv4 addresses) or 16 octets (for IPV6 addresses),
130     representing the lowest and highest address to ping (you can convert a
131     dotted-quad IPv4 address to this format by using C<inet_aton $address>. The
132     range C<interval> is the minimum time in seconds between pings to the
133     given range. If omitted, defaults to C<$send_interval>.
134    
135     The C<$send_interval> is the minimum interval between sending any two
136     packets and is a way to make an overall rate limit. If omitted, pings will
137 root 1.5 be sent as fast as possible.
138 root 1.1
139     The C<$payload> is a 32 bit unsigned integer given as the ICMP ECHO
140     REQUEST ident and sequence numbers (in unspecified order :).
141    
142     The request will be queued and all requests will be served by a background
143     thread in order. When all ranges have been pinged, the C<callback> will be
144     called.
145    
146     Algorithm: Each range has an associated "next time to send packet"
147     time. The algorithm loops as long as there are ranges with hosts to be
148     pinged and always serves the range with the most urgent packet send
149     time. It will at most send one packet every C<$send_interval> seconds.
150    
151     This will ensure that pings to the same range are nicely interleaved with
152     other ranges - this can help reduce per-subnet bandwidth while maintaining
153     an overall high packet rate.
154    
155     The algorithm to send each packet is O(log n) on the number of ranges, so
156     even a large number of ranges (many thousands) is managable.
157    
158     No storage is allocated per address.
159    
160     Performance: On my 2 GHz Opteron system with a pretty average nvidia
161     gigabit network card I can ping around 60k to 200k adresses per second,
162     depending on routing decisions.
163    
164     Example: ping 10.0.0.1-10.0.0.15 with at most 100 packets/s, and
165     11.0.0.1-11.0.255.255 with at most 1000 packets/s. Do not, however, exceed
166     1000 packets/s overall:
167    
168     my $done = AnyEvent->condvar;
169    
170     AnyEvent::FastPing::icmp_ping
171     [
172     [v10.0.0.1, v10.0.0.15, .01],
173     [v11.0.0.1, v11.0.255.255, .001],
174     ],
175     .001, 0x12345678,
176     sub {
177     warn "all ranges pinged\n";
178     $done->broadcast;
179     }
180     ;
181    
182     $done->wait;
183    
184     =cut
185    
186     sub icmp_ping($$$&) {
187 root 1.11 # _send_req _req_icmp_ping @_;
188 root 1.1 }
189    
190     =item AnyEvent::FastPing::register_cb \&cb
191    
192     Register a callback that is called for every received ping reply
193     (regardless of whether a ping is still in process or not and regardless of
194     whether the reply is actually a reply to a ping sent earlier).
195    
196     The code reference gets a single parameter - an arrayref with an
197 root 1.6 entry for each received packet (replies are being batched for greater
198 root 1.1 efficiency). Each packet is represented by an arrayref with three members:
199     the source address (an octet string of either 4 (IPv4) or 16 (IPv6) octets
200     length), the payload as passed to C<icmp_ping> and the round trip time in
201     seconds.
202    
203 root 1.8 Example: register a callback which simply dumps the received data. Since
204     the coderef is created on the fly via sub, it would be hard to unregister
205     this callback again :)
206    
207     AnyEvent::FastPing::register_cb sub {
208     for (@{$_[0]}) {
209     printf "%s %d %g\n",
210     (4 == length $_->[0] ? inet_ntoa $_->[0] : Socket6::inet_ntop (&AF_INET6, $_->[0])),
211     $_->[2],
212     $_->[1];
213     }
214     };
215    
216 root 1.1 Example: a single ping reply with payload of 1 from C<::1> gets passed
217     like this:
218    
219     [ [
220     "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1",
221     "0.000280141830444336",
222     1
223     ] ]
224    
225     Example: ping replies for C<127.0.0.1> and C<127.0.0.2>, with a payload of
226     C<0x12345678>:
227    
228     [
229     [
230     "\177\0\0\1",
231     "0.00015711784362793",
232     305419896
233     ],
234     [
235     "\177\0\0\2",
236     "0.00090184211731",
237     305419896
238     ]
239     ]
240    
241     =item AnyEvent::FastPing::unregister_cb \&cb
242    
243     Unregister the callback again (make sure you pass the same codereference
244     as to C<register_cb>).
245    
246     =cut
247    
248     our @CB;
249    
250 root 1.8 sub register_cb($) {
251 root 1.1 push @CB, $_[0];
252     }
253    
254     sub unregister_cb($) {
255     @CB = grep $_ != $_[0], @CB;
256     }
257    
258     1;
259    
260     =back
261    
262     =head1 AUTHOR
263    
264 root 1.4 Marc Lehmann <schmorp@schmorp.de>
265     http://home.schmorp.de/
266 root 1.1
267 root 1.4 =head1 LICENSE
268 root 1.1
269 root 1.4 This software is distributed under the GENERAL PUBLIC LICENSE, version 2
270     or any later version or, at your option, the Artistic License.
271 root 1.1
272     =cut
273