icmp4em 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +12 -30
- data/lib/icmp4em/common.rb +0 -1
- data/lib/icmp4em/icmpv4.rb +52 -29
- metadata +4 -4
data/README
CHANGED
@@ -3,11 +3,13 @@ icmp4em - ICMP ping using EventMachine
|
|
3
3
|
http://www.github.com/yakischloba/icmp4em
|
4
4
|
|
5
5
|
Asynchronous implementation of ICMP ping using EventMachine. Can be used to ping many hosts
|
6
|
-
at once in a non-blocking fashion, with callbacks for success, timeout, and host failure/recovery
|
6
|
+
at once in a non-blocking (or blocking) fashion, with callbacks for success, timeout, and host failure/recovery
|
7
7
|
based on specified threshold numbers. It was developed as a component for the purpose of monitoring
|
8
8
|
multiple WAN connections on a Linux gateway host, and altering the routing table accordingly, to
|
9
9
|
provide optimal routing and failover in a situation where only consumer-grade broadband is available.
|
10
|
-
Should be useful in many situations.
|
10
|
+
Should be useful in many situations. Still planning on adding ICMPv6 sometime..
|
11
|
+
|
12
|
+
Should work fine with Ruby 1.9.1.
|
11
13
|
|
12
14
|
This must be run as effective root user to use the ICMP sockets.
|
13
15
|
|
@@ -40,7 +42,12 @@ Got echo sequence number 5 from host 127.0.0.1. It took 0.217ms.
|
|
40
42
|
^C
|
41
43
|
============================================================================
|
42
44
|
|
43
|
-
|
45
|
+
One issue may run into is that when using this with a lot of hosts, or using EventMachine for other things,
|
46
|
+
extra apparent latency will be added. This is simply due to other operations blocking the receipt of the ICMP
|
47
|
+
response. For when accurate latency measurement is important, and you can afford to block the reactor, I have
|
48
|
+
added a blocking receive mode. To use this simply pass :block => true to the constructor. This will wait for
|
49
|
+
the response for the duration of the timeout before passing control back to EventMachine.
|
50
|
+
|
44
51
|
|
45
52
|
jakecdouglas@gmail.com
|
46
53
|
yakischloba on Freenode
|
@@ -48,31 +55,6 @@ yakischloba on Freenode
|
|
48
55
|
|
49
56
|
Thanks to the #eventmachine guys for providing a great tool and always being such patient teachers.
|
50
57
|
|
51
|
-
Thanks to imperator and the others that worked on the net-ping library. I used the packet construction and checksum
|
52
|
-
code from that implementation. Here is the pertinent information from their README, so that nothing is missed:
|
53
|
-
|
54
|
-
Acknowledgements from "net-ping-1.2.2/doc/ping.txt":
|
55
|
-
|
56
|
-
= Acknowledgements
|
57
|
-
The Ping::ICMP#ping method is based largely on the identical method from
|
58
|
-
the Net::Ping Perl module by Rob Brown. Much of the code was ported by
|
59
|
-
Jos Backus on ruby-talk.
|
60
58
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
= License
|
65
|
-
Ruby's
|
66
|
-
|
67
|
-
= Copyright
|
68
|
-
(C) 2003-2008 Daniel J. Berger, All Rights Reserved
|
69
|
-
|
70
|
-
= Warranty
|
71
|
-
This package is provided "as is" and without any express or
|
72
|
-
implied warranties, including, without limitation, the implied
|
73
|
-
warranties of merchantability and fitness for a particular purpose.
|
74
|
-
|
75
|
-
= Author
|
76
|
-
Daniel J. Berger
|
77
|
-
djberg96 at gmail dot com
|
78
|
-
imperator on IRC (irc.freenode.net)
|
59
|
+
Thanks to imperator and the others that worked on the net-ping library. I used the packet construction and checksum
|
60
|
+
code from that implementation. See their README for detailed acknowledgements.
|
data/lib/icmp4em/common.rb
CHANGED
data/lib/icmp4em/icmpv4.rb
CHANGED
@@ -11,15 +11,30 @@ module ICMP4EM
|
|
11
11
|
class << self
|
12
12
|
|
13
13
|
attr_reader :instances
|
14
|
-
attr_accessor :recvsocket
|
14
|
+
attr_accessor :recvsocket, :handler
|
15
15
|
|
16
16
|
end
|
17
17
|
|
18
|
-
attr_accessor :bind_host, :interval, :threshold, :timeout, :data
|
18
|
+
attr_accessor :bind_host, :interval, :threshold, :timeout, :data, :block
|
19
19
|
attr_reader :id, :failures_required, :recoveries_required, :seq
|
20
20
|
|
21
|
-
# Create a new ICMP object (host).
|
22
|
-
#
|
21
|
+
# Create a new ICMP object (host). This takes a host and an optional hash of options for modifying the behavior. They are:
|
22
|
+
# * :bind_host
|
23
|
+
# Bind the socket to this address. The operating system will figure this out on it's own unless you need to set it for a special situation.
|
24
|
+
# * :timeout
|
25
|
+
# Timeout, in seconds, before the ping is considered expired and the appropriate callbacks are executed. This should be a numeric class.
|
26
|
+
# * :block
|
27
|
+
# True or false, default is false. True enables a blocking receive mode, for when accurate latency measurement is important. Due to the nature
|
28
|
+
# of event loop architecture, a noticable delay in latency can be added when other things are going on in the reactor.
|
29
|
+
# * :interval
|
30
|
+
# Interval, in seconds, for how often the ping should be sent. Should be a numeric class.
|
31
|
+
# * :stateful
|
32
|
+
# True or false, default is false. Indicates whether or not this ping object should keep track of it's successes and failures and execute
|
33
|
+
# the on_failure/on_recovery callbacks when the specified limits are hit.
|
34
|
+
# * :failures_required
|
35
|
+
# Indicates how many consequtive failures are required to switch to the 'failed' state and execute the on_failure callback. Applies only when :stateful => true
|
36
|
+
# * :recoveries_required
|
37
|
+
# Indicates how many consequtive successes are required to switch to the 'recovered' state and execute the on_recovery callback. Applies only when :stateful => true
|
23
38
|
def initialize(host, options = {})
|
24
39
|
raise 'requires root privileges' if Process.euid > 0
|
25
40
|
@host = host
|
@@ -28,6 +43,7 @@ module ICMP4EM
|
|
28
43
|
@timeout = options[:timeout] || 1
|
29
44
|
@stateful = options[:stateful] || false
|
30
45
|
@bind_host = options[:bind_host] || nil
|
46
|
+
@block = options[:block] || false
|
31
47
|
@recoveries_required = options[:recoveries_required] || 5
|
32
48
|
@failures_required = options[:failures_required] || 5
|
33
49
|
@up = true
|
@@ -38,9 +54,10 @@ module ICMP4EM
|
|
38
54
|
end
|
39
55
|
|
40
56
|
# This must be called when the object will no longer be used, to remove
|
41
|
-
# the object from the class variable
|
42
|
-
# an ICMP echo comes in. Better way to do this whole thing?...
|
43
|
-
def
|
57
|
+
# the object from the class variable hash that is searched for recipients when
|
58
|
+
# an ICMP echo comes in. Also cancels the periodic timer. Better way to do this whole thing?...
|
59
|
+
def stop
|
60
|
+
@ptimer.cancel if @ptimer
|
44
61
|
self.class.instances[@id] = nil
|
45
62
|
end
|
46
63
|
|
@@ -48,15 +65,19 @@ module ICMP4EM
|
|
48
65
|
def ping
|
49
66
|
raise "EM not running" unless EM.reactor_running?
|
50
67
|
init_handler if self.class.recvsocket.nil?
|
51
|
-
seq = ping_send
|
52
|
-
|
68
|
+
@seq = ping_send
|
69
|
+
if @block
|
70
|
+
blocking_receive
|
71
|
+
else
|
72
|
+
EM.add_timer(@timeout) { self.send(:expire, @seq, Timeout.new("Ping timed out")) } unless @timeout == 0
|
73
|
+
end
|
53
74
|
@seq
|
54
75
|
end
|
55
76
|
|
56
|
-
# Uses
|
77
|
+
# Uses a periodic timer to ping the host at @interval.
|
57
78
|
def schedule
|
58
79
|
raise "EM not running" unless EM.reactor_running?
|
59
|
-
EM.
|
80
|
+
@ptimer = EM::PeriodicTimer.new(@interval) { self.ping }
|
60
81
|
end
|
61
82
|
|
62
83
|
private
|
@@ -87,35 +108,27 @@ module ICMP4EM
|
|
87
108
|
|
88
109
|
# Construct and send the ICMP echo request packet.
|
89
110
|
def ping_send
|
90
|
-
|
111
|
+
seq = (@seq + 1) % 65536
|
91
112
|
|
92
|
-
socket =
|
93
|
-
Socket::PF_INET,
|
94
|
-
Socket::SOCK_RAW,
|
95
|
-
Socket::IPPROTO_ICMP
|
96
|
-
)
|
97
|
-
|
98
|
-
if @bind_host
|
99
|
-
saddr = Socket.pack_sockaddr_in(0, @bind_host)
|
100
|
-
socket.bind(saddr)
|
101
|
-
end
|
113
|
+
socket = self.class.recvsocket
|
102
114
|
|
103
115
|
# Generate msg with checksum
|
104
|
-
msg = [ICMP_ECHO, ICMP_SUBCODE, 0, @id,
|
116
|
+
msg = [ICMP_ECHO, ICMP_SUBCODE, 0, @id, seq, @data].pack("C2 n3 A*")
|
105
117
|
msg[2..3] = [generate_checksum(msg)].pack('n')
|
106
118
|
|
107
|
-
# Enqueue
|
119
|
+
# Enqueue so we can expire properly if there is an exception raised during #send
|
108
120
|
@waiting[seq] = Time.now
|
109
121
|
|
110
122
|
begin
|
111
123
|
# Fire it off
|
112
124
|
socket.send(msg, 0, @ipv4_sockaddr)
|
125
|
+
# Re-enqueue AFTER sendto() returns. This ensures we aren't adding latency if the socket blocks.
|
126
|
+
@waiting[seq] = Time.now
|
113
127
|
# Return sequence number to caller
|
114
|
-
|
128
|
+
seq
|
115
129
|
rescue Exception => err
|
116
|
-
expire(
|
117
|
-
|
118
|
-
socket.close if socket
|
130
|
+
expire(seq, err)
|
131
|
+
seq
|
119
132
|
end
|
120
133
|
end
|
121
134
|
|
@@ -130,7 +143,7 @@ module ICMP4EM
|
|
130
143
|
saddr = Socket.pack_sockaddr_in(0, @bind_host)
|
131
144
|
self.class.recvsocket.bind(saddr)
|
132
145
|
end
|
133
|
-
EM.attach self.class.recvsocket, Handler, self.class.recvsocket
|
146
|
+
self.class.handler = EM.attach self.class.recvsocket, Handler, self.class.recvsocket
|
134
147
|
end
|
135
148
|
|
136
149
|
# Sets the instance id to a unique 16 bit integer so it can fit inside relevent the ICMP field.
|
@@ -144,6 +157,16 @@ module ICMP4EM
|
|
144
157
|
end
|
145
158
|
end
|
146
159
|
end
|
160
|
+
|
161
|
+
def blocking_receive
|
162
|
+
r = select([self.class.recvsocket], nil, nil, @timeout)
|
163
|
+
|
164
|
+
if r and r.first.include?(self.class.recvsocket)
|
165
|
+
self.class.handler.notify_readable
|
166
|
+
else
|
167
|
+
expire(@seq, Timeout.new("Ping timed out"))
|
168
|
+
end
|
169
|
+
end
|
147
170
|
|
148
171
|
end
|
149
172
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: icmp4em
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jake Douglas
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-04-03 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -38,7 +38,7 @@ files:
|
|
38
38
|
- lib/icmp4em/common.rb
|
39
39
|
- lib/icmp4em/handler.rb
|
40
40
|
- lib/icmp4em/icmpv4.rb
|
41
|
-
has_rdoc:
|
41
|
+
has_rdoc: true
|
42
42
|
homepage: http://www.github.com/yakischloba/icmp4em
|
43
43
|
post_install_message:
|
44
44
|
rdoc_options: []
|
@@ -60,7 +60,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
60
|
requirements: []
|
61
61
|
|
62
62
|
rubyforge_project: icmp4em
|
63
|
-
rubygems_version: 1.
|
63
|
+
rubygems_version: 1.3.1
|
64
64
|
signing_key:
|
65
65
|
specification_version: 2
|
66
66
|
summary: Asynchronous implementation of ICMP ping using EventMachine
|