ethernet 0.0.0 → 0.1.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/.project +11 -0
- data/Gemfile +10 -6
- data/Gemfile.lock +15 -11
- data/README.rdoc +1 -1
- data/Rakefile +7 -10
- data/VERSION +1 -1
- data/bin/ethernet_ping +30 -0
- data/ethernet.gemspec +36 -21
- data/lib/ethernet/devices.rb +41 -0
- data/lib/ethernet/frame_socket.rb +122 -0
- data/lib/ethernet/ping.rb +89 -0
- data/lib/ethernet/provisioning.rb +44 -0
- data/lib/ethernet/raw_socket_factory.rb +91 -0
- data/lib/ethernet.rb +48 -0
- data/spec/ethernet/devices_spec.rb +39 -0
- data/spec/ethernet/frame_socket_spec.rb +102 -0
- data/spec/ethernet/raw_socket_factory_spec.rb +26 -0
- data/spec/ethernet_spec.rb +41 -3
- data/spec/support/ifconfig_cli.rb +37 -0
- data/spec/support/raw_socket_stub.rb +23 -0
- metadata +95 -27
data/.project
ADDED
data/Gemfile
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
-
source
|
1
|
+
source 'http://rubygems.org'
|
2
2
|
# Add dependencies required to use your gem here.
|
3
3
|
# Example:
|
4
|
-
# gem
|
4
|
+
# gem 'activesupport', '>= 2.3.5'
|
5
|
+
|
6
|
+
gem 'system-getifaddrs', '~> 0.1.1', :platform => :ruby,
|
7
|
+
:require => 'system/getifaddrs'
|
5
8
|
|
6
9
|
# Add dependencies to develop your gem here.
|
7
10
|
# Include everything needed to run rake, tests, features, etc.
|
8
11
|
group :development do
|
9
|
-
gem
|
10
|
-
gem
|
11
|
-
gem
|
12
|
-
gem
|
12
|
+
gem 'rdoc', '~> 3.6.1'
|
13
|
+
gem 'rspec', '~> 2.6.0'
|
14
|
+
gem 'bundler', '~> 1.0.0'
|
15
|
+
gem 'jeweler', '~> 1.6.0'
|
16
|
+
gem 'rcov', '>= 0'
|
13
17
|
end
|
data/Gemfile.lock
CHANGED
@@ -3,26 +3,30 @@ GEM
|
|
3
3
|
specs:
|
4
4
|
diff-lcs (1.1.2)
|
5
5
|
git (1.2.5)
|
6
|
-
jeweler (1.
|
6
|
+
jeweler (1.6.0)
|
7
7
|
bundler (~> 1.0.0)
|
8
8
|
git (>= 1.2.5)
|
9
9
|
rake
|
10
|
-
rake (0.
|
10
|
+
rake (0.9.0)
|
11
11
|
rcov (0.9.9)
|
12
|
-
|
13
|
-
|
14
|
-
rspec-
|
15
|
-
rspec-
|
16
|
-
|
17
|
-
rspec-
|
12
|
+
rdoc (3.6.1)
|
13
|
+
rspec (2.6.0)
|
14
|
+
rspec-core (~> 2.6.0)
|
15
|
+
rspec-expectations (~> 2.6.0)
|
16
|
+
rspec-mocks (~> 2.6.0)
|
17
|
+
rspec-core (2.6.3)
|
18
|
+
rspec-expectations (2.6.0)
|
18
19
|
diff-lcs (~> 1.1.2)
|
19
|
-
rspec-mocks (2.
|
20
|
+
rspec-mocks (2.6.0)
|
21
|
+
system-getifaddrs (0.1.1)
|
20
22
|
|
21
23
|
PLATFORMS
|
22
24
|
ruby
|
23
25
|
|
24
26
|
DEPENDENCIES
|
25
27
|
bundler (~> 1.0.0)
|
26
|
-
jeweler (~> 1.
|
28
|
+
jeweler (~> 1.6.0)
|
27
29
|
rcov
|
28
|
-
|
30
|
+
rdoc (~> 3.6.1)
|
31
|
+
rspec (~> 2.6.0)
|
32
|
+
system-getifaddrs (~> 0.1.1)
|
data/README.rdoc
CHANGED
@@ -16,10 +16,10 @@ This project is a low-level ethernet communication rubygems library. Its initial
|
|
16
16
|
|
17
17
|
== Author
|
18
18
|
|
19
|
+
Victor Costan victor@costan.us
|
19
20
|
HaoQi Li haoqili@mit.edu
|
20
21
|
|
21
22
|
== Copyright
|
22
23
|
|
23
24
|
Copyright (c) 2011 Massachusetts Institute of Technology.
|
24
25
|
See LICENSE.txt for further details.
|
25
|
-
|
data/Rakefile
CHANGED
@@ -13,16 +13,13 @@ require 'jeweler'
|
|
13
13
|
Jeweler::Tasks.new do |gem|
|
14
14
|
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
15
|
gem.name = "ethernet"
|
16
|
-
gem.homepage = "http://github.com/
|
16
|
+
gem.homepage = "http://github.com/costan/ethernet"
|
17
17
|
gem.license = "MIT"
|
18
|
-
gem.summary = %Q{
|
19
|
-
gem.description = %Q{
|
20
|
-
gem.email = "
|
21
|
-
gem.authors = ["HaoQi Li"]
|
22
|
-
#
|
23
|
-
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
24
|
-
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
25
|
-
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
18
|
+
gem.summary = %Q{Ethernet (link-layer level) sockets.}
|
19
|
+
gem.description = %Q{Provides a Socket-like API that bypasses TCP/IP. Useful for exotic devices and FPGA development.}
|
20
|
+
gem.email = "victor@costan.us"
|
21
|
+
gem.authors = ["Victor Costan", "HaoQi Li"]
|
22
|
+
# dependencies defined in Gemfile
|
26
23
|
end
|
27
24
|
Jeweler::RubygemsDotOrgTasks.new
|
28
25
|
|
@@ -39,7 +36,7 @@ end
|
|
39
36
|
|
40
37
|
task :default => :spec
|
41
38
|
|
42
|
-
require '
|
39
|
+
require 'rdoc/task'
|
43
40
|
Rake::RDocTask.new do |rdoc|
|
44
41
|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
45
42
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.0
|
data/bin/ethernet_ping
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'ethernet' #'scratchpad'
|
4
|
+
|
5
|
+
if ARGV.length < 4
|
6
|
+
print <<END_USAGE
|
7
|
+
Usage: #{$0} net_interface ether_type dest_mac data
|
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
|
+
dest_mac: destination MAC for the ping packets, in hex (6 bytes)
|
11
|
+
data: ping packet data, in hex (0-padded to 64 bytes)
|
12
|
+
END_USAGE
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
interface = ARGV[0]
|
17
|
+
ether_type = [ARGV[1]].pack('H*').unpack('n').first
|
18
|
+
dest_mac = ARGV[2]
|
19
|
+
data = [ARGV[3]].pack('H*')
|
20
|
+
|
21
|
+
#client = Scratchpad::Ethernet::PingClient.new interface, ether_type, dest_mac
|
22
|
+
client = Ethernet::PingClient.new interface, ether_type, dest_mac
|
23
|
+
|
24
|
+
loop do
|
25
|
+
print "Pinging #{dest_mac}... "
|
26
|
+
STDOUT.flush
|
27
|
+
puts client.ping(data) ? "OK" : "Failed"
|
28
|
+
STDOUT.flush
|
29
|
+
sleep 1
|
30
|
+
end
|
data/ethernet.gemspec
CHANGED
@@ -5,19 +5,21 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ethernet}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date = %q{2011-
|
13
|
-
s.description = %q{
|
14
|
-
s.email = %q{
|
11
|
+
s.authors = [%q{Victor Costan}, %q{HaoQi Li}]
|
12
|
+
s.date = %q{2011-05-27}
|
13
|
+
s.description = %q{Provides a Socket-like API that bypasses TCP/IP. Useful for exotic devices and FPGA development.}
|
14
|
+
s.email = %q{victor@costan.us}
|
15
|
+
s.executables = [%q{ethernet_ping}]
|
15
16
|
s.extra_rdoc_files = [
|
16
17
|
"LICENSE.txt",
|
17
18
|
"README.rdoc"
|
18
19
|
]
|
19
20
|
s.files = [
|
20
21
|
".document",
|
22
|
+
".project",
|
21
23
|
".rspec",
|
22
24
|
"Gemfile",
|
23
25
|
"Gemfile.lock",
|
@@ -25,39 +27,52 @@ Gem::Specification.new do |s|
|
|
25
27
|
"README.rdoc",
|
26
28
|
"Rakefile",
|
27
29
|
"VERSION",
|
30
|
+
"bin/ethernet_ping",
|
28
31
|
"ethernet.gemspec",
|
29
32
|
"lib/ethernet.rb",
|
33
|
+
"lib/ethernet/devices.rb",
|
34
|
+
"lib/ethernet/frame_socket.rb",
|
35
|
+
"lib/ethernet/ping.rb",
|
36
|
+
"lib/ethernet/provisioning.rb",
|
37
|
+
"lib/ethernet/raw_socket_factory.rb",
|
38
|
+
"spec/ethernet/devices_spec.rb",
|
39
|
+
"spec/ethernet/frame_socket_spec.rb",
|
40
|
+
"spec/ethernet/raw_socket_factory_spec.rb",
|
30
41
|
"spec/ethernet_spec.rb",
|
31
|
-
"spec/spec_helper.rb"
|
32
|
-
|
33
|
-
|
34
|
-
s.licenses = ["MIT"]
|
35
|
-
s.require_paths = ["lib"]
|
36
|
-
s.rubygems_version = %q{1.6.2}
|
37
|
-
s.summary = %q{A ruby gem library for ethernet communication.}
|
38
|
-
s.test_files = [
|
39
|
-
"spec/ethernet_spec.rb",
|
40
|
-
"spec/spec_helper.rb"
|
42
|
+
"spec/spec_helper.rb",
|
43
|
+
"spec/support/ifconfig_cli.rb",
|
44
|
+
"spec/support/raw_socket_stub.rb"
|
41
45
|
]
|
46
|
+
s.homepage = %q{http://github.com/costan/ethernet}
|
47
|
+
s.licenses = [%q{MIT}]
|
48
|
+
s.require_paths = [%q{lib}]
|
49
|
+
s.rubygems_version = %q{1.8.4}
|
50
|
+
s.summary = %q{Ethernet (link-layer level) sockets.}
|
42
51
|
|
43
52
|
if s.respond_to? :specification_version then
|
44
53
|
s.specification_version = 3
|
45
54
|
|
46
55
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
-
s.
|
56
|
+
s.add_runtime_dependency(%q<system-getifaddrs>, ["~> 0.1.1"])
|
57
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.6.1"])
|
58
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
|
48
59
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
49
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.
|
60
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.0"])
|
50
61
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
51
62
|
else
|
52
|
-
s.add_dependency(%q<
|
63
|
+
s.add_dependency(%q<system-getifaddrs>, ["~> 0.1.1"])
|
64
|
+
s.add_dependency(%q<rdoc>, ["~> 3.6.1"])
|
65
|
+
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
53
66
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
54
|
-
s.add_dependency(%q<jeweler>, ["~> 1.
|
67
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
|
55
68
|
s.add_dependency(%q<rcov>, [">= 0"])
|
56
69
|
end
|
57
70
|
else
|
58
|
-
s.add_dependency(%q<
|
71
|
+
s.add_dependency(%q<system-getifaddrs>, ["~> 0.1.1"])
|
72
|
+
s.add_dependency(%q<rdoc>, ["~> 3.6.1"])
|
73
|
+
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
59
74
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
60
|
-
s.add_dependency(%q<jeweler>, ["~> 1.
|
75
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
|
61
76
|
s.add_dependency(%q<rcov>, [">= 0"])
|
62
77
|
end
|
63
78
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# OS-dependent gem includes.
|
2
|
+
case RUBY_PLATFORM
|
3
|
+
when /linux/
|
4
|
+
when /darwin/
|
5
|
+
require 'system/getifaddrs'
|
6
|
+
end
|
7
|
+
|
8
|
+
# :nodoc: namespace
|
9
|
+
module Ethernet
|
10
|
+
|
11
|
+
# Information about the available Ethernet devices.
|
12
|
+
module Devices
|
13
|
+
# An array of device names for the machine's Ethernet devices.
|
14
|
+
def self.all
|
15
|
+
case RUBY_PLATFORM
|
16
|
+
when /linux/, /darwin/
|
17
|
+
System.get_ifaddrs.keys.map(&:to_s)
|
18
|
+
else
|
19
|
+
raise "Unsupported platform #{RUBY_PLATFORM}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# The MAC address for an Ethernet device.
|
24
|
+
#
|
25
|
+
# Args:
|
26
|
+
# eth_device:: device name for the Ethernet card, e.g. 'eth0'
|
27
|
+
def self.mac(eth_device)
|
28
|
+
case RUBY_PLATFORM
|
29
|
+
when /linux/
|
30
|
+
# /usr/include/net/if.h, structure ifreq
|
31
|
+
ifreq = [eth_device].pack 'a32'
|
32
|
+
# 0x8927 is SIOCGIFHWADDR in /usr/include/bits/ioctls.h
|
33
|
+
RawSocketFactory.socket.ioctl 0x8927, ifreq
|
34
|
+
ifreq[18, 6]
|
35
|
+
else
|
36
|
+
raise "Unsupported platform #{RUBY_PLATFORM}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end # class Ethernet::Devices
|
40
|
+
|
41
|
+
end # namespace Ethernet
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module Ethernet
|
3
|
+
|
4
|
+
# Wraps an Ethernet socket and abstracts away the Ethernet II frame.
|
5
|
+
class FrameSocket
|
6
|
+
# Creates a wrapper around a raw Ethernet socket.
|
7
|
+
#
|
8
|
+
# Args:
|
9
|
+
# raw_socket_or_device:: a raw Ethernet socket or a string containing an
|
10
|
+
# Ethernet device name
|
11
|
+
# ether_type:: 2-byte Ethernet packet type number
|
12
|
+
# mac_address:: 6-byte MAC address for the Ethernet socket (optional if
|
13
|
+
# raw_socket_or_device is an Ethernet device name)
|
14
|
+
#
|
15
|
+
# Raises:
|
16
|
+
# RuntimeError:: if mac isn't exactly 6-bytes long
|
17
|
+
def initialize(raw_socket_or_device, ether_type, mac_address = nil)
|
18
|
+
check_mac mac_address if mac_address
|
19
|
+
|
20
|
+
if raw_socket_or_device.respond_to? :to_str
|
21
|
+
@source_mac = mac_address || Ethernet::Devices.mac(raw_socket_or_device)
|
22
|
+
@socket = RawSocketFactory.socket raw_socket_or_device, ether_type
|
23
|
+
else
|
24
|
+
raise 'MAC address needed with raw socket' unless mac_address
|
25
|
+
@source_mac = mac_address.dup
|
26
|
+
@socket = raw_socket_or_device
|
27
|
+
end
|
28
|
+
|
29
|
+
@dest_mac = nil
|
30
|
+
@ether_type = [ether_type].pack('n')
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sets the destination MAC address for future calls to send.
|
34
|
+
#
|
35
|
+
# Args:
|
36
|
+
# mac:: 6-byte MAC address for the Ethernet socket
|
37
|
+
#
|
38
|
+
# Raises:
|
39
|
+
# RuntimeError:: if mac isn't exactly 6-bytes long
|
40
|
+
def connect(mac_address)
|
41
|
+
check_mac mac_address
|
42
|
+
@dest_mac = mac_address
|
43
|
+
end
|
44
|
+
|
45
|
+
# Closes the underlying socket.
|
46
|
+
def close
|
47
|
+
@socket.close
|
48
|
+
end
|
49
|
+
|
50
|
+
# Sends an Ethernet II frame.
|
51
|
+
#
|
52
|
+
# Args:
|
53
|
+
# data:: the data bytes to be sent
|
54
|
+
#
|
55
|
+
# Raises:
|
56
|
+
# RuntimeError:: if connect wasn' previously called
|
57
|
+
def send(data, send_flags = 0)
|
58
|
+
raise "Not connected" unless @dest_mac
|
59
|
+
send_to @dest_mac, data, send_flags
|
60
|
+
end
|
61
|
+
|
62
|
+
# Sends an Ethernet II frame.
|
63
|
+
#
|
64
|
+
# Args:
|
65
|
+
# mac_address:: the destination MAC address
|
66
|
+
# data:: the data bytes to be sent
|
67
|
+
#
|
68
|
+
# Raises:
|
69
|
+
# RuntimeError:: if connect wasn' previously called
|
70
|
+
def send_to(mac_address, data, send_flags = 0)
|
71
|
+
check_mac mac_address
|
72
|
+
|
73
|
+
padding = (data.length < 46) ? "\0" * (46 - data.length) : ''
|
74
|
+
packet = [mac_address, @source_mac, @ether_type, data, padding].join
|
75
|
+
@socket.send packet, send_flags
|
76
|
+
end
|
77
|
+
|
78
|
+
# Receives an Ethernet II frame.
|
79
|
+
#
|
80
|
+
# Args:
|
81
|
+
# buffer_size:: optional maximum packet size argument passed to the raw
|
82
|
+
# socket's recv method
|
83
|
+
#
|
84
|
+
# Returns the data and the source MAC address in the frame.
|
85
|
+
#
|
86
|
+
# This will discard incoming frames that don't match the MAC address that the
|
87
|
+
# socket is connected to, or the Ethernet packet type.
|
88
|
+
def recv(buffer_size = 8192)
|
89
|
+
raise "Not connected" unless @dest_mac
|
90
|
+
loop do
|
91
|
+
data, mac_address = recv_from buffer_size
|
92
|
+
return data if @dest_mac == mac_address
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Receives an Ethernet II frame.
|
97
|
+
#
|
98
|
+
# Args:
|
99
|
+
# buffer_size:: optional maximum packet size argument passed to the raw
|
100
|
+
# socket's recv method
|
101
|
+
#
|
102
|
+
# Returns the data in the frame.
|
103
|
+
#
|
104
|
+
# This will discard incoming frames that don't match the MAC address that the
|
105
|
+
# socket is connected to, or the Ethernet packet type.
|
106
|
+
def recv_from(buffer_size = 8192)
|
107
|
+
loop do
|
108
|
+
packet = @socket.recv buffer_size
|
109
|
+
next unless packet[12, 2] == @ether_type
|
110
|
+
next unless packet[0, 6] == @source_mac
|
111
|
+
return packet[14..-1], packet[6, 6]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Raises an exception if the given MAC address is invalid.
|
116
|
+
def check_mac(mac_address)
|
117
|
+
raise "Invalid MAC address" unless mac_address.length == 6
|
118
|
+
end
|
119
|
+
private :check_mac
|
120
|
+
end # class Ethernet::FrameSocket
|
121
|
+
|
122
|
+
end # namespace Ethernet
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# TODO(pwnall): move to separate gem
|
2
|
+
|
3
|
+
|
4
|
+
# copied from victor's scratchpad/lib/scratchpad/ethernet/ping.rb
|
5
|
+
# changes see raw_socket.rb
|
6
|
+
# :%s/if_name/eth_device/g
|
7
|
+
# .get_interface_mac() --> .mac()
|
8
|
+
# Ethernet.socket --> Ethernet::RawSocket.socket
|
9
|
+
# Ethernet.mac --> Ethernet::RawSocket.mac
|
10
|
+
|
11
|
+
# :nodoc: namespace # module Scratchpad
|
12
|
+
|
13
|
+
# :nodoc: namespace
|
14
|
+
module Ethernet
|
15
|
+
|
16
|
+
# Responder for ping utility using raw Ethernet sockets.
|
17
|
+
class PingServer
|
18
|
+
module Connection
|
19
|
+
def receive_data(packet)
|
20
|
+
source_mac = packet[0, 6].unpack('H*')
|
21
|
+
dest_mac = packet[6, 6].unpack('H*')
|
22
|
+
ether_type = packet[12, 2].unpack('H*')
|
23
|
+
|
24
|
+
puts "Src: #{source_mac} Dst: #{dest_mac} Eth: #{ether_type}\n"
|
25
|
+
puts packet[14..-1].unpack('H*')
|
26
|
+
|
27
|
+
# Exchange the source and destination ARP addresses.
|
28
|
+
packet[0, 6], packet[6, 6] = packet[6, 6], packet[0, 6]
|
29
|
+
send_data packet
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class ConnectionWrapper
|
34
|
+
include Connection
|
35
|
+
|
36
|
+
def initialize(socket)
|
37
|
+
@socket = socket
|
38
|
+
end
|
39
|
+
|
40
|
+
def send_data(data)
|
41
|
+
@socket.send data, 0
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def run
|
46
|
+
connection = ConnectionWrapper.new @socket
|
47
|
+
loop do
|
48
|
+
packet = @socket.recv 65536
|
49
|
+
connection.receive_data packet
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(eth_device, ether_type)
|
54
|
+
@socket = Ethernet::RawSocket.socket eth_device, ether_type
|
55
|
+
end
|
56
|
+
end # module Ethernet::PingServer
|
57
|
+
|
58
|
+
# Ping utility
|
59
|
+
class PingClient
|
60
|
+
def initialize(eth_device, ether_type, destination_mac)
|
61
|
+
@socket = Ethernet::RawSocket.socket eth_device, ether_type
|
62
|
+
@source_mac = [Ethernet::RawSocket.mac(eth_device).unpack('H*').first].pack('H*')[0, 6]
|
63
|
+
@dest_mac = [destination_mac].pack('H*')[0, 6]
|
64
|
+
@ether_type = [ether_type].pack('n')
|
65
|
+
end
|
66
|
+
|
67
|
+
attr_reader :socket
|
68
|
+
attr_reader :source_mac
|
69
|
+
attr_reader :dest_mac
|
70
|
+
|
71
|
+
# Pings over raw Ethernet sockets.
|
72
|
+
#
|
73
|
+
# Returns true if the ping receives a response, false otherwise.
|
74
|
+
def ping(data, timeout = 1)
|
75
|
+
data = data.clone
|
76
|
+
# Pad data to have at least 64 bytes.
|
77
|
+
data += "\0" * (64 - data.length) if data.length < 64
|
78
|
+
|
79
|
+
ping_packet = @dest_mac + @source_mac + @ether_type + data
|
80
|
+
@socket.send ping_packet, 0
|
81
|
+
|
82
|
+
response_packet = @source_mac + @dest_mac + @ether_type + data
|
83
|
+
response = @socket.recv response_packet.length * 2
|
84
|
+
|
85
|
+
response == response_packet
|
86
|
+
end
|
87
|
+
end # module Ethernet::PingClient
|
88
|
+
|
89
|
+
end # namespace Ethernet
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
|
4
|
+
# :nodoc: namespace
|
5
|
+
module Ethernet
|
6
|
+
|
7
|
+
# Setup issues such as assigning permissions for Ethernet-level transmission.
|
8
|
+
module Provisioning
|
9
|
+
# Allow non-root users to create low-level Ethernet sockets.
|
10
|
+
#
|
11
|
+
# This is a security risk, because Ethernet sockets can be used to spy on all
|
12
|
+
# traffic on the machine's network. This should not be called on production
|
13
|
+
# machines.
|
14
|
+
#
|
15
|
+
# Returns true for success, false otherwise. If the call fails, it is most
|
16
|
+
# likely because it is not run by root / Administrator.
|
17
|
+
def self.usermode_sockets
|
18
|
+
case RUBY_PLATFORM
|
19
|
+
when /darwin/
|
20
|
+
return false unless Kernel.system("chmod o+r /dev/bpf*")
|
21
|
+
when /linux/
|
22
|
+
ruby = File.join Config::CONFIG['bindir'],
|
23
|
+
Config::CONFIG['ruby_install_name']
|
24
|
+
unless Kernel.system("setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' #{ruby}")
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
# Try to enable Wireshark packet capture for debugging.
|
29
|
+
# No big deal if this fails.
|
30
|
+
dumpcap = '/usr/bin/dumpcap'
|
31
|
+
if File.exist? dumpcap
|
32
|
+
Kernel.system("setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' #{dumpcap}")
|
33
|
+
end
|
34
|
+
when /win/
|
35
|
+
# NOTE: this might not work
|
36
|
+
return false unless Kernel.system("sc config npf start= auto")
|
37
|
+
else
|
38
|
+
raise "Unsupported platform #{RUBY_PLATFORM}"
|
39
|
+
end
|
40
|
+
true
|
41
|
+
end
|
42
|
+
end # class Ethernet::Provisioning
|
43
|
+
|
44
|
+
end # namespace Ethernet
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
case RUBY_PLATFORM
|
4
|
+
when /linux/
|
5
|
+
require 'system/getifaddrs' # for listing
|
6
|
+
end
|
7
|
+
|
8
|
+
# :nodoc: namespace
|
9
|
+
module Ethernet
|
10
|
+
|
11
|
+
# Low-level socket creation functionality.
|
12
|
+
module RawSocketFactory
|
13
|
+
# A raw socket sends and receives raw Ethernet frames.
|
14
|
+
#
|
15
|
+
# Args:
|
16
|
+
# eth_device:: device name for the Ethernet card, e.g. 'eth0'
|
17
|
+
# ether_type:: only receive Ethernet frames with this protocol number
|
18
|
+
def self.socket(eth_device = nil, ether_type = nil)
|
19
|
+
ether_type ||= all_ethernet_protocols
|
20
|
+
socket = Socket.new raw_address_family, Socket::SOCK_RAW, htons(ether_type)
|
21
|
+
socket.setsockopt Socket::SOL_SOCKET, Socket::SO_BROADCAST, true
|
22
|
+
set_socket_eth_device(socket, eth_device, ether_type) if eth_device
|
23
|
+
socket
|
24
|
+
end
|
25
|
+
|
26
|
+
class <<self
|
27
|
+
# Sets the Ethernet interface and protocol type for a socket.
|
28
|
+
def set_socket_eth_device(socket, eth_device, ether_type)
|
29
|
+
case RUBY_PLATFORM
|
30
|
+
when /linux/
|
31
|
+
if_number = get_interface_number eth_device
|
32
|
+
# struct sockaddr_ll in /usr/include/linux/if_packet.h
|
33
|
+
socket_address = [raw_address_family, htons(ether_type), if_number,
|
34
|
+
0xFFFF, 0, 0, ""].pack 'SSISCCa8'
|
35
|
+
socket.bind socket_address
|
36
|
+
else
|
37
|
+
raise "Unsupported platform #{RUBY_PLATFORM}"
|
38
|
+
end
|
39
|
+
socket
|
40
|
+
end
|
41
|
+
private :set_socket_eth_device
|
42
|
+
|
43
|
+
# The interface number for an Ethernet interface.
|
44
|
+
def get_interface_number(eth_device)
|
45
|
+
case RUBY_PLATFORM
|
46
|
+
when /linux/
|
47
|
+
# /usr/include/net/if.h, structure ifreq
|
48
|
+
ifreq = [eth_device].pack 'a32'
|
49
|
+
# 0x8933 is SIOCGIFINDEX in /usr/include/bits/ioctls.h
|
50
|
+
socket.ioctl 0x8933, ifreq
|
51
|
+
ifreq[16, 4].unpack('I').first
|
52
|
+
else
|
53
|
+
raise "Unsupported platform #{RUBY_PLATFORM}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
private :get_interface_number
|
57
|
+
|
58
|
+
# The protocol number for listening to all ethernet protocols.
|
59
|
+
def all_ethernet_protocols
|
60
|
+
case RUBY_PLATFORM
|
61
|
+
when /linux/
|
62
|
+
3 # cat /usr/include/linux/if_ether.h | grep ETH_P_ALL
|
63
|
+
else
|
64
|
+
raise "Unsupported platform #{RUBY_PLATFORM}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
private :all_ethernet_protocols
|
68
|
+
|
69
|
+
# The AF / PF number for raw sockets.
|
70
|
+
def raw_address_family
|
71
|
+
case RUBY_PLATFORM
|
72
|
+
when /linux/
|
73
|
+
17 # cat /usr/include/bits/socket.h | grep PF_PACKET
|
74
|
+
when /darwin/
|
75
|
+
18 # cat /usr/include/sys/socket.h | grep AF_LINK
|
76
|
+
else
|
77
|
+
raise "Unsupported platform #{RUBY_PLATFORM}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
private :raw_address_family
|
81
|
+
|
82
|
+
# Converts a 16-bit integer from host-order to network-order.
|
83
|
+
def htons(short_integer)
|
84
|
+
[short_integer].pack('n').unpack('S').first
|
85
|
+
end
|
86
|
+
private :htons
|
87
|
+
end
|
88
|
+
end # module Ethernet::RawSocketFactory
|
89
|
+
|
90
|
+
end # namespace Ethernet
|
91
|
+
|
data/lib/ethernet.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Facade methods for the library.
|
2
|
+
#
|
3
|
+
# See the inner classes for more advanced functionality.
|
4
|
+
module Ethernet
|
5
|
+
# Hash mapping Ethernet device names to their MAC addresses.
|
6
|
+
def self.devices
|
7
|
+
Hash[Ethernet::Devices.all.map { |dev| [dev, Ethernet::Devices.mac(dev)] }]
|
8
|
+
end
|
9
|
+
|
10
|
+
# Ethernet socket that abstracts away frames.
|
11
|
+
#
|
12
|
+
# Args:
|
13
|
+
# eth_device:: device name for the Ethernet card, e.g. 'eth0'
|
14
|
+
# ether_type:: 2-byte Ethernet packet type number
|
15
|
+
def self.socket(eth_device, ether_type)
|
16
|
+
Ethernet::FrameSocket.new eth_device, ether_type
|
17
|
+
end
|
18
|
+
|
19
|
+
# Allow non-root users to create low-level Ethernet sockets.
|
20
|
+
#
|
21
|
+
# This is a security risk, because Ethernet sockets can be used to spy on all
|
22
|
+
# traffic on the machine's network. This should not be called on production
|
23
|
+
# machines.
|
24
|
+
#
|
25
|
+
# Returns true for success, false otherwise. If the call fails, it is most
|
26
|
+
# likely because it is not run by root / Administrator.
|
27
|
+
def self.provision
|
28
|
+
Ethernet::Provisioning.usermode_sockets
|
29
|
+
end
|
30
|
+
|
31
|
+
# A socket that sends and receives raw Ethernet frames.
|
32
|
+
#
|
33
|
+
# Args:
|
34
|
+
# eth_device:: device name for the Ethernet card, e.g. 'eth0'
|
35
|
+
# ether_type:: only receive Ethernet frames with this protocol number
|
36
|
+
def self.raw_socket(eth_device = nil, ether_type = nil)
|
37
|
+
Ethernet::RawSocketFactory.socket eth_device, ether_type
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
require 'ethernet/devices.rb'
|
42
|
+
require 'ethernet/frame_socket.rb'
|
43
|
+
require 'ethernet/provisioning.rb'
|
44
|
+
require 'ethernet/raw_socket_factory.rb'
|
45
|
+
|
46
|
+
|
47
|
+
# TODO(pwnall): move to separate gem
|
48
|
+
require 'ethernet/ping.rb'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Ethernet::Devices do
|
4
|
+
let(:golden_devices) { IfconfigCli.run }
|
5
|
+
let(:golden_active_devices) do
|
6
|
+
golden_devices.keys.select { |device| golden_devices[device][:active] }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'testing environment' do
|
10
|
+
it 'should have at least one active ethernet device' do
|
11
|
+
golden_active_devices.should have_at_least(1).device
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'all' do
|
16
|
+
let(:devices) { Ethernet::Devices.all }
|
17
|
+
|
18
|
+
it "should find active ethernet devices" do
|
19
|
+
golden_active_devices.each do |device|
|
20
|
+
devices.should include(device)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:eth_device) { IfconfigCli.live_device }
|
26
|
+
let(:mac) { Ethernet::Devices.mac eth_device }
|
27
|
+
|
28
|
+
describe 'mac' do
|
29
|
+
let(:golden_mac) { [golden_devices[eth_device][:mac]].pack('H*') }
|
30
|
+
|
31
|
+
it 'should have 6 bytes' do
|
32
|
+
mac.length.should == 6
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should match ifconfig output' do
|
36
|
+
mac.should == golden_mac
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Ethernet::FrameSocket do
|
4
|
+
let(:eth_device) { IfconfigCli.live_device }
|
5
|
+
let(:eth_type) { 0x0800 }
|
6
|
+
let(:mac) { Ethernet::Devices.mac eth_device }
|
7
|
+
let(:dest_mac) { "\x00\x11\x22\x33\x44\x55" }
|
8
|
+
let(:bcast_mac) { "\xff" * 6 }
|
9
|
+
|
10
|
+
shared_examples_for 'a real socket' do
|
11
|
+
it 'should output a packet' do
|
12
|
+
@socket.send_to dest_mac, "\r\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should receive some network noise' do
|
16
|
+
@socket.recv_from.first.should_not be_empty
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'on eth0' do
|
21
|
+
before { @socket = Ethernet::FrameSocket.new eth_device, eth_type }
|
22
|
+
after { @socket.close }
|
23
|
+
|
24
|
+
it_should_behave_like 'a real socket'
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'from raw socket' do
|
28
|
+
before do
|
29
|
+
raw_socket = Ethernet::RawSocketFactory.socket eth_device, eth_type
|
30
|
+
@socket = Ethernet::FrameSocket.new raw_socket, eth_type, mac
|
31
|
+
end
|
32
|
+
after { @socket.close }
|
33
|
+
|
34
|
+
it_should_behave_like 'a real socket'
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'stubbed' do
|
38
|
+
let(:socket_stub) do
|
39
|
+
RawSocketStub.new([
|
40
|
+
[mac, dest_mac, "\x88\xB7", 'Wrong Ethernet type'].join,
|
41
|
+
[bcast_mac, dest_mac, [eth_type].pack('n'), 'Wrong dest MAC'].join,
|
42
|
+
[mac, bcast_mac, [eth_type].pack('n'), 'Bcast'].join,
|
43
|
+
[mac, dest_mac, [eth_type].pack('n'), 'Correct'].join,
|
44
|
+
])
|
45
|
+
end
|
46
|
+
let(:socket) { Ethernet::FrameSocket.new socket_stub, eth_type, mac }
|
47
|
+
|
48
|
+
shared_examples_for 'after a small send call' do
|
49
|
+
it 'should send a single packet' do
|
50
|
+
socket_stub.sends.length.should == 1
|
51
|
+
end
|
52
|
+
it 'should pad the packet' do
|
53
|
+
socket_stub.sends.first.length.should == 60
|
54
|
+
end
|
55
|
+
it 'should assemble packet correctly in send' do
|
56
|
+
gold = [dest_mac, mac, [eth_type].pack('n'), 'Send data'].join
|
57
|
+
socket_stub.sends.first[0, gold.length].should == gold
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'send_to' do
|
62
|
+
before { socket.send_to dest_mac, 'Send data' }
|
63
|
+
it_should_behave_like 'after a small send call'
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'recv_from' do
|
67
|
+
it 'should filter down to the correct packet' do
|
68
|
+
socket.recv_from.should == ['Bcast', bcast_mac]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'unconnected' do
|
73
|
+
it 'should complain in recv' do
|
74
|
+
lambda { socket.recv }.should raise_error(RuntimeError)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should complain in send' do
|
78
|
+
lambda { socket.send 'Send data' }.should raise_error(RuntimeError)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'connected' do
|
83
|
+
before { socket.connect dest_mac }
|
84
|
+
|
85
|
+
describe 'send' do
|
86
|
+
before { socket.send 'Send data' }
|
87
|
+
it_should_behave_like 'after a small send call'
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'recv' do
|
91
|
+
it 'should filter down to the correct packet' do
|
92
|
+
socket.recv.should == 'Correct'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should delegate close' do
|
98
|
+
socket_stub.should_receive(:close).once
|
99
|
+
socket.close
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Ethernet::RawSocketFactory do
|
4
|
+
let(:eth_device) { IfconfigCli.live_device }
|
5
|
+
let(:mac) { Ethernet::Devices.mac eth_device }
|
6
|
+
|
7
|
+
describe 'socket' do
|
8
|
+
let(:eth_type) { 0x88B7 }
|
9
|
+
|
10
|
+
before { @socket = Ethernet::RawSocketFactory.socket eth_device }
|
11
|
+
after { @socket.close }
|
12
|
+
|
13
|
+
it 'should be able to receive data' do
|
14
|
+
@socket.should respond_to(:recv)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should output a packet' do
|
18
|
+
packet = [mac, mac, [eth_type].pack('n'), "\r\n" * 32].join
|
19
|
+
@socket.send packet, 0
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should receive some network noise' do
|
23
|
+
@socket.recv(8192).should_not be_empty
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/ethernet_spec.rb
CHANGED
@@ -1,7 +1,45 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
describe
|
4
|
-
|
5
|
-
|
3
|
+
describe Ethernet do
|
4
|
+
describe 'provision' do
|
5
|
+
it 'delegates to Provisioning' do
|
6
|
+
Ethernet::Provisioning.should_receive(:usermode_sockets).and_return(:yay)
|
7
|
+
Ethernet.provision.should == :yay
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'devices' do
|
12
|
+
it 'delegates to Devices' do
|
13
|
+
Ethernet::Devices.should_receive(:all).and_return(['yay'])
|
14
|
+
Ethernet::Devices.should_receive(:mac).with('yay').and_return('42')
|
15
|
+
golden = { 'yay' => '42' }
|
16
|
+
Ethernet.devices.should == golden
|
17
|
+
end
|
18
|
+
|
19
|
+
let (:devices) { Ethernet.devices }
|
20
|
+
|
21
|
+
it 'contains at least one device' do
|
22
|
+
devices.keys.should have_at_least(1).name
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'has a MAC for the first device' do
|
26
|
+
devices[devices.keys.first].length.should == 6
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'socket' do
|
31
|
+
it 'delegates to FrameSocket' do
|
32
|
+
Ethernet::FrameSocket.should_receive(:new).with('yay', 1234).
|
33
|
+
and_return(:yay)
|
34
|
+
Ethernet.socket('yay', 1234).should == :yay
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'raw_socket' do
|
39
|
+
it 'delegates to RawSocketFactory' do
|
40
|
+
Ethernet::RawSocketFactory.should_receive(:socket).with('yay', 1234).
|
41
|
+
and_return(:yay)
|
42
|
+
Ethernet.raw_socket('yay', 1234).should == :yay
|
43
|
+
end
|
6
44
|
end
|
7
45
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Wraps ifconfig calls useful for testing.
|
2
|
+
module IfconfigCli
|
3
|
+
# Runs ifconfig and parses its output.
|
4
|
+
def self.run
|
5
|
+
case RUBY_PLATFORM
|
6
|
+
when /win/
|
7
|
+
else
|
8
|
+
output = `ifconfig -a`
|
9
|
+
info_blocks = output.split /\n(?=\w)/
|
10
|
+
Hash[info_blocks.map { |i|
|
11
|
+
name = i.split(' ', 2).first
|
12
|
+
mac = if match = /hwaddr\s([0-9a-f:]+)\s/i.match(i)
|
13
|
+
# Linux ifconfig output.
|
14
|
+
match[1].gsub(':', '').downcase
|
15
|
+
elsif match = /ether\s([0-9a-f:]+)\s/i.match(i)
|
16
|
+
# OSX ifconfig output.
|
17
|
+
match[1].gsub(':', '').downcase
|
18
|
+
elsif match = /\s(([0-9a-f]{2}:){5}[0-9a-f]{2})\s/i.match(i)
|
19
|
+
# First thing that looks like a MAC address.
|
20
|
+
match[1].gsub(':', '').downcase
|
21
|
+
else
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
active = /inet\s/.match(i) ? true : false
|
25
|
+
[name, { :mac => mac, :active => active }]
|
26
|
+
}]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# The name of the first active Ethernet device.
|
31
|
+
#
|
32
|
+
# The return value will most likely be eth0 on Linux and en0 on OSX.
|
33
|
+
def self.live_device
|
34
|
+
run_result = run
|
35
|
+
run_result.keys.sort.find { |device| run_result[device][:active] }
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class RawSocketStub
|
2
|
+
def initialize(recv_data)
|
3
|
+
@packets = recv_data
|
4
|
+
@sends = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def recv(buffer_size)
|
8
|
+
raise 'recv called too many times' if @packets.empty?
|
9
|
+
@packets.shift
|
10
|
+
end
|
11
|
+
|
12
|
+
def send(data, flags)
|
13
|
+
raise 'Weird flags' if flags != 0
|
14
|
+
@sends << data
|
15
|
+
end
|
16
|
+
|
17
|
+
def close
|
18
|
+
@packets = nil
|
19
|
+
@sends = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :sends
|
23
|
+
end
|
metadata
CHANGED
@@ -1,66 +1,121 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ethernet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
4
5
|
prerelease:
|
5
|
-
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
6
11
|
platform: ruby
|
7
12
|
authors:
|
13
|
+
- Victor Costan
|
8
14
|
- HaoQi Li
|
9
15
|
autorequire:
|
10
16
|
bindir: bin
|
11
17
|
cert_chain: []
|
12
18
|
|
13
|
-
date: 2011-
|
14
|
-
default_executable:
|
19
|
+
date: 2011-05-27 00:00:00 Z
|
15
20
|
dependencies:
|
16
21
|
- !ruby/object:Gem::Dependency
|
17
|
-
|
22
|
+
type: :runtime
|
18
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
19
24
|
none: false
|
20
25
|
requirements:
|
21
26
|
- - ~>
|
22
27
|
- !ruby/object:Gem::Version
|
23
|
-
|
24
|
-
|
28
|
+
hash: 25
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 1
|
32
|
+
- 1
|
33
|
+
version: 0.1.1
|
25
34
|
prerelease: false
|
26
35
|
version_requirements: *id001
|
36
|
+
name: system-getifaddrs
|
27
37
|
- !ruby/object:Gem::Dependency
|
28
|
-
|
38
|
+
type: :development
|
29
39
|
requirement: &id002 !ruby/object:Gem::Requirement
|
30
40
|
none: false
|
31
41
|
requirements:
|
32
42
|
- - ~>
|
33
43
|
- !ruby/object:Gem::Version
|
34
|
-
|
35
|
-
|
44
|
+
hash: 29
|
45
|
+
segments:
|
46
|
+
- 3
|
47
|
+
- 6
|
48
|
+
- 1
|
49
|
+
version: 3.6.1
|
36
50
|
prerelease: false
|
37
51
|
version_requirements: *id002
|
52
|
+
name: rdoc
|
38
53
|
- !ruby/object:Gem::Dependency
|
39
|
-
|
54
|
+
type: :development
|
40
55
|
requirement: &id003 !ruby/object:Gem::Requirement
|
41
56
|
none: false
|
42
57
|
requirements:
|
43
58
|
- - ~>
|
44
59
|
- !ruby/object:Gem::Version
|
45
|
-
|
46
|
-
|
60
|
+
hash: 23
|
61
|
+
segments:
|
62
|
+
- 2
|
63
|
+
- 6
|
64
|
+
- 0
|
65
|
+
version: 2.6.0
|
47
66
|
prerelease: false
|
48
67
|
version_requirements: *id003
|
68
|
+
name: rspec
|
49
69
|
- !ruby/object:Gem::Dependency
|
50
|
-
|
70
|
+
type: :development
|
51
71
|
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 23
|
77
|
+
segments:
|
78
|
+
- 1
|
79
|
+
- 0
|
80
|
+
- 0
|
81
|
+
version: 1.0.0
|
82
|
+
prerelease: false
|
83
|
+
version_requirements: *id004
|
84
|
+
name: bundler
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
type: :development
|
87
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ~>
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 15
|
93
|
+
segments:
|
94
|
+
- 1
|
95
|
+
- 6
|
96
|
+
- 0
|
97
|
+
version: 1.6.0
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: *id005
|
100
|
+
name: jeweler
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
type: :development
|
103
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
52
104
|
none: false
|
53
105
|
requirements:
|
54
106
|
- - ">="
|
55
107
|
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
109
|
+
segments:
|
110
|
+
- 0
|
56
111
|
version: "0"
|
57
|
-
type: :development
|
58
112
|
prerelease: false
|
59
|
-
version_requirements: *
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
113
|
+
version_requirements: *id006
|
114
|
+
name: rcov
|
115
|
+
description: Provides a Socket-like API that bypasses TCP/IP. Useful for exotic devices and FPGA development.
|
116
|
+
email: victor@costan.us
|
117
|
+
executables:
|
118
|
+
- ethernet_ping
|
64
119
|
extensions: []
|
65
120
|
|
66
121
|
extra_rdoc_files:
|
@@ -68,6 +123,7 @@ extra_rdoc_files:
|
|
68
123
|
- README.rdoc
|
69
124
|
files:
|
70
125
|
- .document
|
126
|
+
- .project
|
71
127
|
- .rspec
|
72
128
|
- Gemfile
|
73
129
|
- Gemfile.lock
|
@@ -75,12 +131,22 @@ files:
|
|
75
131
|
- README.rdoc
|
76
132
|
- Rakefile
|
77
133
|
- VERSION
|
134
|
+
- bin/ethernet_ping
|
78
135
|
- ethernet.gemspec
|
79
136
|
- lib/ethernet.rb
|
137
|
+
- lib/ethernet/devices.rb
|
138
|
+
- lib/ethernet/frame_socket.rb
|
139
|
+
- lib/ethernet/ping.rb
|
140
|
+
- lib/ethernet/provisioning.rb
|
141
|
+
- lib/ethernet/raw_socket_factory.rb
|
142
|
+
- spec/ethernet/devices_spec.rb
|
143
|
+
- spec/ethernet/frame_socket_spec.rb
|
144
|
+
- spec/ethernet/raw_socket_factory_spec.rb
|
80
145
|
- spec/ethernet_spec.rb
|
81
146
|
- spec/spec_helper.rb
|
82
|
-
|
83
|
-
|
147
|
+
- spec/support/ifconfig_cli.rb
|
148
|
+
- spec/support/raw_socket_stub.rb
|
149
|
+
homepage: http://github.com/costan/ethernet
|
84
150
|
licenses:
|
85
151
|
- MIT
|
86
152
|
post_install_message:
|
@@ -93,7 +159,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
159
|
requirements:
|
94
160
|
- - ">="
|
95
161
|
- !ruby/object:Gem::Version
|
96
|
-
hash:
|
162
|
+
hash: 3
|
97
163
|
segments:
|
98
164
|
- 0
|
99
165
|
version: "0"
|
@@ -102,14 +168,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
168
|
requirements:
|
103
169
|
- - ">="
|
104
170
|
- !ruby/object:Gem::Version
|
171
|
+
hash: 3
|
172
|
+
segments:
|
173
|
+
- 0
|
105
174
|
version: "0"
|
106
175
|
requirements: []
|
107
176
|
|
108
177
|
rubyforge_project:
|
109
|
-
rubygems_version: 1.
|
178
|
+
rubygems_version: 1.8.4
|
110
179
|
signing_key:
|
111
180
|
specification_version: 3
|
112
|
-
summary:
|
113
|
-
test_files:
|
114
|
-
|
115
|
-
- spec/spec_helper.rb
|
181
|
+
summary: Ethernet (link-layer level) sockets.
|
182
|
+
test_files: []
|
183
|
+
|