… | |
… | |
27 | use common::sense; |
27 | use common::sense; |
28 | |
28 | |
29 | use AnyEvent; |
29 | use AnyEvent; |
30 | |
30 | |
31 | BEGIN { |
31 | BEGIN { |
32 | our $VERSION = 2.03; |
32 | our $VERSION = 2.1; |
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 | |
… | |
… | |
39 | XSLoader::load (__PACKAGE__, $VERSION); |
39 | XSLoader::load (__PACKAGE__, $VERSION); |
40 | } |
40 | } |
41 | |
41 | |
42 | our ($THR_RES_FD, $ICMP4_FD, $ICMP6_FD); |
42 | our ($THR_RES_FD, $ICMP4_FD, $ICMP6_FD); |
43 | |
43 | |
44 | our $THR_RES_FH; open $THR_RES_FH, "<&=$THR_RES_FD" or die "FATAL: cannot fdopen"; |
44 | our $THR_RES_FH; |
45 | |
45 | |
46 | our $ICMP4_FH; |
46 | our $ICMP4_FH; |
47 | our $ICMP6_FH; |
47 | our $ICMP6_FH; |
48 | |
48 | |
49 | our @IDLE_CB; |
49 | our @IDLE_CB; |
50 | |
50 | |
51 | AnyEvent::post_detect { |
51 | =item AnyEvent::FastPing::ipv4_supported |
|
|
52 | |
|
|
53 | Returns true iff IPv4 is supported in this module and on this system. |
|
|
54 | |
|
|
55 | =item AnyEvent::FastPing::ipv6_supported |
|
|
56 | |
|
|
57 | Returns true iff IPv6 is supported in this module and on this system. |
|
|
58 | |
|
|
59 | =item AnyEvent::FastPing::icmp4_pktsize |
|
|
60 | |
|
|
61 | Returns the number of octets per IPv4 ping packet (the whole IP packet |
|
|
62 | including headers, excluding lower-level headers or trailers such as |
|
|
63 | Ethernet). |
|
|
64 | |
|
|
65 | Can 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 | |
|
|
74 | etc. |
|
|
75 | |
|
|
76 | =item AnyEvent::FastPing::icmp6_pktsize |
|
|
77 | |
|
|
78 | Like AnyEvent::FastPing::icmp4_pktsize, but for IPv6. |
|
|
79 | |
|
|
80 | =back |
|
|
81 | |
|
|
82 | =head1 THE AnyEvent::FastPing CLASS |
|
|
83 | |
|
|
84 | The AnyEvent::FastPing class represents a single "pinger". A "pinger" |
|
|
85 | comes with its own thread to send packets in the background, a rate-limit |
|
|
86 | machinery and separate idle/receive callbacks. |
|
|
87 | |
|
|
88 | The recommended workflow (there are others) is this: 1. create a new |
|
|
89 | AnyEvent::FastPing object 2. configure the address lists and ranges to |
|
|
90 | ping, also configure an idle callback and optionally a receive callback |
|
|
91 | 3. C<start> the pinger. |
|
|
92 | |
|
|
93 | When the pinger has finished pinging all the configured addresses it will |
|
|
94 | call the idle callback. |
|
|
95 | |
|
|
96 | The pinging process works like this: every range has a minimum interval |
|
|
97 | between sends, which is used to limit the rate at which hosts in that |
|
|
98 | range are being pinged. Distinct ranges are independent of each other, |
|
|
99 | which is why there is a per-pinger "global" minimum interval as well. |
|
|
100 | |
|
|
101 | The pinger sends pings as fats as possible, while both obeying the pinger |
|
|
102 | rate limit as well as range limits. |
|
|
103 | |
|
|
104 | When a range is exhausted, it is removed. When all ranges are exhausted, |
|
|
105 | the pinger waits another C<max_rtt> seconds and then exits, causing the |
|
|
106 | idle callback to trigger. |
|
|
107 | |
|
|
108 | Performance: On my 2 GHz Opteron system with a pretty average nvidia |
|
|
109 | gigabit network card I can ping around 60k to 200k addresses per second, |
|
|
110 | depending on routing decisions. |
|
|
111 | |
|
|
112 | Example: ping 10.0.0.1-10.0.0.15 with at most 100 packets/s, and |
|
|
113 | 11.0.0.1-11.0.255.255 with at most 1000 packets/s. Also ping the IPv6 |
|
|
114 | loopback address 5 times as fast as possible. Do not, however, exceed 1000 |
|
|
115 | packets/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 | |
|
|
151 | Creates a new pinger - right now there can be at most C<65536> pingers in |
|
|
152 | a process, although that limit might change to something drastically lower |
|
|
153 | - you should be stingy with your pinger objects. |
|
|
154 | |
|
|
155 | =cut |
|
|
156 | |
|
|
157 | sub new { |
|
|
158 | _boot; |
|
|
159 | |
52 | our $ICMP4_W = $ICMP4_FD >= 0 && (open $ICMP4_FH, "<&=$ICMP4_FD") && AE::io $ICMP4_FH, 0, \&_recv_icmp4; |
160 | our $ICMP4_W = $ICMP4_FD >= 0 && (open $ICMP4_FH, "<&=$ICMP4_FD") && AE::io $ICMP4_FH, 0, \&_recv_icmp4; |
53 | our $ICMP6_W = $ICMP6_FD >= 0 && (open $ICMP6_FH, "<&=$ICMP6_FD") && AE::io $ICMP6_FH, 0, \&_recv_icmp6; |
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"; |
54 | |
164 | |
55 | our $THR_RES_W = AE::io $THR_RES_FH, 0, sub { |
165 | our $THR_RES_W = AE::io $THR_RES_FH, 0, sub { |
56 | sysread $THR_RES_FH, my $buf, 8; |
166 | sysread $THR_RES_FH, my $buf, 8; |
57 | |
167 | |
58 | for my $id (unpack "S*", $buf) { |
168 | for my $id (unpack "S*", $buf) { |
59 | _stop_id $id; |
169 | _stop_id $id; |
60 | ($IDLE_CB[$id] || sub { })->(); |
170 | ($IDLE_CB[$id] || sub { })->(); |
61 | } |
171 | } |
62 | }; |
172 | }; |
63 | }; |
|
|
64 | |
173 | |
65 | =item AnyEvent::FastPing::ipv4_supported |
174 | *new = sub { |
66 | |
175 | _new shift, (rand 65536), (rand 65536), (rand 65536) |
67 | Returns true iff IPv4 is supported in this module and on this system. |
|
|
68 | |
|
|
69 | =item AnyEvent::FastPing::ipv6_supported |
|
|
70 | |
|
|
71 | Returns true iff IPv6 is supported in this module and on this system. |
|
|
72 | |
|
|
73 | =item AnyEvent::FastPing::icmp4_pktsize |
|
|
74 | |
|
|
75 | Returns the number of octets per IPv4 ping packet (the whole IP packet |
|
|
76 | including headers, excluding lower-level headers or trailers such as |
|
|
77 | Ethernet). |
|
|
78 | |
|
|
79 | Can 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 | |
|
|
88 | etc. |
|
|
89 | |
|
|
90 | =item AnyEvent::FastPing::icmp6_pktsize |
|
|
91 | |
|
|
92 | Like AnyEvent::FastPing::icmp4_pktsize, but for IPv6. |
|
|
93 | |
|
|
94 | =back |
|
|
95 | |
|
|
96 | =head1 THE AnyEvent::FastPing CLASS |
|
|
97 | |
|
|
98 | The AnyEvent::FastPing class represents a single "pinger". A "pinger" |
|
|
99 | comes with its own thread to send packets in the background, a rate-limit |
|
|
100 | machinery and separate idle/receive callbacks. |
|
|
101 | |
|
|
102 | The recommended workflow (there are others) is this: 1. create a new |
|
|
103 | AnyEvent::FastPing object 2. configure the address lists and ranges to |
|
|
104 | ping, also configure an idle callback and optionally a receive callback |
|
|
105 | 3. C<start> the pinger. |
|
|
106 | |
|
|
107 | When the pinger has finished pinging all the configured addresses it will |
|
|
108 | call the idle callback. |
|
|
109 | |
|
|
110 | The pinging process works like this: every range has a minimum interval |
|
|
111 | between sends, which is used to limit the rate at which hosts in that |
|
|
112 | range are being pinged. Distinct ranges are independent of each other, |
|
|
113 | which is why there is a per-pinger "global" minimum interval as well. |
|
|
114 | |
|
|
115 | The pinger sends pings as fats as possible, while both obeying the pinger |
|
|
116 | rate limit as well as range limits. |
|
|
117 | |
|
|
118 | When a range is exhausted, it is removed. When all ranges are exhausted, |
|
|
119 | the pinger waits another C<max_rtt> seconds and then exits, causing the |
|
|
120 | idle callback to trigger. |
|
|
121 | |
|
|
122 | Performance: On my 2 GHz Opteron system with a pretty average nvidia |
|
|
123 | gigabit network card I can ping around 60k to 200k addresses per second, |
|
|
124 | depending on routing decisions. |
|
|
125 | |
|
|
126 | Example: ping 10.0.0.1-10.0.0.15 with at most 100 packets/s, and |
|
|
127 | 11.0.0.1-11.0.255.255 with at most 1000 packets/s. Also ping the IPv6 |
|
|
128 | loopback address 5 times as fast as possible. Do not, however, exceed 1000 |
|
|
129 | packets/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 | }); |
176 | }; |
150 | |
177 | |
151 | $pinger->on_idle (sub { |
178 | goto &new; |
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 | |
|
|
165 | Creates a new pinger - right now there can be at most C<65536> pingers in |
|
|
166 | a process, although that limit might change to something drastically lower |
|
|
167 | - you should be stingy with your pinger objects. |
|
|
168 | |
|
|
169 | =cut |
|
|
170 | |
|
|
171 | sub new { |
|
|
172 | my ($klass) = @_; |
|
|
173 | |
|
|
174 | AnyEvent::detect unless defined $AnyEvent::MODEL; |
|
|
175 | |
|
|
176 | _new $klass, (rand 65536), (rand 65536), (rand 65536) |
|
|
177 | } |
179 | } |
178 | |
180 | |
179 | sub DESTROY { |
181 | sub DESTROY { |
180 | undef $IDLE_CB[ &id ]; |
182 | undef $IDLE_CB[ &id ]; |
181 | &_free; |
183 | &_free; |