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
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'pio/monkey_patch/integer'
|
4
|
+
|
5
|
+
module Pio
|
6
|
+
# An action to output a packet to a port.
|
7
|
+
class SendOutPort
|
8
|
+
# OpenFlow 1.0 OFPAT_OUTPUT action format.
|
9
|
+
class Format < BinData::Record
|
10
|
+
endian :big
|
11
|
+
|
12
|
+
uint16 :type, value: 0
|
13
|
+
uint16 :message_length, value: 8
|
14
|
+
port_number :port_number
|
15
|
+
uint16 :max_len, initial_value: 2**16 - 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.read(raw_data)
|
19
|
+
send_out_port = allocate
|
20
|
+
send_out_port.instance_variable_set :@format, Format.read(raw_data)
|
21
|
+
send_out_port
|
22
|
+
end
|
23
|
+
|
24
|
+
extend Forwardable
|
25
|
+
|
26
|
+
def_delegators :@format, :message_length
|
27
|
+
def_delegators :@format, :port_number
|
28
|
+
def_delegators :@format, :max_len
|
29
|
+
def_delegator :@format, :to_binary_s, :to_binary
|
30
|
+
|
31
|
+
# Creates an action to output a packet to a port.
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# SendOutPort.new(1)
|
35
|
+
# SendOutPort.new(port_number: 1)
|
36
|
+
# SendOutPort.new(port_number: :controller, max_len: 256)
|
37
|
+
#
|
38
|
+
# @param [Integer|Hash] user_options
|
39
|
+
# the port number or the options hash to create this action
|
40
|
+
# class instance with.
|
41
|
+
#
|
42
|
+
# @option user_options [Number] :port_number
|
43
|
+
# port number an index into switch's physical port list. There
|
44
|
+
# are also fake output ports. For example a port number set to
|
45
|
+
# +:flood+ would output packets to all physical ports except
|
46
|
+
# input port and ports disabled by STP.
|
47
|
+
# @option user_options [Number] :max_len
|
48
|
+
# the maximum number of bytes from a packet to send to
|
49
|
+
# controller when port is set to +:controller+. A zero length
|
50
|
+
# means no bytes of the packet should be sent. It defaults to
|
51
|
+
# 64K.
|
52
|
+
#
|
53
|
+
# @raise [ArgumentError] if +:port_number+ is not an unsigned
|
54
|
+
# 16-bit integer.
|
55
|
+
# @raise [ArgumentError] if +:max_len+ is not an unsigned 16-bit integer.
|
56
|
+
#
|
57
|
+
# rubocop:disable MethodLength
|
58
|
+
def initialize(user_options)
|
59
|
+
options = if user_options.respond_to?(:to_i)
|
60
|
+
{ port_number: user_options.to_i }
|
61
|
+
elsif PortNumber::NUMBERS.key?(user_options)
|
62
|
+
{ port_number: user_options }
|
63
|
+
else
|
64
|
+
user_options
|
65
|
+
end
|
66
|
+
max_len = options[:max_len]
|
67
|
+
if max_len && !max_len.unsigned_16bit?
|
68
|
+
fail ArgumentError, 'The max_len should be an unsigned 16bit integer.'
|
69
|
+
end
|
70
|
+
@format = Format.new(options)
|
71
|
+
end
|
72
|
+
# rubocop:enable MethodLength
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'pio/type/mac_address'
|
4
|
+
|
5
|
+
module Pio
|
6
|
+
# An action to modify the source/destination Ethernet address of a packet.
|
7
|
+
class SetEthAddr
|
8
|
+
# rubocop:disable MethodLength
|
9
|
+
def self.def_format(action_type)
|
10
|
+
str = %(
|
11
|
+
class Format < BinData::Record
|
12
|
+
endian :big
|
13
|
+
|
14
|
+
uint16 :type, value: #{action_type}
|
15
|
+
uint16 :message_length, value: 16
|
16
|
+
mac_address :mac_address
|
17
|
+
uint48 :padding
|
18
|
+
hide :padding
|
19
|
+
end
|
20
|
+
)
|
21
|
+
module_eval str
|
22
|
+
end
|
23
|
+
# rubocop:enable MethodLength
|
24
|
+
|
25
|
+
def self.read(raw_data)
|
26
|
+
set_eth_addr = allocate
|
27
|
+
set_eth_addr.instance_variable_set(:@format,
|
28
|
+
const_get(:Format).read(raw_data))
|
29
|
+
set_eth_addr
|
30
|
+
end
|
31
|
+
|
32
|
+
extend Forwardable
|
33
|
+
|
34
|
+
def_delegators :@format, :message_length
|
35
|
+
def_delegators :@format, :mac_address
|
36
|
+
def_delegator :@format, :to_binary_s, :to_binary
|
37
|
+
|
38
|
+
def initialize(mac_address)
|
39
|
+
@format = self.class.const_get(:Format).new(mac_address: mac_address)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# An action to modify the source Ethernet address of a packet.
|
44
|
+
class SetEthSrcAddr < SetEthAddr
|
45
|
+
def_format 4
|
46
|
+
end
|
47
|
+
|
48
|
+
# An action to modify the destination Ethernet address of a packet.
|
49
|
+
class SetEthDstAddr < SetEthAddr
|
50
|
+
def_format 5
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'pio/type/ip_address'
|
4
|
+
|
5
|
+
module Pio
|
6
|
+
# An action to modify the IPv4 source/destination address of a packet.
|
7
|
+
class SetIpAddr
|
8
|
+
def self.def_format(action_type)
|
9
|
+
str = %(
|
10
|
+
class Format < BinData::Record
|
11
|
+
endian :big
|
12
|
+
|
13
|
+
uint16 :type, value: #{action_type}
|
14
|
+
uint16 :message_length, value: 8
|
15
|
+
ip_address :ip_address
|
16
|
+
end
|
17
|
+
)
|
18
|
+
module_eval str
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.read(raw_data)
|
22
|
+
set_ip_addr = allocate
|
23
|
+
set_ip_addr.instance_variable_set(:@format,
|
24
|
+
const_get(:Format).read(raw_data))
|
25
|
+
set_ip_addr
|
26
|
+
end
|
27
|
+
|
28
|
+
extend Forwardable
|
29
|
+
|
30
|
+
def_delegators :@format, :type
|
31
|
+
def_delegators :@format, :message_length
|
32
|
+
def_delegators :@format, :ip_address
|
33
|
+
def_delegator :@format, :to_binary_s, :to_binary
|
34
|
+
|
35
|
+
def initialize(ip_address)
|
36
|
+
@format = self.class.const_get(:Format).new(ip_address: ip_address)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# An action to modify the IPv4 source address of a packet.
|
41
|
+
class SetIpSrcAddr < SetIpAddr
|
42
|
+
def_format 6
|
43
|
+
end
|
44
|
+
|
45
|
+
# An action to modify the IPv4 source address of a packet.
|
46
|
+
class SetIpDstAddr < SetIpAddr
|
47
|
+
def_format 7
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'pio/monkey_patch/integer'
|
4
|
+
|
5
|
+
module Pio
|
6
|
+
# An action to modify the IP ToS/DSCP field of a packet.
|
7
|
+
class SetIpTos
|
8
|
+
# OpenFlow 1.0 OFPAT_SET_NW_TOS action format.
|
9
|
+
class Format < BinData::Record
|
10
|
+
endian :big
|
11
|
+
|
12
|
+
uint16 :type, value: 8
|
13
|
+
uint16 :message_length, value: 8
|
14
|
+
uint8 :type_of_service
|
15
|
+
uint24 :padding
|
16
|
+
hide :padding
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.read(raw_data)
|
20
|
+
set_ip_tos = allocate
|
21
|
+
set_ip_tos.instance_variable_set :@format, Format.read(raw_data)
|
22
|
+
set_ip_tos
|
23
|
+
end
|
24
|
+
|
25
|
+
extend Forwardable
|
26
|
+
|
27
|
+
def_delegators :@format, :type
|
28
|
+
def_delegators :@format, :message_length
|
29
|
+
def_delegators :@format, :type_of_service
|
30
|
+
def_delegator :@format, :to_binary_s, :to_binary
|
31
|
+
|
32
|
+
def initialize(type_of_service)
|
33
|
+
# nw_tos (IP ToS) value consists of 8 bits, of which only the 6
|
34
|
+
# high-order bits belong to DSCP, the 2 low-order bits must be
|
35
|
+
# zero.
|
36
|
+
unless type_of_service.unsigned_8bit? && type_of_service % 4 == 0
|
37
|
+
fail ArgumentError, 'Invalid type_of_service (ToS) value.'
|
38
|
+
end
|
39
|
+
@format = Format.new(type_of_service: type_of_service)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'pio/monkey_patch/integer'
|
4
|
+
|
5
|
+
module Pio
|
6
|
+
# An action to modify the source/destination TCP/UDP port of a packet.
|
7
|
+
class SetTransportPort
|
8
|
+
# rubocop:disable MethodLength
|
9
|
+
def self.def_format(action_type)
|
10
|
+
str = %(
|
11
|
+
class Format < BinData::Record
|
12
|
+
endian :big
|
13
|
+
|
14
|
+
uint16 :type, value: #{action_type}
|
15
|
+
uint16 :message_length, value: 8
|
16
|
+
uint16 :port_number
|
17
|
+
uint16 :padding
|
18
|
+
hide :padding
|
19
|
+
end
|
20
|
+
)
|
21
|
+
module_eval str
|
22
|
+
end
|
23
|
+
# rubocop:enable MethodLength
|
24
|
+
|
25
|
+
def self.read(raw_data)
|
26
|
+
action = allocate
|
27
|
+
action.instance_variable_set(:@format, const_get(:Format).read(raw_data))
|
28
|
+
action
|
29
|
+
end
|
30
|
+
|
31
|
+
extend Forwardable
|
32
|
+
|
33
|
+
def_delegators :@format, :type
|
34
|
+
def_delegators :@format, :message_length
|
35
|
+
def_delegators :@format, :port_number
|
36
|
+
def_delegator :@format, :to_binary_s, :to_binary
|
37
|
+
|
38
|
+
def initialize(number)
|
39
|
+
port_number = number.to_i
|
40
|
+
unless port_number.unsigned_16bit?
|
41
|
+
fail ArgumentError, 'TCP/UDP port must be an unsigned 16-bit integer.'
|
42
|
+
end
|
43
|
+
@format = self.class.const_get(:Format).new(port_number: port_number)
|
44
|
+
rescue NoMethodError
|
45
|
+
raise TypeError, 'TCP/UDP port must be an unsigned 16-bit integer.'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# An action to modify the source TCP/UDP port of a packet.
|
50
|
+
class SetTransportSrcPort < SetTransportPort
|
51
|
+
def_format 9
|
52
|
+
end
|
53
|
+
|
54
|
+
# An action to modify the source TCP/UDP port of a packet.
|
55
|
+
class SetTransportDstPort < SetTransportPort
|
56
|
+
def_format 10
|
57
|
+
end
|
58
|
+
end
|
data/lib/pio/set_vlan.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Pio
|
5
|
+
# An action to modify the VLAN related fields of a packet.
|
6
|
+
class SetVlan
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# rubocop:disable MethodLength
|
10
|
+
def self.def_format(field_name, action_type)
|
11
|
+
str = %(
|
12
|
+
class Format < BinData::Record
|
13
|
+
endian :big
|
14
|
+
|
15
|
+
uint16 :type, value: #{action_type}
|
16
|
+
uint16 :message_length, value: 8
|
17
|
+
uint16 :#{field_name}
|
18
|
+
uint16 :padding
|
19
|
+
hide :padding
|
20
|
+
end
|
21
|
+
)
|
22
|
+
module_eval str
|
23
|
+
module_eval "def_delegators :@format, :#{field_name}"
|
24
|
+
end
|
25
|
+
# rubocop:enable MethodLength
|
26
|
+
|
27
|
+
def self.read(raw_data)
|
28
|
+
set_vlan = allocate
|
29
|
+
set_vlan.instance_variable_set :@format, const_get(:Format).read(raw_data)
|
30
|
+
set_vlan
|
31
|
+
end
|
32
|
+
|
33
|
+
def_delegators :@format, :type
|
34
|
+
def_delegators :@format, :message_length
|
35
|
+
def_delegator :@format, :to_binary_s, :to_binary
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'pio/set_vlan'
|
2
|
+
|
3
|
+
module Pio
|
4
|
+
# An action to modify the VLAN priority of a packet.
|
5
|
+
class SetVlanPriority < SetVlan
|
6
|
+
def_format :vlan_priority, 2
|
7
|
+
|
8
|
+
def initialize(number)
|
9
|
+
priority = number.to_i
|
10
|
+
if priority < 0 || priority > 7
|
11
|
+
fail ArgumentError, 'VLAN priority must be between 0 and 7 inclusive'
|
12
|
+
end
|
13
|
+
@format = Format.new(vlan_priority: priority)
|
14
|
+
rescue NoMethodError
|
15
|
+
raise TypeError, 'VLAN priority must be an Integer.'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'pio/set_vlan'
|
2
|
+
|
3
|
+
module Pio
|
4
|
+
# An action to modify the VLAN ID of a packet.
|
5
|
+
class SetVlanVid < SetVlan
|
6
|
+
def_format :vlan_id, 1
|
7
|
+
|
8
|
+
# Creates an action to modify the VLAN ID of a packet. The VLAN ID
|
9
|
+
# is 16-bits long but the actual VID (VLAN Identifier) of the IEEE
|
10
|
+
# 802.1Q frame is 12-bits.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# ActionSetVlanVid.new(number)
|
14
|
+
#
|
15
|
+
# @param [Integer] number
|
16
|
+
# the VLAN ID to set to. Only the lower 12-bits are used.
|
17
|
+
#
|
18
|
+
# @raise [ArgumentError] if vlan_id not within 1 and 4095 inclusive.
|
19
|
+
# @raise [TypeError] if vlan_id is not an Integer.
|
20
|
+
def initialize(number)
|
21
|
+
vlan_id = number.to_i
|
22
|
+
unless vlan_id >= 1 && vlan_id <= 4095
|
23
|
+
fail ArgumentError, 'VLAN ID must be between 1 and 4095 inclusive'
|
24
|
+
end
|
25
|
+
@format = Format.new(vlan_id: vlan_id)
|
26
|
+
rescue NoMethodError
|
27
|
+
raise TypeError, 'VLAN ID must be an Integer.'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
module Pio
|
4
|
+
# An action to strip the 802.1q header.
|
5
|
+
class StripVlanHeader < BinData::Record
|
6
|
+
endian :big
|
7
|
+
|
8
|
+
uint16 :type, value: 3
|
9
|
+
uint16 :message_length, value: 8
|
10
|
+
uint32 :padding
|
11
|
+
hide :padding
|
12
|
+
|
13
|
+
alias_method :to_binary, :to_binary_s
|
14
|
+
|
15
|
+
def snapshot
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/pio/type/ip_address.rb
CHANGED
@@ -8,11 +8,20 @@ module Pio
|
|
8
8
|
array :octets, type: :uint8, initial_length: 4
|
9
9
|
|
10
10
|
def set(value)
|
11
|
-
|
11
|
+
case value
|
12
|
+
when String
|
13
|
+
self.octets = value.split('.').map(&:to_i)
|
14
|
+
else
|
15
|
+
self.octets = value
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
19
|
def get
|
15
|
-
IPv4Address.new octets.map { |
|
20
|
+
IPv4Address.new octets.map { |each| format('%d', each) }.join('.')
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
get == other
|
16
25
|
end
|
17
26
|
end
|
18
27
|
end
|
data/lib/pio/type/ipv4_header.rb
CHANGED
data/lib/pio/type/mac_address.rb
CHANGED
data/lib/pio/version.rb
CHANGED
data/pio.gemspec
CHANGED
@@ -37,11 +37,11 @@ Gem::Specification.new do |gem|
|
|
37
37
|
gem.add_dependency 'bindata', '~> 2.1.0'
|
38
38
|
|
39
39
|
gem.add_development_dependency 'rake'
|
40
|
-
gem.add_development_dependency 'bundler', '~> 1.7.
|
40
|
+
gem.add_development_dependency 'bundler', '~> 1.7.11'
|
41
41
|
gem.add_development_dependency 'pry', '~> 0.10.1'
|
42
42
|
|
43
43
|
# Guard
|
44
|
-
gem.add_development_dependency 'guard', '~> 2.10.
|
44
|
+
gem.add_development_dependency 'guard', '~> 2.10.5'
|
45
45
|
gem.add_development_dependency 'guard-bundler', '~> 2.1.0'
|
46
46
|
gem.add_development_dependency 'guard-cucumber', '~> 1.5.3'
|
47
47
|
gem.add_development_dependency 'guard-rspec', '~> 4.5.0'
|
@@ -56,9 +56,9 @@ Gem::Specification.new do |gem|
|
|
56
56
|
gem.add_development_dependency 'yard', '~> 0.8.7.6'
|
57
57
|
|
58
58
|
# Test
|
59
|
-
gem.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.
|
59
|
+
gem.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.4'
|
60
60
|
gem.add_development_dependency 'coveralls', '~> 0.7.2'
|
61
|
-
gem.add_development_dependency 'cucumber', '~> 1.3.
|
61
|
+
gem.add_development_dependency 'cucumber', '~> 1.3.18'
|
62
62
|
gem.add_development_dependency 'flay', '~> 2.5.0'
|
63
63
|
gem.add_development_dependency 'flog', '~> 4.3.0'
|
64
64
|
gem.add_development_dependency 'reek', '~> 1.5.1'
|