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/open_flow.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
require 'pio/enqueue'
|
3
|
+
require 'pio/send_out_port'
|
4
|
+
require 'pio/set_eth_addr'
|
5
|
+
require 'pio/set_ip_addr'
|
6
|
+
require 'pio/set_ip_tos'
|
7
|
+
require 'pio/set_transport_port'
|
8
|
+
require 'pio/set_vlan_priority'
|
9
|
+
require 'pio/set_vlan_vid'
|
10
|
+
require 'pio/strip_vlan_header'
|
11
|
+
|
12
|
+
module Pio
|
13
|
+
module OpenFlow
|
14
|
+
# Actions list.
|
15
|
+
class Actions < BinData::Primitive
|
16
|
+
ACTION_CLASS = {
|
17
|
+
0 => Pio::SendOutPort,
|
18
|
+
1 => Pio::SetVlanVid,
|
19
|
+
2 => Pio::SetVlanPriority,
|
20
|
+
3 => Pio::StripVlanHeader,
|
21
|
+
4 => Pio::SetEthSrcAddr,
|
22
|
+
5 => Pio::SetEthDstAddr,
|
23
|
+
6 => Pio::SetIpSrcAddr,
|
24
|
+
7 => Pio::SetIpDstAddr,
|
25
|
+
8 => Pio::SetIpTos,
|
26
|
+
9 => Pio::SetTransportSrcPort,
|
27
|
+
10 => Pio::SetTransportDstPort,
|
28
|
+
11 => Pio::Enqueue
|
29
|
+
}
|
30
|
+
|
31
|
+
mandatory_parameter :length
|
32
|
+
|
33
|
+
endian :big
|
34
|
+
|
35
|
+
string :binary, read_length: :length
|
36
|
+
|
37
|
+
def set(value)
|
38
|
+
self.binary = [value].flatten.map(&:to_binary).join
|
39
|
+
end
|
40
|
+
|
41
|
+
# rubocop:disable MethodLength
|
42
|
+
# This method smells of :reek:TooManyStatements
|
43
|
+
def get
|
44
|
+
actions = []
|
45
|
+
tmp = binary
|
46
|
+
while tmp.length > 0
|
47
|
+
type = BinData::Uint16be.read(tmp)
|
48
|
+
begin
|
49
|
+
action = ACTION_CLASS.fetch(type).read(tmp)
|
50
|
+
tmp = tmp[action.message_length..-1]
|
51
|
+
actions << action
|
52
|
+
rescue KeyError
|
53
|
+
raise "action type #{type} is not supported."
|
54
|
+
end
|
55
|
+
end
|
56
|
+
actions
|
57
|
+
end
|
58
|
+
# rubocop:enable MethodLength
|
59
|
+
|
60
|
+
def [](index)
|
61
|
+
get[index]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/pio/open_flow/flags.rb
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
# bitmap functions.
|
2
|
+
# This class smells of :reek:DataClump
|
2
3
|
module Flags
|
4
|
+
def flags_32bit(name, flags)
|
5
|
+
_def_flags name, 32, flags
|
6
|
+
end
|
7
|
+
|
8
|
+
def flags_16bit(name, flags)
|
9
|
+
_def_flags name, 16, flags
|
10
|
+
end
|
11
|
+
|
3
12
|
# rubocop:disable MethodLength
|
4
13
|
# This method smells of :reek:TooManyStatements
|
5
|
-
def
|
14
|
+
def _def_flags(name, size, flags)
|
6
15
|
flag_value = case flags
|
7
16
|
when Array
|
8
17
|
shift = 0
|
@@ -14,20 +23,15 @@ module Flags
|
|
14
23
|
when Hash
|
15
24
|
flags
|
16
25
|
end
|
17
|
-
_def_class name, flag_value
|
18
|
-
end
|
19
|
-
# rubocop:enable MethodLength
|
20
26
|
|
21
|
-
# rubocop:disable MethodLength
|
22
|
-
def _def_class(name, flags)
|
23
27
|
klass_name = name.to_s.split('_').map(&:capitalize).join
|
24
|
-
flags_hash =
|
28
|
+
flags_hash = flag_value.inspect
|
25
29
|
|
26
30
|
code = %{
|
27
31
|
class #{klass_name} < BinData::Primitive
|
28
32
|
endian :big
|
29
33
|
|
30
|
-
|
34
|
+
uint#{size} :#{name}
|
31
35
|
|
32
36
|
def get
|
33
37
|
list = #{flags_hash}
|
@@ -48,5 +52,4 @@ module Flags
|
|
48
52
|
}
|
49
53
|
module_eval code
|
50
54
|
end
|
51
|
-
# rubocop:enable MethodLength
|
52
55
|
end
|
@@ -1,4 +1,7 @@
|
|
1
|
+
require 'English'
|
2
|
+
require 'bindata'
|
1
3
|
require 'forwardable'
|
4
|
+
require 'pio/parse_error'
|
2
5
|
|
3
6
|
module Pio
|
4
7
|
module OpenFlow
|
@@ -6,43 +9,124 @@ module Pio
|
|
6
9
|
class Message
|
7
10
|
extend Forwardable
|
8
11
|
|
12
|
+
# struct ofp_header fields.
|
9
13
|
def_delegators :@format, :open_flow_header
|
10
14
|
def_delegators :open_flow_header, :ofp_version
|
11
15
|
def_delegators :open_flow_header, :message_type
|
12
16
|
def_delegators :open_flow_header, :message_length
|
13
17
|
def_delegators :open_flow_header, :transaction_id
|
14
18
|
def_delegator :open_flow_header, :transaction_id, :xid
|
19
|
+
|
15
20
|
def_delegators :@format, :body
|
16
21
|
def_delegator :@format, :body, :user_data
|
22
|
+
|
17
23
|
def_delegator :@format, :to_binary_s, :to_binary
|
18
24
|
|
25
|
+
def self.factory(message_type)
|
26
|
+
@message_type = message_type
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.klass(message_type)
|
31
|
+
@messages.fetch(message_type)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.inherited(child)
|
35
|
+
child.const_set(:MESSAGE_TYPE, @message_type)
|
36
|
+
@messages ||= {}
|
37
|
+
@messages[@message_type] = child
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.read(raw_data)
|
41
|
+
message = allocate
|
42
|
+
message.instance_variable_set(:@format, format.read(raw_data))
|
43
|
+
message
|
44
|
+
rescue BinData::ValidityError
|
45
|
+
message_name = name.split('::')[1..-1].join(' ')
|
46
|
+
raise Pio::ParseError, "Invalid #{message_name} message."
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.format
|
50
|
+
const_get(:Format)
|
51
|
+
rescue NameError
|
52
|
+
define_message_format const_get(:MESSAGE_TYPE)
|
53
|
+
retry
|
54
|
+
end
|
55
|
+
|
19
56
|
# rubocop:disable MethodLength
|
20
|
-
def self.
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
57
|
+
def self.define_message_format(message_type)
|
58
|
+
code = %(
|
59
|
+
class Format < BinData::Record
|
60
|
+
endian :big
|
61
|
+
|
62
|
+
open_flow_header :open_flow_header,
|
63
|
+
message_type_value: #{message_type}
|
64
|
+
virtual assert: -> do
|
65
|
+
open_flow_header.message_type == #{message_type}
|
66
|
+
end
|
67
|
+
|
68
|
+
#{body_type} :body
|
69
|
+
end
|
33
70
|
)
|
34
|
-
module_eval
|
71
|
+
module_eval code
|
35
72
|
end
|
36
73
|
# rubocop:enable MethodLength
|
37
74
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
75
|
+
def self.body_type
|
76
|
+
klass_name = name.split('::').last + 'Body'
|
77
|
+
const_get(klass_name)
|
78
|
+
klass_name.sub(/.*::/, '')
|
79
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
80
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
81
|
+
.tr('-', '_')
|
82
|
+
.downcase
|
83
|
+
rescue NameError
|
84
|
+
'string'
|
85
|
+
end
|
86
|
+
|
87
|
+
def initialize(user_options = {})
|
88
|
+
header_options = parse_header_options(user_options)
|
89
|
+
body_options = parse_body_options(user_options)
|
90
|
+
@format = self.class.format.new(open_flow_header: header_options,
|
91
|
+
body: body_options)
|
45
92
|
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# This method smells of :reek:FeatureEnvy
|
97
|
+
def parse_header_options(options)
|
98
|
+
xid = if options.respond_to?(:to_i)
|
99
|
+
options.to_i
|
100
|
+
elsif options.respond_to?(:fetch)
|
101
|
+
options[:transaction_id] || options[:xid] || 0
|
102
|
+
else
|
103
|
+
fail TypeError
|
104
|
+
end
|
105
|
+
return { transaction_id: xid } if xid.unsigned_32bit?
|
106
|
+
fail(ArgumentError,
|
107
|
+
'Transaction ID should be an unsigned 32-bit integer.')
|
108
|
+
end
|
109
|
+
|
110
|
+
# rubocop:disable MethodLength
|
111
|
+
# This method smells of :reek:TooManyStatements
|
112
|
+
# This method smells of :reek:FeatureEnvy
|
113
|
+
# This method smells of :reek:UtilityFunction
|
114
|
+
def parse_body_options(options)
|
115
|
+
if options.respond_to?(:fetch)
|
116
|
+
options.delete :transaction_id
|
117
|
+
options.delete :xid
|
118
|
+
dpid = options[:dpid]
|
119
|
+
options[:datapath_id] = dpid if dpid
|
120
|
+
if options.keys.size > 1
|
121
|
+
options
|
122
|
+
else
|
123
|
+
options[:user_data] || ''
|
124
|
+
end
|
125
|
+
else
|
126
|
+
''
|
127
|
+
end
|
128
|
+
end
|
129
|
+
# rubocop:enable MethodLength
|
46
130
|
end
|
47
131
|
end
|
48
132
|
end
|
@@ -9,37 +9,37 @@ module Pio
|
|
9
9
|
extend Flags
|
10
10
|
|
11
11
|
# enum ofp_port_config
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
flags_32bit :port_config,
|
13
|
+
[:port_down,
|
14
|
+
:no_stp,
|
15
|
+
:no_recv,
|
16
|
+
:no_recv_stp,
|
17
|
+
:no_flood,
|
18
|
+
:no_fwd,
|
19
|
+
:no_packet_in]
|
20
20
|
|
21
21
|
# enum ofp_port_state
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
flags_32bit :port_state,
|
23
|
+
link_down: 1 << 0,
|
24
|
+
stp_listen: 0 << 8,
|
25
|
+
stp_learn: 1 << 8,
|
26
|
+
stp_forward: 2 << 8,
|
27
|
+
stp_block: 3 << 8
|
28
28
|
|
29
29
|
# enum ofp_port_features
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
30
|
+
flags_32bit :port_feature,
|
31
|
+
[:port_10mb_hd,
|
32
|
+
:port_10mb_fd,
|
33
|
+
:port_100mb_hd,
|
34
|
+
:port_100mb_fd,
|
35
|
+
:port_1gb_hd,
|
36
|
+
:port_1gb_fd,
|
37
|
+
:port_10gb_fd,
|
38
|
+
:port_copper,
|
39
|
+
:port_fiber,
|
40
|
+
:port_autoneg,
|
41
|
+
:port_pause,
|
42
|
+
:port_pause_asym]
|
43
43
|
|
44
44
|
endian :big
|
45
45
|
|
@@ -52,6 +52,10 @@ module Pio
|
|
52
52
|
port_feature :advertised
|
53
53
|
port_feature :supported
|
54
54
|
port_feature :peer
|
55
|
+
|
56
|
+
def mac_address
|
57
|
+
hardware_address
|
58
|
+
end
|
55
59
|
end
|
56
60
|
end
|
57
61
|
end
|
data/lib/pio/open_flow/type.rb
CHANGED
data/lib/pio/packet_in.rb
CHANGED
@@ -4,7 +4,7 @@ require 'pio/parse_error'
|
|
4
4
|
|
5
5
|
module Pio
|
6
6
|
# OpenFlow 1.0 Packet-In message
|
7
|
-
class PacketIn <
|
7
|
+
class PacketIn < OpenFlow::Message.factory(OpenFlow::Type::PACKET_IN)
|
8
8
|
# Why is this packet being sent to the controller?
|
9
9
|
# (enum ofp_packet_in_reason)
|
10
10
|
class Reason < BinData::Primitive
|
@@ -22,7 +22,7 @@ module Pio
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# Message body of Packet-In.
|
25
|
-
class
|
25
|
+
class PacketInBody < BinData::Record
|
26
26
|
endian :big
|
27
27
|
|
28
28
|
uint32 :buffer_id
|
@@ -42,16 +42,6 @@ module Pio
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
def_format Pio::OpenFlow::Type::PACKET_IN
|
46
|
-
|
47
|
-
def self.read(raw_data)
|
48
|
-
packet_in = allocate
|
49
|
-
packet_in.instance_variable_set :@format, Format.read(raw_data)
|
50
|
-
packet_in
|
51
|
-
rescue BinData::ValidityError
|
52
|
-
raise Pio::ParseError, 'Invalid Packet-In message.'
|
53
|
-
end
|
54
|
-
|
55
45
|
def_delegators :body, :buffer_id
|
56
46
|
def_delegators :body, :total_len
|
57
47
|
def_delegators :body, :in_port
|
data/lib/pio/packet_out.rb
CHANGED
@@ -1,75 +1,17 @@
|
|
1
1
|
require 'bindata'
|
2
|
-
require 'pio/enqueue'
|
3
2
|
require 'pio/open_flow'
|
4
|
-
require 'pio/send_out_port'
|
5
|
-
require 'pio/set_eth_addr'
|
6
|
-
require 'pio/set_ip_addr'
|
7
|
-
require 'pio/set_ip_tos'
|
8
|
-
require 'pio/set_transport_port'
|
9
|
-
require 'pio/set_vlan_priority'
|
10
|
-
require 'pio/set_vlan_vid'
|
11
|
-
require 'pio/strip_vlan_header'
|
12
3
|
|
13
4
|
module Pio
|
14
5
|
# OpenFlow 1.0 Packet-Out message
|
15
|
-
class PacketOut <
|
16
|
-
# Actions list.
|
17
|
-
class Actions < BinData::Primitive
|
18
|
-
ACTION_CLASS = {
|
19
|
-
0 => Pio::SendOutPort,
|
20
|
-
1 => Pio::SetVlanVid,
|
21
|
-
2 => Pio::SetVlanPriority,
|
22
|
-
3 => Pio::StripVlanHeader,
|
23
|
-
4 => Pio::SetEthSrcAddr,
|
24
|
-
5 => Pio::SetEthDstAddr,
|
25
|
-
6 => Pio::SetIpSrcAddr,
|
26
|
-
7 => Pio::SetIpDstAddr,
|
27
|
-
8 => Pio::SetIpTos,
|
28
|
-
9 => Pio::SetTransportSrcPort,
|
29
|
-
10 => Pio::SetTransportDstPort,
|
30
|
-
11 => Pio::Enqueue
|
31
|
-
}
|
32
|
-
|
33
|
-
endian :big
|
34
|
-
|
35
|
-
uint16 :actions_len, initial_value: -> { binary.length }
|
36
|
-
string :binary, read_length: :actions_len
|
37
|
-
|
38
|
-
def set(value)
|
39
|
-
self.binary = [value].flatten.map(&:to_binary).join
|
40
|
-
end
|
41
|
-
|
42
|
-
# rubocop:disable MethodLength
|
43
|
-
# This method smells of :reek:TooManyStatements
|
44
|
-
def get
|
45
|
-
actions = []
|
46
|
-
tmp = binary
|
47
|
-
while tmp.length > 0
|
48
|
-
type = BinData::Uint16be.read(tmp)
|
49
|
-
begin
|
50
|
-
action = ACTION_CLASS.fetch(type).read(tmp)
|
51
|
-
tmp = tmp[action.message_length..-1]
|
52
|
-
actions << action
|
53
|
-
rescue KeyError
|
54
|
-
raise "action type #{type} is not supported."
|
55
|
-
end
|
56
|
-
end
|
57
|
-
actions
|
58
|
-
end
|
59
|
-
# rubocop:enable MethodLength
|
60
|
-
|
61
|
-
def [](index)
|
62
|
-
get[index]
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
6
|
+
class PacketOut < OpenFlow::Message.factory(OpenFlow::Type::PACKET_OUT)
|
66
7
|
# Message body of Packet-Out
|
67
|
-
class
|
8
|
+
class PacketOutBody < BinData::Record
|
68
9
|
endian :big
|
69
10
|
|
70
11
|
uint32 :buffer_id
|
71
12
|
uint16 :in_port
|
72
|
-
|
13
|
+
uint16 :actions_len, initial_value: -> { actions.binary.length }
|
14
|
+
actions :actions, length: -> { actions_len }
|
73
15
|
rest :data
|
74
16
|
|
75
17
|
def empty?
|
@@ -79,18 +21,6 @@ module Pio
|
|
79
21
|
def length
|
80
22
|
8 + actions_len + data.length
|
81
23
|
end
|
82
|
-
|
83
|
-
def actions_len
|
84
|
-
actions.actions_len
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def_format Pio::OpenFlow::Type::PACKET_OUT
|
89
|
-
|
90
|
-
def self.read(raw_data)
|
91
|
-
packet_out = allocate
|
92
|
-
packet_out.instance_variable_set :@format, Format.read(raw_data)
|
93
|
-
packet_out
|
94
24
|
end
|
95
25
|
|
96
26
|
def_delegators :body, :buffer_id
|