pio 0.20.1 → 0.21.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/README.md +1 -0
  4. data/Rakefile +5 -8
  5. data/bin/byebug +16 -0
  6. data/bin/terminal-notifier +16 -0
  7. data/features/open_flow10/flow_mod.feature +5 -5
  8. data/features/open_flow10/packet_in.feature +2 -2
  9. data/features/open_flow10/port_status.feature +1 -1
  10. data/features/open_flow13/action_copy_ttl_in.raw +0 -0
  11. data/features/open_flow13/action_copy_ttl_out.raw +0 -0
  12. data/features/open_flow13/action_dec_mpls_ttl.raw +0 -0
  13. data/features/open_flow13/action_dec_nw_ttl.raw +0 -0
  14. data/features/open_flow13/action_group.raw +0 -0
  15. data/features/open_flow13/action_pop_mpls.raw +0 -0
  16. data/features/open_flow13/action_pop_pbb.raw +0 -0
  17. data/features/open_flow13/action_pop_vlan.raw +0 -0
  18. data/features/open_flow13/action_push_mpls.raw +0 -0
  19. data/features/open_flow13/action_push_pbb.raw +0 -0
  20. data/features/open_flow13/action_push_vlan.raw +0 -0
  21. data/features/open_flow13/action_set_field.raw +0 -0
  22. data/features/open_flow13/action_set_mpls_ttl.raw +0 -0
  23. data/features/open_flow13/action_set_nw_ttl.raw +0 -0
  24. data/features/open_flow13/action_set_queue.raw +0 -0
  25. data/features/open_flow13/apply_actions.feature +43 -0
  26. data/features/open_flow13/apply_actions.raw +0 -0
  27. data/features/open_flow13/flow_add_apply_no_match.raw +0 -0
  28. data/features/open_flow13/flow_mod.feature +146 -0
  29. data/features/open_flow13/flow_mod_add_apply_no_match.raw +0 -0
  30. data/features/open_flow13/flow_mod_no_match_or_instructions.raw +0 -0
  31. data/features/open_flow13/goto_table.feature +26 -0
  32. data/features/open_flow13/instruction_clear_actions.raw +0 -0
  33. data/features/open_flow13/instruction_goto_table.raw +0 -0
  34. data/features/open_flow13/instruction_meter.raw +0 -0
  35. data/features/open_flow13/instruction_write_actions.raw +0 -0
  36. data/features/open_flow13/instruction_write_metadata.raw +0 -0
  37. data/features/open_flow13/match.feature +36 -0
  38. data/features/open_flow13/meter.feature +26 -0
  39. data/features/open_flow13/send_out_port.feature +28 -0
  40. data/features/open_flow13/send_out_port.raw +0 -0
  41. data/features/open_flow13/write_metadata.feature +28 -0
  42. data/features/step_definitions/open_flow_steps.rb +20 -0
  43. data/features/step_definitions/packet_data_steps.rb +5 -15
  44. data/lib/pio/monkey_patch/integer.rb +2 -0
  45. data/lib/pio/monkey_patch/integer/base_conversions.rb +10 -0
  46. data/lib/pio/open_flow.rb +0 -2
  47. data/lib/pio/open_flow10.rb +1 -0
  48. data/lib/pio/{open_flow → open_flow10}/actions.rb +1 -1
  49. data/lib/pio/open_flow10/echo.rb +1 -0
  50. data/lib/pio/{open_flow → open_flow10}/message.rb +1 -0
  51. data/lib/pio/{send_out_port.rb → open_flow10/send_out_port.rb} +0 -0
  52. data/lib/pio/open_flow13.rb +6 -0
  53. data/lib/pio/open_flow13/apply.rb +74 -0
  54. data/lib/pio/open_flow13/flow_mod.rb +195 -0
  55. data/lib/pio/open_flow13/goto_table.rb +33 -0
  56. data/lib/pio/open_flow13/hello.rb +1 -2
  57. data/lib/pio/open_flow13/match.rb +38 -5
  58. data/lib/pio/open_flow13/meter.rb +32 -0
  59. data/lib/pio/open_flow13/send_out_port.rb +48 -0
  60. data/lib/pio/open_flow13/write_metadata.rb +51 -0
  61. data/lib/pio/version.rb +1 -1
  62. data/spec/pio/{send_out_port_spec.rb → open_flow10/send_out_port_spec.rb} +1 -1
  63. data/spec/pio/open_flow13/goto_table_spec.rb +12 -0
  64. data/spec/pio/open_flow13/match_spec.rb +45 -0
  65. data/spec/pio/open_flow13/meter_spec.rb +12 -0
  66. data/spec/pio/open_flow13/write_metadata_spec.rb +28 -0
  67. metadata +185 -108
@@ -0,0 +1,26 @@
1
+ Feature: Pio::Meter
2
+ Background:
3
+ Given I use OpenFlow 1.3
4
+
5
+ Scenario: new(1)
6
+ When I try to create an OpenFlow instruction with:
7
+ """
8
+ Pio::Meter.new(1)
9
+ """
10
+ Then it should finish successfully
11
+ And the message have the following fields and values:
12
+ | field | value |
13
+ | class | Pio::Meter |
14
+ | instruction_type | 6 |
15
+ | instruction_length | 8 |
16
+ | meter_id | 1 |
17
+
18
+ Scenario: read
19
+ When I try to parse a file named "open_flow13/instruction_meter.raw" with "Pio::Meter" class
20
+ Then it should finish successfully
21
+ And the message have the following fields and values:
22
+ | field | value |
23
+ | class | Pio::Meter |
24
+ | instruction_type | 6 |
25
+ | instruction_length | 8 |
26
+ | meter_id | 1 |
@@ -0,0 +1,28 @@
1
+ Feature: Pio::SendOutPort
2
+ Background:
3
+ Given I use OpenFlow 1.3
4
+
5
+ Scenario: new(1)
6
+ When I try to create an OpenFlow action with:
7
+ """
8
+ Pio::SendOutPort.new(1)
9
+ """
10
+ Then it should finish successfully
11
+ And the message have the following fields and values:
12
+ | field | value |
13
+ | class | Pio::SendOutPort |
14
+ | action_type | 0 |
15
+ | action_length | 16 |
16
+ | port | 1 |
17
+ | max_length | :no_buffer |
18
+
19
+ Scenario: read
20
+ When I try to parse a file named "open_flow13/send_out_port.raw" with "Pio::SendOutPort" class
21
+ Then it should finish successfully
22
+ And the message have the following fields and values:
23
+ | field | value |
24
+ | class | Pio::SendOutPort |
25
+ | action_type | 0 |
26
+ | action_length | 16 |
27
+ | port | 1 |
28
+ | max_length | :no_buffer |
@@ -0,0 +1,28 @@
1
+ Feature: Pio::WriteMetadata
2
+ Background:
3
+ Given I use OpenFlow 1.3
4
+
5
+ Scenario: new(metadata: 1)
6
+ When I try to create an OpenFlow instruction with:
7
+ """
8
+ Pio::WriteMetadata.new(metadata: 1)
9
+ """
10
+ Then it should finish successfully
11
+ And the message have the following fields and values:
12
+ | field | value |
13
+ | class | Pio::WriteMetadata |
14
+ | instruction_type | 2 |
15
+ | instruction_length | 24 |
16
+ | metadata | 1 |
17
+ | metadata_mask | 0 |
18
+
19
+ Scenario: read
20
+ When I try to parse a file named "open_flow13/instruction_write_metadata.raw" with "Pio::WriteMetadata" class
21
+ Then it should finish successfully
22
+ And the message have the following fields and values:
23
+ | field | value |
24
+ | class | Pio::WriteMetadata |
25
+ | instruction_type | 2 |
26
+ | instruction_length | 24 |
27
+ | metadata | 1 |
28
+ | metadata_mask | 1 |
@@ -1,3 +1,23 @@
1
1
  Given(/^I use OpenFlow 1\.3$/) do
2
2
  require 'pio/open_flow13'
3
3
  end
4
+
5
+ When(/^I try to create a packet with:$/) do |ruby_code|
6
+ begin
7
+ @result = Pio.module_eval(ruby_code)
8
+ rescue
9
+ @last_error = $ERROR_INFO
10
+ end
11
+ end
12
+
13
+ When(/^I try to create an OpenFlow message with:$/) do |ruby_code|
14
+ step 'I try to create a packet with:', ruby_code
15
+ end
16
+
17
+ When(/^I try to create an OpenFlow action with:$/) do |ruby_code|
18
+ step 'I try to create a packet with:', ruby_code
19
+ end
20
+
21
+ When(/^I try to create an OpenFlow instruction with:$/) do |ruby_code|
22
+ step 'I try to create a packet with:', ruby_code
23
+ end
@@ -1,15 +1,3 @@
1
- When(/^I try to create a packet with:$/) do |ruby_code|
2
- begin
3
- @result = Pio.module_eval(ruby_code)
4
- rescue
5
- @last_error = $ERROR_INFO
6
- end
7
- end
8
-
9
- When(/^I try to create an OpenFlow message with:$/) do |ruby_code|
10
- step 'I try to create a packet with:', ruby_code
11
- end
12
-
13
1
  # rubocop:disable LineLength
14
2
  When(/^I try to parse a file named "(.*?\.raw)" with "(.*?)" class$/) do |path, klass|
15
3
  full_path = File.expand_path(File.join(__dir__, '..', path))
@@ -54,10 +42,12 @@ end
54
42
 
55
43
  Then(/^the packet have the following fields and values:$/) do |table|
56
44
  table.hashes.each do |each|
57
- output = each['field'].split('.').inject(@result) do |memo, method|
58
- memo.__send__(method)
45
+ output = @result.instance_eval("self.#{each['field']}")
46
+ if /^:/ =~ output.inspect
47
+ expect(output.inspect).to eq(each['value'])
48
+ else
49
+ expect(output.to_s).to eq(each['value'])
59
50
  end
60
- expect(output.to_s).to eq(each['value'])
61
51
  end
62
52
  end
63
53
 
@@ -1,6 +1,8 @@
1
+ require 'pio/monkey_patch/integer/base_conversions'
1
2
  require 'pio/monkey_patch/integer/ranges'
2
3
 
3
4
  # Monkey patch to Ruby's Integer class.
4
5
  class Integer
6
+ include MonkeyPatch::Integer::BaseConversions
5
7
  include MonkeyPatch::Integer::Ranges
6
8
  end
@@ -0,0 +1,10 @@
1
+ module MonkeyPatch
2
+ module Integer
3
+ # to_hex etc.
4
+ module BaseConversions
5
+ def to_hex
6
+ format '%#x', self
7
+ end
8
+ end
9
+ end
10
+ end
data/lib/pio/open_flow.rb CHANGED
@@ -14,9 +14,7 @@ module Pio
14
14
  end
15
15
  end
16
16
 
17
- require 'pio/open_flow/actions'
18
17
  require 'pio/open_flow/flags'
19
- require 'pio/open_flow/message'
20
18
  require 'pio/open_flow/open_flow_header'
21
19
  require 'pio/open_flow/phy_port'
22
20
  require 'pio/open_flow/port_number'
@@ -5,6 +5,7 @@ module Pio
5
5
  end
6
6
  end
7
7
 
8
+ require 'pio/open_flow10/actions'
8
9
  require 'pio/open_flow10/echo'
9
10
  require 'pio/open_flow10/exact_match'
10
11
  require 'pio/open_flow10/features'
@@ -1,6 +1,6 @@
1
1
  require 'bindata'
2
2
  require 'pio/enqueue'
3
- require 'pio/send_out_port'
3
+ require 'pio/open_flow10/send_out_port'
4
4
  require 'pio/set_ether_address'
5
5
  require 'pio/set_ip_address'
6
6
  require 'pio/set_ip_tos'
@@ -1,5 +1,6 @@
1
1
  require 'forwardable'
2
2
  require 'pio/open_flow'
3
+ require 'pio/open_flow10/message'
3
4
 
4
5
  module Pio
5
6
  # OpenFlow 1.0 Echo Request and Reply message.
@@ -1,6 +1,7 @@
1
1
  require 'English'
2
2
  require 'bindata'
3
3
  require 'forwardable'
4
+ require 'pio/open_flow/open_flow_header'
4
5
  require 'pio/parse_error'
5
6
 
6
7
  module Pio
@@ -6,11 +6,17 @@ module Pio
6
6
  end
7
7
  end
8
8
 
9
+ require 'pio/open_flow13/apply'
10
+ require 'pio/open_flow13/goto_table'
11
+ require 'pio/open_flow13/write_metadata'
12
+ require 'pio/open_flow13/meter'
9
13
  require 'pio/open_flow13/echo'
10
14
  require 'pio/open_flow13/features_reply'
11
15
  require 'pio/open_flow13/features_request'
16
+ require 'pio/open_flow13/flow_mod'
12
17
  require 'pio/open_flow13/hello'
13
18
  require 'pio/open_flow13/match'
19
+ require 'pio/open_flow13/send_out_port'
14
20
 
15
21
  # Base module.
16
22
  module Pio
@@ -0,0 +1,74 @@
1
+ require 'bindata'
2
+ require 'forwardable'
3
+
4
+ module Pio
5
+ # An instruction to apply a list of actions to a packet in-order.
6
+ class Apply
7
+ # Actions not yet implemented.
8
+ class UnsupportedAction < BinData::Record
9
+ endian :big
10
+
11
+ uint16 :action_type
12
+ uint16 :action_length
13
+ end
14
+
15
+ # Actions list of actions-apply instruction.
16
+ class Actions < BinData::Primitive
17
+ mandatory_parameter :length
18
+
19
+ endian :big
20
+
21
+ string :binary, read_length: :length
22
+
23
+ def set(value)
24
+ self.binary = [value].flatten.map(&:to_binary).join
25
+ end
26
+
27
+ # rubocop:disable MethodLength
28
+ def get
29
+ actions = []
30
+ tmp = binary
31
+ while tmp.length > 0
32
+ action = case BinData::Uint16be.read(tmp)
33
+ when 0
34
+ SendOutPort.read(tmp)
35
+ else
36
+ UnsupportedAction.read(tmp)
37
+ end
38
+ tmp = tmp[action.action_length..-1]
39
+ actions << action
40
+ end
41
+ actions
42
+ end
43
+ # rubocop:enable MethodLength
44
+ end
45
+
46
+ # OpenFlow 1.3.4 OFPIT_APPLY_ACTIONS instruction format.
47
+ class Format < BinData::Record
48
+ endian :big
49
+
50
+ uint16 :instruction_type, value: 4
51
+ uint16 :instruction_length,
52
+ initial_value: -> { 8 + actions.binary.length }
53
+ string :padding, length: 4
54
+ actions :actions, length: -> { instruction_length - 8 }
55
+ end
56
+
57
+ def self.read(raw_data)
58
+ allocate.tap do |apply|
59
+ apply.instance_variable_set :@format, Format.read(raw_data)
60
+ end
61
+ end
62
+
63
+ extend Forwardable
64
+
65
+ def_delegators :@format, :instruction_type
66
+ def_delegators :@format, :instruction_length
67
+ def_delegators :@format, :actions
68
+ def_delegators :@format, :to_binary_s
69
+
70
+ def initialize(actions = [])
71
+ @format = Format.new(actions: actions)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,195 @@
1
+ require 'forwardable'
2
+ require 'pio/open_flow'
3
+ require 'pio/open_flow13/match'
4
+
5
+ # Base module.
6
+ module Pio
7
+ remove_const :FlowMod if const_defined?(:FlowMod)
8
+
9
+ # OpenFlow 1.3 FlowMod message parser and generator
10
+ class FlowMod
11
+ # enum ofp_flow_mod_command
12
+ class Command < BinData::Primitive
13
+ COMMANDS = {
14
+ add: 0,
15
+ modify: 1,
16
+ modify_strict: 2,
17
+ delete: 3,
18
+ delete_strict: 4
19
+ }
20
+
21
+ uint8 :command
22
+
23
+ def get
24
+ COMMANDS.invert.fetch(command)
25
+ end
26
+
27
+ def set(value)
28
+ self.command = COMMANDS.fetch(value)
29
+ end
30
+ end
31
+
32
+ # Buffered packet to apply to, or :no_buffer.
33
+ class BufferId < BinData::Primitive
34
+ NO_BUFFER = 0xffffffff
35
+
36
+ endian :big
37
+ uint32 :buffer_id, initial_value: NO_BUFFER
38
+
39
+ def get
40
+ (buffer_id == NO_BUFFER) ? :no_buffer : buffer_id
41
+ end
42
+
43
+ def set(value)
44
+ self.buffer_id = (value == :no_buffer ? NO_BUFFER : value)
45
+ end
46
+ end
47
+
48
+ # For delete commands, require matching entries to include this as
49
+ # an output port.
50
+ class OutPort < BinData::Primitive
51
+ ANY = 0xffffffff
52
+
53
+ endian :big
54
+ uint32 :out_port, initial_value: ANY
55
+
56
+ def get
57
+ (out_port == ANY) ? :any : out_port
58
+ end
59
+
60
+ def set(value)
61
+ self.out_port = (value == :any ? ANY : value)
62
+ end
63
+ end
64
+
65
+ # For delete commands, require matching entries to include this as
66
+ # an output group.
67
+ class OutGroup < BinData::Primitive
68
+ ANY = 0xffffffff
69
+
70
+ endian :big
71
+ uint32 :out_group, initial_value: ANY
72
+
73
+ def get
74
+ (out_group == ANY) ? :any : out_group
75
+ end
76
+
77
+ def set(value)
78
+ self.out_group = (value == :any ? ANY : value)
79
+ end
80
+ end
81
+
82
+ # OpenFlow 1.3 instructions
83
+ class Instructions < BinData::Primitive
84
+ endian :big
85
+
86
+ count_bytes_remaining :instructions_length
87
+ string :instructions, read_length: :instructions_length
88
+
89
+ def set(object)
90
+ self.instructions = [object].flatten.map(&:to_binary_s).join
91
+ end
92
+
93
+ # rubocop:disable MethodLength
94
+ def get
95
+ list = []
96
+ tmp = instructions
97
+ while tmp.length > 0
98
+ instruction_type = BinData::Uint16be.read(tmp)
99
+ instruction = case instruction_type
100
+ when 1
101
+ GotoTable.read(tmp)
102
+ when 2
103
+ WriteMetadata.read(tmp)
104
+ when 4
105
+ Apply.read(tmp)
106
+ when 6
107
+ Meter.read(tmp)
108
+ else
109
+ fail "Unsupported instruction #{instruction_type}"
110
+ end
111
+ tmp = tmp[instruction.instruction_length..-1]
112
+ list << instruction
113
+ end
114
+ list
115
+ end
116
+ # rubocop:enable MethodLength
117
+
118
+ def length
119
+ instructions.length
120
+ end
121
+ end
122
+
123
+ # OpenFlow 1.3 FlowMod message body
124
+ class Body < BinData::Record
125
+ extend OpenFlow::Flags
126
+ flags_16bit :flags,
127
+ [:send_flow_rem,
128
+ :check_overwrap,
129
+ :reset_counts,
130
+ :no_packet_counts,
131
+ :no_byte_counts]
132
+
133
+ endian :big
134
+
135
+ uint64 :cookie
136
+ uint64 :cookie_mask
137
+ uint8 :table_id
138
+ command :command
139
+ uint16 :idle_timeout
140
+ uint16 :hard_timeout
141
+ uint16 :priority, initial_value: 0xffff
142
+ buffer_id :buffer_id
143
+ out_port :out_port
144
+ out_group :out_group
145
+ flags :flags
146
+ string :padding, length: 2
147
+ hide :padding
148
+ oxm :match
149
+ instructions :instructions
150
+
151
+ def length
152
+ 40 + match.length + instructions.length
153
+ end
154
+ end
155
+
156
+ # OpenFlow 1.3 FlowMod message format
157
+ class Format < BinData::Record
158
+ extend Forwardable
159
+
160
+ endian :big
161
+
162
+ open_flow_header :open_flow_header,
163
+ ofp_version_value: 4,
164
+ message_type_value: OpenFlow::FLOW_MOD
165
+ body :body
166
+
167
+ def_delegators :open_flow_header, :ofp_version
168
+ def_delegators :open_flow_header, :message_type
169
+ def_delegators :open_flow_header, :message_length
170
+ def_delegators :open_flow_header, :transaction_id
171
+ def_delegator :open_flow_header, :transaction_id, :xid
172
+
173
+ def method_missing(method, *args, &block)
174
+ body.__send__ method, *args, &block
175
+ end
176
+ end
177
+
178
+ def self.read(raw_data)
179
+ allocate.tap do |message|
180
+ message.instance_variable_set(:@format, Format.read(raw_data))
181
+ end
182
+ end
183
+
184
+ def initialize(user_attrs = {})
185
+ header_attrs = OpenFlowHeader::Options.parse(user_attrs)
186
+ body_attrs = { match: user_attrs[:match],
187
+ instructions: user_attrs[:instructions] }
188
+ @format = Format.new(open_flow_header: header_attrs, body: body_attrs)
189
+ end
190
+
191
+ def method_missing(method, *args, &block)
192
+ @format.__send__ method, *args, &block
193
+ end
194
+ end
195
+ end