ether_ping 0.2.0 → 0.3.0

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/Gemfile CHANGED
@@ -7,5 +7,5 @@ group :development do
7
7
  gem 'rspec', '~> 2.6.0'
8
8
  gem 'bundler', '~> 1.0.0'
9
9
  gem 'jeweler', '~> 1.6.0'
10
- gem 'rcov', '>= 0'
10
+ gem 'rcov', '>= 0', :platform => :mri
11
11
  end
data/Gemfile.lock CHANGED
@@ -2,8 +2,9 @@ GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
4
  diff-lcs (1.1.2)
5
- ethernet (0.1.1)
6
- system-getifaddrs (~> 0.1.1)
5
+ ethernet (0.1.2)
6
+ ffi (>= 1.0.0)
7
+ ffi (1.0.9)
7
8
  git (1.2.5)
8
9
  jeweler (1.6.0)
9
10
  bundler (~> 1.0.0)
@@ -20,7 +21,6 @@ GEM
20
21
  rspec-expectations (2.6.0)
21
22
  diff-lcs (~> 1.1.2)
22
23
  rspec-mocks (2.6.0)
23
- system-getifaddrs (0.1.1)
24
24
 
25
25
  PLATFORMS
26
26
  ruby
data/README.rdoc CHANGED
@@ -8,16 +8,14 @@ While ping uses an echo service embedded in every TCP/IP stack, ether_ping
8
8
  relies on a server program to have its frames echoed. First start the server on
9
9
  the destination computer, and tell it what Ethernet device it should listen to.
10
10
 
11
- ether_ping_server eth0 # on Linux
12
- ether_ping_server en0 # on Mac OS
11
+ ether_ping_server -e eth0 # on Linux
12
+ ether_ping_server -e en0 # on Mac OS
13
13
 
14
- Then launch the ping utility. It wants to know the following information:
15
- * the Ethernet device that will be used for sending ping frames
16
- * the MAC of the destination's Ethernet device
17
- * the data to be sent
14
+ Then launch the ping utility.
18
15
 
19
- ether_ping eth0 c42c0337fffc aabbccdd
16
+ ether_ping -e eth0 c42c0337fffc
20
17
 
18
+ Both commands accept the -h / --help option.
21
19
 
22
20
  == Contributing to ether_ping
23
21
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
data/bin/ether_ping CHANGED
@@ -1,41 +1,62 @@
1
1
  #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
2
4
  require 'rubygems'
3
5
  require 'ether_ping'
4
6
 
5
- if ARGV.length < 3
6
- print <<END_USAGE
7
- Usage: #{$0} net_interface dest_mac [ether_type] data
8
- net_interface: name of the Ethernet interface, e.g. eth0
9
- dest_mac: destination MAC for the ping packets, in hex (6 bytes)
10
- ether_type: packet type for the Ethernet II frame, in hex (2 bytes)
11
- data: ping packet data, in hex (0-padded to 64 bytes)
7
+ options = {}
8
+ parser = OptionParser.new do |opts|
9
+ opts.banner = "Usage: #{$0} [options] destination_mac"
10
+
11
+ opts.on('-e', '--interface ETH', 'Ethernet interface name, e.g. eth0') do |i|
12
+ options[:interface] = i
13
+ end
14
+
15
+ opts.on('-t', '--type TYPE',
16
+ 'Ethernet II frame type, as 2 hex bytes, e.g. 0800 for IP') do |p|
17
+ options[:ether_type] = [p].pack('H*').unpack('n').first
18
+ end
19
+
20
+ opts.on('-d', '--data HEXBYTES',
21
+ 'Ping packet data, in a sequence of hexadecimal bytes') do |d|
22
+ options[:data] = [d].pack('H*')
23
+ end
24
+
25
+ opts.on('-s', '--size SIZE', 'Number of bytes in a ping packet') do |s|
26
+ options[:size] = s.to_i
27
+ end
28
+
29
+ opts.on('-h', '--help', 'Display this screen') do
30
+ puts opts
31
+ exit
32
+ end
33
+ end
34
+
35
+ parser.parse!
36
+ options[:interface] ||= Ethernet.devices.keys.sort.first
37
+ options[:ether_type] ||= 0x88B5
38
+ options[:size] ||= 64 unless options[:data]
12
39
 
13
- Available Ethernet interfaces:
14
- #{Ethernet.devices.keys.sort.join("\n")}
15
- END_USAGE
40
+ if ARGV.length < 1
41
+ puts "Missing destination MAC address"
16
42
  exit 1
17
43
  end
18
-
19
- interface = ARGV[0]
20
- dest_mac = ARGV[1]
21
- if ARGV.length == 3
22
- ether_type_string = '88B5'
23
- data = [ARGV[2]].pack('H*')
24
- else
25
- ether_type_string = ARGV[2]
26
- data = [ARGV[3]].pack('H*')
27
- end
28
- ether_type = [ether_type_string].pack('H*').unpack('n').first
44
+ destination_mac = ARGV.first
45
+
46
+ print "Pinging #{destination_mac} on #{options[:interface]} "
47
+ print "with a #{options[:size] || options[:data].length}-byte frame "
48
+ print "of type #{'%04X' % options[:ether_type]}\n"
29
49
 
30
- client = EtherPing::Client.new interface, ether_type, dest_mac
50
+ client = EtherPing::Client.new options[:interface], options[:ether_type],
51
+ destination_mac
31
52
 
32
53
  loop do
33
- print "Pinging #{dest_mac}... "
54
+ print "Ping... "
34
55
  STDOUT.flush
35
- result = client.ping data
56
+ result = client.ping options[:size] || options[:data]
36
57
  if result
37
- if result == true
38
- puts "OK"
58
+ if result.kind_of? Numeric
59
+ puts "#{result * 1000000}us"
39
60
  else
40
61
  puts "Failed"
41
62
  puts "Expected:\n#{result.first.unpack('H*').first}"
@@ -1,22 +1,39 @@
1
1
  #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
2
4
  require 'rubygems'
3
5
  require 'ether_ping'
4
6
 
5
- if ARGV.length < 1
6
- print <<END_USAGE
7
- Usage: #{$0} net_interface [ether_type]
8
- net_interface: name of the Ethernet interface, e.g. eth0
9
- ether_type: packet type for the Ethernet II frame, in hex (2 bytes)
10
- the default is 88B5, intended for research and experiments
11
-
12
- Available Ethernet interfaces:
13
- #{Ethernet.devices.keys.sort.join("\n")}
14
- END_USAGE
15
- exit 1
7
+ options = { :verbose => true }
8
+ parser = OptionParser.new do |opts|
9
+ opts.banner = "Usage: #{$0} [options]"
10
+
11
+ opts.on('-e', '--interface ETH', 'Ethernet interface name, e.g. eth0') do |i|
12
+ options[:interface] = i
13
+ end
14
+
15
+ opts.on('-t', '--type TYPE',
16
+ 'Ethernet II frame type, as 2 hex bytes, e.g. 0800 for IP') do |p|
17
+ options[:ether_type] = [p].pack('H*').unpack('n').first
18
+ end
19
+
20
+ opts.on('-s', '--silent', 'Do not display packets as they come in') do
21
+ options[:verbose] = false
22
+ end
23
+
24
+ opts.on('-h', '--help', 'Display this screen') do
25
+ puts opts
26
+ exit
27
+ end
16
28
  end
17
-
18
- interface = ARGV[0]
19
- ether_type = [ARGV[1] || '88B5'].pack('H*').unpack('n').first
20
29
 
21
- ping_server = EtherPing::Server.new interface, ether_type
30
+ parser.parse!
31
+ options[:interface] ||= Ethernet.devices.keys.sort.first
32
+ options[:ether_type] ||= 0x88B5
33
+ interface_mac = Ethernet::Devices.mac(options[:interface]).unpack('H*').first
34
+
35
+ print "Waiting for frames of type #{'%04X' % options[:ether_type]} "
36
+ print "on #{options[:interface]} with MAC #{interface_mac}\n"
37
+ ping_server = EtherPing::Server.new options[:interface], options[:ether_type],
38
+ options[:verbose]
22
39
  ping_server.run
data/ether_ping.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ether_ping}
8
- s.version = "0.2.0"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = [%q{Victor Costan}]
12
- s.date = %q{2011-05-29}
12
+ s.date = %q{2011-05-30}
13
13
  s.description = %q{Contains an Ethernet-level ping server and client.}
14
14
  s.email = %q{victor@costan.us}
15
15
  s.executables = [%q{ether_ping_server}, %q{ether_ping}]
@@ -32,9 +32,7 @@ Gem::Specification.new do |s|
32
32
  "ether_ping.gemspec",
33
33
  "lib/ether_ping.rb",
34
34
  "lib/ether_ping/client.rb",
35
- "lib/ether_ping/server.rb",
36
- "spec/ether_ping_spec.rb",
37
- "spec/spec_helper.rb"
35
+ "lib/ether_ping/server.rb"
38
36
  ]
39
37
  s.homepage = %q{http://github.com/pwnall/ether_ping}
40
38
  s.licenses = [%q{MIT}]
@@ -18,28 +18,36 @@ class Client
18
18
 
19
19
  # Pings over raw Ethernet sockets.
20
20
  #
21
- # Returns true if the ping response matches, an array of [expected,
22
- # received] strings if it doesn't match, and false if the ping times out.
21
+ # Returns a Number representing the ping latency (in seconds) if the ping
22
+ # response matches, an array of [expected, received] strings if it doesn't
23
+ # match, and false if the ping times out.
23
24
  def ping(data, timeout = 1)
24
- data = data.clone
25
+ if data.kind_of? Numeric
26
+ data = "\0" * data
27
+ end
25
28
  # Pad data to have at least 64 bytes.
26
29
  data += "\0" * (64 - data.length) if data.length < 64
27
30
 
28
- ping_packet = @dest_mac + @source_mac + @ether_type + data
29
- @socket.send ping_packet, 0
31
+ ping_packet = [@dest_mac, @source_mac, @ether_type, data].join
30
32
 
31
- response_packet = @source_mac + @dest_mac + @ether_type + data
32
-
33
33
  response = nil
34
+ receive_ts = nil
35
+ send_ts = nil
34
36
  begin
35
37
  Timeout.timeout timeout do
36
- response = @socket.recv response_packet.length * 2
38
+ send_ts = Time.now
39
+ @socket.send ping_packet, 0
40
+ response = @socket.recv ping_packet.length * 2
41
+ receive_ts = Time.now
37
42
  end
38
43
  rescue Timeout::Error
39
44
  response = nil
40
45
  end
41
46
  return false unless response
42
- response == response_packet || [response, response_packet]
47
+
48
+ response_packet = [@source_mac, @dest_mac, @ether_type, data].join
49
+ response == response_packet ? receive_ts - send_ts :
50
+ [response, response_packet]
43
51
  end
44
52
  end # module EtherPing::Client
45
53
 
@@ -7,43 +7,28 @@ module EtherPing
7
7
 
8
8
  # Responder for ping utility using raw Ethernet sockets.
9
9
  class Server
10
- module Connection
11
- def receive_data(packet)
12
- dest_mac = packet[0, 6].unpack('H*')
13
- source_mac = packet[6, 6].unpack('H*')
14
- ether_type = packet[12, 2].unpack('H*')
15
-
16
- puts "Src: #{source_mac} Dst: #{dest_mac} Eth: #{ether_type}\n"
17
- puts packet[14..-1].unpack('H*')
18
-
19
- # Exchange the source and destination MAC addresses.
20
- packet[0, 6], packet[6, 6] = packet[6, 6], packet[0, 6]
21
- send_data packet
22
- end
23
- end
24
-
25
- class ConnectionWrapper
26
- include Connection
27
-
28
- def initialize(socket)
29
- @socket = socket
30
- end
31
-
32
- def send_data(data)
33
- @socket.send data, 0
34
- end
35
- end
36
-
37
10
  def run
38
- connection = ConnectionWrapper.new @socket
39
11
  loop do
40
12
  packet = @socket.recv 65536
41
- connection.receive_data packet
13
+ # Respond afap: exchange the source and destination MAC addresses.
14
+ packet[0, 6], packet[6, 6] = packet[6, 6], packet[0, 6]
15
+ @socket.send packet, 0
16
+
17
+ if @verbose
18
+ # The MAC addresses were switched above, before responding.
19
+ dest_mac = packet[6, 6].unpack('H*').first
20
+ source_mac = packet[0, 6].unpack('H*').first
21
+ ether_type = packet[12, 2].unpack('H*').first
22
+
23
+ puts "Src: #{source_mac} Dst: #{dest_mac} Eth: #{ether_type} Data:\n"
24
+ puts packet[14..-1].unpack('H*').first
25
+ end
42
26
  end
43
27
  end
44
28
 
45
- def initialize(eth_device, ether_type)
29
+ def initialize(eth_device, ether_type, verbose = true)
46
30
  @socket = Ethernet.raw_socket eth_device, ether_type
31
+ @verbose = verbose
47
32
  end
48
33
  end # module EtherPing::Server
49
34
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ether_ping
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
8
+ - 3
9
9
  - 0
10
- version: 0.2.0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Victor Costan
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-29 00:00:00 Z
18
+ date: 2011-05-30 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  type: :runtime
@@ -137,8 +137,6 @@ files:
137
137
  - lib/ether_ping.rb
138
138
  - lib/ether_ping/client.rb
139
139
  - lib/ether_ping/server.rb
140
- - spec/ether_ping_spec.rb
141
- - spec/spec_helper.rb
142
140
  homepage: http://github.com/pwnall/ether_ping
143
141
  licenses:
144
142
  - MIT
@@ -1,7 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe "EtherPing" do
4
- it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
6
- end
7
- end
data/spec/spec_helper.rb DELETED
@@ -1,12 +0,0 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
- $LOAD_PATH.unshift(File.dirname(__FILE__))
3
- require 'rspec'
4
- require 'ether_ping'
5
-
6
- # Requires supporting files with custom matchers and macros, etc,
7
- # in ./support/ and its subdirectories.
8
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
-
10
- RSpec.configure do |config|
11
-
12
- end