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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines