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/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
|