pio 0.18.2 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/README.md +40 -344
- data/Rakefile +32 -0
- data/bin/_guard-core +16 -0
- data/bin/byebug +16 -0
- data/bin/cc-tddium-post-worker +16 -0
- data/bin/cdiff +16 -0
- data/bin/coderay +16 -0
- data/bin/colortab +16 -0
- data/bin/coveralls +16 -0
- data/bin/cucumber +16 -0
- data/bin/decolor +16 -0
- data/bin/flay +16 -0
- data/bin/flog +16 -0
- data/bin/guard +16 -0
- data/bin/htmldiff +16 -0
- data/bin/inch +16 -0
- data/bin/ldiff +16 -0
- data/bin/listen +16 -0
- data/bin/minitar +16 -0
- data/bin/pry +16 -0
- data/bin/rake +16 -0
- data/bin/reek +16 -0
- data/bin/relish +16 -0
- data/bin/restclient +16 -0
- data/bin/rspec +16 -0
- data/bin/rubocop +16 -0
- data/bin/ruby-parse +16 -0
- data/bin/ruby-rewrite +16 -0
- data/bin/ruby_parse +16 -0
- data/bin/ruby_parse_extract_error +16 -0
- data/bin/sparkr +16 -0
- data/bin/term_display +16 -0
- data/bin/term_mandel +16 -0
- data/bin/thor +16 -0
- data/bin/unparser +16 -0
- data/bin/yard +16 -0
- data/bin/yardoc +16 -0
- data/bin/yri +16 -0
- data/features/arp.feature +61 -0
- data/features/dhcp.feature +4 -0
- data/features/icmp.feature +130 -0
- data/features/lldp.feature +47 -0
- data/features/open_flow10/echo_reply.feature +95 -0
- data/features/open_flow10/echo_request.feature +95 -0
- data/features/open_flow10/exact_match.feature +36 -0
- data/features/{features_read.feature → open_flow10/features_reply.feature} +54 -17
- data/features/open_flow10/features_request.feature +79 -0
- data/features/{flow_mod_read.feature → open_flow10/flow_mod.feature} +16 -21
- data/features/open_flow10/hello.feature +79 -0
- data/features/open_flow10/packet_in.feature +58 -0
- data/features/{packet_out_read.feature → open_flow10/packet_out.feature} +4 -5
- data/features/open_flow10/port_status.feature +23 -0
- data/features/open_flow13/echo_reply.feature +115 -0
- data/features/open_flow13/echo_request.feature +115 -0
- data/features/open_flow13/hello.feature +74 -0
- data/features/packet_data/echo13_reply_body.raw +0 -0
- data/features/packet_data/echo13_reply_no_body.raw +0 -0
- data/features/packet_data/echo13_request_body.raw +0 -0
- data/features/packet_data/echo13_request_no_body.raw +0 -0
- data/features/packet_data/hello13_no_version_bitmap.raw +0 -0
- data/features/packet_data/hello13_version_bitmap.raw +0 -0
- data/features/packet_data/udp_no_payload.raw +0 -0
- data/features/packet_data/udp_with_payload.raw +0 -0
- data/features/step_definitions/packet_data_steps.rb +49 -29
- data/features/support/env.rb +3 -0
- data/features/{udp_read.feature → udp.feature} +3 -4
- data/lib/pio.rb +1 -0
- data/lib/pio/echo.rb +67 -0
- data/lib/pio/hello13.rb +111 -0
- data/lib/pio/open_flow/message.rb +2 -1
- data/lib/pio/open_flow/open_flow_header.rb +21 -38
- data/lib/pio/open_flow/transaction_id.rb +25 -0
- data/lib/pio/version.rb +1 -1
- data/pio.gemspec +9 -14
- data/spec/pio/flow_mod_spec.rb +1 -1
- data/spec/pio/hello13_spec.rb +114 -0
- metadata +182 -138
- data/examples/arp_new.rb +0 -16
- data/examples/arp_read.rb +0 -4
- data/examples/dhcp_new.rb +0 -34
- data/examples/dhcp_read.rb +0 -4
- data/examples/echo_new.rb +0 -9
- data/examples/echo_read.rb +0 -4
- data/examples/features_new.rb +0 -28
- data/examples/features_read.rb +0 -4
- data/examples/flow_mod_new.rb +0 -13
- data/examples/flow_mod_read.rb +0 -6
- data/examples/hello_new.rb +0 -4
- data/examples/hello_read.rb +0 -4
- data/examples/icmp_new.rb +0 -21
- data/examples/icmp_read.rb +0 -4
- data/examples/lldp_new.rb +0 -4
- data/examples/lldp_read.rb +0 -4
- data/examples/packet_in_new.rb +0 -17
- data/examples/packet_in_read.rb +0 -5
- data/examples/packet_out_new.rb +0 -18
- data/examples/packet_out_read.rb +0 -6
- data/features/arp_read.feature +0 -10
- data/features/dhcp_read.feature +0 -6
- data/features/echo_read.feature +0 -29
- data/features/exact_match.feature +0 -38
- data/features/hello_read.feature +0 -14
- data/features/icmp_read.feature +0 -55
- data/features/lldp_read.feature +0 -26
- data/features/packet_in_read.feature +0 -22
- data/features/port_status_read.feature +0 -24
- data/features/step_definitions/pending_steps.rb +0 -3
- data/spec/pio/echo/reply_spec.rb +0 -135
- data/spec/pio/echo/request_spec.rb +0 -137
- data/spec/pio/features/reply_spec.rb +0 -137
- data/spec/pio/features/request_spec.rb +0 -112
- data/spec/pio/hello_spec.rb +0 -106
- data/spec/pio/lldp_spec.rb +0 -244
- data/spec/pio/packet_in_spec.rb +0 -146
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,42 +1,58 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
32
|
-
|
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
|
-
|
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
|
-
|
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
|
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)
|
data/features/support/env.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
Feature:
|
1
|
+
Feature: UDP
|
2
2
|
Scenario: dhcp.pcap
|
3
|
-
|
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
|
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
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
|
data/lib/pio/hello13.rb
ADDED
@@ -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
|
@@ -1,46 +1,29 @@
|
|
1
1
|
require 'bindata'
|
2
|
+
require 'pio/open_flow/transaction_id'
|
2
3
|
|
3
4
|
module Pio
|
4
|
-
# OpenFlow
|
5
|
-
|
6
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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