pio 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,90 @@
1
+ module Pio
2
+ #
3
+ # A wrapper class to IPAddr
4
+ #
5
+ class IP
6
+ require "ipaddr"
7
+
8
+
9
+ #
10
+ # @return [IPAddr] value object instance of proxied IPAddr.
11
+ #
12
+ attr_reader :value
13
+
14
+
15
+ attr_reader :prefixlen
16
+
17
+
18
+ #
19
+ # Creates a {IP} instance object as a proxy to IPAddr class.
20
+ #
21
+ # @overload initialize(addr)
22
+ #
23
+ # @param [String, Number] addr
24
+ # an IPv4 address specified either as a String or Number.
25
+ #
26
+ # @param [Number] prefixlen
27
+ # masking IPv4 address with given prefixlen.
28
+ #
29
+ # @raise [ArgumentError] invalid address if supplied argument is invalid
30
+ # IPv4 address.
31
+ #
32
+ # @return [IP] self
33
+ # a proxy to IPAddr.
34
+ #
35
+ def initialize addr, prefixlen = 32
36
+ @prefixlen = prefixlen
37
+ case addr
38
+ when Integer
39
+ @value = IPAddr.new( addr, Socket::AF_INET )
40
+ when String
41
+ @value = IPAddr.new( addr )
42
+ else
43
+ raise TypeError, "Invalid IP address: #{ addr.inspect }"
44
+ end
45
+ if prefixlen < 32
46
+ @value = @value.mask( prefixlen )
47
+ end
48
+ end
49
+
50
+
51
+ #
52
+ # @return [String] the IPv4 address in its text representation.
53
+ #
54
+ def to_s
55
+ @value.to_s
56
+ end
57
+
58
+
59
+ #
60
+ # @return [Number] the IPv4 address in its numeric representation.
61
+ #
62
+ def to_i
63
+ @value.to_i
64
+ end
65
+
66
+
67
+ def == other
68
+ to_s == other.to_s
69
+ end
70
+
71
+
72
+ #
73
+ # @return [Array]
74
+ # an array of decimal numbers converted from IP address.
75
+ #
76
+ def to_a
77
+ to_s.split( "." ).collect do | each |
78
+ each.to_i
79
+ end
80
+ end
81
+ alias :to_array :to_a
82
+ end
83
+ end
84
+
85
+
86
+ ### Local variables:
87
+ ### mode: Ruby
88
+ ### coding: utf-8-unix
89
+ ### indent-tabs-mode: nil
90
+ ### End:
@@ -1,6 +1,3 @@
1
- require "rubygems"
2
-
3
- require "bindata"
4
1
  require "forwardable"
5
2
  require "pio/lldp/frame"
6
3
 
@@ -8,27 +5,92 @@ require "pio/lldp/frame"
8
5
  module Pio
9
6
  # LLDP frame parser and generator.
10
7
  class Lldp
8
+ # User options for creating an LLDP frame.
9
+ class Options
10
+ def initialize options
11
+ @options = options
12
+
13
+ unless dpid
14
+ raise TypeError, "Invalid DPID: #{ dpid.inspect }"
15
+ end
16
+ unless port_id
17
+ raise TypeError, "Invalid port number: #{ port_id.inspect }"
18
+ end
19
+ end
20
+
21
+
22
+ def to_hash
23
+ {
24
+ :destination_mac => Mac.new( destination_mac ).to_a,
25
+ :source_mac => Mac.new( source_mac ).to_a,
26
+ :chassis_id => dpid,
27
+ :port_id => port_id
28
+ }
29
+ end
30
+
31
+
32
+ ##########################################################################
33
+ private
34
+ ##########################################################################
35
+
36
+
37
+ def dpid
38
+ @options[ :dpid ]
39
+ end
40
+
41
+
42
+ def port_id
43
+ @options[ :port_number ]
44
+ end
45
+
46
+
47
+ def destination_mac
48
+ @options[ :destination_mac ] || "01:80:c2:00:00:0e"
49
+ end
50
+
51
+
52
+ def source_mac
53
+ @options[ :source_mac ] || "01:02:03:04:05:06"
54
+ end
55
+ end
56
+
57
+
11
58
  extend Forwardable
12
59
 
13
60
 
14
61
  def self.read raw_data
15
- frame = Frame.read( raw_data )
16
- new frame.dpid, frame.port_id
62
+ begin
63
+ frame = Frame.read( raw_data )
64
+ rescue
65
+ raise Pio::ParseError, $!.message
66
+ end
67
+ lldp = allocate
68
+ lldp.instance_variable_set :@frame, frame
69
+ lldp
17
70
  end
18
71
 
19
72
 
20
- def initialize dpid, port_number, destination_mac = "01:80:c2:00:00:0e"
21
- @frame = Frame.new
22
- @frame.destination_mac = destination_mac
23
- @frame.source_mac = "11:22:33:44:55:66" # FIXME
24
- @frame.chassis_id = dpid
25
- @frame.port_id = port_number
73
+ def initialize options
74
+ @frame = Frame.new( Options.new( options ).to_hash )
26
75
  end
27
76
 
28
77
 
78
+ def_delegator :@frame, :destination_mac
79
+ def_delegator :@frame, :source_mac
80
+ def_delegator :@frame, :ether_type
81
+ def_delegator :@frame, :chassis_id
29
82
  def_delegator :@frame, :dpid
30
83
  def_delegator :@frame, :optional_tlv
31
- def_delegator :@frame, :port_id, :port_number
84
+ def_delegator :@frame, :port_id
85
+ alias :port_number :port_id
86
+ def_delegator :@frame, :ttl
87
+ def_delegator :@frame, :port_description
88
+ def_delegator :@frame, :system_name
89
+ def_delegator :@frame, :system_description
90
+ def_delegator :@frame, :system_capabilities
91
+ def_delegator :@frame, :enabled_capabilities
92
+ def_delegator :@frame, :management_address
93
+ def_delegator :@frame, :organizationally_specific
32
94
 
33
95
 
34
96
  def to_binary
@@ -2,21 +2,21 @@ require "rubygems"
2
2
  require "bindata"
3
3
 
4
4
  require "pio/lldp/chassis-id-tlv"
5
- require "pio/lldp/mac-address"
6
5
  require "pio/lldp/optional-tlv"
7
6
  require "pio/lldp/port-id-tlv"
8
7
  require "pio/lldp/ttl-tlv"
8
+ require "pio/type/ethernet-header"
9
9
 
10
10
 
11
11
  module Pio
12
12
  class Lldp
13
13
  # LLDP frame
14
14
  class Frame < BinData::Record
15
+ extend Type::EthernetHeader
16
+
15
17
  endian :big
16
18
 
17
- mac_address :destination_mac
18
- mac_address :source_mac
19
- uint16 :ethertype, :value => 0x88cc
19
+ ethernet_header :ether_type => 0x88cc
20
20
  chassis_id_tlv :chassis_id
21
21
  port_id_tlv :port_id
22
22
  ttl_tlv :ttl, :initial_value => 120
@@ -26,6 +26,55 @@ module Pio
26
26
  def dpid
27
27
  chassis_id
28
28
  end
29
+
30
+
31
+ def port_description
32
+ get_tlv_field 4, "port_description"
33
+ end
34
+
35
+
36
+ def system_name
37
+ get_tlv_field 5, "system_name"
38
+ end
39
+
40
+
41
+ def system_description
42
+ get_tlv_field 6, "system_description"
43
+ end
44
+
45
+
46
+ def system_capabilities
47
+ get_tlv 7
48
+ end
49
+
50
+
51
+ def management_address
52
+ get_tlv_field 8, "management_address"
53
+ end
54
+
55
+
56
+ def organizationally_specific
57
+ get_tlv 127
58
+ end
59
+
60
+
61
+ ##########################################################################
62
+ private
63
+ ##########################################################################
64
+
65
+
66
+ def get_tlv tlv_type
67
+ tlv = optional_tlv.find do | each |
68
+ each[ "tlv_type" ] == tlv_type
69
+ end
70
+ tlv[ "tlv_value" ] if tlv
71
+ end
72
+
73
+
74
+ def get_tlv_field tlv_type, name
75
+ tlv = get_tlv( tlv_type )
76
+ tlv[ name ] if tlv
77
+ end
29
78
  end
30
79
  end
31
80
  end
@@ -8,7 +8,13 @@ module Pio
8
8
  class ManagementAddressValue < BinData::Record
9
9
  endian :big
10
10
 
11
- stringz :management_address
11
+ uint8 :string_length
12
+ uint8 :subtype
13
+ string :management_address, :read_length => lambda { string_length - 1 }
14
+ uint8 :interface_numbering_subtype
15
+ uint32 :interface_number
16
+ uint8 :oid_string_length
17
+ string :object_identifier, :read_length => lambda { oid_string_length }
12
18
  end
13
19
  end
14
20
  end
@@ -3,6 +3,7 @@ require "pio/lldp/system-name-value"
3
3
  require "pio/lldp/system-description-value"
4
4
  require "pio/lldp/system-capabilities-value"
5
5
  require "pio/lldp/management-address-value"
6
+ require "pio/lldp/organizationally-specific-value"
6
7
  require "pio/lldp/end-of-lldpdu-value"
7
8
 
8
9
 
@@ -24,6 +25,7 @@ module Pio
24
25
  system_description_value 6
25
26
  system_capabilities_value 7
26
27
  management_address_value 8
28
+ organizationally_specific_value 127
27
29
  string "unknown"
28
30
  end
29
31
 
@@ -54,7 +56,7 @@ module Pio
54
56
 
55
57
  def optional_tlv?
56
58
  tmp_tlv_type = tlv_type
57
- 4 <= tmp_tlv_type and tmp_tlv_type <= 8
59
+ 4 <= tmp_tlv_type and tmp_tlv_type <= 127
58
60
  end
59
61
 
60
62
 
@@ -0,0 +1,23 @@
1
+ require "rubygems"
2
+ require "bindata"
3
+
4
+
5
+ module Pio
6
+ class Lldp
7
+ # The V of Organizationally specfic TLV.
8
+ class OrganizationallySpecificValue < BinData::Record
9
+ endian :big
10
+
11
+ uint24be :oui
12
+ uint8 :subtype
13
+ stringz :information
14
+ end
15
+ end
16
+ end
17
+
18
+
19
+ ### Local variables:
20
+ ### mode: Ruby
21
+ ### coding: utf-8-unix
22
+ ### indent-tabs-mode: nil
23
+ ### End:
@@ -0,0 +1,156 @@
1
+ require "forwardable"
2
+
3
+
4
+ module Pio
5
+ #
6
+ # Ethernet address class
7
+ #
8
+ class Mac
9
+ extend Forwardable
10
+ def_delegator :@value, :hash
11
+
12
+
13
+ #
14
+ # Returns an Ethernet address in its numeric presentation.
15
+ #
16
+ # @example
17
+ # Mac.new("11:22:33:44:55:66") #=> 18838586676582
18
+ #
19
+ # @return [Number] the Ethernet address in numeric format
20
+ #
21
+ attr_reader :value
22
+
23
+
24
+ #
25
+ # Creates a {Mac} instance that encapsulates Ethernet addresses.
26
+ #
27
+ # @overload initialize(value)
28
+ #
29
+ # @param [String,Integer] value
30
+ # the Ethernet address to set to.
31
+ #
32
+ # @example address as a hexadecimal string
33
+ # Mac.new("11:22:33:44:55:66")
34
+ #
35
+ # @example address as a hexadecimal number
36
+ # Mac.new(0xffffffffffff)
37
+ #
38
+ # @raise [ArgumentError] if invalid format is detected.
39
+ # @raise [TypeError] if supplied argument is not a String or Integer.
40
+ #
41
+ def initialize value
42
+ case value
43
+ when String
44
+ @value = create_from( value )
45
+ when Integer
46
+ @value = value
47
+ validate_value_range
48
+ when Mac
49
+ @value = create_from( value.to_s )
50
+ else
51
+ raise TypeError, "Invalid MAC address: #{ value.inspect }"
52
+ end
53
+ @string = string_format
54
+ end
55
+
56
+
57
+ #
58
+ # Returns the Ethernet address as 6 pairs of hexadecimal digits
59
+ # delimited by colons.
60
+ #
61
+ # @example
62
+ # Mac.new(18838586676582).to_s #=> "11:22:33:44:55:66"
63
+ #
64
+ # @return [String] the Ethernet address in String format
65
+ #
66
+ def to_s
67
+ @string
68
+ end
69
+
70
+
71
+ #
72
+ # Returns an array of decimal numbers converted from Ethernet's
73
+ # address string format.
74
+ #
75
+ # @example
76
+ # Mac.new("11:22:33:44:55:66").to_a #=> [ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 ]
77
+ #
78
+ # @return [Array] the Ethernet address in Array format
79
+ #
80
+ def to_a
81
+ @string.split( ":" ).collect do | each |
82
+ each.hex
83
+ end
84
+ end
85
+
86
+
87
+ #
88
+ # @private
89
+ #
90
+ def == other
91
+ to_s == other.to_s
92
+ end
93
+ alias :eql? :==
94
+
95
+
96
+ #
97
+ # Returns true if Ethernet address is a multicast address.
98
+ #
99
+ # @example
100
+ # Mac.new("01:00:00:00:00:00").multicast? #=> true
101
+ # Mac.new("00:00:00:00:00:00").multicast? #=> false
102
+ #
103
+ # @return [Boolean] whether the Ethernet address is multicast
104
+ #
105
+ def multicast?
106
+ to_a[ 0 ] & 1 == 1
107
+ end
108
+
109
+
110
+ #
111
+ # Returns true if Ethernet address is a broadcast address.
112
+ #
113
+ # @example
114
+ # Mac.new("ff:ff:ff:ff:ff:ff").broadcast? #=> true
115
+ #
116
+ # @return [Boolean] whether the Ethernet address is broadcast
117
+ #
118
+ def broadcast?
119
+ to_a.all? { | each | each == 0xff }
120
+ end
121
+
122
+
123
+ ################################################################################
124
+ private
125
+ ################################################################################
126
+
127
+
128
+ def create_from string
129
+ octet_regex = "[0-9a-fA-F][0-9a-fA-F]"
130
+ if /^(#{ octet_regex }:){5}(#{ octet_regex })$/=~ string
131
+ string.gsub( ":", "" ).hex
132
+ else
133
+ raise ArgumentError, %{Invalid MAC address: "#{ string }"}
134
+ end
135
+ end
136
+
137
+
138
+ def validate_value_range
139
+ unless ( @value >= 0 and @value <= 0xffffffffffff )
140
+ raise ArgumentError, "Invalid MAC address: #{ @value }"
141
+ end
142
+ end
143
+
144
+
145
+ def string_format
146
+ sprintf( "%012x", @value ).unpack( "a2" * 6 ).join( ":" )
147
+ end
148
+ end
149
+ end
150
+
151
+
152
+ ### Local variables:
153
+ ### mode: Ruby
154
+ ### coding: utf-8-unix
155
+ ### indent-tabs-mode: nil
156
+ ### End: