pio 0.6.0 → 0.7.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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +56 -22
  4. data/examples/dhcp_new.rb +22 -18
  5. data/examples/features_new.rb +14 -0
  6. data/examples/features_read.rb +4 -0
  7. data/features/arp_read.feature +4 -4
  8. data/features/dhcp_read.feature +2 -2
  9. data/features/echo_read.feature +5 -0
  10. data/features/features_read.feature +10 -0
  11. data/features/hello_read.feature +5 -0
  12. data/features/icmp_read.feature +2 -2
  13. data/features/lldp_read.feature +4 -4
  14. data/features/{pcap → packet_data}/arp-storm.pcap +0 -0
  15. data/features/{pcap → packet_data}/arp.pcap +0 -0
  16. data/features/{pcap → packet_data}/dhcp.pcap +0 -0
  17. data/features/packet_data/echo.raw +0 -0
  18. data/features/packet_data/features_reply.raw +0 -0
  19. data/features/packet_data/features_request.raw +0 -0
  20. data/features/packet_data/hello.raw +0 -0
  21. data/features/{pcap → packet_data}/icmp.pcap +0 -0
  22. data/features/{pcap → packet_data}/lldp.detailed.pcap +0 -0
  23. data/features/{pcap → packet_data}/lldp.minimal.pcap +0 -0
  24. data/features/step_definitions/packet_data_steps.rb +32 -0
  25. data/features/step_definitions/pending_steps.rb +5 -0
  26. data/lib/pio.rb +1 -0
  27. data/lib/pio/arp.rb +1 -1
  28. data/lib/pio/arp/{frame.rb → format.rb} +2 -2
  29. data/lib/pio/arp/message.rb +2 -2
  30. data/lib/pio/dhcp/dhcp_field.rb +3 -3
  31. data/lib/pio/dhcp/frame.rb +6 -6
  32. data/lib/pio/dhcp/optional_tlv.rb +3 -3
  33. data/lib/pio/echo.rb +4 -11
  34. data/lib/pio/echo/format.rb +5 -5
  35. data/lib/pio/echo/message.rb +13 -4
  36. data/lib/pio/echo/reply.rb +29 -0
  37. data/lib/pio/echo/request.rb +29 -0
  38. data/lib/pio/features.rb +18 -0
  39. data/lib/pio/features/format.rb +18 -0
  40. data/lib/pio/features/message.rb +14 -0
  41. data/lib/pio/features/reply.rb +73 -0
  42. data/lib/pio/features/request.rb +63 -0
  43. data/lib/pio/hello.rb +40 -9
  44. data/lib/pio/icmp.rb +1 -1
  45. data/lib/pio/icmp/{frame.rb → format.rb} +2 -2
  46. data/lib/pio/icmp/message.rb +2 -2
  47. data/lib/pio/icmp/request.rb +30 -14
  48. data/lib/pio/message_type_selector.rb +4 -7
  49. data/lib/pio/type/ethernet_header.rb +0 -2
  50. data/lib/pio/type/ipv4_header.rb +0 -1
  51. data/lib/pio/type/open_flow.rb +34 -0
  52. data/lib/pio/type/udp_header.rb +0 -1
  53. data/lib/pio/version.rb +1 -1
  54. data/pio.gemspec +2 -2
  55. data/spec/pio/dhcp/ack_spec.rb +1 -1
  56. data/spec/pio/dhcp_spec.rb +2 -2
  57. data/spec/pio/echo/reply_spec.rb +69 -4
  58. data/spec/pio/echo/request_spec.rb +48 -10
  59. data/spec/pio/echo_spec.rb +8 -0
  60. data/spec/pio/features/reply_spec.rb +30 -0
  61. data/spec/pio/features/request_spec.rb +70 -0
  62. data/spec/pio/features_spec.rb +78 -0
  63. data/spec/pio/hello_spec.rb +35 -6
  64. data/spec/spec_helper.rb +3 -0
  65. metadata +70 -40
  66. data/features/step_definitions/pcap_steps.rb +0 -18
@@ -3,23 +3,16 @@
3
3
  require 'pio/echo/format'
4
4
  require 'pio/echo/reply'
5
5
  require 'pio/echo/request'
6
+ require 'pio/message_type_selector'
6
7
 
7
8
  module Pio
8
9
  # OpenFlow Echo Request and Reply message parser.
9
10
  class Echo
11
+ extend MessageTypeSelector
12
+
10
13
  REQUEST = 2
11
14
  REPLY = 3
12
15
 
13
- def self.read(raw_data)
14
- echo = Echo::Format.read(raw_data)
15
- case echo.message_type
16
- when REQUEST
17
- Echo::Request.create_from(echo)
18
- when REPLY
19
- Echo::Reply.create_from(echo)
20
- else
21
- fail 'Unknown Echo message type.'
22
- end
23
- end
16
+ message_type REQUEST => Request, REPLY => Reply
24
17
  end
25
18
  end
@@ -1,18 +1,18 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'bindata'
4
+ require 'pio/type/open_flow'
4
5
 
5
6
  module Pio
6
7
  class Echo
7
8
  # OpenFlow 1.0 Echo message format.
8
9
  class Format < BinData::Record
10
+ extend Type::OpenFlow
11
+
9
12
  endian :big
10
13
 
11
- uint8 :version, value: 1
12
- uint8 :message_type
13
- uint16 :message_length, value: -> { 8 + data.length }
14
- uint32 :transaction_id, initial_value: 0
15
- string :data
14
+ openflow_header
15
+ string :body
16
16
  end
17
17
  end
18
18
  end
@@ -14,7 +14,7 @@ module Pio
14
14
  def_delegators :@echo, :message_length
15
15
  def_delegators :@echo, :transaction_id
16
16
  def_delegator :@echo, :transaction_id, :xid
17
- def_delegators :@echo, :data
17
+ def_delegator :@echo, :body, :data
18
18
  def_delegator :@echo, :to_binary_s, :to_binary
19
19
 
20
20
  def self.create_from(echo)
@@ -24,15 +24,24 @@ module Pio
24
24
  end
25
25
 
26
26
  def initialize(message_type, user_options = {})
27
- @options = user_options.dup.merge(message_type: message_type)
28
- handle_option_aliases
27
+ if user_options.respond_to?(:to_i)
28
+ @options = { transaction_id: user_options.to_i,
29
+ message_type: message_type }
30
+ elsif user_options.respond_to?(:[])
31
+ @options = user_options.dup.merge(message_type: message_type)
32
+ handle_user_hash_options
33
+ else
34
+ fail TypeError
35
+ end
29
36
  @echo = Format.new(@options)
30
37
  end
31
38
 
32
39
  private
33
40
 
34
- def handle_option_aliases
41
+ def handle_user_hash_options
42
+ @options[:body] = @options[:data]
35
43
  @options[:transaction_id] ||= @options[:xid]
44
+ @options[:transaction_id] = 0 unless @options[:transaction_id]
36
45
  end
37
46
  end
38
47
  end
@@ -8,6 +8,35 @@ module Pio
8
8
  class Echo
9
9
  # OpenFlow 1.0 Echo Reply message.
10
10
  class Reply < Message
11
+ # Creates an EchoReply OpenFlow message. This message can be
12
+ # used to measure the bandwidth of a controller/switch
13
+ # connection as well as to verify its liveness.
14
+ #
15
+ # @overload initialize()
16
+ # @example
17
+ # Pio::Echo::Reply.new
18
+ #
19
+ # @overload initialize(transaction_id)
20
+ # @example
21
+ # Pio::Echo::Reply.new(123)
22
+ # @param [Number] transaction_id
23
+ # An unsigned 32-bit integer number associated with this
24
+ # message.
25
+ #
26
+ # @overload initialize(user_options)
27
+ # @example
28
+ # Pio::Echo::Reply.new(
29
+ # transaction_id: transaction_id,
30
+ # user_data: 'Thu Aug 25 13:09:00 +0900 2011'
31
+ # )
32
+ # @param [Hash] user_options The options to create a message with.
33
+ # @option user_options [Number] :transaction_id
34
+ # @option user_options [Number] :xid An alias to transaction_id.
35
+ # @option user_options [String] :user_data
36
+ # The user data field specified as a String may be a message
37
+ # timestamp to check latency, various lengths to measure
38
+ # bandwidth or zero-size(nil) to verify liveness between the
39
+ # switch and controller.
11
40
  def initialize(user_options = {})
12
41
  super REPLY, user_options
13
42
  end
@@ -8,6 +8,35 @@ module Pio
8
8
  class Echo
9
9
  # OpenFlow 1.0 Echo Request message.
10
10
  class Request < Message
11
+ # Creates an EchoRequest OpenFlow message. This message can be
12
+ # used to measure the bandwidth of a controller/switch
13
+ # connection as well as to verify its liveness.
14
+ #
15
+ # @overload initialize()
16
+ # @example
17
+ # Pio::Echo::Request.new
18
+ #
19
+ # @overload initialize(transaction_id)
20
+ # @example
21
+ # Pio::Echo::Request.new(123)
22
+ # @param [Number] transaction_id
23
+ # An unsigned 32-bit integer number associated with this
24
+ # message.
25
+ #
26
+ # @overload initialize(user_options)
27
+ # @example
28
+ # Pio::Echo::Request.new(
29
+ # transaction_id: transaction_id,
30
+ # user_data: 'Thu Aug 25 13:09:00 +0900 2011'
31
+ # )
32
+ # @param [Hash] user_options The options to create a message with.
33
+ # @option user_options [Number] :transaction_id
34
+ # @option user_options [Number] :xid An alias to transaction_id.
35
+ # @option user_options [String] :user_data
36
+ # The user data field specified as a String may be a message
37
+ # timestamp to check latency, various lengths to measure
38
+ # bandwidth or zero-size(nil) to verify liveness between the
39
+ # switch and controller.
11
40
  def initialize(user_options = {})
12
41
  super REQUEST, user_options
13
42
  end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ require 'pio/features/format'
4
+ require 'pio/features/reply'
5
+ require 'pio/features/request'
6
+ require 'pio/message_type_selector'
7
+
8
+ module Pio
9
+ # OpenFlow 1.0 Features messages
10
+ class Features
11
+ extend MessageTypeSelector
12
+
13
+ REQUEST = 5
14
+ REPLY = 6
15
+
16
+ message_type REQUEST => Request, REPLY => Reply
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bindata'
4
+ require 'pio/type/open_flow'
5
+
6
+ module Pio
7
+ class Features
8
+ # OpenFlow 1.0 Features message format.
9
+ class Format < BinData::Record
10
+ extend Type::OpenFlow
11
+
12
+ endian :big
13
+
14
+ openflow_header
15
+ string :body, read_length: -> { message_length - 8 }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ module Pio
4
+ class Features
5
+ # Base class of Features request and reply.
6
+ class Message
7
+ def self.create_from(features)
8
+ message = allocate
9
+ message.instance_variable_set :@features, features
10
+ message
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bindata'
4
+ require 'forwardable'
5
+ require 'pio/features/message'
6
+
7
+ module Pio
8
+ # OpenFlow 1.0 Features messages
9
+ class Features
10
+ # OpenFlow 1.0 Features Reply message
11
+ class Reply < Message
12
+ # Message body of Features Reply
13
+ class Body < BinData::Record
14
+ endian :big
15
+
16
+ uint64 :datapath_id
17
+ uint32 :n_buffers
18
+ uint8 :n_tables
19
+ uint24 :padding
20
+ uint32 :capabilities
21
+ uint32 :actions
22
+ array :ports, type: :phy_port, read_until: :eof
23
+ end
24
+
25
+ extend Forwardable
26
+
27
+ def_delegators :@features, :version
28
+ def_delegators :@features, :message_type
29
+ def_delegators :@features, :message_length
30
+ def_delegators :@features, :transaction_id
31
+ def_delegator :@features, :transaction_id, :xid
32
+ def_delegators :@features, :body
33
+
34
+ def initialize(user_options = {})
35
+ @options = user_options.dup.merge(datapath_id: user_options[:dpid])
36
+ body = Body.new(@options)
37
+ @features = Format.new(@options.merge(message_type: 6,
38
+ body: body.to_binary_s))
39
+ end
40
+
41
+ def datapath_id
42
+ @body ||= Body.read(@features.body)
43
+ @body.datapath_id
44
+ end
45
+ alias_method :dpid, :datapath_id
46
+
47
+ def n_buffers
48
+ @body ||= Body.read(@features.body)
49
+ @body.n_buffers
50
+ end
51
+
52
+ def n_tables
53
+ @body ||= Body.read(@features.body)
54
+ @body.n_tables
55
+ end
56
+
57
+ def capabilities
58
+ @body ||= Body.read(@features.body)
59
+ @body.capabilities
60
+ end
61
+
62
+ def actions
63
+ @body ||= Body.read(@features.body)
64
+ @body.actions
65
+ end
66
+
67
+ def ports
68
+ @body ||= Body.read(@features.body)
69
+ @body.ports
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ require 'forwardable'
4
+ require 'pio/features/message'
5
+
6
+ module Pio
7
+ # OpenFlow 1.0 Features messages
8
+ class Features
9
+ # OpenFlow 1.0 Features Request message
10
+ class Request < Message
11
+ extend Forwardable
12
+
13
+ def_delegators :@features, :version
14
+ def_delegators :@features, :message_type
15
+ def_delegators :@features, :message_length
16
+ def_delegators :@features, :transaction_id
17
+ def_delegator :@features, :transaction_id, :xid
18
+ def_delegators :@features, :body
19
+ def_delegator :@features, :to_binary_s, :to_binary
20
+
21
+ # Creates a Features Request OpenFlow message.
22
+ #
23
+ # @overload initialize()
24
+ # @example
25
+ # Pio::Features::Request.new
26
+ #
27
+ # @overload initialize(transaction_id)
28
+ # @example
29
+ # Pio::Features::Request.new(123)
30
+ # @param [Number] transaction_id
31
+ # An unsigned 32-bit integer number associated with this
32
+ # message.
33
+ #
34
+ # @overload initialize(user_options)
35
+ # @example
36
+ # Pio::Features::Request.new(transaction_id: 123)
37
+ # Pio::Features::Request.new(xid: 123)
38
+ # @param [Hash] user_options
39
+ # The options to create a message with.
40
+ # @option user_options [Number] :transaction_id
41
+ # @option user_options [Number] :xid An alias to transaction_id.
42
+ def initialize(user_options = {})
43
+ if user_options.respond_to?(:to_i)
44
+ @options = { transaction_id: user_options.to_i,
45
+ message_type: 5 }
46
+ elsif user_options.respond_to?(:[])
47
+ @options = user_options.dup.merge(message_type: 5)
48
+ handle_user_hash_options
49
+ else
50
+ fail TypeError
51
+ end
52
+ @features = Format.new(@options)
53
+ end
54
+
55
+ private
56
+
57
+ def handle_user_hash_options
58
+ @options[:transaction_id] ||= @options[:xid]
59
+ @options[:transaction_id] = 0 unless @options[:transaction_id]
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'English'
3
4
  require 'forwardable'
4
5
  require 'pio/hello/format'
5
6
 
@@ -16,22 +17,52 @@ module Pio
16
17
  def_delegators :@data, :body
17
18
  def_delegator :@data, :to_binary_s, :to_binary
18
19
 
20
+ # Parses +raw_data+ binary string into a Hello message object.
21
+ #
22
+ # @example
23
+ # Pio::Hello.read("\x01\x00\x00\b\x00\x00\x00\x00")
24
+ # @return [Pio::Hello]
19
25
  def self.read(raw_data)
20
26
  hello = allocate
21
- hello.instance_variable_set :@data, Format.read(raw_data)
27
+ begin
28
+ hello.instance_variable_set :@data, Format.read(raw_data)
29
+ rescue BinData::ValidityError
30
+ raise ParseError, $ERROR_INFO.message
31
+ end
22
32
  hello
23
33
  end
24
34
 
35
+ # Creates a Hello OpenFlow message.
36
+ #
37
+ # @overload initialize()
38
+ # @example
39
+ # Pio::Hello.new
40
+ #
41
+ # @overload initialize(transaction_id)
42
+ # @example
43
+ # Pio::Hello.new(123)
44
+ # @param [Number] transaction_id
45
+ # An unsigned 32-bit integer number associated with this
46
+ # message.
47
+ #
48
+ # @overload initialize(user_options)
49
+ # @example
50
+ # Pio::Hello.new(transaction_id: 123)
51
+ # Pio::Hello.new(xid: 123)
52
+ # @param [Hash] user_options The options to create a message with.
53
+ # @option user_options [Number] :transaction_id
54
+ # @option user_options [Number] :xid An alias to transaction_id.
25
55
  def initialize(user_options = {})
26
- @options = user_options.dup
27
- handle_option_aliases
56
+ if user_options.respond_to?(:to_i)
57
+ @options = { transaction_id: user_options.to_i }
58
+ elsif user_options.respond_to?(:[])
59
+ @options = user_options.dup
60
+ @options[:transaction_id] ||= @options[:xid]
61
+ @options[:transaction_id] = 0 unless @options[:transaction_id]
62
+ else
63
+ fail TypeError
64
+ end
28
65
  @data = Format.new(@options)
29
66
  end
30
-
31
- private
32
-
33
- def handle_option_aliases
34
- @options[:transaction_id] ||= @options[:xid]
35
- end
36
67
  end
37
68
  end
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'pio/icmp/frame'
3
+ require 'pio/icmp/format'
4
4
  require 'pio/icmp/reply'
5
5
  require 'pio/icmp/request'
6
6
  require 'pio/message_type_selector'
@@ -6,8 +6,8 @@ require 'pio/type/ipv4_header'
6
6
 
7
7
  module Pio
8
8
  class Icmp
9
- # Icmp frame parser.
10
- class Frame < BinData::Record
9
+ # Icmp parser.
10
+ class Format < BinData::Record
11
11
  PADDED_PACKET_LENGTH = 50
12
12
  MINIMUM_PACKET_LENGTH = 36
13
13
  MINIMUM_FRAME_LENGTH = 64
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'pio/icmp/frame'
3
+ require 'pio/icmp/format'
4
4
  require 'forwardable'
5
5
 
6
6
  module Pio
@@ -42,7 +42,7 @@ module Pio
42
42
 
43
43
  def initialize(user_options)
44
44
  options = self.class.const_get(:Options).new(user_options)
45
- @frame = Icmp::Frame.new(options.to_hash)
45
+ @frame = Icmp::Format.new(options.to_hash)
46
46
  end
47
47
  end
48
48
  end