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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines