pio 0.10.1 → 0.11.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 +6 -0
- data/README.md +33 -2
- data/examples/echo_read.rb +1 -1
- data/examples/features_read.rb +1 -1
- data/examples/flow_mod_new.rb +13 -0
- data/examples/flow_mod_read.rb +6 -0
- data/features/echo_read.feature +27 -3
- data/features/features_read.feature +46 -2
- data/features/flow_mod_read.feature +186 -0
- data/features/hello_read.feature +9 -0
- data/features/packet_data/flow_mod_add.raw +0 -0
- data/features/packet_data/flow_mod_delete.raw +0 -0
- data/features/packet_data/flow_mod_delete_strict.raw +0 -0
- data/features/packet_data/flow_mod_modify.raw +0 -0
- data/features/packet_data/flow_mod_modify_strict.raw +0 -0
- data/features/packet_in_read.feature +13 -0
- data/features/packet_out_read.feature +16 -0
- data/features/step_definitions/packet_data_steps.rb +10 -1
- data/lib/pio.rb +1 -0
- data/lib/pio/echo.rb +10 -8
- data/lib/pio/enqueue.rb +1 -1
- data/lib/pio/features.rb +64 -7
- data/lib/pio/flow_mod.rb +86 -0
- data/lib/pio/hello.rb +4 -74
- data/lib/pio/ipv4_address.rb +1 -1
- data/lib/pio/match.rb +167 -0
- data/lib/pio/open_flow.rb +1 -0
- data/lib/pio/open_flow/actions.rb +65 -0
- data/lib/pio/open_flow/flags.rb +12 -9
- data/lib/pio/open_flow/message.rb +105 -21
- data/lib/pio/open_flow/phy_port.rb +31 -27
- data/lib/pio/open_flow/type.rb +1 -0
- data/lib/pio/packet_in.rb +2 -12
- data/lib/pio/packet_out.rb +4 -74
- data/lib/pio/send_out_port.rb +1 -0
- data/lib/pio/type/ip_address.rb +2 -7
- data/lib/pio/version.rb +1 -1
- data/pio.gemspec +8 -8
- data/spec/pio/echo/reply_spec.rb +61 -6
- data/spec/pio/echo/request_spec.rb +61 -6
- data/spec/pio/features/reply_spec.rb +81 -4
- data/spec/pio/features/request_spec.rb +88 -41
- data/spec/pio/flow_mod_spec.rb +151 -0
- data/spec/pio/hello_spec.rb +73 -56
- data/spec/pio/match_spec.rb +194 -0
- data/spec/pio/packet_in_spec.rb +35 -38
- data/spec/pio/packet_out_spec.rb +243 -182
- data/spec/pio/wildcards_spec.rb +115 -0
- metadata +37 -30
- data/features/packet_data/echo.raw +0 -0
- data/lib/pio/echo/message.rb +0 -49
- data/lib/pio/echo/reply.rb +0 -41
- data/lib/pio/echo/request.rb +0 -43
- data/lib/pio/features/reply.rb +0 -88
- data/lib/pio/features/request.rb +0 -68
- data/lib/pio/open_flow/parser.rb +0 -22
- data/spec/pio/echo_spec.rb +0 -49
- data/spec/pio/features_spec.rb +0 -96
data/lib/pio/echo/reply.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'pio/echo/message'
|
2
|
-
require 'pio/open_flow'
|
3
|
-
|
4
|
-
module Pio
|
5
|
-
class Echo
|
6
|
-
# OpenFlow 1.0 Echo Reply message generator.
|
7
|
-
class Reply < Message
|
8
|
-
def_format Pio::OpenFlow::Type::ECHO_REPLY
|
9
|
-
|
10
|
-
# Creates an EchoReply OpenFlow message. This message can be
|
11
|
-
# used to measure the bandwidth of a controller/switch
|
12
|
-
# connection as well as to verify its liveness.
|
13
|
-
#
|
14
|
-
# @overload initialize()
|
15
|
-
# @example
|
16
|
-
# Pio::Echo::Reply.new
|
17
|
-
#
|
18
|
-
# @overload initialize(transaction_id)
|
19
|
-
# @example
|
20
|
-
# Pio::Echo::Reply.new(123)
|
21
|
-
# @param [Number] transaction_id
|
22
|
-
# An unsigned 32-bit integer number associated with this
|
23
|
-
# message.
|
24
|
-
#
|
25
|
-
# @overload initialize(user_options)
|
26
|
-
# @example
|
27
|
-
# Pio::Echo::Reply.new(
|
28
|
-
# transaction_id: transaction_id,
|
29
|
-
# user_data: 'Thu Aug 25 13:09:00 +0900 2011'
|
30
|
-
# )
|
31
|
-
# @param [Hash] user_options The options to create a message with.
|
32
|
-
# @option user_options [Number] :transaction_id
|
33
|
-
# @option user_options [Number] :xid An alias to transaction_id.
|
34
|
-
# @option user_options [String] :user_data
|
35
|
-
# The user data field specified as a String may be a message
|
36
|
-
# timestamp to check latency, various lengths to measure
|
37
|
-
# bandwidth or zero-size(nil) to verify liveness between the
|
38
|
-
# switch and controller.
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
data/lib/pio/echo/request.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'pio/echo/message'
|
2
|
-
require 'pio/open_flow'
|
3
|
-
|
4
|
-
module Pio
|
5
|
-
class Echo
|
6
|
-
# OpenFlow 1.0 Echo Request message.
|
7
|
-
class Request < Message
|
8
|
-
def_format Pio::OpenFlow::Type::ECHO_REQUEST
|
9
|
-
|
10
|
-
# Creates an EchoRequest OpenFlow message. This message can be
|
11
|
-
# used to measure the bandwidth of a controller/switch
|
12
|
-
# connection as well as to verify its liveness.
|
13
|
-
#
|
14
|
-
# @overload initialize()
|
15
|
-
# @example
|
16
|
-
# Pio::Echo::Request.new
|
17
|
-
#
|
18
|
-
# @overload initialize(transaction_id)
|
19
|
-
# @example
|
20
|
-
# Pio::Echo::Request.new(123)
|
21
|
-
# @param [Number] transaction_id
|
22
|
-
# An unsigned 32-bit integer number associated with this
|
23
|
-
# message.
|
24
|
-
#
|
25
|
-
# @overload initialize(user_options)
|
26
|
-
# @example
|
27
|
-
# Pio::Echo::Request.new(
|
28
|
-
# transaction_id: transaction_id,
|
29
|
-
# user_data: 'Thu Aug 25 13:09:00 +0900 2011'
|
30
|
-
# )
|
31
|
-
# @param [Hash] user_options The options to create a message with.
|
32
|
-
# @option user_options [Number] :transaction_id
|
33
|
-
# @option user_options [Number] :xid An alias to transaction_id.
|
34
|
-
# @option user_options [String] :user_data
|
35
|
-
# The user data field specified as a String may be a message
|
36
|
-
# timestamp to check latency, various lengths to measure
|
37
|
-
# bandwidth or zero-size(nil) to verify liveness between the
|
38
|
-
# switch and controller.
|
39
|
-
#
|
40
|
-
# rubocop:disable MethodLength
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
data/lib/pio/features/reply.rb
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
require 'bindata'
|
2
|
-
require 'pio/open_flow'
|
3
|
-
|
4
|
-
module Pio
|
5
|
-
# OpenFlow 1.0 Features messages
|
6
|
-
class Features
|
7
|
-
# OpenFlow 1.0 Features Reply message
|
8
|
-
class Reply < Pio::OpenFlow::Message
|
9
|
-
# Message body of features reply.
|
10
|
-
class Body < BinData::Record
|
11
|
-
extend Flags
|
12
|
-
|
13
|
-
# enum ofp_capabilities
|
14
|
-
def_flags :capabilities,
|
15
|
-
[:flow_stats,
|
16
|
-
:table_stats,
|
17
|
-
:port_stats,
|
18
|
-
:stp,
|
19
|
-
:reserved,
|
20
|
-
:ip_reasm,
|
21
|
-
:queue_stats,
|
22
|
-
:arp_match_ip]
|
23
|
-
|
24
|
-
# enum ofp_action_type
|
25
|
-
def_flags :actions,
|
26
|
-
[:output,
|
27
|
-
:set_vlan_vid,
|
28
|
-
:set_vlan_pcp,
|
29
|
-
:strip_vlan,
|
30
|
-
:set_dl_src,
|
31
|
-
:set_dl_dst,
|
32
|
-
:set_nw_src,
|
33
|
-
:set_nw_dst,
|
34
|
-
:set_nw_tos,
|
35
|
-
:set_tp_src,
|
36
|
-
:set_tp_dst,
|
37
|
-
:enqueue]
|
38
|
-
|
39
|
-
endian :big
|
40
|
-
|
41
|
-
uint64 :datapath_id
|
42
|
-
uint32 :n_buffers
|
43
|
-
uint8 :n_tables
|
44
|
-
uint24 :padding
|
45
|
-
hide :padding
|
46
|
-
capabilities :capabilities
|
47
|
-
actions :actions
|
48
|
-
array :ports, type: :phy_port, read_until: :eof
|
49
|
-
|
50
|
-
def empty?
|
51
|
-
false
|
52
|
-
end
|
53
|
-
|
54
|
-
def length
|
55
|
-
24 + ports.to_binary_s.length
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def_format Pio::OpenFlow::Type::FEATURES_REPLY
|
60
|
-
|
61
|
-
def datapath_id
|
62
|
-
@format.body.datapath_id
|
63
|
-
end
|
64
|
-
|
65
|
-
def_delegators :body, :datapath_id
|
66
|
-
def_delegator :body, :datapath_id, :dpid
|
67
|
-
def_delegators :body, :n_buffers
|
68
|
-
def_delegators :body, :n_tables
|
69
|
-
def_delegators :body, :capabilities
|
70
|
-
def_delegators :body, :actions
|
71
|
-
def_delegators :body, :ports
|
72
|
-
|
73
|
-
# @reek This method smells of :reek:FeatureEnvy
|
74
|
-
def initialize(user_options)
|
75
|
-
body_options =
|
76
|
-
{
|
77
|
-
datapath_id: user_options[:dpid],
|
78
|
-
n_buffers: user_options[:n_buffers],
|
79
|
-
n_tables: user_options[:n_tables],
|
80
|
-
capabilities: user_options[:capabilities],
|
81
|
-
actions: user_options[:actions],
|
82
|
-
ports: user_options[:ports]
|
83
|
-
}
|
84
|
-
@format = Format.new(user_options.merge(body: body_options))
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
data/lib/pio/features/request.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'pio/open_flow'
|
3
|
-
|
4
|
-
module Pio
|
5
|
-
# OpenFlow 1.0 Features messages
|
6
|
-
class Features
|
7
|
-
# OpenFlow 1.0 Features Request message
|
8
|
-
class Request < Pio::OpenFlow::Message
|
9
|
-
# Message body of Features Request
|
10
|
-
class Body < BinData::Record
|
11
|
-
endian :big
|
12
|
-
|
13
|
-
string :body # ignored
|
14
|
-
|
15
|
-
def length
|
16
|
-
0
|
17
|
-
end
|
18
|
-
|
19
|
-
def empty?
|
20
|
-
true
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def_format Pio::OpenFlow::Type::FEATURES_REQUEST
|
25
|
-
|
26
|
-
# Creates a Features Request OpenFlow message.
|
27
|
-
#
|
28
|
-
# @overload initialize()
|
29
|
-
# @example
|
30
|
-
# Pio::Features::Request.new
|
31
|
-
#
|
32
|
-
# @overload initialize(transaction_id)
|
33
|
-
# @example
|
34
|
-
# Pio::Features::Request.new(123)
|
35
|
-
# @param [Number] transaction_id
|
36
|
-
# An unsigned 32-bit integer number associated with this
|
37
|
-
# message.
|
38
|
-
#
|
39
|
-
# @overload initialize(user_options)
|
40
|
-
# @example
|
41
|
-
# Pio::Features::Request.new(transaction_id: 123)
|
42
|
-
# Pio::Features::Request.new(xid: 123)
|
43
|
-
# @param [Hash] user_options
|
44
|
-
# The options to create a message with.
|
45
|
-
# @option user_options [Number] :transaction_id
|
46
|
-
# @option user_options [Number] :xid An alias to transaction_id.
|
47
|
-
#
|
48
|
-
# @reek This method smells of :reek:FeatureEnvy
|
49
|
-
# rubocop:disable MethodLength
|
50
|
-
def initialize(user_options = {})
|
51
|
-
options = if user_options.respond_to?(:to_i)
|
52
|
-
{ open_flow_header: { transaction_id: user_options.to_i } }
|
53
|
-
elsif user_options.respond_to?(:fetch)
|
54
|
-
transaction_id =
|
55
|
-
user_options[:transaction_id] || user_options[:xid] || 0
|
56
|
-
{ open_flow_header: { transaction_id: transaction_id } }
|
57
|
-
else
|
58
|
-
fail TypeError
|
59
|
-
end
|
60
|
-
if options[:open_flow_header][:transaction_id] >= 2**32
|
61
|
-
fail ArgumentError, 'Transaction ID >= 2**32'
|
62
|
-
end
|
63
|
-
@format = Format.new(options)
|
64
|
-
end
|
65
|
-
# rubocop:enable MethodLength
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
data/lib/pio/open_flow/parser.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'pio/open_flow'
|
2
|
-
require 'pio/parse_error'
|
3
|
-
|
4
|
-
module Pio
|
5
|
-
module OpenFlow
|
6
|
-
# OpenFlow message parser interface.
|
7
|
-
module Parser
|
8
|
-
# This method smells of :reek:TooManyStatements
|
9
|
-
# This method smells of :reek:FeatureEnvy
|
10
|
-
def read(raw_data)
|
11
|
-
header = Pio::Type::OpenFlow::OpenFlowHeader.read(raw_data)
|
12
|
-
klass = const_get(:KLASS).fetch(header.message_type)
|
13
|
-
format = klass.const_get(:Format).read(raw_data)
|
14
|
-
message = klass.allocate
|
15
|
-
message.instance_variable_set :@format, format
|
16
|
-
message
|
17
|
-
rescue KeyError
|
18
|
-
raise Pio::ParseError, 'Invalid OpenFlow message.'
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
data/spec/pio/echo_spec.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'pio/echo'
|
2
|
-
|
3
|
-
describe Pio::Echo do
|
4
|
-
describe '.read' do
|
5
|
-
context 'with an Echo Request message' do
|
6
|
-
Given(:echo_request_dump) do
|
7
|
-
[1, 2, 0, 8, 0, 0, 0, 0].pack('C*')
|
8
|
-
end
|
9
|
-
|
10
|
-
When(:echo_request) do
|
11
|
-
Pio::Echo.read echo_request_dump
|
12
|
-
end
|
13
|
-
|
14
|
-
Then { echo_request.class == Pio::Echo::Request }
|
15
|
-
Then { echo_request.ofp_version == 1 }
|
16
|
-
Then { echo_request.message_type == Pio::OpenFlow::Type::ECHO_REQUEST }
|
17
|
-
Then { echo_request.message_length == 8 }
|
18
|
-
Then { echo_request.xid == 0 }
|
19
|
-
Then { echo_request.user_data == '' }
|
20
|
-
Then { echo_request.to_binary == echo_request_dump }
|
21
|
-
end
|
22
|
-
|
23
|
-
context 'with an Echo Reply message' do
|
24
|
-
Given(:echo_reply_dump) do
|
25
|
-
[1, 3, 0, 8, 0, 0, 0, 0].pack('C*')
|
26
|
-
end
|
27
|
-
|
28
|
-
When(:echo_reply) do
|
29
|
-
Pio::Echo.read echo_reply_dump
|
30
|
-
end
|
31
|
-
|
32
|
-
Then { echo_reply.class == Pio::Echo::Reply }
|
33
|
-
Then { echo_reply.ofp_version == 1 }
|
34
|
-
Then { echo_reply.message_type == Pio::OpenFlow::Type::ECHO_REPLY }
|
35
|
-
Then { echo_reply.message_length == 8 }
|
36
|
-
Then { echo_reply.xid == 0 }
|
37
|
-
Then { echo_reply.user_data == '' }
|
38
|
-
Then { echo_reply.to_binary == echo_reply_dump }
|
39
|
-
end
|
40
|
-
|
41
|
-
context 'with a Features Request message' do
|
42
|
-
Given(:features_request_dump) { [1, 5, 0, 8, 0, 0, 0, 0].pack('C*') }
|
43
|
-
|
44
|
-
When(:result) { Pio::Echo.read(features_request_dump) }
|
45
|
-
|
46
|
-
Then { result == Failure(Pio::ParseError) }
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
data/spec/pio/features_spec.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
require 'pio/features'
|
2
|
-
|
3
|
-
describe Pio::Features do
|
4
|
-
describe '.read' do
|
5
|
-
context 'with a Features Request message' do
|
6
|
-
Given(:request_dump) { [1, 5, 0, 8, 0, 0, 0, 0].pack('C*') }
|
7
|
-
|
8
|
-
When(:request) { Pio::Features.read(request_dump) }
|
9
|
-
|
10
|
-
Then { request.class == Pio::Features::Request }
|
11
|
-
Then { request.ofp_version == 1 }
|
12
|
-
Then do
|
13
|
-
request.message_type ==
|
14
|
-
Pio::OpenFlow::Type::FEATURES_REQUEST
|
15
|
-
end
|
16
|
-
Then { request.message_length == 8 }
|
17
|
-
Then { request.transaction_id == 0 }
|
18
|
-
Then { request.xid == 0 }
|
19
|
-
Then { request.body.empty? }
|
20
|
-
end
|
21
|
-
|
22
|
-
context 'with a Features Reply message' do
|
23
|
-
Given(:reply_dump) do
|
24
|
-
[0x01, 0x06, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
|
25
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00,
|
26
|
-
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00,
|
27
|
-
0x0f, 0xff, 0x00, 0x02, 0x16, 0x7d, 0xa4, 0x37, 0xba, 0x10,
|
28
|
-
0x74, 0x72, 0x65, 0x6d, 0x61, 0x30, 0x2d, 0x30, 0x00, 0x00,
|
29
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
30
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
|
31
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
32
|
-
0xff, 0xfe, 0x2a, 0xb4, 0xd6, 0x3c, 0x66, 0xba, 0x76, 0x73,
|
33
|
-
0x77, 0x5f, 0x30, 0x78, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
|
34
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
35
|
-
0x00, 0x01, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00,
|
36
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
37
|
-
0x62, 0x94, 0x3a, 0xf6, 0x40, 0xdb, 0x74, 0x72, 0x65, 0x6d,
|
38
|
-
0x61, 0x31, 0x2d, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
39
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
40
|
-
0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
41
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00].pack('C*')
|
42
|
-
end
|
43
|
-
|
44
|
-
When(:reply) { Pio::Features.read(reply_dump) }
|
45
|
-
|
46
|
-
Then { reply.class == Pio::Features::Reply }
|
47
|
-
Then { reply.ofp_version == 1 }
|
48
|
-
Then do
|
49
|
-
reply.message_type ==
|
50
|
-
Pio::OpenFlow::Type::FEATURES_REPLY
|
51
|
-
end
|
52
|
-
Then { reply.message_length == 176 }
|
53
|
-
Then { reply.transaction_id == 2 }
|
54
|
-
Then { reply.xid == 2 }
|
55
|
-
Then { !reply.body.empty? }
|
56
|
-
Then { reply.datapath_id == 1 }
|
57
|
-
Then { reply.n_buffers == 0x100 }
|
58
|
-
Then { reply.n_tables == 1 }
|
59
|
-
Then do
|
60
|
-
reply.capabilities ==
|
61
|
-
[:flow_stats, :table_stats, :port_stats, :arp_match_ip]
|
62
|
-
end
|
63
|
-
Then do
|
64
|
-
reply.actions ==
|
65
|
-
[:output, :set_vlan_vid, :set_vlan_pcp, :strip_vlan,
|
66
|
-
:set_dl_src, :set_dl_dst, :set_nw_src, :set_nw_dst,
|
67
|
-
:set_nw_tos, :set_tp_src, :set_tp_dst, :enqueue]
|
68
|
-
end
|
69
|
-
Then { reply.ports.size == 3 }
|
70
|
-
Then { reply.ports.all? { |each| each.port_no > 0 } }
|
71
|
-
Then do
|
72
|
-
reply.ports.all? do |each|
|
73
|
-
/^([0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}$/i=~
|
74
|
-
each.hardware_address.to_s
|
75
|
-
end
|
76
|
-
end
|
77
|
-
Then { reply.ports.all? { |each| !each.name.empty? } }
|
78
|
-
Then { reply.ports.any? { |each| each.config == [:port_down] } }
|
79
|
-
Then { reply.ports.any? { |each| each.state == [:link_down] } }
|
80
|
-
Then { reply.ports.all? { |each| each.curr.include? :port_copper } }
|
81
|
-
Then { reply.ports.all? { |each| each.advertised.empty? } }
|
82
|
-
Then { reply.ports.all? { |each| each.supported.empty? } }
|
83
|
-
Then { reply.ports.all? { |each| each.peer.empty? } }
|
84
|
-
end
|
85
|
-
|
86
|
-
context 'with a Hello message' do
|
87
|
-
Given(:hello_dump) { [1, 0, 0, 8, 0, 0, 0, 0].pack('C*') }
|
88
|
-
|
89
|
-
When(:result) { Pio::Features.read(hello_dump) }
|
90
|
-
|
91
|
-
Then do
|
92
|
-
result == Failure(Pio::ParseError, 'Invalid OpenFlow message.')
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|