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