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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +36 -0
  4. data/examples/packet_out_new.rb +18 -0
  5. data/examples/packet_out_read.rb +6 -0
  6. data/features/packet_out_read.feature +5 -0
  7. data/lib/pio.rb +1 -0
  8. data/lib/pio/echo.rb +5 -6
  9. data/lib/pio/echo/message.rb +39 -13
  10. data/lib/pio/echo/reply.rb +1 -6
  11. data/lib/pio/echo/request.rb +3 -5
  12. data/lib/pio/enqueue.rb +62 -0
  13. data/lib/pio/features.rb +5 -15
  14. data/lib/pio/features/reply.rb +28 -48
  15. data/lib/pio/features/request.rb +11 -8
  16. data/lib/pio/hello.rb +24 -2
  17. data/lib/pio/monkey_patch/integer.rb +6 -0
  18. data/lib/pio/monkey_patch/integer/ranges.rb +22 -0
  19. data/lib/pio/open_flow.rb +1 -0
  20. data/lib/pio/open_flow/flags.rb +40 -26
  21. data/lib/pio/open_flow/message.rb +28 -0
  22. data/lib/pio/open_flow/parser.rb +22 -0
  23. data/lib/pio/open_flow/phy_port.rb +34 -50
  24. data/lib/pio/open_flow/port_number.rb +39 -0
  25. data/lib/pio/open_flow/type.rb +1 -0
  26. data/lib/pio/packet_in.rb +42 -9
  27. data/lib/pio/packet_out.rb +102 -0
  28. data/lib/pio/send_out_port.rb +74 -0
  29. data/lib/pio/set_eth_addr.rb +52 -0
  30. data/lib/pio/set_ip_addr.rb +49 -0
  31. data/lib/pio/set_ip_tos.rb +42 -0
  32. data/lib/pio/set_transport_port.rb +58 -0
  33. data/lib/pio/set_vlan.rb +37 -0
  34. data/lib/pio/set_vlan_priority.rb +18 -0
  35. data/lib/pio/set_vlan_vid.rb +30 -0
  36. data/lib/pio/strip_vlan_header.rb +19 -0
  37. data/lib/pio/type/ip_address.rb +11 -2
  38. data/lib/pio/type/ipv4_header.rb +1 -0
  39. data/lib/pio/type/mac_address.rb +2 -0
  40. data/lib/pio/version.rb +1 -1
  41. data/pio.gemspec +4 -4
  42. data/spec/pio/echo/reply_spec.rb +1 -4
  43. data/spec/pio/echo/request_spec.rb +5 -8
  44. data/spec/pio/enqueue_spec.rb +67 -0
  45. data/spec/pio/features/request_spec.rb +4 -4
  46. data/spec/pio/features_spec.rb +1 -2
  47. data/spec/pio/hello_spec.rb +1 -4
  48. data/spec/pio/packet_out_spec.rb +290 -0
  49. data/spec/pio/send_out_port_spec.rb +121 -0
  50. data/spec/pio/set_eth_dst_addr_spec.rb +28 -0
  51. data/spec/pio/set_eth_src_addr_spec.rb +28 -0
  52. data/spec/pio/set_ip_dst_addr_spec.rb +25 -0
  53. data/spec/pio/set_ip_src_addr_spec.rb +25 -0
  54. data/spec/pio/set_ip_tos_spec.rb +30 -0
  55. data/spec/pio/set_transport_dst_port_spec.rb +42 -0
  56. data/spec/pio/set_transport_src_port_spec.rb +42 -0
  57. data/spec/pio/set_vlan_priority_spec.rb +42 -0
  58. data/spec/pio/set_vlan_vid_spec.rb +42 -0
  59. data/spec/pio/strip_vlan_header_spec.rb +20 -0
  60. metadata +55 -13
  61. data/lib/pio/echo/format.rb +0 -22
  62. data/lib/pio/hello/format.rb +0 -18
  63. 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
@@ -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
@@ -8,11 +8,20 @@ module Pio
8
8
  array :octets, type: :uint8, initial_length: 4
9
9
 
10
10
  def set(value)
11
- self.octets = value
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 { | each | format('%d', each) }.join('.')
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
@@ -4,6 +4,7 @@ module Pio
4
4
  module Type
5
5
  # IP Version 4 Header Format
6
6
  module IPv4Header
7
+ # This method smells of :reek:TooManyStatements
7
8
  # rubocop:disable MethodLength
8
9
  # rubocop:disable AbcSize
9
10
  def ipv4_header(options)
@@ -11,6 +11,8 @@ module Pio
11
11
  case value
12
12
  when String
13
13
  self.octets = value.split(':').map { |each| ('0x' + each).hex }
14
+ when Integer
15
+ self.octets = (0..5).map { |each| value >> ((5 - each) * 8) & 0xff }
14
16
  else
15
17
  self.octets = value.to_a
16
18
  end
data/lib/pio/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # Base module.
2
2
  module Pio
3
3
  # gem version.
4
- VERSION = '0.9.0'.freeze
4
+ VERSION = '0.10.0'.freeze
5
5
  end
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.9'
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.4'
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.3'
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.17'
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'