pio 0.9.0 → 0.10.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 +36 -0
- data/examples/packet_out_new.rb +18 -0
- data/examples/packet_out_read.rb +6 -0
- data/features/packet_out_read.feature +5 -0
- data/lib/pio.rb +1 -0
- data/lib/pio/echo.rb +5 -6
- data/lib/pio/echo/message.rb +39 -13
- data/lib/pio/echo/reply.rb +1 -6
- data/lib/pio/echo/request.rb +3 -5
- data/lib/pio/enqueue.rb +62 -0
- data/lib/pio/features.rb +5 -15
- data/lib/pio/features/reply.rb +28 -48
- data/lib/pio/features/request.rb +11 -8
- data/lib/pio/hello.rb +24 -2
- data/lib/pio/monkey_patch/integer.rb +6 -0
- data/lib/pio/monkey_patch/integer/ranges.rb +22 -0
- data/lib/pio/open_flow.rb +1 -0
- data/lib/pio/open_flow/flags.rb +40 -26
- data/lib/pio/open_flow/message.rb +28 -0
- data/lib/pio/open_flow/parser.rb +22 -0
- data/lib/pio/open_flow/phy_port.rb +34 -50
- data/lib/pio/open_flow/port_number.rb +39 -0
- data/lib/pio/open_flow/type.rb +1 -0
- data/lib/pio/packet_in.rb +42 -9
- data/lib/pio/packet_out.rb +102 -0
- data/lib/pio/send_out_port.rb +74 -0
- data/lib/pio/set_eth_addr.rb +52 -0
- data/lib/pio/set_ip_addr.rb +49 -0
- data/lib/pio/set_ip_tos.rb +42 -0
- data/lib/pio/set_transport_port.rb +58 -0
- data/lib/pio/set_vlan.rb +37 -0
- data/lib/pio/set_vlan_priority.rb +18 -0
- data/lib/pio/set_vlan_vid.rb +30 -0
- data/lib/pio/strip_vlan_header.rb +19 -0
- data/lib/pio/type/ip_address.rb +11 -2
- data/lib/pio/type/ipv4_header.rb +1 -0
- data/lib/pio/type/mac_address.rb +2 -0
- data/lib/pio/version.rb +1 -1
- data/pio.gemspec +4 -4
- data/spec/pio/echo/reply_spec.rb +1 -4
- data/spec/pio/echo/request_spec.rb +5 -8
- data/spec/pio/enqueue_spec.rb +67 -0
- data/spec/pio/features/request_spec.rb +4 -4
- data/spec/pio/features_spec.rb +1 -2
- data/spec/pio/hello_spec.rb +1 -4
- data/spec/pio/packet_out_spec.rb +290 -0
- data/spec/pio/send_out_port_spec.rb +121 -0
- data/spec/pio/set_eth_dst_addr_spec.rb +28 -0
- data/spec/pio/set_eth_src_addr_spec.rb +28 -0
- data/spec/pio/set_ip_dst_addr_spec.rb +25 -0
- data/spec/pio/set_ip_src_addr_spec.rb +25 -0
- data/spec/pio/set_ip_tos_spec.rb +30 -0
- data/spec/pio/set_transport_dst_port_spec.rb +42 -0
- data/spec/pio/set_transport_src_port_spec.rb +42 -0
- data/spec/pio/set_vlan_priority_spec.rb +42 -0
- data/spec/pio/set_vlan_vid_spec.rb +42 -0
- data/spec/pio/strip_vlan_header_spec.rb +20 -0
- metadata +55 -13
- data/lib/pio/echo/format.rb +0 -22
- data/lib/pio/hello/format.rb +0 -18
- data/lib/pio/packet_in/format.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b5745888688705d7d5a5cf305072b022821e9c2
|
4
|
+
data.tar.gz: 565161541fb6cf0090bce51b060478c455c3cc2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4af67cdf203efe01f2a6c6c1128adc142ca72015bbb2fb5d5d3448c1ea4700bb5eb30fb7ec5329ab3bef2f9bb0b0e678438825c75c6b31f0e0467b21966be9e6
|
7
|
+
data.tar.gz: 0b12f27bd5eed509ff5471cd4c0ea6d72b193fdfe9a645c979b5bf6b0910d290233988fb2fef0a9aa86dab0e7c9b0540d848ef1e638c8830ffc1ca7abc0dface
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -22,6 +22,7 @@ supports the following packet formats:
|
|
22
22
|
- Echo
|
23
23
|
- Features
|
24
24
|
- Packet-In
|
25
|
+
- Packet-Out
|
25
26
|
- (…currently there are just a few formats supported but I'm sure this list will grow)
|
26
27
|
|
27
28
|
## Features Overview
|
@@ -284,6 +285,41 @@ like below:
|
|
284
285
|
)
|
285
286
|
packet_in.to_binary # => Packet-In message in binary format.
|
286
287
|
|
288
|
+
### Packet-Out
|
289
|
+
|
290
|
+
To parse an OpenFlow 1.0 Packet-Out message, use the API
|
291
|
+
`Pio::PacketOut.read` and you can access each field of the parsed
|
292
|
+
Packet-Out message.
|
293
|
+
|
294
|
+
require 'pio'
|
295
|
+
|
296
|
+
packet_out = Pio::PacketOut.read(binary_data)
|
297
|
+
packet_out.actions.length # => 1
|
298
|
+
packet_out.actions[0] # => Pio::SendOutPort
|
299
|
+
packet_out.actions[0].port_number # => 2
|
300
|
+
|
301
|
+
Also you can use `Pio::PacketOut#new` to generate a Packet-Out message
|
302
|
+
like below:
|
303
|
+
|
304
|
+
require 'pio'
|
305
|
+
|
306
|
+
data_dump = [
|
307
|
+
0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x01, 0x02, 0x03, 0x04,
|
308
|
+
0x05, 0x06, 0x88, 0xcc, 0x02, 0x09, 0x07, 0x00, 0x00, 0x00,
|
309
|
+
0x00, 0x00, 0x00, 0x01, 0x23, 0x04, 0x05, 0x07, 0x00, 0x00,
|
310
|
+
0x00, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00,
|
311
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
312
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
313
|
+
0x00, 0x00, 0x00, 0x00
|
314
|
+
].pack('C*')
|
315
|
+
|
316
|
+
packet_out = Pio::PacketOut.new(transaction_id: 0x16,
|
317
|
+
buffer_id: 0xffffffff,
|
318
|
+
in_port: 0xffff,
|
319
|
+
actions: Pio::SendOutPort.new(2),
|
320
|
+
data: data_dump)
|
321
|
+
packet_out.to_binary # => Packet-Out message in binary format.
|
322
|
+
|
287
323
|
## Installation
|
288
324
|
|
289
325
|
The simplest way to install Pio is to use [Bundler](http://gembundler.com/).
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'pio'
|
2
|
+
|
3
|
+
data_dump = [
|
4
|
+
0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x01, 0x02, 0x03, 0x04,
|
5
|
+
0x05, 0x06, 0x88, 0xcc, 0x02, 0x09, 0x07, 0x00, 0x00, 0x00,
|
6
|
+
0x00, 0x00, 0x00, 0x01, 0x23, 0x04, 0x05, 0x07, 0x00, 0x00,
|
7
|
+
0x00, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00,
|
8
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
9
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
10
|
+
0x00, 0x00, 0x00, 0x00
|
11
|
+
].pack('C*')
|
12
|
+
|
13
|
+
packet_out = Pio::PacketOut.new(transaction_id: 0x16,
|
14
|
+
buffer_id: 0xffffffff,
|
15
|
+
in_port: 0xffff,
|
16
|
+
actions: Pio::SendOutPort.new(2),
|
17
|
+
data: data_dump)
|
18
|
+
packet_out.to_binary # => Packet-Out message in binary format.
|
data/lib/pio.rb
CHANGED
data/lib/pio/echo.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
|
-
require 'pio/echo/format'
|
2
1
|
require 'pio/echo/reply'
|
3
2
|
require 'pio/echo/request'
|
4
|
-
require 'pio/message_type_selector'
|
5
3
|
require 'pio/open_flow'
|
4
|
+
require 'pio/open_flow/parser'
|
6
5
|
|
7
6
|
module Pio
|
8
|
-
# OpenFlow Echo Request and Reply message parser.
|
7
|
+
# OpenFlow 1.0 Echo Request and Reply message parser.
|
9
8
|
class Echo
|
10
|
-
|
9
|
+
KLASS = { Pio::OpenFlow::Type::ECHO_REQUEST => Pio::Echo::Request,
|
10
|
+
Pio::OpenFlow::Type::ECHO_REPLY => Pio::Echo::Reply }
|
11
11
|
|
12
|
-
|
13
|
-
Pio::OpenFlow::Type::ECHO_REPLY => Reply
|
12
|
+
extend Pio::OpenFlow::Parser
|
14
13
|
end
|
15
14
|
end
|
data/lib/pio/echo/message.rb
CHANGED
@@ -1,23 +1,49 @@
|
|
1
|
+
require 'pio/open_flow'
|
2
|
+
|
1
3
|
module Pio
|
2
4
|
class Echo
|
3
5
|
# Base class of Echo request and reply.
|
4
6
|
class Message < Pio::OpenFlow::Message
|
5
|
-
#
|
6
|
-
|
7
|
+
# Message body of Echo
|
8
|
+
class Body < BinData::Record
|
9
|
+
endian :big
|
10
|
+
|
11
|
+
string :body
|
12
|
+
|
13
|
+
def length
|
14
|
+
body.length
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty?
|
18
|
+
length == 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
body == other
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# rubocop:disable MethodLength
|
27
|
+
# rubocop:disable AbcSize
|
28
|
+
# This method smells of :reek:FeatureEnvy
|
7
29
|
def initialize(user_options = {})
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
body: user_options[:user_data] }
|
15
|
-
|
16
|
-
|
17
|
-
|
30
|
+
if user_options.respond_to?(:to_i)
|
31
|
+
options = { open_flow_header: { transaction_id: user_options.to_i } }
|
32
|
+
elsif user_options.respond_to?(:fetch)
|
33
|
+
header_options = { transaction_id: user_options[:transaction_id] ||
|
34
|
+
user_options[:xid] || 0 }
|
35
|
+
options = { open_flow_header: header_options,
|
36
|
+
body: { body: user_options[:user_data] } }
|
37
|
+
else
|
38
|
+
fail TypeError
|
39
|
+
end
|
40
|
+
if options[:open_flow_header][:transaction_id] >= 2**32
|
41
|
+
fail ArgumentError, 'Transaction ID >= 2**32'
|
42
|
+
end
|
18
43
|
@format = self.class.const_get(:Format).new(options)
|
19
44
|
end
|
20
|
-
# rubocop:enable
|
45
|
+
# rubocop:enable AbcSize
|
46
|
+
# rubocop:enable MethodLength
|
21
47
|
end
|
22
48
|
end
|
23
49
|
end
|
data/lib/pio/echo/reply.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'pio/echo/format'
|
3
1
|
require 'pio/echo/message'
|
4
2
|
require 'pio/open_flow'
|
5
3
|
|
@@ -7,10 +5,7 @@ module Pio
|
|
7
5
|
class Echo
|
8
6
|
# OpenFlow 1.0 Echo Reply message generator.
|
9
7
|
class Reply < Message
|
10
|
-
|
11
|
-
class Format < Pio::Echo::Format
|
12
|
-
default_parameters message_type_value: Pio::OpenFlow::Type::ECHO_REPLY
|
13
|
-
end
|
8
|
+
def_format Pio::OpenFlow::Type::ECHO_REPLY
|
14
9
|
|
15
10
|
# Creates an EchoReply OpenFlow message. This message can be
|
16
11
|
# used to measure the bandwidth of a controller/switch
|
data/lib/pio/echo/request.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'pio/echo/format'
|
2
1
|
require 'pio/echo/message'
|
3
2
|
require 'pio/open_flow'
|
4
3
|
|
@@ -6,10 +5,7 @@ module Pio
|
|
6
5
|
class Echo
|
7
6
|
# OpenFlow 1.0 Echo Request message.
|
8
7
|
class Request < Message
|
9
|
-
|
10
|
-
class Format < Pio::Echo::Format
|
11
|
-
default_parameters message_type_value: Pio::OpenFlow::Type::ECHO_REQUEST
|
12
|
-
end
|
8
|
+
def_format Pio::OpenFlow::Type::ECHO_REQUEST
|
13
9
|
|
14
10
|
# Creates an EchoRequest OpenFlow message. This message can be
|
15
11
|
# used to measure the bandwidth of a controller/switch
|
@@ -40,6 +36,8 @@ module Pio
|
|
40
36
|
# timestamp to check latency, various lengths to measure
|
41
37
|
# bandwidth or zero-size(nil) to verify liveness between the
|
42
38
|
# switch and controller.
|
39
|
+
#
|
40
|
+
# rubocop:disable MethodLength
|
43
41
|
end
|
44
42
|
end
|
45
43
|
end
|
data/lib/pio/enqueue.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'pio/monkey_patch/integer'
|
4
|
+
require 'pio/open_flow'
|
5
|
+
|
6
|
+
module Pio
|
7
|
+
# An action to enqueue the packet on the specified queue attached to
|
8
|
+
# a port.
|
9
|
+
class Enqueue
|
10
|
+
# OpenFlow 1.0 OFPAT_ENQUEUE action format.
|
11
|
+
class Format < BinData::Record
|
12
|
+
endian :big
|
13
|
+
|
14
|
+
uint16 :type, value: 11
|
15
|
+
uint16 :message_length, value: 16
|
16
|
+
port_number :port_number
|
17
|
+
uint48 :padding
|
18
|
+
hide :padding
|
19
|
+
uint32 :queue_id
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.read(raw_data)
|
23
|
+
enqueue = allocate
|
24
|
+
enqueue.instance_variable_set :@format, Format.read(raw_data)
|
25
|
+
enqueue
|
26
|
+
end
|
27
|
+
|
28
|
+
extend Forwardable
|
29
|
+
|
30
|
+
def_delegators :@format, :type
|
31
|
+
def_delegators :@format, :message_length
|
32
|
+
def_delegators :@format, :port_number
|
33
|
+
def_delegators :@format, :queue_id
|
34
|
+
def_delegator :@format, :to_binary_s, :to_binary
|
35
|
+
|
36
|
+
def initialize(user_options)
|
37
|
+
validate_port_number user_options
|
38
|
+
validate_queue_id user_options
|
39
|
+
@format = Format.new(user_options)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def validate_port_number(user_options)
|
45
|
+
port_number = user_options.fetch(:port_number)
|
46
|
+
if port_number.is_a?(Symbol) && port_number != :in_port
|
47
|
+
fail(ArgumentError,
|
48
|
+
':port_number must be a valid physical port or :in_port')
|
49
|
+
end
|
50
|
+
rescue KeyError
|
51
|
+
raise ArgumentError, ':port_number is a mandatory option'
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_queue_id(user_options)
|
55
|
+
unless user_options.fetch(:queue_id).unsigned_32bit?
|
56
|
+
fail ArgumentError, ':queue_id must be an unsigned 32-bit integer'
|
57
|
+
end
|
58
|
+
rescue KeyError
|
59
|
+
raise ArgumentError, ':queue_id is a mandatory option'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/pio/features.rb
CHANGED
@@ -1,24 +1,14 @@
|
|
1
1
|
require 'pio/features/reply'
|
2
2
|
require 'pio/features/request'
|
3
3
|
require 'pio/open_flow'
|
4
|
-
require 'pio/
|
4
|
+
require 'pio/open_flow/parser'
|
5
5
|
|
6
6
|
module Pio
|
7
|
-
# OpenFlow 1.0 Features
|
7
|
+
# OpenFlow 1.0 Features Request and Reply message parser.
|
8
8
|
class Features
|
9
|
-
|
9
|
+
KLASS = { Pio::OpenFlow::Type::FEATURES_REQUEST => Pio::Features::Request,
|
10
|
+
Pio::OpenFlow::Type::FEATURES_REPLY => Pio::Features::Reply }
|
10
11
|
|
11
|
-
|
12
|
-
def self.read(raw_data)
|
13
|
-
header = Pio::Type::OpenFlow::OpenFlowHeader.read(raw_data)
|
14
|
-
klass = { FEATURES_REQUEST => Request,
|
15
|
-
FEATURES_REPLY => Reply }.fetch(header.message_type)
|
16
|
-
format = klass.const_get(:Format).read(raw_data)
|
17
|
-
message = klass.allocate
|
18
|
-
message.instance_variable_set :@format, format
|
19
|
-
message
|
20
|
-
rescue KeyError
|
21
|
-
raise Pio::ParseError, 'Invalid features request/reply message.'
|
22
|
-
end
|
12
|
+
extend Pio::OpenFlow::Parser
|
23
13
|
end
|
24
14
|
end
|
data/lib/pio/features/reply.rb
CHANGED
@@ -6,46 +6,36 @@ module Pio
|
|
6
6
|
class Features
|
7
7
|
# OpenFlow 1.0 Features Reply message
|
8
8
|
class Reply < Pio::OpenFlow::Message
|
9
|
-
#
|
10
|
-
class
|
11
|
-
extend Flags
|
12
|
-
|
13
|
-
endian :big
|
14
|
-
|
15
|
-
flags :capabilities,
|
16
|
-
flow_stats: 1 << 0,
|
17
|
-
table_stats: 1 << 1,
|
18
|
-
port_stats: 1 << 2,
|
19
|
-
stp: 1 << 3,
|
20
|
-
reserved: 1 << 4,
|
21
|
-
ip_reasm: 1 << 5,
|
22
|
-
queue_stats: 1 << 6,
|
23
|
-
arp_match_ip: 1 << 7
|
24
|
-
end
|
25
|
-
|
26
|
-
# enum ofp_action_type
|
27
|
-
class Actions < BinData::Primitive
|
9
|
+
# Message body of features reply.
|
10
|
+
class Body < BinData::Record
|
28
11
|
extend Flags
|
29
12
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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]
|
46
38
|
|
47
|
-
# Message body of features reply.
|
48
|
-
class Body < BinData::Record
|
49
39
|
endian :big
|
50
40
|
|
51
41
|
uint64 :datapath_id
|
@@ -66,17 +56,7 @@ module Pio
|
|
66
56
|
end
|
67
57
|
end
|
68
58
|
|
69
|
-
|
70
|
-
class Format < BinData::Record
|
71
|
-
include Pio::OpenFlow::Type
|
72
|
-
|
73
|
-
endian :big
|
74
|
-
|
75
|
-
open_flow_header :open_flow_header, message_type_value: FEATURES_REPLY
|
76
|
-
virtual assert: -> { open_flow_header.message_type == FEATURES_REPLY }
|
77
|
-
|
78
|
-
body :body
|
79
|
-
end
|
59
|
+
def_format Pio::OpenFlow::Type::FEATURES_REPLY
|
80
60
|
|
81
61
|
def datapath_id
|
82
62
|
@format.body.datapath_id
|
data/lib/pio/features/request.rb
CHANGED
@@ -6,20 +6,23 @@ module Pio
|
|
6
6
|
class Features
|
7
7
|
# OpenFlow 1.0 Features Request message
|
8
8
|
class Request < Pio::OpenFlow::Message
|
9
|
-
#
|
10
|
-
class
|
11
|
-
include Pio::OpenFlow::Type
|
12
|
-
|
9
|
+
# Message body of Features Request
|
10
|
+
class Body < BinData::Record
|
13
11
|
endian :big
|
14
12
|
|
15
|
-
|
16
|
-
|
13
|
+
string :body # ignored
|
14
|
+
|
15
|
+
def length
|
16
|
+
0
|
17
|
+
end
|
17
18
|
|
18
|
-
def
|
19
|
-
|
19
|
+
def empty?
|
20
|
+
true
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
24
|
+
def_format Pio::OpenFlow::Type::FEATURES_REQUEST
|
25
|
+
|
23
26
|
# Creates a Features Request OpenFlow message.
|
24
27
|
#
|
25
28
|
# @overload initialize()
|