pio 0.1.1 → 0.2.1
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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/Gemfile +3 -2
- data/Guardfile +4 -1
- data/README.md +44 -6
- data/Rakefile +5 -23
- data/lib/pio.rb +15 -0
- data/lib/pio/arp.rb +31 -0
- data/lib/pio/arp/frame.rb +38 -0
- data/lib/pio/arp/message.rb +66 -0
- data/lib/pio/arp/reply.rb +55 -0
- data/lib/pio/arp/request.rb +55 -0
- data/lib/pio/ip.rb +90 -0
- data/lib/pio/lldp.rb +74 -12
- data/lib/pio/lldp/frame.rb +53 -4
- data/lib/pio/lldp/management-address-value.rb +7 -1
- data/lib/pio/lldp/optional-tlv.rb +3 -1
- data/lib/pio/lldp/organizationally-specific-value.rb +23 -0
- data/lib/pio/mac.rb +156 -0
- data/lib/pio/type/ethernet-header.rb +16 -0
- data/lib/pio/type/ip-address.rb +28 -0
- data/lib/pio/{lldp → type}/mac-address.rb +4 -3
- data/lib/pio/version.rb +1 -19
- data/pio.org +518 -0
- data/pio.org_archive +389 -0
- data/spec/pio/arp/reply_spec.rb +150 -0
- data/spec/pio/arp/request_spec.rb +131 -0
- data/spec/pio/arp_spec.rb +136 -0
- data/spec/pio/ip_spec.rb +38 -0
- data/spec/pio/lldp_spec.rb +195 -38
- data/spec/pio/mac_spec.rb +82 -0
- data/spec/spec_helper.rb +9 -0
- metadata +27 -17
- data/spec/pio/lldp/chassis-id-tlv_spec.rb +0 -27
- data/spec/pio/lldp/end-of-lldpdu-value_spec.rb +0 -20
- data/spec/pio/lldp/frame_spec.rb +0 -82
- data/spec/pio/lldp/mac-address_spec.rb +0 -20
- data/spec/pio/lldp/optional-tlv_spec.rb +0 -81
- data/spec/pio/lldp/port-id-tlv_spec.rb +0 -27
- data/spec/pio/lldp/ttl-tlv_spec.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a99eb3c57f09bc9a82c53d916636509695770620
|
4
|
+
data.tar.gz: 5da352b9a4cbe2660ce0fc71e086f15c697f52b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50e299558aea50ece6f430e007b428b9b5cc53439883043aca7128fe67ed3d08c50ec2e8ffac4f5d115735e77eb09289d25a249ee22f812edfbca2f91289aacc
|
7
|
+
data.tar.gz: b01bab2207f6556d2ca45ff72281c1475195511767d64b6b4847100961ada881b3406aeee2cd068d6998881f08d07dc026ca6c991a594a87d84aa2e722dd419e
|
data/.rspec
ADDED
data/Gemfile
CHANGED
@@ -4,14 +4,15 @@ source "https://rubygems.org"
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
|
7
|
-
group :development do
|
7
|
+
group :development, :test do
|
8
8
|
gem "coveralls", "~> 0.6.7", :require => false
|
9
9
|
gem "flay", "~> 2.4.0"
|
10
10
|
gem "flog", "~> 4.1.1"
|
11
|
+
gem "fuubar", "~> 1.2.1"
|
11
12
|
gem "guard", "~> 1.8.2"
|
12
13
|
gem "guard-bundler", "~> 1.0.0"
|
13
14
|
gem "guard-rspec", "~> 3.0.2"
|
14
|
-
gem "json", "~> 1.8.0"
|
15
|
+
gem "json", "~> 1.8.0"
|
15
16
|
gem "rake", "~> 10.1.0"
|
16
17
|
gem "rb-fchange", "~> 0.0.6", :require => false
|
17
18
|
gem "rb-fsevent", "~> 0.9.3", :require => false
|
data/Guardfile
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# A sample Guardfile
|
2
2
|
# More info at https://github.com/guard/guard#readme
|
3
3
|
|
4
|
+
notification :terminal_notifier
|
5
|
+
notification :tmux, :display_message => true
|
6
|
+
|
4
7
|
|
5
8
|
guard 'bundler' do
|
6
9
|
watch('Gemfile')
|
@@ -11,6 +14,6 @@ end
|
|
11
14
|
|
12
15
|
guard :rspec do
|
13
16
|
watch(%r{^spec/pio/.+_spec\.rb$})
|
14
|
-
watch(%r{^lib/pio/(.+)\.rb$}) { |m| "spec/
|
17
|
+
watch(%r{^lib/pio/(.+)\.rb$}) { |m| "spec/pio/#{m[1]}_spec.rb" }
|
15
18
|
watch('spec/spec_helper.rb') { "spec" }
|
16
19
|
end
|
data/README.md
CHANGED
@@ -10,7 +10,8 @@ Pio
|
|
10
10
|
|
11
11
|
Pio is a ruby gem to easily parse and generate network packets. It supports the following packet formats:
|
12
12
|
|
13
|
-
*
|
13
|
+
* ARP
|
14
|
+
* LLDP
|
14
15
|
* (...currently there are just a few formats supported but I'm sure this list will grow)
|
15
16
|
|
16
17
|
|
@@ -25,14 +26,51 @@ Features Overview
|
|
25
26
|
* Clean Code. Pio is built on
|
26
27
|
[BinData](https://github.com/dmendel/bindata)'s declarative binary
|
27
28
|
format DSL so that it is easy to read and debug by human beings.
|
28
|
-
|
29
|
+
|
29
30
|
|
30
31
|
Example
|
31
32
|
-------
|
32
33
|
|
33
|
-
Its usage is dead simple
|
34
|
-
|
35
|
-
|
34
|
+
Its usage is dead simple.
|
35
|
+
|
36
|
+
### ARP
|
37
|
+
|
38
|
+
To parse an ARP frame, use the API `Pio::Arp.read` and you can access
|
39
|
+
each field of the parsed ARP frame.
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
require "pio"
|
43
|
+
|
44
|
+
arp = Pio::Arp.read( binary_data )
|
45
|
+
arp.source_mac.to_s #=> "00:26:82:eb:ea:d1"
|
46
|
+
```
|
47
|
+
|
48
|
+
Also you can use `Pio::Arp::Request#new` or `Pio::Arp::Reply#new` to
|
49
|
+
generate an Arp Request/Reply frame like below:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require "pio"
|
53
|
+
|
54
|
+
request = Pio::Arp::Request.new(
|
55
|
+
source_mac: "00:26:82:eb:ea:d1",
|
56
|
+
sender_protocol_address: "192.168.83.3",
|
57
|
+
target_protocol_address: "192.168.83.254"
|
58
|
+
)
|
59
|
+
request.to_binary #=> Arp Request frame in binary format.
|
60
|
+
|
61
|
+
reply = Pio::Arp::Reply.new(
|
62
|
+
source_mac: "00:26:82:eb:ea:d1",
|
63
|
+
destination_mac: "00:26:82:eb:ea:d1",
|
64
|
+
sender_protocol_address: "192.168.83.3",
|
65
|
+
target_protocol_address: "192.168.83.254"
|
66
|
+
)
|
67
|
+
reply.to_binary #=> Arp Reply frame in binary format.
|
68
|
+
```
|
69
|
+
|
70
|
+
### LLDP
|
71
|
+
|
72
|
+
To parse an LLDP frame, use the API `Pio::Lldp.read` and you can
|
73
|
+
access each field of the parsed LLDP frame.
|
36
74
|
|
37
75
|
```ruby
|
38
76
|
require "pio"
|
@@ -46,7 +84,7 @@ Also you can use `Pio::Lldp#new` to generate an LLDP frame like below:
|
|
46
84
|
```ruby
|
47
85
|
require "pio"
|
48
86
|
|
49
|
-
lldp = Pio::Lldp.new( 0x123, 12 )
|
87
|
+
lldp = Pio::Lldp.new( dpid: 0x123, port_number: 12 )
|
50
88
|
lldp.to_binary #=> LLDP frame in binary format.
|
51
89
|
```
|
52
90
|
|
data/Rakefile
CHANGED
@@ -1,21 +1,3 @@
|
|
1
|
-
#
|
2
|
-
# Copyright (C) 2013 NEC Corporation
|
3
|
-
#
|
4
|
-
# This program is free software; you can redistribute it and/or modify
|
5
|
-
# it under the terms of the GNU General Public License, version 3, as
|
6
|
-
# published by the Free Software Foundation.
|
7
|
-
#
|
8
|
-
# This program is distributed in the hope that it will be useful,
|
9
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
-
# GNU General Public License for more details.
|
12
|
-
#
|
13
|
-
# You should have received a copy of the GNU General Public License along
|
14
|
-
# with this program; if not, write to the Free Software Foundation, Inc.,
|
15
|
-
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
1
|
require "bundler/gem_tasks"
|
20
2
|
require "coveralls/rake/task"
|
21
3
|
require "flay"
|
@@ -34,19 +16,19 @@ $ruby_source = FileList[ "lib/**/*.rb" ]
|
|
34
16
|
|
35
17
|
task :default => :travis
|
36
18
|
task :travis => [ :spec, :quality, "coveralls:push" ]
|
19
|
+
|
20
|
+
desc "Check for code quality"
|
37
21
|
task :quality => [ :reek, :flog, :flay ]
|
38
22
|
|
39
23
|
|
40
24
|
Coveralls::RakeTask.new
|
41
25
|
|
42
26
|
|
43
|
-
RSpec::Core::RakeTask.new
|
44
|
-
task.rspec_opts = "--format documentation --color"
|
45
|
-
end
|
27
|
+
RSpec::Core::RakeTask.new
|
46
28
|
|
47
29
|
|
48
30
|
Reek::Rake::Task.new do | t |
|
49
|
-
t.fail_on_error =
|
31
|
+
t.fail_on_error = false
|
50
32
|
t.verbose = false
|
51
33
|
t.ruby_opts = [ "-rubygems" ]
|
52
34
|
t.reek_opts = "--quiet"
|
@@ -69,7 +51,7 @@ task :flog do
|
|
69
51
|
puts "%8.1f: %s" % [ score, name ]
|
70
52
|
end
|
71
53
|
unless bad_methods.empty?
|
72
|
-
|
54
|
+
$stderr.puts "#{ bad_methods.size } methods have a flog complexity > #{ threshold }"
|
73
55
|
end
|
74
56
|
end
|
75
57
|
|
data/lib/pio.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "pio/arp"
|
2
|
+
require "pio/lldp"
|
3
|
+
|
4
|
+
|
5
|
+
module Pio
|
6
|
+
# Raised when the packet data is in wrong format.
|
7
|
+
class ParseError < StandardError; end
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
### Local variables:
|
12
|
+
### mode: Ruby
|
13
|
+
### coding: utf-8-unix
|
14
|
+
### indent-tabs-mode: nil
|
15
|
+
### End:
|
data/lib/pio/arp.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bindata"
|
3
|
+
|
4
|
+
require "pio/arp/request"
|
5
|
+
require "pio/arp/reply"
|
6
|
+
|
7
|
+
|
8
|
+
module Pio
|
9
|
+
# ARP parser and generator.
|
10
|
+
class Arp
|
11
|
+
ARP_MESSAGE_TYPE = { Request::OPERATION => Request, Reply::OPERATION => Reply }
|
12
|
+
|
13
|
+
|
14
|
+
def self.read( raw_data )
|
15
|
+
begin
|
16
|
+
frame = Arp::Frame.read( raw_data )
|
17
|
+
rescue
|
18
|
+
raise Pio::ParseError, $!.message
|
19
|
+
end
|
20
|
+
|
21
|
+
ARP_MESSAGE_TYPE[ frame.operation ].create_from frame
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
### Local variables:
|
28
|
+
### mode: Ruby
|
29
|
+
### coding: utf-8-unix
|
30
|
+
### indent-tabs-mode: nil
|
31
|
+
### End:
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "pio/type/ethernet-header"
|
2
|
+
require "pio/type/ip-address"
|
3
|
+
require "pio/type/mac-address"
|
4
|
+
|
5
|
+
|
6
|
+
module Pio
|
7
|
+
class Arp
|
8
|
+
# ARP frame parser.
|
9
|
+
class Frame < BinData::Record
|
10
|
+
extend Type::EthernetHeader
|
11
|
+
|
12
|
+
endian :big
|
13
|
+
|
14
|
+
ethernet_header :ether_type => 0x0806
|
15
|
+
uint16 :hardware_type, :value => 1
|
16
|
+
uint16 :protocol_type, :value => 0x0800
|
17
|
+
uint8 :hardware_length, :value => 6
|
18
|
+
uint8 :protocol_length, :value => 4
|
19
|
+
uint16 :operation
|
20
|
+
mac_address :sender_hardware_address
|
21
|
+
ip_address :sender_protocol_address
|
22
|
+
mac_address :target_hardware_address
|
23
|
+
ip_address :target_protocol_address
|
24
|
+
|
25
|
+
|
26
|
+
def to_binary
|
27
|
+
to_binary_s + "\000" * ( 64 - num_bytes )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
### Local variables:
|
35
|
+
### mode: Ruby
|
36
|
+
### coding: utf-8-unix
|
37
|
+
### indent-tabs-mode: nil
|
38
|
+
### End:
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "pio/arp/frame"
|
2
|
+
require "forwardable"
|
3
|
+
|
4
|
+
|
5
|
+
module Pio
|
6
|
+
class Arp
|
7
|
+
# Base class of ARP Request and Reply
|
8
|
+
class Message
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
|
12
|
+
def self.create_from frame
|
13
|
+
message = allocate
|
14
|
+
message.instance_variable_set :@frame, frame
|
15
|
+
message
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def initialize options
|
20
|
+
@options = options
|
21
|
+
@frame = Arp::Frame.new( option_hash )
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def_delegators :@frame, :destination_mac
|
26
|
+
def_delegators :@frame, :source_mac
|
27
|
+
def_delegators :@frame, :ether_type
|
28
|
+
def_delegators :@frame, :hardware_type
|
29
|
+
def_delegators :@frame, :protocol_type
|
30
|
+
def_delegators :@frame, :hardware_length
|
31
|
+
def_delegators :@frame, :protocol_length
|
32
|
+
def_delegators :@frame, :operation
|
33
|
+
def_delegators :@frame, :sender_hardware_address
|
34
|
+
def_delegators :@frame, :sender_protocol_address
|
35
|
+
def_delegators :@frame, :target_hardware_address
|
36
|
+
def_delegators :@frame, :target_protocol_address
|
37
|
+
def_delegators :@frame, :to_binary
|
38
|
+
|
39
|
+
|
40
|
+
##########################################################################
|
41
|
+
private
|
42
|
+
##########################################################################
|
43
|
+
|
44
|
+
|
45
|
+
def option_hash
|
46
|
+
mandatory_options.inject( {} ) do | opt, each |
|
47
|
+
klass = option_to_klass[ each ]
|
48
|
+
opt_pair = { each => klass.new( user_options[ each ] ).to_a }
|
49
|
+
opt.merge opt_pair
|
50
|
+
end.merge default_options
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def option_to_klass
|
55
|
+
{
|
56
|
+
:source_mac => Mac,
|
57
|
+
:destination_mac => Mac,
|
58
|
+
:sender_hardware_address => Mac,
|
59
|
+
:target_hardware_address => Mac,
|
60
|
+
:sender_protocol_address => IP,
|
61
|
+
:target_protocol_address => IP,
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "pio/arp/message"
|
3
|
+
require "pio/ip"
|
4
|
+
require "pio/mac"
|
5
|
+
|
6
|
+
|
7
|
+
module Pio
|
8
|
+
class Arp
|
9
|
+
# ARP Reply packet generator
|
10
|
+
class Reply < Message
|
11
|
+
OPERATION = 2
|
12
|
+
|
13
|
+
|
14
|
+
##########################################################################
|
15
|
+
private
|
16
|
+
##########################################################################
|
17
|
+
|
18
|
+
|
19
|
+
def default_options
|
20
|
+
{
|
21
|
+
:operation => OPERATION,
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def user_options
|
27
|
+
@options.merge(
|
28
|
+
{
|
29
|
+
:sender_hardware_address => @options[ :source_mac ],
|
30
|
+
:target_hardware_address => @options[ :destination_mac ]
|
31
|
+
}
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def mandatory_options
|
37
|
+
[
|
38
|
+
:source_mac,
|
39
|
+
:destination_mac,
|
40
|
+
:sender_hardware_address,
|
41
|
+
:target_hardware_address,
|
42
|
+
:sender_protocol_address,
|
43
|
+
:target_protocol_address,
|
44
|
+
]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
### Local variables:
|
52
|
+
### mode: Ruby
|
53
|
+
### coding: utf-8-unix
|
54
|
+
### indent-tabs-mode: nil
|
55
|
+
### End:
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "pio/arp/frame"
|
2
|
+
require "pio/arp/message"
|
3
|
+
require "pio/ip"
|
4
|
+
require "pio/mac"
|
5
|
+
|
6
|
+
|
7
|
+
module Pio
|
8
|
+
class Arp
|
9
|
+
# ARP Request packet generator
|
10
|
+
class Request < Message
|
11
|
+
OPERATION = 1
|
12
|
+
|
13
|
+
BROADCAST_MAC_ADDRESS = Mac.new( 0xffffffffffff ).to_a
|
14
|
+
ALL_ZERO_MAC_ADDRESS = Mac.new( 0 ).to_a
|
15
|
+
|
16
|
+
|
17
|
+
########################################################################
|
18
|
+
private
|
19
|
+
########################################################################
|
20
|
+
|
21
|
+
|
22
|
+
def default_options
|
23
|
+
{
|
24
|
+
:operation => OPERATION,
|
25
|
+
:destination_mac => BROADCAST_MAC_ADDRESS,
|
26
|
+
:target_hardware_address => ALL_ZERO_MAC_ADDRESS
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def user_options
|
32
|
+
@options.merge(
|
33
|
+
{ :sender_hardware_address => @options[ :source_mac ] }
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def mandatory_options
|
39
|
+
[
|
40
|
+
:source_mac,
|
41
|
+
:sender_hardware_address,
|
42
|
+
:sender_protocol_address,
|
43
|
+
:target_protocol_address,
|
44
|
+
]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
### Local variables:
|
52
|
+
### mode: Ruby
|
53
|
+
### coding: utf-8-unix
|
54
|
+
### indent-tabs-mode: nil
|
55
|
+
### End:
|