ether_ping 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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