pio 0.18.2 → 0.19.0

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