ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-FastPing/FastPing.pm
Revision: 1.11
Committed: Mon Jan 31 05:35:48 2011 UTC (13 years, 3 months ago) by root
Branch: MAIN
Changes since 1.10: +49 -27 lines
Log Message:
halfway?

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 (v1.0.0.1, v1.255.255.254, 0);
98 $p->on_idle (my $cv = AE::cv);
99 my $cnt;
100 $p->on_recv (sub {
101 $cnt++;
102 });
103 $p->start;
104 $cv->recv;
105 warn $cnt;
106 }
107
108 =item AnyEvent::FastPing::icmp_ping [ranges...], $send_interval, $payload, \&callback
109
110 Ping the given IPv4 address ranges. Each range is an arrayref of the
111 form C<[lo, hi, interval]>, where C<lo> and C<hi> are octet strings with
112 either 4 octets (for IPv4 addresses) or 16 octets (for IPV6 addresses),
113 representing the lowest and highest address to ping (you can convert a
114 dotted-quad IPv4 address to this format by using C<inet_aton $address>. The
115 range C<interval> is the minimum time in seconds between pings to the
116 given range. If omitted, defaults to C<$send_interval>.
117
118 The C<$send_interval> is the minimum interval between sending any two
119 packets and is a way to make an overall rate limit. If omitted, pings will
120 be sent as fast as possible.
121
122 The C<$payload> is a 32 bit unsigned integer given as the ICMP ECHO
123 REQUEST ident and sequence numbers (in unspecified order :).
124
125 The request will be queued and all requests will be served by a background
126 thread in order. When all ranges have been pinged, the C<callback> will be
127 called.
128
129 Algorithm: Each range has an associated "next time to send packet"
130 time. The algorithm loops as long as there are ranges with hosts to be
131 pinged and always serves the range with the most urgent packet send
132 time. It will at most send one packet every C<$send_interval> seconds.
133
134 This will ensure that pings to the same range are nicely interleaved with
135 other ranges - this can help reduce per-subnet bandwidth while maintaining
136 an overall high packet rate.
137
138 The algorithm to send each packet is O(log n) on the number of ranges, so
139 even a large number of ranges (many thousands) is managable.
140
141 No storage is allocated per address.
142
143 Performance: On my 2 GHz Opteron system with a pretty average nvidia
144 gigabit network card I can ping around 60k to 200k adresses per second,
145 depending on routing decisions.
146
147 Example: ping 10.0.0.1-10.0.0.15 with at most 100 packets/s, and
148 11.0.0.1-11.0.255.255 with at most 1000 packets/s. Do not, however, exceed
149 1000 packets/s overall:
150
151 my $done = AnyEvent->condvar;
152
153 AnyEvent::FastPing::icmp_ping
154 [
155 [v10.0.0.1, v10.0.0.15, .01],
156 [v11.0.0.1, v11.0.255.255, .001],
157 ],
158 .001, 0x12345678,
159 sub {
160 warn "all ranges pinged\n";
161 $done->broadcast;
162 }
163 ;
164
165 $done->wait;
166
167 =cut
168
169 sub icmp_ping($$$&) {
170 # _send_req _req_icmp_ping @_;
171 }
172
173 =item AnyEvent::FastPing::register_cb \&cb
174
175 Register a callback that is called for every received ping reply
176 (regardless of whether a ping is still in process or not and regardless of
177 whether the reply is actually a reply to a ping sent earlier).
178
179 The code reference gets a single parameter - an arrayref with an
180 entry for each received packet (replies are being batched for greater
181 efficiency). Each packet is represented by an arrayref with three members:
182 the source address (an octet string of either 4 (IPv4) or 16 (IPv6) octets
183 length), the payload as passed to C<icmp_ping> and the round trip time in
184 seconds.
185
186 Example: register a callback which simply dumps the received data. Since
187 the coderef is created on the fly via sub, it would be hard to unregister
188 this callback again :)
189
190 AnyEvent::FastPing::register_cb sub {
191 for (@{$_[0]}) {
192 printf "%s %d %g\n",
193 (4 == length $_->[0] ? inet_ntoa $_->[0] : Socket6::inet_ntop (&AF_INET6, $_->[0])),
194 $_->[2],
195 $_->[1];
196 }
197 };
198
199 Example: a single ping reply with payload of 1 from C<::1> gets passed
200 like this:
201
202 [ [
203 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1",
204 "0.000280141830444336",
205 1
206 ] ]
207
208 Example: ping replies for C<127.0.0.1> and C<127.0.0.2>, with a payload of
209 C<0x12345678>:
210
211 [
212 [
213 "\177\0\0\1",
214 "0.00015711784362793",
215 305419896
216 ],
217 [
218 "\177\0\0\2",
219 "0.00090184211731",
220 305419896
221 ]
222 ]
223
224 =item AnyEvent::FastPing::unregister_cb \&cb
225
226 Unregister the callback again (make sure you pass the same codereference
227 as to C<register_cb>).
228
229 =cut
230
231 our @CB;
232
233 sub register_cb($) {
234 push @CB, $_[0];
235 }
236
237 sub unregister_cb($) {
238 @CB = grep $_ != $_[0], @CB;
239 }
240
241 1;
242
243 =back
244
245 =head1 AUTHOR
246
247 Marc Lehmann <schmorp@schmorp.de>
248 http://home.schmorp.de/
249
250 =head1 LICENSE
251
252 This software is distributed under the GENERAL PUBLIC LICENSE, version 2
253 or any later version or, at your option, the Artistic License.
254
255 =cut
256