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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +1 -0
- data/Rakefile +5 -8
- data/bin/byebug +16 -0
- data/bin/terminal-notifier +16 -0
- data/features/open_flow10/flow_mod.feature +5 -5
- data/features/open_flow10/packet_in.feature +2 -2
- data/features/open_flow10/port_status.feature +1 -1
- data/features/open_flow13/action_copy_ttl_in.raw +0 -0
- data/features/open_flow13/action_copy_ttl_out.raw +0 -0
- data/features/open_flow13/action_dec_mpls_ttl.raw +0 -0
- data/features/open_flow13/action_dec_nw_ttl.raw +0 -0
- data/features/open_flow13/action_group.raw +0 -0
- data/features/open_flow13/action_pop_mpls.raw +0 -0
- data/features/open_flow13/action_pop_pbb.raw +0 -0
- data/features/open_flow13/action_pop_vlan.raw +0 -0
- data/features/open_flow13/action_push_mpls.raw +0 -0
- data/features/open_flow13/action_push_pbb.raw +0 -0
- data/features/open_flow13/action_push_vlan.raw +0 -0
- data/features/open_flow13/action_set_field.raw +0 -0
- data/features/open_flow13/action_set_mpls_ttl.raw +0 -0
- data/features/open_flow13/action_set_nw_ttl.raw +0 -0
- data/features/open_flow13/action_set_queue.raw +0 -0
- data/features/open_flow13/apply_actions.feature +43 -0
- data/features/open_flow13/apply_actions.raw +0 -0
- data/features/open_flow13/flow_add_apply_no_match.raw +0 -0
- data/features/open_flow13/flow_mod.feature +146 -0
- data/features/open_flow13/flow_mod_add_apply_no_match.raw +0 -0
- data/features/open_flow13/flow_mod_no_match_or_instructions.raw +0 -0
- data/features/open_flow13/goto_table.feature +26 -0
- data/features/open_flow13/instruction_clear_actions.raw +0 -0
- data/features/open_flow13/instruction_goto_table.raw +0 -0
- data/features/open_flow13/instruction_meter.raw +0 -0
- data/features/open_flow13/instruction_write_actions.raw +0 -0
- data/features/open_flow13/instruction_write_metadata.raw +0 -0
- data/features/open_flow13/match.feature +36 -0
- data/features/open_flow13/meter.feature +26 -0
- data/features/open_flow13/send_out_port.feature +28 -0
- data/features/open_flow13/send_out_port.raw +0 -0
- data/features/open_flow13/write_metadata.feature +28 -0
- data/features/step_definitions/open_flow_steps.rb +20 -0
- data/features/step_definitions/packet_data_steps.rb +5 -15
- data/lib/pio/monkey_patch/integer.rb +2 -0
- data/lib/pio/monkey_patch/integer/base_conversions.rb +10 -0
- data/lib/pio/open_flow.rb +0 -2
- data/lib/pio/open_flow10.rb +1 -0
- data/lib/pio/{open_flow → open_flow10}/actions.rb +1 -1
- data/lib/pio/open_flow10/echo.rb +1 -0
- data/lib/pio/{open_flow → open_flow10}/message.rb +1 -0
- data/lib/pio/{send_out_port.rb → open_flow10/send_out_port.rb} +0 -0
- data/lib/pio/open_flow13.rb +6 -0
- data/lib/pio/open_flow13/apply.rb +74 -0
- data/lib/pio/open_flow13/flow_mod.rb +195 -0
- data/lib/pio/open_flow13/goto_table.rb +33 -0
- data/lib/pio/open_flow13/hello.rb +1 -2
- data/lib/pio/open_flow13/match.rb +38 -5
- data/lib/pio/open_flow13/meter.rb +32 -0
- data/lib/pio/open_flow13/send_out_port.rb +48 -0
- data/lib/pio/open_flow13/write_metadata.rb +51 -0
- data/lib/pio/version.rb +1 -1
- data/spec/pio/{send_out_port_spec.rb → open_flow10/send_out_port_spec.rb} +1 -1
- data/spec/pio/open_flow13/goto_table_spec.rb +12 -0
- data/spec/pio/open_flow13/match_spec.rb +45 -0
- data/spec/pio/open_flow13/meter_spec.rb +12 -0
- data/spec/pio/open_flow13/write_metadata_spec.rb +28 -0
- 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 |
|
Binary file
|
@@ -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']
|
58
|
-
|
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
|
|
data/lib/pio/open_flow.rb
CHANGED
data/lib/pio/open_flow10.rb
CHANGED
data/lib/pio/open_flow10/echo.rb
CHANGED
File without changes
|
data/lib/pio/open_flow13.rb
CHANGED
@@ -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
|