pio 0.18.2 → 0.19.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 (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +40 -344
  4. data/Rakefile +32 -0
  5. data/bin/_guard-core +16 -0
  6. data/bin/byebug +16 -0
  7. data/bin/cc-tddium-post-worker +16 -0
  8. data/bin/cdiff +16 -0
  9. data/bin/coderay +16 -0
  10. data/bin/colortab +16 -0
  11. data/bin/coveralls +16 -0
  12. data/bin/cucumber +16 -0
  13. data/bin/decolor +16 -0
  14. data/bin/flay +16 -0
  15. data/bin/flog +16 -0
  16. data/bin/guard +16 -0
  17. data/bin/htmldiff +16 -0
  18. data/bin/inch +16 -0
  19. data/bin/ldiff +16 -0
  20. data/bin/listen +16 -0
  21. data/bin/minitar +16 -0
  22. data/bin/pry +16 -0
  23. data/bin/rake +16 -0
  24. data/bin/reek +16 -0
  25. data/bin/relish +16 -0
  26. data/bin/restclient +16 -0
  27. data/bin/rspec +16 -0
  28. data/bin/rubocop +16 -0
  29. data/bin/ruby-parse +16 -0
  30. data/bin/ruby-rewrite +16 -0
  31. data/bin/ruby_parse +16 -0
  32. data/bin/ruby_parse_extract_error +16 -0
  33. data/bin/sparkr +16 -0
  34. data/bin/term_display +16 -0
  35. data/bin/term_mandel +16 -0
  36. data/bin/thor +16 -0
  37. data/bin/unparser +16 -0
  38. data/bin/yard +16 -0
  39. data/bin/yardoc +16 -0
  40. data/bin/yri +16 -0
  41. data/features/arp.feature +61 -0
  42. data/features/dhcp.feature +4 -0
  43. data/features/icmp.feature +130 -0
  44. data/features/lldp.feature +47 -0
  45. data/features/open_flow10/echo_reply.feature +95 -0
  46. data/features/open_flow10/echo_request.feature +95 -0
  47. data/features/open_flow10/exact_match.feature +36 -0
  48. data/features/{features_read.feature → open_flow10/features_reply.feature} +54 -17
  49. data/features/open_flow10/features_request.feature +79 -0
  50. data/features/{flow_mod_read.feature → open_flow10/flow_mod.feature} +16 -21
  51. data/features/open_flow10/hello.feature +79 -0
  52. data/features/open_flow10/packet_in.feature +58 -0
  53. data/features/{packet_out_read.feature → open_flow10/packet_out.feature} +4 -5
  54. data/features/open_flow10/port_status.feature +23 -0
  55. data/features/open_flow13/echo_reply.feature +115 -0
  56. data/features/open_flow13/echo_request.feature +115 -0
  57. data/features/open_flow13/hello.feature +74 -0
  58. data/features/packet_data/echo13_reply_body.raw +0 -0
  59. data/features/packet_data/echo13_reply_no_body.raw +0 -0
  60. data/features/packet_data/echo13_request_body.raw +0 -0
  61. data/features/packet_data/echo13_request_no_body.raw +0 -0
  62. data/features/packet_data/hello13_no_version_bitmap.raw +0 -0
  63. data/features/packet_data/hello13_version_bitmap.raw +0 -0
  64. data/features/packet_data/udp_no_payload.raw +0 -0
  65. data/features/packet_data/udp_with_payload.raw +0 -0
  66. data/features/step_definitions/packet_data_steps.rb +49 -29
  67. data/features/support/env.rb +3 -0
  68. data/features/{udp_read.feature → udp.feature} +3 -4
  69. data/lib/pio.rb +1 -0
  70. data/lib/pio/echo.rb +67 -0
  71. data/lib/pio/hello13.rb +111 -0
  72. data/lib/pio/open_flow/message.rb +2 -1
  73. data/lib/pio/open_flow/open_flow_header.rb +21 -38
  74. data/lib/pio/open_flow/transaction_id.rb +25 -0
  75. data/lib/pio/version.rb +1 -1
  76. data/pio.gemspec +9 -14
  77. data/spec/pio/flow_mod_spec.rb +1 -1
  78. data/spec/pio/hello13_spec.rb +114 -0
  79. metadata +182 -138
  80. data/examples/arp_new.rb +0 -16
  81. data/examples/arp_read.rb +0 -4
  82. data/examples/dhcp_new.rb +0 -34
  83. data/examples/dhcp_read.rb +0 -4
  84. data/examples/echo_new.rb +0 -9
  85. data/examples/echo_read.rb +0 -4
  86. data/examples/features_new.rb +0 -28
  87. data/examples/features_read.rb +0 -4
  88. data/examples/flow_mod_new.rb +0 -13
  89. data/examples/flow_mod_read.rb +0 -6
  90. data/examples/hello_new.rb +0 -4
  91. data/examples/hello_read.rb +0 -4
  92. data/examples/icmp_new.rb +0 -21
  93. data/examples/icmp_read.rb +0 -4
  94. data/examples/lldp_new.rb +0 -4
  95. data/examples/lldp_read.rb +0 -4
  96. data/examples/packet_in_new.rb +0 -17
  97. data/examples/packet_in_read.rb +0 -5
  98. data/examples/packet_out_new.rb +0 -18
  99. data/examples/packet_out_read.rb +0 -6
  100. data/features/arp_read.feature +0 -10
  101. data/features/dhcp_read.feature +0 -6
  102. data/features/echo_read.feature +0 -29
  103. data/features/exact_match.feature +0 -38
  104. data/features/hello_read.feature +0 -14
  105. data/features/icmp_read.feature +0 -55
  106. data/features/lldp_read.feature +0 -26
  107. data/features/packet_in_read.feature +0 -22
  108. data/features/port_status_read.feature +0 -24
  109. data/features/step_definitions/pending_steps.rb +0 -3
  110. data/spec/pio/echo/reply_spec.rb +0 -135
  111. data/spec/pio/echo/request_spec.rb +0 -137
  112. data/spec/pio/features/reply_spec.rb +0 -137
  113. data/spec/pio/features/request_spec.rb +0 -112
  114. data/spec/pio/hello_spec.rb +0 -106
  115. data/spec/pio/lldp_spec.rb +0 -244
  116. data/spec/pio/packet_in_spec.rb +0 -146
@@ -1,42 +1,58 @@
1
- Given(/^a packet data file "(.*?)"$/) do |name|
2
- path = File.expand_path(File.join(File.dirname(__FILE__),
3
- '..', 'packet_data', name))
4
- case File.extname(name)
5
- when '.raw'
6
- @raw = path
7
- when '.pcap'
8
- @pcap = path
9
- else
10
- fail "Unsupported file extension: #{name}"
1
+ When(/^I try to create a packet with:$/) do |ruby_code|
2
+ begin
3
+ @result = Pio.module_eval(ruby_code)
4
+ rescue
5
+ @last_error = $ERROR_INFO
11
6
  end
12
7
  end
13
8
 
14
- When(/^I try to parse the file with "(.*?)" class$/) do |parser|
15
- parser_klass = Pio.const_get(parser)
16
- if @raw
17
- @result = parser_klass.read(IO.read(@raw))
18
- elsif @pcap
19
- File.open(@pcap) do |file|
20
- @result = []
21
- pcap = Pio::Pcap::Frame.read(file)
22
- pcap.records.each do |each|
23
- @result << parser_klass.read(each.data)
24
- end
25
- end
26
- else
27
- fail 'Packet data file is not specified.'
9
+ When(/^I try to create an OpenFlow message with:$/) do |ruby_code|
10
+ step 'I try to create a packet with:', ruby_code
11
+ end
12
+
13
+ # rubocop:disable LineLength
14
+ When(/^I try to parse a file named "(.*?)\.raw" with "(.*?)" class$/) do |file_name, klass|
15
+ path = File.expand_path(File.join(__dir__, '..', 'packet_data', file_name + '.raw'))
16
+ raw_data = IO.read(path)
17
+ parser_klass = Pio.const_get(klass)
18
+ begin
19
+ @result = parser_klass.read(raw_data)
20
+ rescue
21
+ @last_error = $ERROR_INFO
28
22
  end
29
23
  end
24
+ # rubocop:enable LineLength
30
25
 
31
- When(/^I try to create an exact match from the packet$/) do
32
- @result = Pio::ExactMatch.new(Pio::PacketIn.read(IO.read(@raw)))
26
+ # rubocop:disable LineLength
27
+ When(/^I try to parse a file named "(.*?)\.pcap" with "(.*?)" class$/) do |file_name, klass|
28
+ path = File.expand_path(File.join(__dir__, '..', 'packet_data', file_name + '.pcap'))
29
+ pcap = Pio::Pcap::Frame.read(IO.read(path))
30
+ parser_klass = Pio.const_get(klass)
31
+ begin
32
+ @result = pcap.records.each_with_object([]) do |each, result|
33
+ result << parser_klass.read(each.data)
34
+ end
35
+ rescue
36
+ @last_error = $ERROR_INFO
37
+ end
33
38
  end
39
+ # rubocop:enable LineLength
34
40
 
35
41
  Then(/^it should finish successfully$/) do
36
- # Noop.
42
+ expect(@last_error).to be_nil
43
+ end
44
+
45
+ Then(/^it should fail with "([^"]*)", "([^"]*)"$/) do |error, message|
46
+ expect(@last_error.class.to_s).to eq(error)
47
+ expect(@last_error.message).to eq(message)
37
48
  end
38
49
 
39
- Then(/^the parsed data have the following field and value:$/) do |table|
50
+ When(/^I create an exact match from "(.*?)"$/) do |file_name|
51
+ path = File.expand_path(File.join(__dir__, '..', 'packet_data', file_name))
52
+ @result = Pio::ExactMatch.new(Pio::PacketIn.read(IO.read(path)))
53
+ end
54
+
55
+ Then(/^the packet have the following fields and values:$/) do |table|
40
56
  table.hashes.each do |each|
41
57
  output = each['field'].split('.').inject(@result) do |memo, method|
42
58
  memo.__send__(method)
@@ -45,8 +61,12 @@ Then(/^the parsed data have the following field and value:$/) do |table|
45
61
  end
46
62
  end
47
63
 
64
+ Then(/^the message have the following fields and values:$/) do |table|
65
+ step 'the packet have the following fields and values:', table
66
+ end
67
+
48
68
  # rubocop:disable LineLength
49
- Then(/^the parsed data \#(\d+) have the following field and value:$/) do |index, table|
69
+ Then(/^the message \#(\d+) have the following fields and values:$/) do |index, table|
50
70
  table.hashes.each do |each|
51
71
  output = each['field'].split('.').inject(@result[index.to_i - 1]) do |memo, method|
52
72
  memo.__send__(method)
@@ -1,2 +1,5 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
1
4
  require 'pio'
2
5
  require 'pio/pcap'
@@ -1,9 +1,8 @@
1
- Feature: Pio::Udp.read
1
+ Feature: UDP
2
2
  Scenario: dhcp.pcap
3
- Given a packet data file "dhcp.pcap"
4
- When I try to parse the file with "Udp" class
3
+ When I try to parse a file named "dhcp.pcap" with "Udp" class
5
4
  Then it should finish successfully
6
- And the parsed data #1 have the following field and value:
5
+ And the message #1 have the following fields and values:
7
6
  | field | value |
8
7
  | class | Pio::Udp |
9
8
  | destination_mac | ff:ff:ff:ff:ff:ff |
data/lib/pio.rb CHANGED
@@ -7,6 +7,7 @@ require 'pio/exact_match'
7
7
  require 'pio/features'
8
8
  require 'pio/flow_mod'
9
9
  require 'pio/hello'
10
+ require 'pio/hello13'
10
11
  require 'pio/icmp'
11
12
  require 'pio/lldp'
12
13
  require 'pio/mac'
data/lib/pio/echo.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'pio/open_flow'
2
3
 
3
4
  module Pio
@@ -11,4 +12,70 @@ module Pio
11
12
  class Reply; end
12
13
  OpenFlow::Message.factory(Reply, OpenFlow::ECHO_REPLY)
13
14
  end
15
+
16
+ module Echo13
17
+ # Base class of Echo Request and Reply.
18
+ class Message
19
+ def self.message_name
20
+ name.split('::')[1..-1].map { |each| each.gsub(/\d+$/, '') }.join(' ')
21
+ end
22
+
23
+ def self.read(raw_data)
24
+ allocate.tap do |message|
25
+ message.instance_variable_set(:@format,
26
+ const_get(:Format).read(raw_data))
27
+ end
28
+ rescue BinData::ValidityError
29
+ raise Pio::ParseError, "Invalid #{message_name} 1.3 message."
30
+ end
31
+
32
+ def initialize(user_attrs = {})
33
+ unknown_attrs = user_attrs.keys - [:transaction_id, :xid, :body]
34
+ unless unknown_attrs.empty?
35
+ fail "Unknown keyword: #{unknown_attrs.first}"
36
+ end
37
+ header_options = OpenFlowHeader::Options.parse(user_attrs)
38
+ @format =
39
+ self.class.const_get(:Format).new(open_flow_header: header_options,
40
+ body: user_attrs[:body])
41
+ end
42
+
43
+ def method_missing(method, *args, &block)
44
+ @format.__send__ method, *args, &block
45
+ end
46
+ end
47
+
48
+ # Base class of Echo Request and Reply Format
49
+ class Format < BinData::Record
50
+ extend Forwardable
51
+
52
+ def_delegators :open_flow_header, :ofp_version
53
+ def_delegators :open_flow_header, :message_type
54
+ def_delegators :open_flow_header, :message_length
55
+ def_delegators :open_flow_header, :transaction_id
56
+ def_delegator :open_flow_header, :transaction_id, :xid
57
+ end
58
+
59
+ # OpenFlow 1.3 Echo Request message.
60
+ class Request < Message
61
+ # OpenFlow 1.3 Echo Request message format.
62
+ class Format < Echo13::Format
63
+ endian :big
64
+ open_flow_header :open_flow_header,
65
+ ofp_version_value: 4, message_type_value: 2
66
+ string :body, read_length: -> { message_length - 8 }
67
+ end
68
+ end
69
+
70
+ # OpenFlow 1.3 Echo Reply message.
71
+ class Reply < Message
72
+ # OpenFlow 1.3 Echo Request message format.
73
+ class Format < Echo13::Format
74
+ endian :big
75
+ open_flow_header :open_flow_header,
76
+ ofp_version_value: 4, message_type_value: 3
77
+ string :body, read_length: -> { message_length - 8 }
78
+ end
79
+ end
80
+ end
14
81
  end
@@ -0,0 +1,111 @@
1
+ require 'bindata'
2
+ require 'forwardable'
3
+ require 'pio/open_flow'
4
+ require 'pio/parse_error'
5
+
6
+ module Pio
7
+ # OpenFlow 1.3 Hello message parser and generator
8
+ class Hello13
9
+ # ofp_hello_elem_header and value
10
+ class Element < BinData::Record
11
+ VERSION_BITMAP = 1
12
+
13
+ endian :big
14
+
15
+ uint16 :element_type
16
+ uint16 :element_length
17
+ choice :element_value, selection: :chooser do
18
+ string 'unknown', read_length: -> { element_length - 4 }
19
+ uint32 'version_bitmap'
20
+ end
21
+
22
+ def version_bitmap?
23
+ element_type == VERSION_BITMAP
24
+ end
25
+
26
+ private
27
+
28
+ def chooser
29
+ version_bitmap? ? 'version_bitmap' : 'unknown'
30
+ end
31
+ end
32
+
33
+ # OpenFlow 1.3 Hello message body.
34
+ class Body < BinData::Record
35
+ array :elements, type: :element, read_until: :eof
36
+
37
+ def length
38
+ if elements.empty?
39
+ 0
40
+ else
41
+ elements.length * 4 + 4
42
+ end
43
+ end
44
+ end
45
+
46
+ # OpenFlow 1.3 Hello message format
47
+ class Format < BinData::Record
48
+ extend Forwardable
49
+
50
+ endian :big
51
+
52
+ open_flow_header :open_flow_header,
53
+ ofp_version_value: 4, message_type_value: 0
54
+ body :body
55
+
56
+ def_delegators :open_flow_header, :ofp_version
57
+ def_delegators :open_flow_header, :message_type
58
+ def_delegators :open_flow_header, :message_length
59
+ def_delegators :open_flow_header, :transaction_id
60
+ def_delegator :open_flow_header, :transaction_id, :xid
61
+ def_delegators :body, :elements
62
+
63
+ def supported_versions
64
+ supported_versions_list.map do |each|
65
+ "open_flow1#{each - 1}".to_sym
66
+ end
67
+ end
68
+
69
+ alias_method :to_binary, :to_binary_s
70
+
71
+ private
72
+
73
+ def supported_versions_list
74
+ (1..32).each_with_object([]) do |each, result|
75
+ result << each if (version_bitmap >> each & 1) == 1
76
+ end
77
+ end
78
+
79
+ def version_bitmap
80
+ bitmap = elements.find(&:version_bitmap?)
81
+ bitmap ? bitmap.element_value : 0
82
+ end
83
+ end
84
+
85
+ def self.read(raw_data)
86
+ allocate.tap do |message|
87
+ message.instance_variable_set(:@format, Format.read(raw_data))
88
+ end
89
+ rescue BinData::ValidityError
90
+ raise Pio::ParseError, 'Invalid Hello 1.3 message.'
91
+ end
92
+
93
+ def initialize(user_attrs = {})
94
+ unknown_keywords = user_attrs.keys - [:transaction_id, :xid]
95
+ unless unknown_keywords.empty?
96
+ fail "Unknown keyword: #{unknown_keywords.first}"
97
+ end
98
+
99
+ header_attrs = OpenFlowHeader::Options.parse(user_attrs)
100
+ body_attrs = { elements: [{ element_type: 1,
101
+ element_length: 8,
102
+ element_value: 16 }] }
103
+ @format = Format.new(open_flow_header: header_attrs,
104
+ body: body_attrs)
105
+ end
106
+
107
+ def method_missing(method, *args, &block)
108
+ @format.__send__ method, *args, &block
109
+ end
110
+ end
111
+ end
@@ -24,7 +24,8 @@ module Pio
24
24
  endian :big
25
25
 
26
26
  open_flow_header :open_flow_header,
27
- message_type_value: #{message_type}
27
+ ofp_version_value: 1,
28
+ message_type_value: #{message_type}
28
29
  virtual assert: -> do
29
30
  open_flow_header.message_type == #{message_type}
30
31
  end
@@ -1,46 +1,29 @@
1
1
  require 'bindata'
2
+ require 'pio/open_flow/transaction_id'
2
3
 
3
4
  module Pio
4
- # OpenFlow 1.0 format.
5
- module OpenFlow
6
- # OpenFlow 1.0 message header format.
7
- class OpenFlowHeader < BinData::Record
8
- # Transaction ID (uint32)
9
- class TransactionId < BinData::Primitive
10
- endian :big
11
- uint32 :xid
5
+ # OpenFlow message header.
6
+ class OpenFlowHeader < BinData::Record
7
+ endian :big
12
8
 
13
- def set(value)
14
- unless value.unsigned_32bit?
15
- fail(ArgumentError,
16
- 'Transaction ID should be an unsigned 32-bit integer.')
17
- end
18
- self.xid = value
19
- end
9
+ uint8 :ofp_version, value: :ofp_version_value
10
+ virtual assert: -> { ofp_version == ofp_version_value }
11
+ uint8 :message_type, value: :message_type_value
12
+ virtual assert: -> { message_type == message_type_value }
13
+ uint16 :message_length, initial_value: -> { 8 + body.length }
14
+ transaction_id :transaction_id, initial_value: 0
20
15
 
21
- def get
22
- xid
23
- end
24
- end
25
-
26
- endian :big
27
- uint8 :ofp_version, value: 1
28
- uint8 :message_type, initial_value: :message_type_value
29
- uint16 :message_length, initial_value: -> { 8 + body.length }
30
- transaction_id :transaction_id, initial_value: 0
31
-
32
- # parse header options
33
- class Options
34
- def self.parse(options)
35
- xid = if options.respond_to?(:to_i)
36
- options.to_i
37
- elsif options.respond_to?(:fetch)
38
- options[:transaction_id] || options[:xid] || 0
39
- else
40
- fail TypeError
41
- end
42
- { transaction_id: xid }
43
- end
16
+ # parse header options
17
+ class Options
18
+ def self.parse(options)
19
+ xid = if options.respond_to?(:to_i)
20
+ options.to_i
21
+ elsif options.respond_to?(:fetch)
22
+ options[:transaction_id] || options[:xid] || 0
23
+ else
24
+ fail TypeError
25
+ end
26
+ { transaction_id: xid }
44
27
  end
45
28
  end
46
29
  end
@@ -0,0 +1,25 @@
1
+ require 'bindata'
2
+ require 'pio/monkey_patch/integer'
3
+
4
+ module Pio
5
+ module OpenFlow
6
+ # Transaction ID (uint32)
7
+ class TransactionId < BinData::Primitive
8
+ endian :big
9
+
10
+ uint32 :xid
11
+
12
+ def set(value)
13
+ unless value.unsigned_32bit?
14
+ fail(ArgumentError,
15
+ 'Transaction ID should be an unsigned 32-bit integer.')
16
+ end
17
+ self.xid = value
18
+ end
19
+
20
+ def get
21
+ xid
22
+ end
23
+ end
24
+ end
25
+ 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.18.2'.freeze
4
+ VERSION = '0.19.0'.freeze
5
5
  end