ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-FastPing/FastPing.pm
(Generate patch)

Comparing AnyEvent-FastPing/FastPing.pm (file contents):
Revision 1.2 by root, Sat May 17 21:47:29 2008 UTC vs.
Revision 1.17 by root, Mon Oct 13 11:15:15 2014 UTC

10 10
11This module was written for a single purpose only: sending ICMP ECHO 11This module was written for a single purpose only: sending ICMP ECHO
12REQUEST packets as quickly as possible to a large number of hosts 12REQUEST packets as quickly as possible to a large number of hosts
13(thousands to millions). 13(thousands to millions).
14 14
15It employs a sending thread and is fully event-driven (using AnyEvent), so 15It employs a separate thread and is fully event-driven (using AnyEvent),
16you have to run an event model supported by AnyEvent to use this module. 16so you have to run an event model supported by AnyEvent to use this
17module.
17 18
18=head1 FUNCTIONS 19=head1 FUNCTIONS
19 20
20=over 4 21=over 4
21 22
22=cut 23=cut
23 24
24package AnyEvent::FastPing; 25package AnyEvent::FastPing;
25 26
26use strict; 27use common::sense;
27no warnings;
28 28
29use AnyEvent; 29use AnyEvent;
30 30
31BEGIN { 31BEGIN {
32 our $VERSION = '1.1'; 32 our $VERSION = 2.02;
33 our @ISA = qw(Exporter); 33 our @ISA = qw(Exporter);
34 34
35 require Exporter; 35 require Exporter;
36 #Exporter::export_ok_tags (keys %EXPORT_TAGS); 36 #Exporter::export_ok_tags (keys %EXPORT_TAGS);
37 37
38 require XSLoader; 38 require XSLoader;
39 XSLoader::load (__PACKAGE__, $VERSION); 39 XSLoader::load (__PACKAGE__, $VERSION);
40} 40}
41 41
42our ($THR_REQ_FD, $THR_RES_FD, $ICMP4_FD, $ICMP6_FD); 42our ($THR_RES_FD, $ICMP4_FD, $ICMP6_FD);
43 43
44our $THR_REQ_FH; open $THR_REQ_FH, ">&=$THR_REQ_FD" or die "FATAL: cannot fdopen";
45our $THR_RES_FH; open $THR_RES_FH, "<&=$THR_RES_FD" or die "FATAL: cannot fdopen"; 44our $THR_RES_FH; open $THR_RES_FH, "<&=$THR_RES_FD" or die "FATAL: cannot fdopen";
46 45
47our $THR_REQ_W; 46our $ICMP4_FH; our $ICMP4_W = $ICMP4_FD >= 0 && (open $ICMP4_FH, "<&=$ICMP4_FD") && AE::io $ICMP4_FH, 0, \&_recv_icmp4;
48our $THR_RES_W = AnyEvent->io (fh => $THR_RES_FH, poll => 'r', cb => sub { 47our $ICMP6_FH; our $ICMP6_W = $ICMP6_FD >= 0 && (open $ICMP6_FH, "<&=$ICMP6_FD") && AE::io $ICMP6_FH, 0, \&_recv_icmp6;
49 my $sv = _read_res
50 or return;
51 48
52 $sv->(); 49=item AnyEvent::FastPing::ipv4_supported
53});
54 50
55our $THR_REQ_BUF; 51Returns true iff IPv4 is supported in this module and on this system.
56 52
57sub _send_req($) { 53=item AnyEvent::FastPing::ipv6_supported
58 $THR_REQ_BUF .= $_[0];
59 54
60 $THR_REQ_W ||= AnyEvent->io (fh => $THR_REQ_FH, poll => 'w', cb => sub { 55Returns true iff IPv6 is supported in this module and on this system.
61 my $len = syswrite $THR_REQ_FH, $THR_REQ_BUF;
62 substr $THR_REQ_BUF, 0, $len, "";
63 56
64 undef $THR_REQ_W unless length $THR_REQ_BUF; 57=item AnyEvent::FastPing::icmp4_pktsize
58
59Returns the number of octets per IPv4 ping packet (the whole IP packet
60including headers, excluding lower-level headers or trailers such as
61Ethernet).
62
63Can be used to calculate e.g. octets/s from rate ...
64
65 my $octets_per_second = $packets_per_second * AnyEvent::FastPing::icmp4_pktsize;
66
67... or convert kilobit/second to packet rate ...
68
69 my $packets_per_second = $kilobit_per_second
70 * (1000 / 8 / AnyEvent::FastPing::icmp4_pktsize);
71
72etc.
73
74=item AnyEvent::FastPing::icmp6_pktsize
75
76Like AnyEvent::FastPing::icmp4_pktsize, but for IPv6.
77
78=back
79
80=head1 THE AnyEvent::FastPing CLASS
81
82The AnyEvent::FastPing class represents a single "pinger". A "pinger"
83comes with its own thread to send packets in the background, a rate-limit
84machinery and separate idle/receive callbacks.
85
86The recommended workflow (there are others) is this: 1. create a new
87AnyEvent::FastPing object 2. configure the address lists and ranges to
88ping, also configure an idle callback and optionally a receive callback
893. C<start> the pinger.
90
91When the pinger has finished pinging all the configured addresses it will
92call the idle callback.
93
94The pinging process works like this: every range has a minimum interval
95between sends, which is used to limit the rate at which hosts in that
96range are being pinged. Distinct ranges are independent of each other,
97which is why there is a per-pinger "global" minimum interval as well.
98
99The pinger sends pings as fats as possible, while both obeying the pinger
100rate limit as well as range limits.
101
102When a range is exhausted, it is removed. When all ranges are exhausted,
103the pinger waits another C<max_rtt> seconds and then exits, causing the
104idle callback to trigger.
105
106Performance: On my 2 GHz Opteron system with a pretty average nvidia
107gigabit network card I can ping around 60k to 200k addresses per second,
108depending on routing decisions.
109
110Example: ping 10.0.0.1-10.0.0.15 with at most 100 packets/s, and
11111.0.0.1-11.0.255.255 with at most 1000 packets/s. Also ping the IPv6
112loopback address 5 times as fast as possible. Do not, however, exceed 1000
113packets/s overall. Also dump each received reply.
114
115 use AnyEvent::Socket;
116 use AnyEvent::FastPing;
117
118 my $done = AnyEvent->condvar;
119
120 my $pinger = new AnyEvent::FastPing;
121
122 $pinger->interval (1/1000);
123 $pinger->max_rtt (0.1); # reasonably fast/reliable network
124
125 $pinger->add_range (v10.0.0.1, v10.0.0.15, 1/100);
126 $pinger->add_range (v11.0.0.1, v11.0.255.255, 1/1000);
127 $pinger->add_hosts ([ (v0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1) x 5 ]);
128
129 $pinger->on_recv (sub {
130 for (@{ $_[0] }) {
131 printf "%s %g\n", (AnyEvent::Socket::format_address $_->[0]), $_->[1];
132 }
65 }); 133 });
134
135 $pinger->on_idle (sub {
136 print "done\n";
137 undef $pinger;
138 });
139
140 $pinger->start;
141 $done->wait;
142
143=head2 METHODS
144
145=over 4
146
147=item $pinger = new AnyEvent::FastPing
148
149Creates a new pinger - right now there can be at most C<65536> pingers in
150a process, although that limit might change to something drastically lower
151- you should be stingy with your pinger objects.
152
153=cut
154
155sub new {
156 my ($klass) = @_;
157
158 _new $klass, (rand 65536), (rand 65536), (rand 65536)
66} 159}
67 160
68=item AnyEvent::FastPing::ipv4_supported 161our @IDLE_CB;
69 162
70Returns true if IPv4 is supported in this module and on this system. 163sub DESTROY {
164 undef $IDLE_CB[ &id ];
165 &_free;
166}
71 167
72=item AnyEvent::FastPing::ipv6_supported 168=item $pinger->on_recv ($callback->([[$host, $rtt], ...]))
73 169
74Returns true if IPv6 is supported in this module and on this system. 170Registers a callback to be called for ping replies. If no callback has
171been registered than ping replies will be ignored, otherwise this module
172calculates the round trip time, in seconds, for each reply and calls this
173callback.
75 174
76=item AnyEvent::FastPing::icmp4_pktsize 175The callback receives a single argument, which is an array reference
176with an entry for each reply packet (the replies will be batched for
177efficiency). Each member in the array reference is again an array
178reference with exactly two members: the binary host address (4 octets for
179IPv4, 16 for IPv6) and the approximate round trip time, in seconds.
77 180
78Returns the number of bytes each IPv4 ping packet has. 181The replies will be passed to the callback as soon as they arrive, and
182this callback can be called many times with batches of replies.
79 183
80=item AnyEvent::FastPing::icmp6_pktsize 184The receive callback will be called whenever a suitable reply arrives,
185whether generated by this pinger or not, whether this pinger is started
186or not. The packets will have a unique 64 bit ID to distinguish them from
187other pinger objects and other generators, but this doesn't help against
188malicious replies.
81 189
82Returns the number of bytes each IPv4 ping packet has. 190Note that very high packet rates can overwhelm your process, causing
191replies to be dropped (configure your kernel with long receive queues for
192raw sockets if this is a problem).
83 193
84=item AnyEvent::FastPing::icmp_ping [ranges...], $send_interval, $payload, \&callback 194Example: register a callback which simply dumps the received data.
85 195
86Ping the given IPv4 address ranges. Each range is an arrayref of the 196 use AnyEvent::Socket;
87form C<[lo, hi, interval]>, where C<lo> and C<hi> are octet strings with
88either 4 octets (for IPv4 addresses) or 16 octets (for IPV6 addresses),
89representing the lowest and highest address to ping (you can convert a
90dotted-quad IPv4 address to this format by using C<inet_aton $address>. The
91range C<interval> is the minimum time in seconds between pings to the
92given range. If omitted, defaults to C<$send_interval>.
93 197
94The C<$send_interval> is the minimum interval between sending any two 198 $pinger->on_recv (sub {
95packets and is a way to make an overall rate limit. If omitted, pings will 199 for (@{ $_[0] }) {
96be send as fast as possible. 200 printf "%s %g\n", (AnyEvent::Socket::format_address $_->[0]), $_->[1];
97
98The C<$payload> is a 32 bit unsigned integer given as the ICMP ECHO
99REQUEST ident and sequence numbers (in unspecified order :).
100
101The request will be queued and all requests will be served by a background
102thread in order. When all ranges have been pinged, the C<callback> will be
103called.
104
105Algorithm: Each range has an associated "next time to send packet"
106time. The algorithm loops as long as there are ranges with hosts to be
107pinged and always serves the range with the most urgent packet send
108time. It will at most send one packet every C<$send_interval> seconds.
109
110This will ensure that pings to the same range are nicely interleaved with
111other ranges - this can help reduce per-subnet bandwidth while maintaining
112an overall high packet rate.
113
114The algorithm to send each packet is O(log n) on the number of ranges, so
115even a large number of ranges (many thousands) is managable.
116
117No storage is allocated per address.
118
119Performance: On my 2 GHz Opteron system with a pretty average nvidia
120gigabit network card I can ping around 60k to 200k adresses per second,
121depending on routing decisions.
122
123Example: ping 10.0.0.1-10.0.0.15 with at most 100 packets/s, and
12411.0.0.1-11.0.255.255 with at most 1000 packets/s. Do not, however, exceed
1251000 packets/s overall:
126
127 my $done = AnyEvent->condvar;
128
129 AnyEvent::FastPing::icmp_ping
130 [
131 [v10.0.0.1, v10.0.0.15, .01],
132 [v11.0.0.1, v11.0.255.255, .001],
133 ],
134 .001, 0x12345678,
135 sub {
136 warn "all ranges pinged\n";
137 $done->broadcast;
138 } 201 }
139 ; 202 });
140
141 $done->wait;
142
143=cut
144
145sub icmp_ping($$$&) {
146 _send_req _req_icmp_ping @_;
147}
148
149our $ICMP4_FH;
150our $ICMP4_W = (open $ICMP4_FH, "<&=$ICMP4_FD") && AnyEvent->io (fh => $ICMP4_FH, poll => 'r', cb => \&_recv_icmp4);
151our $ICMP6_FH;
152our $ICMP6_W = (open $ICMP6_FH, "<&=$ICMP6_FD") && AnyEvent->io (fh => $ICMP6_FH, poll => 'r', cb => \&_recv_icmp6);
153
154=item AnyEvent::FastPing::register_cb \&cb
155
156Register a callback that is called for every received ping reply
157(regardless of whether a ping is still in process or not and regardless of
158whether the reply is actually a reply to a ping sent earlier).
159
160The code reference gets a single parameter - an arrayref with an
161entry for each received packet (replies are beign batched for greater
162efficiency). Each packet is represented by an arrayref with three members:
163the source address (an octet string of either 4 (IPv4) or 16 (IPv6) octets
164length), the payload as passed to C<icmp_ping> and the round trip time in
165seconds.
166 203
167Example: a single ping reply with payload of 1 from C<::1> gets passed 204Example: a single ping reply with payload of 1 from C<::1> gets passed
168like this: 205like this:
169 206
170 [ [
171 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1",
172 "0.000280141830444336",
173 1
174 ] ]
175
176Example: ping replies for C<127.0.0.1> and C<127.0.0.2>, with a payload of
177C<0x12345678>:
178
179 [ 207 [
180 [ 208 [ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1", 0.000280141830444336 ]
181 "\177\0\0\1",
182 "0.00015711784362793",
183 305419896
184 ],
185 [
186 "\177\0\0\2",
187 "0.00090184211731",
188 305419896
189 ]
190 ] 209 ]
191 210
192=item AnyEvent::FastPing::unregister_cb \&cb 211Example: ping replies for C<127.0.0.1> and C<127.0.0.2>:
193 212
194Unregister the callback again (make sure you pass the same codereference 213 [
195as to C<register_cb>). 214 [ "\177\0\0\1", 0.00015711784362793 ],
215 [ "\177\0\0\2", 0.00090184211731 ]
216 ]
196 217
197=cut 218=item $pinger->on_idle ($callback->())
198 219
199our @CB; 220Registers a callback to be called when the pinger becomes I<idle>, that
221is, it has been started, has exhausted all ping ranges and waited for
222the C<max_rtt> time. An idle pinger is also stopped, so the callback can
223instantly add new ranges, if it so desires.
200 224
201sub register_cb(&) { 225=cut
202 push @CB, $_[0]; 226
227sub on_idle {
228 $IDLE_CB[ &id ] = $_[1];
203} 229}
204 230
205sub unregister_cb($) { 231our $THR_RES_W = AE::io $THR_RES_FH, 0, sub {
206 @CB = grep $_ != $_[0], @CB; 232 sysread $THR_RES_FH, my $buf, 8;
207} 233
234 for my $id (unpack "S*", $buf) {
235 _stop_id $id;
236 ($IDLE_CB[$id] || sub { })->();
237 }
238};
239
240=item $pinger->interval ($seconds)
241
242Configures the minimum interval between packet sends for this pinger - the
243pinger will not send packets faster than this rate (or actually 1 / rate),
244even if individual ranges have a lower interval.
245
246A value of C<0> selects the fastest possible speed (currently no faster
247than 1_000_000 packets/s).
248
249=item $pinger->max_rtt ($seconds)
250
251If your idle callback were called instantly after all ranges were
252exhausted and you destroyed the object inside (which is common), then
253there would be no chance to receive some replies, as there would be no
254time of the packet to travel over the network.
255
256This can be fixed by starting a timer in the idle callback, or more simply
257by selecting a suitable C<max_rtt> value, which should be the maximum time
258you allow a ping packet to travel to its destination and back.
259
260The pinger thread automatically waits for this amount of time before becoming idle.
261
262The default is currently C<0.5> seconds, which is usually plenty.
263
264=item $pinger->add_range ($lo, $hi[, $interval])
265
266Ping the IPv4 (or IPv6, but see below) address range, starting at binary
267address C<$lo> and ending at C<$hi> (both C<$lo> and C<$hi> will be
268pinged), generating no more than one ping per C<$interval> seconds (or as
269fast as possible if omitted).
270
271You can convert IP addresses from text to binary form by
272using C<AnyEvent::Util::parse_address>, C<Socket::inet_aton>,
273C<Socket6::inet_pton> or any other method that you like :)
274
275The algorithm to select the next address is O(log n) on the number of
276ranges, so even a large number of ranges (many thousands) is manageable.
277
278No storage is allocated per address.
279
280Note that, while IPv6 addresses are currently supported, the usefulness of
281this option is extremely limited and might be gone in future versions - if
282you want to ping a number of IPv6 hosts, better specify them individually
283using the C<add_hosts> method.
284
285=item $pinger->add_hosts ([$host...], $interval, $interleave)
286
287Similar to C<add_range>, but uses a list of single addresses instead. The
288list is specified as an array reference as first argument. Each entry in
289the array should be a binary host address, either IPv4 or IPv6. If all
290addresses are IPv4 addresses, then a compact IPv4-only format will be used
291to store the list internally.
292
293Minimum C<$interval> is the same as for C<add_range> and can be left out.
294
295C<$interlave> specifies an increment between addresses: often address
296lists are generated in a way that results in clustering - first all
297addresses from one subnet, then from the next, and so on. To avoid this,
298you can specify an interleave factor. If it is C<1> (the default), then
299every address is pinged in the order specified. If it is C<2>, then only
300every second address will be pinged in the first round, followed by a
301second round with the others. Higher factors will create C<$interleave>
302runs of addresses spaced C<$interleave> indices in the list.
303
304The special value C<0> selects a (hopefully) suitable interleave factor
305automatically - currently C<256> for lists with less than 65536 addresses,
306and the square root of the list length otherwise.
307
308=item $pinger->start
309
310Start the pinger, unless it is running already. While a pinger is running
311you must not modify the pinger. If you want to change a parameter, you
312have to C<stop> the pinger first.
313
314The pinger will automatically stop when destroyed.
315
316=item $pinger->stop
317
318Stop the pinger, if it is running. A pinger can be stopped at any time,
319after which it's current state is preserved - starting it again will
320continue where it left off.
321
322=cut
208 323
2091; 3241;
210 325
211=back 326=back
212 327
213=head1 AUTHOR 328=head1 AUTHOR
214 329
215 Marc Lehmann <schmorp@schmorp.de> 330 Marc Lehmann <schmorp@schmorp.de>
216 http://home.schmorp.de/ 331 http://home.schmorp.de/
217 332
218=head1 AUTHOR 333=head1 LICENSE
219 334
220 This software is distributed under the GENERAL PUBLIC LICENSE, version 2 335 This software is distributed under the GENERAL PUBLIC LICENSE, version 2
221 or any later version or, at your option, the Artistic License. 336 or any later version or, at your option, the Artistic License.
222 337
223=cut 338=cut
224 339

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines