pio 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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'