pio 0.9.0 → 0.10.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 +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()
|