pio 0.20.1 → 0.21.0

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