pio 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +33 -2
  4. data/examples/echo_read.rb +1 -1
  5. data/examples/features_read.rb +1 -1
  6. data/examples/flow_mod_new.rb +13 -0
  7. data/examples/flow_mod_read.rb +6 -0
  8. data/features/echo_read.feature +27 -3
  9. data/features/features_read.feature +46 -2
  10. data/features/flow_mod_read.feature +186 -0
  11. data/features/hello_read.feature +9 -0
  12. data/features/packet_data/flow_mod_add.raw +0 -0
  13. data/features/packet_data/flow_mod_delete.raw +0 -0
  14. data/features/packet_data/flow_mod_delete_strict.raw +0 -0
  15. data/features/packet_data/flow_mod_modify.raw +0 -0
  16. data/features/packet_data/flow_mod_modify_strict.raw +0 -0
  17. data/features/packet_in_read.feature +13 -0
  18. data/features/packet_out_read.feature +16 -0
  19. data/features/step_definitions/packet_data_steps.rb +10 -1
  20. data/lib/pio.rb +1 -0
  21. data/lib/pio/echo.rb +10 -8
  22. data/lib/pio/enqueue.rb +1 -1
  23. data/lib/pio/features.rb +64 -7
  24. data/lib/pio/flow_mod.rb +86 -0
  25. data/lib/pio/hello.rb +4 -74
  26. data/lib/pio/ipv4_address.rb +1 -1
  27. data/lib/pio/match.rb +167 -0
  28. data/lib/pio/open_flow.rb +1 -0
  29. data/lib/pio/open_flow/actions.rb +65 -0
  30. data/lib/pio/open_flow/flags.rb +12 -9
  31. data/lib/pio/open_flow/message.rb +105 -21
  32. data/lib/pio/open_flow/phy_port.rb +31 -27
  33. data/lib/pio/open_flow/type.rb +1 -0
  34. data/lib/pio/packet_in.rb +2 -12
  35. data/lib/pio/packet_out.rb +4 -74
  36. data/lib/pio/send_out_port.rb +1 -0
  37. data/lib/pio/type/ip_address.rb +2 -7
  38. data/lib/pio/version.rb +1 -1
  39. data/pio.gemspec +8 -8
  40. data/spec/pio/echo/reply_spec.rb +61 -6
  41. data/spec/pio/echo/request_spec.rb +61 -6
  42. data/spec/pio/features/reply_spec.rb +81 -4
  43. data/spec/pio/features/request_spec.rb +88 -41
  44. data/spec/pio/flow_mod_spec.rb +151 -0
  45. data/spec/pio/hello_spec.rb +73 -56
  46. data/spec/pio/match_spec.rb +194 -0
  47. data/spec/pio/packet_in_spec.rb +35 -38
  48. data/spec/pio/packet_out_spec.rb +243 -182
  49. data/spec/pio/wildcards_spec.rb +115 -0
  50. metadata +37 -30
  51. data/features/packet_data/echo.raw +0 -0
  52. data/lib/pio/echo/message.rb +0 -49
  53. data/lib/pio/echo/reply.rb +0 -41
  54. data/lib/pio/echo/request.rb +0 -43
  55. data/lib/pio/features/reply.rb +0 -88
  56. data/lib/pio/features/request.rb +0 -68
  57. data/lib/pio/open_flow/parser.rb +0 -22
  58. data/spec/pio/echo_spec.rb +0 -49
  59. data/spec/pio/features_spec.rb +0 -96
data/lib/pio/open_flow.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'pio/open_flow/actions'
1
2
  require 'pio/open_flow/flags'
2
3
  require 'pio/open_flow/message'
3
4
  require 'pio/open_flow/open_flow_header'
@@ -0,0 +1,65 @@
1
+ require 'bindata'
2
+ require 'pio/enqueue'
3
+ require 'pio/send_out_port'
4
+ require 'pio/set_eth_addr'
5
+ require 'pio/set_ip_addr'
6
+ require 'pio/set_ip_tos'
7
+ require 'pio/set_transport_port'
8
+ require 'pio/set_vlan_priority'
9
+ require 'pio/set_vlan_vid'
10
+ require 'pio/strip_vlan_header'
11
+
12
+ module Pio
13
+ module OpenFlow
14
+ # Actions list.
15
+ class Actions < BinData::Primitive
16
+ ACTION_CLASS = {
17
+ 0 => Pio::SendOutPort,
18
+ 1 => Pio::SetVlanVid,
19
+ 2 => Pio::SetVlanPriority,
20
+ 3 => Pio::StripVlanHeader,
21
+ 4 => Pio::SetEthSrcAddr,
22
+ 5 => Pio::SetEthDstAddr,
23
+ 6 => Pio::SetIpSrcAddr,
24
+ 7 => Pio::SetIpDstAddr,
25
+ 8 => Pio::SetIpTos,
26
+ 9 => Pio::SetTransportSrcPort,
27
+ 10 => Pio::SetTransportDstPort,
28
+ 11 => Pio::Enqueue
29
+ }
30
+
31
+ mandatory_parameter :length
32
+
33
+ endian :big
34
+
35
+ string :binary, read_length: :length
36
+
37
+ def set(value)
38
+ self.binary = [value].flatten.map(&:to_binary).join
39
+ end
40
+
41
+ # rubocop:disable MethodLength
42
+ # This method smells of :reek:TooManyStatements
43
+ def get
44
+ actions = []
45
+ tmp = binary
46
+ while tmp.length > 0
47
+ type = BinData::Uint16be.read(tmp)
48
+ begin
49
+ action = ACTION_CLASS.fetch(type).read(tmp)
50
+ tmp = tmp[action.message_length..-1]
51
+ actions << action
52
+ rescue KeyError
53
+ raise "action type #{type} is not supported."
54
+ end
55
+ end
56
+ actions
57
+ end
58
+ # rubocop:enable MethodLength
59
+
60
+ def [](index)
61
+ get[index]
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,8 +1,17 @@
1
1
  # bitmap functions.
2
+ # This class smells of :reek:DataClump
2
3
  module Flags
4
+ def flags_32bit(name, flags)
5
+ _def_flags name, 32, flags
6
+ end
7
+
8
+ def flags_16bit(name, flags)
9
+ _def_flags name, 16, flags
10
+ end
11
+
3
12
  # rubocop:disable MethodLength
4
13
  # This method smells of :reek:TooManyStatements
5
- def def_flags(name, flags)
14
+ def _def_flags(name, size, flags)
6
15
  flag_value = case flags
7
16
  when Array
8
17
  shift = 0
@@ -14,20 +23,15 @@ module Flags
14
23
  when Hash
15
24
  flags
16
25
  end
17
- _def_class name, flag_value
18
- end
19
- # rubocop:enable MethodLength
20
26
 
21
- # rubocop:disable MethodLength
22
- def _def_class(name, flags)
23
27
  klass_name = name.to_s.split('_').map(&:capitalize).join
24
- flags_hash = flags.inspect
28
+ flags_hash = flag_value.inspect
25
29
 
26
30
  code = %{
27
31
  class #{klass_name} < BinData::Primitive
28
32
  endian :big
29
33
 
30
- uint32 :#{name}
34
+ uint#{size} :#{name}
31
35
 
32
36
  def get
33
37
  list = #{flags_hash}
@@ -48,5 +52,4 @@ module Flags
48
52
  }
49
53
  module_eval code
50
54
  end
51
- # rubocop:enable MethodLength
52
55
  end
@@ -1,4 +1,7 @@
1
+ require 'English'
2
+ require 'bindata'
1
3
  require 'forwardable'
4
+ require 'pio/parse_error'
2
5
 
3
6
  module Pio
4
7
  module OpenFlow
@@ -6,43 +9,124 @@ module Pio
6
9
  class Message
7
10
  extend Forwardable
8
11
 
12
+ # struct ofp_header fields.
9
13
  def_delegators :@format, :open_flow_header
10
14
  def_delegators :open_flow_header, :ofp_version
11
15
  def_delegators :open_flow_header, :message_type
12
16
  def_delegators :open_flow_header, :message_length
13
17
  def_delegators :open_flow_header, :transaction_id
14
18
  def_delegator :open_flow_header, :transaction_id, :xid
19
+
15
20
  def_delegators :@format, :body
16
21
  def_delegator :@format, :body, :user_data
22
+
17
23
  def_delegator :@format, :to_binary_s, :to_binary
18
24
 
25
+ def self.factory(message_type)
26
+ @message_type = message_type
27
+ self
28
+ end
29
+
30
+ def self.klass(message_type)
31
+ @messages.fetch(message_type)
32
+ end
33
+
34
+ def self.inherited(child)
35
+ child.const_set(:MESSAGE_TYPE, @message_type)
36
+ @messages ||= {}
37
+ @messages[@message_type] = child
38
+ end
39
+
40
+ def self.read(raw_data)
41
+ message = allocate
42
+ message.instance_variable_set(:@format, format.read(raw_data))
43
+ message
44
+ rescue BinData::ValidityError
45
+ message_name = name.split('::')[1..-1].join(' ')
46
+ raise Pio::ParseError, "Invalid #{message_name} message."
47
+ end
48
+
49
+ def self.format
50
+ const_get(:Format)
51
+ rescue NameError
52
+ define_message_format const_get(:MESSAGE_TYPE)
53
+ retry
54
+ end
55
+
19
56
  # rubocop:disable MethodLength
20
- def self.def_format(message_type)
21
- str = %(
22
- class Format < BinData::Record
23
- endian :big
24
-
25
- open_flow_header :open_flow_header,
26
- message_type_value: #{message_type}
27
- virtual assert: -> do
28
- open_flow_header.message_type == #{message_type}
29
- end
30
-
31
- body :body
32
- end
57
+ def self.define_message_format(message_type)
58
+ code = %(
59
+ class Format < BinData::Record
60
+ endian :big
61
+
62
+ open_flow_header :open_flow_header,
63
+ message_type_value: #{message_type}
64
+ virtual assert: -> do
65
+ open_flow_header.message_type == #{message_type}
66
+ end
67
+
68
+ #{body_type} :body
69
+ end
33
70
  )
34
- module_eval str
71
+ module_eval code
35
72
  end
36
73
  # rubocop:enable MethodLength
37
74
 
38
- # @reek This method smells of :reek:FeatureEnvy
39
- def initialize(user_options)
40
- header_options = { transaction_id: user_options[:transaction_id] ||
41
- user_options[:xid] || 0 }
42
- format_klass = self.class.const_get(:Format)
43
- @format = format_klass.new(open_flow_header: header_options,
44
- body: user_options)
75
+ def self.body_type
76
+ klass_name = name.split('::').last + 'Body'
77
+ const_get(klass_name)
78
+ klass_name.sub(/.*::/, '')
79
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
80
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
81
+ .tr('-', '_')
82
+ .downcase
83
+ rescue NameError
84
+ 'string'
85
+ end
86
+
87
+ def initialize(user_options = {})
88
+ header_options = parse_header_options(user_options)
89
+ body_options = parse_body_options(user_options)
90
+ @format = self.class.format.new(open_flow_header: header_options,
91
+ body: body_options)
45
92
  end
93
+
94
+ private
95
+
96
+ # This method smells of :reek:FeatureEnvy
97
+ def parse_header_options(options)
98
+ xid = if options.respond_to?(:to_i)
99
+ options.to_i
100
+ elsif options.respond_to?(:fetch)
101
+ options[:transaction_id] || options[:xid] || 0
102
+ else
103
+ fail TypeError
104
+ end
105
+ return { transaction_id: xid } if xid.unsigned_32bit?
106
+ fail(ArgumentError,
107
+ 'Transaction ID should be an unsigned 32-bit integer.')
108
+ end
109
+
110
+ # rubocop:disable MethodLength
111
+ # This method smells of :reek:TooManyStatements
112
+ # This method smells of :reek:FeatureEnvy
113
+ # This method smells of :reek:UtilityFunction
114
+ def parse_body_options(options)
115
+ if options.respond_to?(:fetch)
116
+ options.delete :transaction_id
117
+ options.delete :xid
118
+ dpid = options[:dpid]
119
+ options[:datapath_id] = dpid if dpid
120
+ if options.keys.size > 1
121
+ options
122
+ else
123
+ options[:user_data] || ''
124
+ end
125
+ else
126
+ ''
127
+ end
128
+ end
129
+ # rubocop:enable MethodLength
46
130
  end
47
131
  end
48
132
  end
@@ -9,37 +9,37 @@ module Pio
9
9
  extend Flags
10
10
 
11
11
  # enum ofp_port_config
12
- def_flags :port_config,
13
- [:port_down,
14
- :no_stp,
15
- :no_recv,
16
- :no_recv_stp,
17
- :no_flood,
18
- :no_fwd,
19
- :no_packet_in]
12
+ flags_32bit :port_config,
13
+ [:port_down,
14
+ :no_stp,
15
+ :no_recv,
16
+ :no_recv_stp,
17
+ :no_flood,
18
+ :no_fwd,
19
+ :no_packet_in]
20
20
 
21
21
  # enum ofp_port_state
22
- def_flags :port_state,
23
- link_down: 1 << 0,
24
- stp_listen: 0 << 8,
25
- stp_learn: 1 << 8,
26
- stp_forward: 2 << 8,
27
- stp_block: 3 << 8
22
+ flags_32bit :port_state,
23
+ link_down: 1 << 0,
24
+ stp_listen: 0 << 8,
25
+ stp_learn: 1 << 8,
26
+ stp_forward: 2 << 8,
27
+ stp_block: 3 << 8
28
28
 
29
29
  # enum ofp_port_features
30
- def_flags :port_feature,
31
- [:port_10mb_hd,
32
- :port_10mb_fd,
33
- :port_100mb_hd,
34
- :port_100mb_fd,
35
- :port_1gb_hd,
36
- :port_1gb_fd,
37
- :port_10gb_fd,
38
- :port_copper,
39
- :port_fiber,
40
- :port_autoneg,
41
- :port_pause,
42
- :port_pause_asym]
30
+ flags_32bit :port_feature,
31
+ [:port_10mb_hd,
32
+ :port_10mb_fd,
33
+ :port_100mb_hd,
34
+ :port_100mb_fd,
35
+ :port_1gb_hd,
36
+ :port_1gb_fd,
37
+ :port_10gb_fd,
38
+ :port_copper,
39
+ :port_fiber,
40
+ :port_autoneg,
41
+ :port_pause,
42
+ :port_pause_asym]
43
43
 
44
44
  endian :big
45
45
 
@@ -52,6 +52,10 @@ module Pio
52
52
  port_feature :advertised
53
53
  port_feature :supported
54
54
  port_feature :peer
55
+
56
+ def mac_address
57
+ hardware_address
58
+ end
55
59
  end
56
60
  end
57
61
  end
@@ -9,6 +9,7 @@ module Pio
9
9
  FEATURES_REPLY = 6
10
10
  PACKET_IN = 10
11
11
  PACKET_OUT = 13
12
+ FLOW_MOD = 14
12
13
  end
13
14
  end
14
15
  end
data/lib/pio/packet_in.rb CHANGED
@@ -4,7 +4,7 @@ require 'pio/parse_error'
4
4
 
5
5
  module Pio
6
6
  # OpenFlow 1.0 Packet-In message
7
- class PacketIn < Pio::OpenFlow::Message
7
+ class PacketIn < OpenFlow::Message.factory(OpenFlow::Type::PACKET_IN)
8
8
  # Why is this packet being sent to the controller?
9
9
  # (enum ofp_packet_in_reason)
10
10
  class Reason < BinData::Primitive
@@ -22,7 +22,7 @@ module Pio
22
22
  end
23
23
 
24
24
  # Message body of Packet-In.
25
- class Body < BinData::Record
25
+ class PacketInBody < BinData::Record
26
26
  endian :big
27
27
 
28
28
  uint32 :buffer_id
@@ -42,16 +42,6 @@ module Pio
42
42
  end
43
43
  end
44
44
 
45
- def_format Pio::OpenFlow::Type::PACKET_IN
46
-
47
- def self.read(raw_data)
48
- packet_in = allocate
49
- packet_in.instance_variable_set :@format, Format.read(raw_data)
50
- packet_in
51
- rescue BinData::ValidityError
52
- raise Pio::ParseError, 'Invalid Packet-In message.'
53
- end
54
-
55
45
  def_delegators :body, :buffer_id
56
46
  def_delegators :body, :total_len
57
47
  def_delegators :body, :in_port
@@ -1,75 +1,17 @@
1
1
  require 'bindata'
2
- require 'pio/enqueue'
3
2
  require 'pio/open_flow'
4
- require 'pio/send_out_port'
5
- require 'pio/set_eth_addr'
6
- require 'pio/set_ip_addr'
7
- require 'pio/set_ip_tos'
8
- require 'pio/set_transport_port'
9
- require 'pio/set_vlan_priority'
10
- require 'pio/set_vlan_vid'
11
- require 'pio/strip_vlan_header'
12
3
 
13
4
  module Pio
14
5
  # OpenFlow 1.0 Packet-Out message
15
- class PacketOut < Pio::OpenFlow::Message
16
- # Actions list.
17
- class Actions < BinData::Primitive
18
- ACTION_CLASS = {
19
- 0 => Pio::SendOutPort,
20
- 1 => Pio::SetVlanVid,
21
- 2 => Pio::SetVlanPriority,
22
- 3 => Pio::StripVlanHeader,
23
- 4 => Pio::SetEthSrcAddr,
24
- 5 => Pio::SetEthDstAddr,
25
- 6 => Pio::SetIpSrcAddr,
26
- 7 => Pio::SetIpDstAddr,
27
- 8 => Pio::SetIpTos,
28
- 9 => Pio::SetTransportSrcPort,
29
- 10 => Pio::SetTransportDstPort,
30
- 11 => Pio::Enqueue
31
- }
32
-
33
- endian :big
34
-
35
- uint16 :actions_len, initial_value: -> { binary.length }
36
- string :binary, read_length: :actions_len
37
-
38
- def set(value)
39
- self.binary = [value].flatten.map(&:to_binary).join
40
- end
41
-
42
- # rubocop:disable MethodLength
43
- # This method smells of :reek:TooManyStatements
44
- def get
45
- actions = []
46
- tmp = binary
47
- while tmp.length > 0
48
- type = BinData::Uint16be.read(tmp)
49
- begin
50
- action = ACTION_CLASS.fetch(type).read(tmp)
51
- tmp = tmp[action.message_length..-1]
52
- actions << action
53
- rescue KeyError
54
- raise "action type #{type} is not supported."
55
- end
56
- end
57
- actions
58
- end
59
- # rubocop:enable MethodLength
60
-
61
- def [](index)
62
- get[index]
63
- end
64
- end
65
-
6
+ class PacketOut < OpenFlow::Message.factory(OpenFlow::Type::PACKET_OUT)
66
7
  # Message body of Packet-Out
67
- class Body < BinData::Record
8
+ class PacketOutBody < BinData::Record
68
9
  endian :big
69
10
 
70
11
  uint32 :buffer_id
71
12
  uint16 :in_port
72
- actions :actions
13
+ uint16 :actions_len, initial_value: -> { actions.binary.length }
14
+ actions :actions, length: -> { actions_len }
73
15
  rest :data
74
16
 
75
17
  def empty?
@@ -79,18 +21,6 @@ module Pio
79
21
  def length
80
22
  8 + actions_len + data.length
81
23
  end
82
-
83
- def actions_len
84
- actions.actions_len
85
- end
86
- end
87
-
88
- def_format Pio::OpenFlow::Type::PACKET_OUT
89
-
90
- def self.read(raw_data)
91
- packet_out = allocate
92
- packet_out.instance_variable_set :@format, Format.read(raw_data)
93
- packet_out
94
24
  end
95
25
 
96
26
  def_delegators :body, :buffer_id