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

# Content
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 This module was written for a single purpose only: sending ICMP ECHO
12 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 use common::sense;
27
28 use AnyEvent;
29
30 BEGIN {
31 our $VERSION = '2.0';
32 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 our ($THR_RES_FD, $ICMP4_FD, $ICMP6_FD);
42
43 our $THR_RES_FH; open $THR_RES_FH, "<&=$THR_RES_FD" or die "FATAL: cannot fdopen";
44
45 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
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 =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 $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 $p->on_idle (my $cv = AE::cv);
100 my $cnt;
101 $p->on_recv (sub {
102 $cnt++;
103 });
104 $p->start;
105
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 $cv->recv;
122 warn $cnt;
123 }
124
125 =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 be sent as fast as possible.
138
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 # _send_req _req_icmp_ping @_;
188 }
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 entry for each received packet (replies are being batched for greater
198 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 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 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 sub register_cb($) {
251 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 Marc Lehmann <schmorp@schmorp.de>
265 http://home.schmorp.de/
266
267 =head1 LICENSE
268
269 This software is distributed under the GENERAL PUBLIC LICENSE, version 2
270 or any later version or, at your option, the Artistic License.
271
272 =cut
273