pio 0.15.2 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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, Reply, TTL Exceeded and destination unreachable.
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
@@ -4,8 +4,6 @@ module Pio
4
4
  class Icmp
5
5
  # User options for creating an ICMP messages.
6
6
  class Options < Pio::Options
7
- DEFAULT_ECHO_DATA = ''.freeze
8
-
9
7
  def to_hash
10
8
  {
11
9
  icmp_type: @type,
@@ -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] || DEFAULT_ECHO_DATA
36
+ @echo_data = options[:echo_data] || ''
36
37
  end
37
38
  # rubocop:enable AbcSize
38
39
  # rubocop:enable MethodLength
@@ -22,45 +22,30 @@ module Pio
22
22
  option :sequence_number
23
23
  option :echo_data
24
24
 
25
- def initialize(user_options)
25
+ # rubocop:disable MethodLength
26
+ # rubocop:disable AbcSize
27
+ def initialize(options)
28
+ validate options
26
29
  @type = TYPE
27
30
 
28
- @options = user_options.dup
29
- validate @options
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(@options[:ip_source_address]).freeze
34
+ IPv4Address.new(options[:ip_source_address]).freeze
43
35
  @ip_destination_address =
44
- IPv4Address.new(@options[:ip_destination_address]).freeze
45
- end
46
-
47
- def set_identifier_option
36
+ IPv4Address.new(options[:ip_destination_address]).freeze
48
37
  @identifier =
49
- @options[:icmp_identifier] ||
50
- @options[:identifier] ||
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
- @options[:icmp_sequence_number] ||
57
- @options[:sequence_number] ||
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
- def set_echo_data_option
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
@@ -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
- extend Type::EthernetHeader
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
- extend Pio::Type::EthernetHeader
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
- extend Pio::Type::EthernetHeader
63
- extend Type::IPv4Header
62
+ include EthernetHeader
63
+ include IPv4Header
64
64
 
65
65
  endian :big
66
66
 
@@ -0,0 +1,11 @@
1
+ module Pio
2
+ # Get payload fields.
3
+ module Payload
4
+ def binary_after(field_name)
5
+ all_fields = field_names(true)
6
+ all_fields[(all_fields.index(field_name) + 1)..-1].map do |each|
7
+ __send__(each).to_binary_s
8
+ end.join
9
+ end
10
+ end
11
+ end
@@ -15,6 +15,14 @@ module Pio
15
15
  IPv4Address.new(octets.map { |each| format('%d', each) }.join('.'))
16
16
  end
17
17
 
18
+ def >>(other)
19
+ get.to_i >> other
20
+ end
21
+
22
+ def &(other)
23
+ get.to_i & other
24
+ end
25
+
18
26
  def ==(other)
19
27
  get == other
20
28
  end
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
@@ -1,5 +1,5 @@
1
1
  # Base module.
2
2
  module Pio
3
3
  # gem version.
4
- VERSION = '0.15.2'.freeze
4
+ VERSION = '0.16.0'.freeze
5
5
  end
@@ -208,13 +208,13 @@ describe Pio::Dhcp, '.read' do
208
208
  end
209
209
  end
210
210
 
211
- describe '#udp_src_port' do
212
- subject { super().udp_src_port }
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 '#udp_dst_port' do
217
- subject { super().udp_dst_port }
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 '#udp_src_port' do
534
- subject { super().udp_src_port }
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 '#udp_dst_port' do
539
- subject { super().udp_dst_port }
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 '#udp_src_port' do
864
- subject { super().udp_src_port }
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 '#udp_dst_port' do
869
- subject { super().udp_dst_port }
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 '#udp_src_port' do
1186
- subject { super().udp_src_port }
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 '#udp_dst_port' do
1191
- subject { super().udp_dst_port }
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
- ].pack('C*')
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
- ].pack('C*')
152
+ ]
134
153
  end
135
154
  end
136
155
  end