pio 0.15.2 → 0.16.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 +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
|