pio 0.15.2 → 0.16.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/features/dhcp_read.feature +1 -0
- data/features/icmp_read.feature +50 -0
- data/features/step_definitions/packet_data_steps.rb +15 -1
- data/features/udp_read.feature +29 -0
- data/lib/pio.rb +1 -0
- data/lib/pio/arp/format.rb +2 -2
- data/lib/pio/dhcp/boot_reply_options.rb +2 -2
- data/lib/pio/dhcp/boot_request_options.rb +2 -2
- data/lib/pio/dhcp/frame.rb +16 -71
- data/lib/pio/dhcp/message.rb +2 -2
- data/lib/pio/ethernet_header.rb +40 -0
- data/lib/pio/exact_match.rb +0 -1
- data/lib/pio/icmp/format.rb +23 -97
- data/lib/pio/icmp/message.rb +5 -27
- data/lib/pio/icmp/options.rb +0 -2
- data/lib/pio/icmp/reply.rb +2 -1
- data/lib/pio/icmp/request.rb +15 -30
- data/lib/pio/ipv4_header.rb +64 -0
- data/lib/pio/lldp/frame.rb +2 -2
- data/lib/pio/packet_in.rb +5 -5
- data/lib/pio/payload.rb +11 -0
- data/lib/pio/type/ip_address.rb +8 -0
- data/lib/pio/udp.rb +19 -0
- data/lib/pio/udp_header.rb +56 -0
- data/lib/pio/version.rb +1 -1
- data/spec/pio/dhcp_spec.rb +16 -16
- data/spec/pio/icmp/request_spec.rb +23 -4
- metadata +86 -83
- data/lib/pio/dhcp/csum_util.rb +0 -81
- data/lib/pio/type/ethernet_header.rb +0 -43
- data/lib/pio/type/ipv4_header.rb +0 -33
- data/lib/pio/type/udp_header.rb +0 -15
data/lib/pio/icmp/message.rb
CHANGED
@@ -1,41 +1,19 @@
|
|
1
1
|
require 'pio/icmp/format'
|
2
|
-
require 'forwardable'
|
3
2
|
|
4
3
|
module Pio
|
5
4
|
class Icmp
|
6
|
-
# Base class of Request
|
5
|
+
# Base class of Icmp::Request and Icmp::Reply.
|
7
6
|
class Message
|
8
|
-
extend Forwardable
|
9
|
-
|
10
|
-
def_delegators :@format, :destination_mac
|
11
|
-
def_delegators :@format, :source_mac
|
12
|
-
def_delegators :@format, :ether_type
|
13
|
-
def_delegators :@format, :ip_version
|
14
|
-
def_delegators :@format, :ip_header_length
|
15
|
-
def_delegators :@format, :ip_type_of_service
|
16
|
-
def_delegators :@format, :ip_total_length
|
17
|
-
def_delegators :@format, :ip_identifier
|
18
|
-
def_delegators :@format, :ip_flag
|
19
|
-
def_delegators :@format, :ip_fragment
|
20
|
-
def_delegators :@format, :ip_ttl
|
21
|
-
def_delegators :@format, :ip_protocol
|
22
|
-
def_delegators :@format, :ip_header_checksum
|
23
|
-
def_delegators :@format, :ip_source_address
|
24
|
-
def_delegators :@format, :ip_destination_address
|
25
|
-
def_delegators :@format, :icmp_type
|
26
|
-
def_delegators :@format, :icmp_code
|
27
|
-
def_delegators :@format, :icmp_checksum
|
28
|
-
def_delegators :@format, :icmp_identifier
|
29
|
-
def_delegators :@format, :icmp_sequence_number
|
30
|
-
def_delegators :@format, :echo_data
|
31
|
-
def_delegators :@format, :to_binary
|
32
|
-
|
33
7
|
private_class_method :new
|
34
8
|
|
35
9
|
def initialize(user_options)
|
36
10
|
options = self.class.const_get(:Options).new(user_options)
|
37
11
|
@format = Icmp::Format.new(options.to_hash)
|
38
12
|
end
|
13
|
+
|
14
|
+
def method_missing(method, *args)
|
15
|
+
@format.__send__(method, *args)
|
16
|
+
end
|
39
17
|
end
|
40
18
|
end
|
41
19
|
end
|
data/lib/pio/icmp/options.rb
CHANGED
data/lib/pio/icmp/reply.rb
CHANGED
@@ -24,6 +24,7 @@ module Pio
|
|
24
24
|
def initialize(options)
|
25
25
|
validate options
|
26
26
|
@type = TYPE
|
27
|
+
|
27
28
|
@source_mac = Mac.new(options[:source_mac]).freeze
|
28
29
|
@destination_mac = Mac.new(options[:destination_mac]).freeze
|
29
30
|
@ip_source_address =
|
@@ -32,7 +33,7 @@ module Pio
|
|
32
33
|
IPv4Address.new(options[:ip_destination_address]).freeze
|
33
34
|
@identifier = options[:identifier]
|
34
35
|
@sequence_number = options[:sequence_number]
|
35
|
-
@echo_data = options[:echo_data] ||
|
36
|
+
@echo_data = options[:echo_data] || ''
|
36
37
|
end
|
37
38
|
# rubocop:enable AbcSize
|
38
39
|
# rubocop:enable MethodLength
|
data/lib/pio/icmp/request.rb
CHANGED
@@ -22,45 +22,30 @@ module Pio
|
|
22
22
|
option :sequence_number
|
23
23
|
option :echo_data
|
24
24
|
|
25
|
-
|
25
|
+
# rubocop:disable MethodLength
|
26
|
+
# rubocop:disable AbcSize
|
27
|
+
def initialize(options)
|
28
|
+
validate options
|
26
29
|
@type = TYPE
|
27
30
|
|
28
|
-
@
|
29
|
-
|
30
|
-
set_mac_and_ip_address_options
|
31
|
-
set_identifier_option
|
32
|
-
set_sequence_number_option
|
33
|
-
set_echo_data_option
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def set_mac_and_ip_address_options
|
39
|
-
@source_mac = Mac.new(@options[:source_mac]).freeze
|
40
|
-
@destination_mac = Mac.new(@options[:destination_mac]).freeze
|
31
|
+
@source_mac = Mac.new(options[:source_mac]).freeze
|
32
|
+
@destination_mac = Mac.new(options[:destination_mac]).freeze
|
41
33
|
@ip_source_address =
|
42
|
-
IPv4Address.new(
|
34
|
+
IPv4Address.new(options[:ip_source_address]).freeze
|
43
35
|
@ip_destination_address =
|
44
|
-
IPv4Address.new(
|
45
|
-
end
|
46
|
-
|
47
|
-
def set_identifier_option
|
36
|
+
IPv4Address.new(options[:ip_destination_address]).freeze
|
48
37
|
@identifier =
|
49
|
-
|
50
|
-
|
38
|
+
options[:icmp_identifier] ||
|
39
|
+
options[:identifier] ||
|
51
40
|
DEFAULT_IDENTIFIER
|
52
|
-
end
|
53
|
-
|
54
|
-
def set_sequence_number_option
|
55
41
|
@sequence_number =
|
56
|
-
|
57
|
-
|
42
|
+
options[:icmp_sequence_number] ||
|
43
|
+
options[:sequence_number] ||
|
58
44
|
DEFAULT_SEQUENCE_NUMBER
|
45
|
+
@echo_data = options[:echo_data] || ''
|
59
46
|
end
|
60
|
-
|
61
|
-
|
62
|
-
@echo_data = (@options[:echo_data] || DEFAULT_ECHO_DATA).freeze
|
63
|
-
end
|
47
|
+
# rubocop:enable AbcSize
|
48
|
+
# rubocop:enable MethodLength
|
64
49
|
end
|
65
50
|
end
|
66
51
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'pio/payload'
|
2
|
+
require 'pio/type/ip_address'
|
3
|
+
|
4
|
+
module Pio
|
5
|
+
# IP Version 4 Header Format
|
6
|
+
module IPv4Header
|
7
|
+
include Payload
|
8
|
+
|
9
|
+
IP_PROTOCOL_ICMP = 1
|
10
|
+
IP_PROTOCOL_UDP = 17
|
11
|
+
|
12
|
+
# rubocop:disable MethodLength
|
13
|
+
# rubocop:disable AbcSize
|
14
|
+
# This method smells of :reek:TooManyStatements
|
15
|
+
def self.included(klass)
|
16
|
+
def klass.ipv4_header(options = {})
|
17
|
+
bit4 :ip_version, value: 0x4
|
18
|
+
bit4 :ip_header_length, initial_value: 0x5
|
19
|
+
uint8 :ip_type_of_service, initial_value: 0
|
20
|
+
uint16be :ip_total_length, initial_value: :calculate_ip_length
|
21
|
+
uint16be :ip_identifier, initial_value: 0
|
22
|
+
bit3 :ip_flag, initial_value: 0
|
23
|
+
bit13 :ip_fragment, initial_value: 0
|
24
|
+
uint8 :ip_ttl, initial_value: 128
|
25
|
+
uint8 :ip_protocol, initial_value: options[:ip_protocol] || 0
|
26
|
+
uint16be :ip_header_checksum, initial_value: :calculate_ip_checksum
|
27
|
+
ip_address :ip_source_address
|
28
|
+
ip_address :ip_destination_address
|
29
|
+
string :ip_option, read_length: :ip_option_length
|
30
|
+
end
|
31
|
+
end
|
32
|
+
# rubocop:enable MethodLength
|
33
|
+
# rubocop:enable AbcSize
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def calculate_ip_length
|
38
|
+
ip_header_length * 4 + ip_payload_binary.length
|
39
|
+
end
|
40
|
+
|
41
|
+
# rubocop:disable AbcSize
|
42
|
+
def calculate_ip_checksum
|
43
|
+
sum = [ip_version << 12 | ip_header_length << 8 | ip_type_of_service,
|
44
|
+
ip_total_length,
|
45
|
+
ip_identifier,
|
46
|
+
ip_flag << 13 | ip_fragment,
|
47
|
+
ip_ttl << 8 | ip_protocol,
|
48
|
+
ip_source_address >> 16,
|
49
|
+
ip_source_address & 0xffff,
|
50
|
+
ip_destination_address >> 16,
|
51
|
+
ip_destination_address & 0xffff].reduce(:+)
|
52
|
+
~((sum & 0xffff) + (sum >> 16)) & 0xffff
|
53
|
+
end
|
54
|
+
# rubocop:enable AbcSize
|
55
|
+
|
56
|
+
def ip_payload_binary
|
57
|
+
binary_after(:ip_option)
|
58
|
+
end
|
59
|
+
|
60
|
+
def ip_option_length
|
61
|
+
20 - ip_header_length * 4
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/pio/lldp/frame.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'bindata'
|
2
2
|
|
3
|
+
require 'pio/ethernet_header'
|
3
4
|
require 'pio/lldp/chassis_id_tlv'
|
4
5
|
require 'pio/lldp/optional_tlv'
|
5
6
|
require 'pio/lldp/port_id_tlv'
|
6
7
|
require 'pio/lldp/ttl_tlv'
|
7
|
-
require 'pio/type/ethernet_header'
|
8
8
|
|
9
9
|
module Pio
|
10
10
|
class Lldp
|
11
11
|
# LLDP frame
|
12
12
|
class Frame < BinData::Record
|
13
|
-
|
13
|
+
include EthernetHeader
|
14
14
|
|
15
15
|
endian :big
|
16
16
|
|
data/lib/pio/packet_in.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'bindata'
|
2
|
+
require 'pio/ethernet_header'
|
3
|
+
require 'pio/ipv4_header'
|
2
4
|
require 'pio/open_flow'
|
3
5
|
require 'pio/parse_error'
|
4
|
-
require 'pio/type/ethernet_header'
|
5
|
-
require 'pio/type/ipv4_header'
|
6
6
|
|
7
7
|
# Base module.
|
8
8
|
module Pio
|
@@ -49,7 +49,7 @@ module Pio
|
|
49
49
|
class DataParser
|
50
50
|
# Ethernet header parser
|
51
51
|
class EthernetHeaderParser < BinData::Record
|
52
|
-
|
52
|
+
include EthernetHeader
|
53
53
|
|
54
54
|
endian :big
|
55
55
|
|
@@ -59,8 +59,8 @@ module Pio
|
|
59
59
|
|
60
60
|
# IPv4 packet parser
|
61
61
|
class IPv4Packet < BinData::Record
|
62
|
-
|
63
|
-
|
62
|
+
include EthernetHeader
|
63
|
+
include IPv4Header
|
64
64
|
|
65
65
|
endian :big
|
66
66
|
|
data/lib/pio/payload.rb
ADDED
data/lib/pio/type/ip_address.rb
CHANGED
data/lib/pio/udp.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
require 'pio/ethernet_header'
|
3
|
+
require 'pio/ipv4_header'
|
4
|
+
require 'pio/udp_header'
|
5
|
+
|
6
|
+
module Pio
|
7
|
+
# UDP packet format
|
8
|
+
class Udp < BinData::Record
|
9
|
+
include EthernetHeader
|
10
|
+
include IPv4Header
|
11
|
+
include UdpHeader
|
12
|
+
|
13
|
+
endian :big
|
14
|
+
ethernet_header ether_type: EthernetHeader::ETHER_TYPE_IP
|
15
|
+
ipv4_header ip_protocol: IPv4Header::IP_PROTOCOL_UDP
|
16
|
+
udp_header
|
17
|
+
rest :udp_payload
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'pio/payload'
|
2
|
+
|
3
|
+
module Pio
|
4
|
+
# UDP Header Format.
|
5
|
+
module UdpHeader
|
6
|
+
include Payload
|
7
|
+
|
8
|
+
# Pseudo UDP header
|
9
|
+
class PseudoUdpHeader < BinData::Record
|
10
|
+
endian :big
|
11
|
+
ip_address :ip_source_address
|
12
|
+
ip_address :ip_destination_address
|
13
|
+
uint8 :padding
|
14
|
+
uint8 :ip_protocol, value: 17
|
15
|
+
uint16 :udp_length
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.included(klass)
|
19
|
+
def klass.udp_header
|
20
|
+
uint16 :udp_source_port
|
21
|
+
uint16 :udp_destination_port
|
22
|
+
uint16 :udp_length, initial_value: :calculate_udp_length
|
23
|
+
uint16 :udp_checksum, initial_value: :calculate_udp_checksum
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def calculate_udp_length
|
30
|
+
8 + udp_payload_binary.length
|
31
|
+
end
|
32
|
+
|
33
|
+
def calculate_udp_checksum
|
34
|
+
sum = [*pseudo_udp_header.unpack('n*'),
|
35
|
+
udp_source_port,
|
36
|
+
udp_destination_port,
|
37
|
+
udp_length,
|
38
|
+
*udp_payload_multiple_of_2octets.unpack('n*')].inject(:+)
|
39
|
+
~((sum & 0xffff) + (sum >> 16)) & 0xffff
|
40
|
+
end
|
41
|
+
|
42
|
+
def udp_payload_binary
|
43
|
+
binary_after :udp_checksum
|
44
|
+
end
|
45
|
+
|
46
|
+
def pseudo_udp_header
|
47
|
+
PseudoUdpHeader.new(ip_source_address: ip_source_address,
|
48
|
+
ip_destination_address: ip_destination_address,
|
49
|
+
udp_length: udp_length).to_binary_s
|
50
|
+
end
|
51
|
+
|
52
|
+
def udp_payload_multiple_of_2octets
|
53
|
+
udp_payload_binary + '\x00' * (udp_payload_binary.length.even? ? 0 : 1)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/pio/version.rb
CHANGED
data/spec/pio/dhcp_spec.rb
CHANGED
@@ -208,13 +208,13 @@ describe Pio::Dhcp, '.read' do
|
|
208
208
|
end
|
209
209
|
end
|
210
210
|
|
211
|
-
describe '#
|
212
|
-
subject { super().
|
211
|
+
describe '#udp_source_port' do
|
212
|
+
subject { super().udp_source_port }
|
213
213
|
it { is_expected.to eq 68 }
|
214
214
|
end
|
215
215
|
|
216
|
-
describe '#
|
217
|
-
subject { super().
|
216
|
+
describe '#udp_destination_port' do
|
217
|
+
subject { super().udp_destination_port }
|
218
218
|
it { is_expected.to eq 67 }
|
219
219
|
end
|
220
220
|
|
@@ -530,13 +530,13 @@ describe Pio::Dhcp, '.read' do
|
|
530
530
|
end
|
531
531
|
end
|
532
532
|
|
533
|
-
describe '#
|
534
|
-
subject { super().
|
533
|
+
describe '#udp_source_port' do
|
534
|
+
subject { super().udp_source_port }
|
535
535
|
it { is_expected.to eq 67 }
|
536
536
|
end
|
537
537
|
|
538
|
-
describe '#
|
539
|
-
subject { super().
|
538
|
+
describe '#udp_destination_port' do
|
539
|
+
subject { super().udp_destination_port }
|
540
540
|
it { is_expected.to eq 68 }
|
541
541
|
end
|
542
542
|
|
@@ -860,13 +860,13 @@ describe Pio::Dhcp, '.read' do
|
|
860
860
|
end
|
861
861
|
end
|
862
862
|
|
863
|
-
describe '#
|
864
|
-
subject { super().
|
863
|
+
describe '#udp_source_port' do
|
864
|
+
subject { super().udp_source_port }
|
865
865
|
it { is_expected.to eq 68 }
|
866
866
|
end
|
867
867
|
|
868
|
-
describe '#
|
869
|
-
subject { super().
|
868
|
+
describe '#udp_destination_port' do
|
869
|
+
subject { super().udp_destination_port }
|
870
870
|
it { is_expected.to eq 67 }
|
871
871
|
end
|
872
872
|
|
@@ -1182,13 +1182,13 @@ describe Pio::Dhcp, '.read' do
|
|
1182
1182
|
end
|
1183
1183
|
end
|
1184
1184
|
|
1185
|
-
describe '#
|
1186
|
-
subject { super().
|
1185
|
+
describe '#udp_source_port' do
|
1186
|
+
subject { super().udp_source_port }
|
1187
1187
|
it { is_expected.to eq 67 }
|
1188
1188
|
end
|
1189
1189
|
|
1190
|
-
describe '#
|
1191
|
-
subject { super().
|
1190
|
+
describe '#udp_destination_port' do
|
1191
|
+
subject { super().udp_destination_port }
|
1192
1192
|
it { is_expected.to eq 68 }
|
1193
1193
|
end
|
1194
1194
|
|
@@ -4,6 +4,25 @@ describe Pio::ICMP::Request do
|
|
4
4
|
Then { Pio::ICMP::Request == Pio::Icmp::Request }
|
5
5
|
end
|
6
6
|
|
7
|
+
describe Pio::Icmp do
|
8
|
+
Given(:icmp_request) do
|
9
|
+
Pio::Icmp::Request.new(
|
10
|
+
destination_mac: '24:db:ac:41:e5:5b',
|
11
|
+
source_mac: '74:e5:0b:2a:18:f8',
|
12
|
+
ip_source_address: '192.168.1.101',
|
13
|
+
ip_destination_address: '8.8.8.8',
|
14
|
+
identifier: 0x123,
|
15
|
+
sequence_number: 0x321,
|
16
|
+
echo_data: 'abcdefghijklmnopqrstuvwabcdefghi'
|
17
|
+
).to_binary
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.read' do
|
21
|
+
When(:result) { Pio::Icmp.read icmp_request }
|
22
|
+
Then { result.echo_data == 'abcdefghijklmnopqrstuvwabcdefghi' }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
7
26
|
describe Pio::Icmp::Request, '.new' do
|
8
27
|
context 'with echo_data' do
|
9
28
|
Given(:icmp_request) do
|
@@ -22,7 +41,7 @@ describe Pio::Icmp::Request, '.new' do
|
|
22
41
|
When(:result) { icmp_request.to_binary }
|
23
42
|
|
24
43
|
Then do
|
25
|
-
result ==
|
44
|
+
result.unpack('C*') ==
|
26
45
|
[
|
27
46
|
# Destination MAC
|
28
47
|
0x24, 0xdb, 0xac, 0x41, 0xe5, 0x5b,
|
@@ -66,7 +85,7 @@ describe Pio::Icmp::Request, '.new' do
|
|
66
85
|
0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
|
67
86
|
0x76, 0x77, 0x61, 0x62, 0x63, 0x64, 0x65,
|
68
87
|
0x66, 0x67, 0x68, 0x69
|
69
|
-
]
|
88
|
+
]
|
70
89
|
end
|
71
90
|
end
|
72
91
|
end
|
@@ -87,7 +106,7 @@ describe Pio::Icmp::Request, '.new' do
|
|
87
106
|
When(:result) { icmp_request.to_binary }
|
88
107
|
|
89
108
|
Then do
|
90
|
-
result ==
|
109
|
+
result.unpack('C*') ==
|
91
110
|
[
|
92
111
|
# Destination MAC
|
93
112
|
0x24, 0xdb, 0xac, 0x41, 0xe5, 0x5b,
|
@@ -130,7 +149,7 @@ describe Pio::Icmp::Request, '.new' do
|
|
130
149
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
131
150
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
132
151
|
0x00, 0x00, 0x00, 0x00
|
133
|
-
]
|
152
|
+
]
|
134
153
|
end
|
135
154
|
end
|
136
155
|
end
|