pio 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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