pio 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ require "rubygems"
2
+ require "bindata"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ # TLV value field of system capabilities TLV
8
+ class SystemCapabilitiesValue < BinData::Record
9
+ endian :big
10
+
11
+ uint16be :system_capabilities
12
+ uint16be :enabled_capabilities
13
+ end
14
+ end
15
+ end
16
+
17
+
18
+ ### Local variables:
19
+ ### mode: Ruby
20
+ ### coding: utf-8-unix
21
+ ### indent-tabs-mode: nil
22
+ ### End:
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ require "bindata"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ # TLV value field of system description TLV
8
+ class SystemDescriptionValue < BinData::Record
9
+ endian :big
10
+
11
+ stringz :system_description
12
+ end
13
+ end
14
+ end
15
+
16
+
17
+ ### Local variables:
18
+ ### mode: Ruby
19
+ ### coding: utf-8-unix
20
+ ### indent-tabs-mode: nil
21
+ ### End:
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ require "bindata"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ # TLV value field of system name TLV
8
+ class SystemNameValue < BinData::Record
9
+ endian :big
10
+
11
+ stringz :system_name
12
+ end
13
+ end
14
+ end
15
+
16
+
17
+ ### Local variables:
18
+ ### mode: Ruby
19
+ ### coding: utf-8-unix
20
+ ### indent-tabs-mode: nil
21
+ ### End:
@@ -0,0 +1,33 @@
1
+ require "rubygems"
2
+ require "bindata"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ # Time to live TLV
8
+ class TtlTlv < BinData::Primitive
9
+ endian :big
10
+
11
+ bit7 :tlv_type, :value => 3
12
+ bit9 :tlv_info_length, :value => 2
13
+ string :ttl, :read_length => :tlv_info_length
14
+
15
+
16
+ def get
17
+ BinData::Int16be.read( ttl )
18
+ end
19
+
20
+
21
+ def set value
22
+ self.ttl = BinData::Int16be.new( value ).to_binary_s
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+
29
+ ### Local variables:
30
+ ### mode: Ruby
31
+ ### coding: utf-8-unix
32
+ ### indent-tabs-mode: nil
33
+ ### End:
@@ -0,0 +1,30 @@
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
+ # Base module.
20
+ module Pio
21
+ # gem version.
22
+ VERSION = "0.1.0"
23
+ end
24
+
25
+
26
+ ### Local variables:
27
+ ### mode: Ruby
28
+ ### coding: utf-8
29
+ ### indent-tabs-mode: nil
30
+ ### End:
data/pio.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ lib = File.expand_path( "../lib", __FILE__ )
2
+ $LOAD_PATH.unshift( lib ) unless $LOAD_PATH.include?( lib )
3
+ require "pio/version"
4
+
5
+
6
+ Gem::Specification.new do | gem |
7
+ gem.name = "pio"
8
+ gem.version = Pio::VERSION
9
+ gem.summary = "Packet parser and generator."
10
+ gem.description = "Pure ruby packet parser and generator."
11
+
12
+ gem.license = "GPL3"
13
+
14
+ gem.authors = [ "Yasuhito Takamiya" ]
15
+ gem.email = [ "yasuhito@gmail.com" ]
16
+ gem.homepage = "http://github.com/trema/pio"
17
+
18
+ gem.files = `git ls-files`.split( "\n" )
19
+
20
+ gem.require_paths = [ "lib" ]
21
+
22
+ gem.extra_rdoc_files = [ "README.md" ]
23
+ gem.test_files = `git ls-files -- {spec,features}/*`.split( "\n" )
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,27 @@
1
+ require File.join( File.dirname( __FILE__ ), "..", "..", "spec_helper" )
2
+ require "pio/lldp/chassis-id-tlv"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ describe ChassisIdTlv do
8
+ subject { ChassisIdTlv.read( data.pack( "C*" ) ) }
9
+
10
+ context "parsing a raw data" do
11
+ let( :data ) { [ 0x02, 0x07, 0x04, 0x00, 0x19, 0x2f, 0xa7, 0xb2, 0x8d ] }
12
+
13
+ its( :tlv_type ) { should eq 1 }
14
+ its( :tlv_info_length ) { should eq 7 }
15
+ its( :subtype ) { should eq 4 }
16
+ its( :chassis_id ) { should eq data[ -6..-1 ].pack( "C*" ) }
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+
23
+ ### Local variables:
24
+ ### mode: Ruby
25
+ ### coding: utf-8-unix
26
+ ### indent-tabs-mode: nil
27
+ ### End:
@@ -0,0 +1,20 @@
1
+ require File.join( File.dirname( __FILE__ ), "..", "..", "spec_helper" )
2
+ require "pio/lldp/end-of-lldpdu-value"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ describe EndOfLldpduValue do
8
+ subject { EndOfLldpduValue.new }
9
+
10
+ its( :tlv_info_string ) { should be_empty }
11
+ end
12
+ end
13
+ end
14
+
15
+
16
+ ### Local variables:
17
+ ### mode: Ruby
18
+ ### coding: utf-8-unix
19
+ ### indent-tabs-mode: nil
20
+ ### End:
@@ -0,0 +1,82 @@
1
+ require File.join( File.dirname( __FILE__ ), "..", "..", "spec_helper" )
2
+ require "pio/lldp/frame"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ describe Frame do
8
+ it "should parse a minimal LLDP" do
9
+ sample_packet = [
10
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, # Destination MAC
11
+ 0x00, 0x19, 0x2f, 0xa7, 0xb2, 0x8d, # Source MAC
12
+ 0x88, 0xcc, # Ethertype
13
+ 0x02, 0x07, 0x04, 0x00, 0x19, 0x2f, 0xa7, 0xb2, 0x8d, # Chassis ID TLV
14
+ 0x04, 0x0d, 0x01, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x6f, 0x20, 0x53, 0x31, # Port ID TLV
15
+ 0x06, 0x02, 0x00, 0x78, # Time to live TLV
16
+ 0x00, 0x00, # End of LLDPDU TLV
17
+ ].pack( "C*" )
18
+
19
+ lldp = Frame.read( sample_packet )
20
+ end
21
+
22
+
23
+ it "should parse LLDP" do
24
+ sample_packet = [
25
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, # Destination MAC
26
+ 0x00, 0x19, 0x2f, 0xa7, 0xb2, 0x8d, # Source MAC
27
+ 0x88, 0xcc, # Ethertype
28
+ 0x02, 0x07, 0x04, 0x00, 0x19, 0x2f, 0xa7, 0xb2, 0x8d, # Chassis ID TLV
29
+ 0x04, 0x0d, 0x01, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x6f, 0x20, 0x53, 0x31, # Port ID TLV
30
+ 0x06, 0x02, 0x00, 0x78, # Time to live TLV
31
+ 0x08, 0x17, 0x53, 0x75, 0x6d, 0x6d, 0x69, 0x74, 0x33, 0x30, 0x30, 0x2d, 0x34, 0x38, 0x2d, 0x50, 0x6f, 0x72, 0x74, 0x20, 0x31, 0x30, 0x30, 0x31, 0x00, # Port Description
32
+ 0x0a, 0x0d, 0x53, 0x75, 0x6d, 0x6d, 0x69, 0x74, 0x33, 0x30, 0x30, 0x2d, 0x34, 0x38, 0x00, # System Name
33
+ 0x0c, 0x4c, 0x53, 0x75, 0x6d, 0x6d, 0x69, 0x74, 0x33, 0x30, 0x30, 0x2d, 0x34, 0x38, 0x20, 0x2d, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x37, 0x2e, 0x34, 0x65, 0x2e, 0x31, 0x20, 0x28, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x20, 0x35, 0x29, 0x20, 0x62, 0x79, 0x20, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x30, 0x35, 0x2f, 0x32, 0x37, 0x2f, 0x30, 0x35, 0x20, 0x30, 0x34, 0x3a, 0x35, 0x33, 0x3a, 0x31, 0x31, 0x00, # System Description
34
+ 0x0e, 0x04, 0x00, 0x14, 0x00, 0x14, # System Capabilities
35
+ 0x10, 0x0e, 0x07, 0x06, 0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0, 0x02, 0x00, 0x00, 0x03, 0xe9, 0x00, # Management Address
36
+ 0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, 0x07, 0x01, 0x00,
37
+ 0x00, 0x00, # End of LLDPDU TLV
38
+ ].pack( "C*" )
39
+
40
+ lldp = Frame.read( sample_packet )
41
+ lldp.optional_tlv[ 0 ][ :tlv_type ].should eq 4
42
+ lldp.optional_tlv[ 1 ][ :tlv_type ].should eq 5
43
+ lldp.optional_tlv[ 2 ][ :tlv_type ].should eq 6
44
+ lldp.optional_tlv[ 3 ][ :tlv_type ].should eq 7
45
+ lldp.optional_tlv[ 4 ][ :tlv_type ].should eq 8
46
+ lldp.optional_tlv[ 5 ][ :tlv_type ].should eq 0
47
+ end
48
+
49
+
50
+ it "should create a valid LLDP packet" do
51
+ # sample packet taken from http://www.cloudshark.org/captures/05a981251df9
52
+ sample_packet = [
53
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, # Destination MAC
54
+ 0x00, 0x19, 0x2f, 0xa7, 0xb2, 0x8d, # Source MAC
55
+ 0x88, 0xcc, # Ethertype
56
+ 0x02, 0x07, 0x04, 0x00, 0x19, 0x2f, 0xa7, 0xb2, 0x8d, # Chassis ID TLV
57
+ 0x04, 0x0d, 0x01, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x6f, 0x20, 0x53, 0x31, # Port ID TLV
58
+ 0x06, 0x02, 0x00, 0x78, # Time to live TLV
59
+ 0x00, 0x00 # End of LLDPDU TLV
60
+ ].pack( "C*" )
61
+
62
+ lldp_frame = Frame.new
63
+ lldp_frame.destination_mac = "01:80:c2:00:00:0e"
64
+ lldp_frame.source_mac = "00:19:2f:a7:b2:8d"
65
+ lldp_frame.chassis_id.subtype = 4
66
+ lldp_frame.chassis_id = "\x00\x19\x2f\xa7\xb2\x8d"
67
+ lldp_frame.port_id.subtype = 1
68
+ lldp_frame.port_id = "Uplink to S1"
69
+ lldp_frame.ttl = 120
70
+
71
+ ( lldp_frame.to_binary_s.unpack( "H*" )[ 0 ] + "0000" ).should == sample_packet.unpack( "H*" )[ 0 ]
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+
78
+ ### Local variables:
79
+ ### mode: Ruby
80
+ ### coding: utf-8-unix
81
+ ### indent-tabs-mode: nil
82
+ ### End:
@@ -0,0 +1,20 @@
1
+ require File.join( File.dirname( __FILE__ ), "..", "..", "spec_helper" )
2
+ require "pio/lldp/mac-address"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ describe MacAddress do
8
+ subject { MacAddress.new( "01:80:c2:00:00:0e" ) }
9
+
10
+ its( :to_binary_s ) { should eq "\001\200\302\000\000\016" }
11
+ end
12
+ end
13
+ end
14
+
15
+
16
+ ### Local variables:
17
+ ### mode: Ruby
18
+ ### coding: utf-8-unix
19
+ ### indent-tabs-mode: nil
20
+ ### End:
@@ -0,0 +1,81 @@
1
+ require File.join( File.dirname( __FILE__ ), "..", "..", "spec_helper" )
2
+ require "pio/lldp/optional-tlv"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ describe OptionalTlv do
8
+ subject { OptionalTlv.read( data ) }
9
+
10
+
11
+ context "when parsing End Of LLDPDU TLV" do
12
+ let( :data ) { [ 0x00, 0x00 ].pack( "C*" ) }
13
+
14
+ its( :tlv_type ) { should eq 0 }
15
+ its( :tlv_info_length ) { should eq 0 }
16
+ its( :tlv_value ) { should eq "tlv_info_string" => "" }
17
+ end
18
+
19
+
20
+ context "when parsing Port Description TLV" do
21
+ let( :data ) { [ 0x08, 0x17, 0x53, 0x75, 0x6d, 0x6d, 0x69, 0x74, 0x33, 0x30, 0x30, 0x2d, 0x34, 0x38, 0x2d, 0x50, 0x6f, 0x72, 0x74, 0x20, 0x31, 0x30, 0x30, 0x31, 0x00 ].pack( "C*" ) }
22
+
23
+ its( :tlv_type ) { should eq 4 }
24
+ its( :tlv_info_length ) { should eq 23 }
25
+ its( :tlv_value ) { should eq( { "port_description" => "Summit300-48-Port 1001" } ) }
26
+ end
27
+
28
+
29
+ context "when parsing System Name TLV" do
30
+ let( :data ) { [ 0x0a, 0x0d, 0x53, 0x75, 0x6d, 0x6d, 0x69, 0x74, 0x33, 0x30, 0x30, 0x2d, 0x34, 0x38, 0x00 ].pack( "C*" ) }
31
+
32
+ its( :tlv_type ) { should eq 5 }
33
+ its( :tlv_info_length ) { should eq 13 }
34
+ its( :tlv_value ) { should eq( { "system_name" => "Summit300-48" } ) }
35
+ end
36
+
37
+
38
+ context "when parsing System Description TLV" do
39
+ let( :data ) { [ 0x0c, 0x4c, 0x53, 0x75, 0x6d, 0x6d, 0x69, 0x74, 0x33, 0x30, 0x30, 0x2d, 0x34, 0x38, 0x20, 0x2d, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x37, 0x2e, 0x34, 0x65, 0x2e, 0x31, 0x20, 0x28, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x20, 0x35, 0x29, 0x20, 0x62, 0x79, 0x20, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x30, 0x35, 0x2f, 0x32, 0x37, 0x2f, 0x30, 0x35, 0x20, 0x30, 0x34, 0x3a, 0x35, 0x33, 0x3a, 0x31, 0x31, 0x00 ].pack( "C*" ) }
40
+
41
+ its( :tlv_type ) { should eq 6 }
42
+ its( :tlv_info_length ) { should eq 76 }
43
+ its( :tlv_value ) { should eq( { "system_description"=>"Summit300-48 - Version 7.4e.1 (Build 5) by Release_Master 05/27/05 04:53:11" } ) }
44
+ end
45
+
46
+
47
+ context "when parsing System Capabilities TLV" do
48
+ let( :data ) { [ 0x0e, 0x04, 0x00, 0x14, 0x00, 0x14 ].pack( "C*" ) }
49
+
50
+ its( :tlv_type ) { should eq 7 }
51
+ its( :tlv_info_length ) { should eq 4 }
52
+ its( :tlv_value ) { should eq( { "system_capabilities" => 20, "enabled_capabilities" => 20 } ) }
53
+ end
54
+
55
+
56
+ context "when parsing Management Address TLV" do
57
+ let( :data ) { [ 0x10, 0x0e, 0x07, 0x06, 0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0, 0x02, 0x00, 0x00, 0x03, 0xe9, 0x00 ].pack( "C*" ) }
58
+
59
+ its( :tlv_type ) { should eq 8 }
60
+ its( :tlv_info_length ) { should eq 14 }
61
+ its( :tlv_value ) { should eq( { "management_address" => "\a\006" } ) } # FIXME
62
+ end
63
+
64
+
65
+ context "when parsing Organizationally Specific TLV" do
66
+ let( :data ) { [ 0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, 0x07, 0x01, 0x00 ].pack( "C*" ) }
67
+
68
+ its( :tlv_type ) { should eq 127 }
69
+ its( :tlv_info_length ) { should eq 7 }
70
+ its( :tlv_value ) { should eq "" } # FIXME
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+
77
+ ### Local variables:
78
+ ### mode: Ruby
79
+ ### coding: utf-8-unix
80
+ ### indent-tabs-mode: nil
81
+ ### End:
@@ -0,0 +1,27 @@
1
+ require File.join( File.dirname( __FILE__ ), "..", "..", "spec_helper" )
2
+ require "pio/lldp/port-id-tlv"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ describe PortIdTlv do
8
+ subject { PortIdTlv.read( data.pack( "C*" ) ) }
9
+
10
+ context "parsing a raw data" do
11
+ let( :data ) { [ 0x04, 0x0d, 0x01, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x6f, 0x20, 0x53, 0x31 ] }
12
+
13
+ its( :tlv_type ) { should eq 2 }
14
+ its( :tlv_info_length ) { should eq 13 }
15
+ its( :subtype ) { should eq 1 }
16
+ its( :port_id ) { should eq data[ -12..-1 ].pack( "C*" ) }
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+
23
+ ### Local variables:
24
+ ### mode: Ruby
25
+ ### coding: utf-8-unix
26
+ ### indent-tabs-mode: nil
27
+ ### End:
@@ -0,0 +1,26 @@
1
+ require File.join( File.dirname( __FILE__ ), "..", "..", "spec_helper" )
2
+ require "pio/lldp/ttl-tlv"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ describe TtlTlv do
8
+ subject { TtlTlv.read( data.pack( "C*" ) ) }
9
+
10
+ context "parsing a raw data" do
11
+ let( :data ) { [ 0x06, 0x02, 0x00, 0x78 ] }
12
+
13
+ its( :tlv_type ) { should eq 3 }
14
+ its( :tlv_info_length ) { should eq 2 }
15
+ its( :ttl ) { should eq data[ -2..-1 ].pack( "C*" ) }
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+
22
+ ### Local variables:
23
+ ### mode: Ruby
24
+ ### coding: utf-8-unix
25
+ ### indent-tabs-mode: nil
26
+ ### End: