Skip to content

Commit

Permalink
Add Icmp::Request#to_ruby and Reply#to_ruby
Browse files Browse the repository at this point in the history
  • Loading branch information
yasuhito committed Jul 11, 2016
1 parent 987ade1 commit 6897791
Show file tree
Hide file tree
Showing 16 changed files with 263 additions and 102 deletions.
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'bundler/gem_tasks'

RELISH_PROJECT = 'trema/pio'.freeze
FLAY_THRESHOLD = 1474
FLAY_THRESHOLD = 1524

task default: :travis
task test: [:spec, :cucumber]
Expand Down
2 changes: 2 additions & 0 deletions features/.nav
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
- icmp.feature
- icmp_request.feature
- icmp_reply.feature
- arp.feature
- arp_request.feature
- arp_reply.feature
Expand Down
95 changes: 16 additions & 79 deletions features/icmp.feature
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
Feature: Icmp
Scenario: create an ICMP request
Scenario: read an ICMP request
Given I use the fixture "icmp"
When I create a packet with:
"""
Pio::Icmp::Request.new(
source_mac: '00:16:9d:1d:9c:c4',
destination_mac: '00:26:82:eb:ea:d1',
source_ip_address: '192.168.83.3',
destination_ip_address: '192.168.83.254'
)
"""ruby
Pio::Icmp.read(eval(IO.read('icmp_request.rb')))
"""
Then the packet has the following fields and values:
| field | value |
| class | Pio::Icmp::Request |
| destination_mac | 00:26:82:eb:ea:d1 |
| source_mac | 00:16:9d:1d:9c:c4 |
| destination_mac | 11:22:33:44:55:66 |
| source_mac | 66:55:44:33:22:11 |
| ether_type | 2048 |
| ip_version | 4 |
| ip_header_length | 5 |
Expand All @@ -33,25 +29,19 @@ Feature: Icmp
| icmp_checksum | 63231 |
| icmp_identifier | 256 |
| icmp_sequence_number | 0 |
| echo_data | |
| echo_data.length | 22 |

Scenario: create an ICMP reply
Scenario: read an ICMP reply
Given I use the fixture "icmp"
When I create a packet with:
"""
Pio::Icmp::Reply.new(
source_mac: '00:26:82:eb:ea:d1',
destination_mac: '00:16:9d:1d:9c:c4',
source_ip_address: '192.168.83.254',
destination_ip_address: '192.168.83.3',
identifier: 256,
sequence_number: 0
)
"""ruby
Pio::Icmp.read(eval(IO.read('icmp_reply.rb')))
"""
Then the packet has the following fields and values:
| field | value |
| class | Pio::Icmp::Reply |
| destination_mac | 00:16:9d:1d:9c:c4 |
| source_mac | 00:26:82:eb:ea:d1 |
| destination_mac | 11:22:33:44:55:66 |
| source_mac | 66:55:44:33:22:11 |
| ether_type | 2048 |
| ip_version | 4 |
| ip_header_length | 5 |
Expand All @@ -63,65 +53,12 @@ Feature: Icmp
| ip_ttl | 128 |
| ip_protocol | 1 |
| ip_header_checksum | 4729 |
| source_ip_address | 192.168.83.254 |
| destination_ip_address | 192.168.83.3 |
| source_ip_address | 192.168.83.3 |
| destination_ip_address | 192.168.83.254 |
| ip_option | |
| icmp_type | 0 |
| icmp_code | 0 |
| icmp_checksum | 65279 |
| icmp_identifier | 256 |
| icmp_sequence_number | 0 |
| echo_data | |

Scenario: parse icmp.pcap
When I parse a file named "icmp.pcap" with "Pio::Icmp" class
Then the message #1 have the following fields and values:
| field | value |
| class | Pio::Icmp::Request |
| destination_mac | 00:13:46:0b:22:ba |
| source_mac | 00:16:ce:6e:8b:24 |
| ether_type | 2048 |
| ip_version | 4 |
| ip_header_length | 5 |
| ip_type_of_service | 0 |
| ip_total_length | 60 |
| ip_identifier | 36507 |
| ip_flag | 0 |
| ip_fragment | 0 |
| ip_ttl | 128 |
| ip_protocol | 1 |
| ip_header_checksum | 10850 |
| source_ip_address | 192.168.0.114 |
| destination_ip_address | 192.168.0.1 |
| ip_option | |
| icmp_type | 8 |
| icmp_code | 0 |
| icmp_checksum | 18780 |
| icmp_identifier | 768 |
| icmp_sequence_number | 256 |
| echo_data | abcdefghijklmnopqrstuvwabcdefghi |
Then the message #2 have the following fields and values:
| field | value |
| class | Pio::Icmp::Reply |
| destination_mac | 00:16:ce:6e:8b:24 |
| source_mac | 00:13:46:0b:22:ba |
| ether_type | 2048 |
| ip_version | 4 |
| ip_header_length | 5 |
| ip_type_of_service | 0 |
| ip_total_length | 60 |
| ip_identifier | 24150 |
| ip_flag | 0 |
| ip_fragment | 0 |
| ip_ttl | 127 |
| ip_protocol | 1 |
| ip_header_checksum | 23463 |
| source_ip_address | 192.168.0.1 |
| destination_ip_address | 192.168.0.114 |
| ip_option | |
| icmp_type | 0 |
| icmp_code | 0 |
| icmp_checksum | 20828 |
| icmp_identifier | 768 |
| icmp_sequence_number | 256 |
| echo_data | abcdefghijklmnopqrstuvwabcdefghi |
| echo_data.length | 22 |
65 changes: 65 additions & 0 deletions features/icmp_reply.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
Feature: Icmp::Reply
Scenario: create an ICMP reply
When I create a packet with:
"""
Pio::Icmp::Reply.new(
source_mac: '00:26:82:eb:ea:d1',
destination_mac: '00:16:9d:1d:9c:c4',
source_ip_address: '192.168.83.254',
destination_ip_address: '192.168.83.3',
identifier: 256,
sequence_number: 0
)
"""
Then the packet has the following fields and values:
| field | value |
| class | Pio::Icmp::Reply |
| destination_mac | 00:16:9d:1d:9c:c4 |
| source_mac | 00:26:82:eb:ea:d1 |
| ether_type | 2048 |
| ip_version | 4 |
| ip_header_length | 5 |
| ip_type_of_service | 0 |
| ip_total_length | 50 |
| ip_identifier | 0 |
| ip_flag | 0 |
| ip_fragment | 0 |
| ip_ttl | 128 |
| ip_protocol | 1 |
| ip_header_checksum | 4729 |
| source_ip_address | 192.168.83.254 |
| destination_ip_address | 192.168.83.3 |
| ip_option | |
| icmp_type | 0 |
| icmp_code | 0 |
| icmp_checksum | 65279 |
| icmp_identifier | 256 |
| icmp_sequence_number | 0 |
| echo_data | |

Scenario: Icmp::Reply instance inspection
When I create a packet with:
"""
Pio::Icmp::Reply.new(
destination_mac: '00:26:82:eb:ea:d1',
source_mac: '00:16:9d:1d:9c:c4',
source_ip_address: '1.2.3.4',
destination_ip_address: '4.3.2.1',
identifier: 256,
sequence_number: 0
).inspect
"""
Then the result of eval should be:
"""
#<Icmp::Reply destination_mac: "00:26:82:eb:ea:d1", source_mac: "00:16:9d:1d:9c:c4", ether_type: 2048, ip_version: 4, ip_header_length: 5, ip_type_of_service: 0, ip_total_length: 50, ip_identifier: 0, ip_flag: 0, ip_fragment: 0, ip_ttl: 128, ip_protocol: 1, ip_header_checksum: 12482, source_ip_address: "1.2.3.4", destination_ip_address: "4.3.2.1", ip_option: "", icmp_type: 0, icmp_code: 0, icmp_checksum: 65279, icmp_identifier: 256, icmp_sequence_number: 0, echo_data: "">
"""

Scenario: Icmp::Reply class inspection
When I eval the following Ruby code:
"""ruby
Pio::Icmp::Reply.inspect
"""
Then the result of eval should be:
"""
Icmp::Reply(destination_mac: mac_address, source_mac: mac_address, ether_type: uint16, vlan_pcp: bit3, vlan_cfi: bit1, vlan_vid: bit12, ip_version: bit4, ip_header_length: bit4, ip_type_of_service: uint8, ip_total_length: uint16, ip_identifier: uint16, ip_flag: bit3, ip_fragment: bit13, ip_ttl: uint8, ip_protocol: uint8, ip_header_checksum: uint16, source_ip_address: ip_address, destination_ip_address: ip_address, ip_option: string, icmp_type: uint8, icmp_code: uint8, icmp_checksum: uint16, icmp_identifier: uint16, icmp_sequence_number: uint16, echo_data: string)
"""
61 changes: 61 additions & 0 deletions features/icmp_request.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
Feature: Icmp::Request
Scenario: create an ICMP request
When I create a packet with:
"""
Pio::Icmp::Request.new(
source_mac: '00:16:9d:1d:9c:c4',
destination_mac: '00:26:82:eb:ea:d1',
source_ip_address: '192.168.83.3',
destination_ip_address: '192.168.83.254'
)
"""
Then the packet has the following fields and values:
| field | value |
| class | Pio::Icmp::Request |
| destination_mac | 00:26:82:eb:ea:d1 |
| source_mac | 00:16:9d:1d:9c:c4 |
| ether_type | 2048 |
| ip_version | 4 |
| ip_header_length | 5 |
| ip_type_of_service | 0 |
| ip_total_length | 50 |
| ip_identifier | 0 |
| ip_flag | 0 |
| ip_fragment | 0 |
| ip_ttl | 128 |
| ip_protocol | 1 |
| ip_header_checksum | 4729 |
| source_ip_address | 192.168.83.3 |
| destination_ip_address | 192.168.83.254 |
| ip_option | |
| icmp_type | 8 |
| icmp_code | 0 |
| icmp_checksum | 63231 |
| icmp_identifier | 256 |
| icmp_sequence_number | 0 |
| echo_data | |

Scenario: Icmp::Request instance inspection
When I create a packet with:
"""
Pio::Icmp::Request.new(
destination_mac: '00:26:82:eb:ea:d1',
source_mac: '00:16:9d:1d:9c:c4',
source_ip_address: '1.2.3.4',
destination_ip_address: '4.3.2.1'
).inspect
"""
Then the result of eval should be:
"""
#<Icmp::Request destination_mac: "00:26:82:eb:ea:d1", source_mac: "00:16:9d:1d:9c:c4", ether_type: 2048, ip_version: 4, ip_header_length: 5, ip_type_of_service: 0, ip_total_length: 50, ip_identifier: 0, ip_flag: 0, ip_fragment: 0, ip_ttl: 128, ip_protocol: 1, ip_header_checksum: 12482, source_ip_address: "1.2.3.4", destination_ip_address: "4.3.2.1", ip_option: "", icmp_type: 8, icmp_code: 0, icmp_checksum: 63231, icmp_identifier: 256, icmp_sequence_number: 0, echo_data: "">
"""

Scenario: Icmp::Request class inspection
When I eval the following Ruby code:
"""ruby
Pio::Icmp::Request.inspect
"""
Then the result of eval should be:
"""
Icmp::Request(destination_mac: mac_address, source_mac: mac_address, ether_type: uint16, vlan_pcp: bit3, vlan_cfi: bit1, vlan_vid: bit12, ip_version: bit4, ip_header_length: bit4, ip_type_of_service: uint8, ip_total_length: uint16, ip_identifier: uint16, ip_flag: bit3, ip_fragment: bit13, ip_ttl: uint8, ip_protocol: uint8, ip_header_checksum: uint16, source_ip_address: ip_address, destination_ip_address: ip_address, ip_option: string, icmp_type: uint8, icmp_code: uint8, icmp_checksum: uint16, icmp_identifier: uint16, icmp_sequence_number: uint16, echo_data: string)
"""
Binary file removed fixtures/icmp.pcap
Binary file not shown.
23 changes: 23 additions & 0 deletions fixtures/icmp/icmp_reply.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, # destination_mac
0x66, 0x55, 0x44, 0x33, 0x22, 0x11, # source_mac
0x08, 0x00, # ether_type
0b0100_0101, # ip_version, ip_header_length
0x00, # ip_type_of_service
0x00, 0x32, # ip_total_length
0x00, 0x00, # ip_identifier
0b000_0000000000000, # ip_flag, ip_fragment
0x80, # ip_ttl
0x01, # ip_protocol
0x12, 0x79, # ip_header_checksum
0xc0, 0xa8, 0x53, 0x03, # source_ip_address
0xc0, 0xa8, 0x53, 0xfe, # destination_ip_address
0x00, # icmp_type
0x00, # icmp_code
0xfe, 0xff, # icmp_checksum
0x01, 0x00, # icmp_identifier
0x00, 0x00, # icmp_sequence_number
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, # padding
].pack('C20nC42')
23 changes: 23 additions & 0 deletions fixtures/icmp/icmp_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, # destination_mac
0x66, 0x55, 0x44, 0x33, 0x22, 0x11, # source_mac
0x08, 0x00, # ether_type
0b0100_0101, # ip_version, ip_header_length
0x00, # ip_type_of_service
0x00, 0x32, # ip_total_length
0x00, 0x00, # ip_identifier
0b000_0000000000000, # ip_flag, ip_fragment
0x80, # ip_ttl
0x01, # ip_protocol
0x12, 0x79, # ip_header_checksum
0xc0, 0xa8, 0x53, 0x03, # source_ip_address
0xc0, 0xa8, 0x53, 0xfe, # destination_ip_address
0x08, # icmp_type
0x00, # icmp_code
0xf6, 0xff, # icmp_checksum
0x01, 0x00, # icmp_identifier
0x00, 0x00, # icmp_sequence_number
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, # padding
].pack('C20nC42')
6 changes: 6 additions & 0 deletions lib/pio/ethernet_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
module Pio
# Adds ethernet_header macro.
module Ethernet
MINIMUM_FRAME_SIZE = 64

# EtherType constants
module Type
ARP = 0x0806
Expand Down Expand Up @@ -31,6 +33,10 @@ def klass.ethernet_header(options = nil)
end
# rubocop:enable MethodLength

def ethernet_header_length
vlan? ? 18 : 14
end

private

def vlan?
Expand Down
12 changes: 9 additions & 3 deletions lib/pio/icmp.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
require 'pio/icmp/format'
require 'pio/icmp/reply'
require 'pio/icmp/request'
require 'pio/message_type_selector'

# Packet parser and generator library.
module Pio
# Icmp parser and generator.
class Icmp
extend MessageTypeSelector
message_type Request::TYPE => Request, Reply::TYPE => Reply
def self.read(raw_data)
format = Format.read(raw_data)
message = { Request::TYPE => Request,
Reply::TYPE => Reply }.fetch(format.icmp_type).allocate
message.instance_variable_set :@format, format
message
rescue
raise Pio::ParseError, $ERROR_INFO.message
end
end
ICMP = Icmp
end
Loading

0 comments on commit 6897791

Please sign in to comment.