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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f75df3f4f4091dda09de3b77789eea400df989dc
4
- data.tar.gz: 1cc6ef0de733c14e84efe1fff569ed7de0943c03
3
+ metadata.gz: 399c1ee151a9c001cf2f626ae5cb9f20554664f6
4
+ data.tar.gz: 5aa7d55d32839bb91f762b1a601c20a72b4da882
5
5
  SHA512:
6
- metadata.gz: 19d72385b493052449cd2de18df8bed3a6385fb452ffc52883f6b9f781b09c9807596869920d59fce09dc812e5f9d9434be249f27389095ce650655b69e1d7e2
7
- data.tar.gz: 6862a78e369025dc34d89086a8f10cf4fd7ca19dbdea867de0d583350cb4fd9c937f4cef1bb09117e87912d5d747ec4c5deeb8c9f5bfafccb87c155d079baaa3
6
+ metadata.gz: 0f990c0cf9aef24a57f1c04a67f8134e1b6972dca1e0573f6274c9adeebddf5768f38266ad60a7766707ff6560cc031696a6ee1b6187794ec3d56374f3b33c61
7
+ data.tar.gz: b82319079f8387ef3c52d457bb4f69aff2cb2a34e2b3dcc04e9546534cc5d1ec0f690d162c99ff2a78dada0de47e19815abd8da40274fa0259274d98bd0cef39
data/CHANGELOG.md CHANGED
@@ -3,6 +3,11 @@
3
3
  ## develop (unreleased)
4
4
 
5
5
 
6
+ ## 0.16.0 (3/2/2015)
7
+ ### New features
8
+ * [#129](https://github.com/trema/pio/pull/129): Add new class `Pio::Udp`.
9
+
10
+
6
11
  ## 0.15.2 (2/18/2015)
7
12
  ### Changes
8
13
  * [#128](https://github.com/trema/pio/pull/128): Field accessors return Ruby primitives (Fixnum, Symbol, etc.).
@@ -1,3 +1,4 @@
1
+ @announce
1
2
  Feature: Pio::Dhcp.read
2
3
  Scenario: dhcp.pcap
3
4
  Given a packet data file "dhcp.pcap"
@@ -3,3 +3,53 @@ Feature: Pio::Icmp.read
3
3
  Given a packet data file "icmp.pcap"
4
4
  When I try to parse the file with "Icmp" class
5
5
  Then it should finish successfully
6
+ And the parsed data #1 have the following field and value:
7
+ | field | value |
8
+ | class | Pio::Icmp::Request |
9
+ | destination_mac | 00:13:46:0b:22:ba |
10
+ | source_mac | 00:16:ce:6e:8b:24 |
11
+ | ether_type | 2048 |
12
+ | ip_version | 4 |
13
+ | ip_header_length | 5 |
14
+ | ip_type_of_service | 0 |
15
+ | ip_total_length | 60 |
16
+ | ip_identifier | 36507 |
17
+ | ip_flag | 0 |
18
+ | ip_fragment | 0 |
19
+ | ip_ttl | 128 |
20
+ | ip_protocol | 1 |
21
+ | ip_header_checksum | 10850 |
22
+ | ip_source_address | 192.168.0.114 |
23
+ | ip_destination_address | 192.168.0.1 |
24
+ | ip_option | |
25
+ | icmp_type | 8 |
26
+ | icmp_code | 0 |
27
+ | icmp_checksum | 18780 |
28
+ | icmp_identifier | 768 |
29
+ | icmp_sequence_number | 256 |
30
+ | echo_data | abcdefghijklmnopqrstuvwabcdefghi |
31
+ And the parsed data #2 have the following field and value:
32
+ | field | value |
33
+ | class | Pio::Icmp::Reply |
34
+ | destination_mac | 00:16:ce:6e:8b:24 |
35
+ | source_mac | 00:13:46:0b:22:ba |
36
+ | ether_type | 2048 |
37
+ | ip_version | 4 |
38
+ | ip_header_length | 5 |
39
+ | ip_type_of_service | 0 |
40
+ | ip_total_length | 60 |
41
+ | ip_identifier | 24150 |
42
+ | ip_flag | 0 |
43
+ | ip_fragment | 0 |
44
+ | ip_ttl | 127 |
45
+ | ip_protocol | 1 |
46
+ | ip_header_checksum | 23463 |
47
+ | ip_source_address | 192.168.0.1 |
48
+ | ip_destination_address | 192.168.0.114 |
49
+ | ip_option | |
50
+ | icmp_type | 0 |
51
+ | icmp_code | 0 |
52
+ | icmp_checksum | 20828 |
53
+ | icmp_identifier | 768 |
54
+ | icmp_sequence_number | 256 |
55
+ | echo_data | abcdefghijklmnopqrstuvwabcdefghi |
@@ -17,8 +17,11 @@ When(/^I try to parse the file with "(.*?)" class$/) do |parser|
17
17
  @result = parser_klass.read(IO.read(@raw))
18
18
  elsif @pcap
19
19
  File.open(@pcap) do |file|
20
+ @result = []
20
21
  pcap = Pio::Pcap::Frame.read(file)
21
- pcap.records.each { |each| parser_klass.read each.data }
22
+ pcap.records.each do |each|
23
+ @result << parser_klass.read(each.data)
24
+ end
22
25
  end
23
26
  else
24
27
  fail 'Packet data file is not specified.'
@@ -41,3 +44,14 @@ Then(/^the parsed data have the following field and value:$/) do |table|
41
44
  expect(output.to_s).to eq(each['value'])
42
45
  end
43
46
  end
47
+
48
+ # rubocop:disable LineLength
49
+ Then(/^the parsed data \#(\d+) have the following field and value:$/) do |index, table|
50
+ table.hashes.each do |each|
51
+ output = each['field'].split('.').inject(@result[index.to_i - 1]) do |memo, method|
52
+ memo.__send__(method)
53
+ end
54
+ expect(output.to_s).to eq(each['value'])
55
+ end
56
+ end
57
+ # rubocop:enable LineLength
@@ -0,0 +1,29 @@
1
+ Feature: Pio::Udp.read
2
+ Scenario: dhcp.pcap
3
+ Given a packet data file "dhcp.pcap"
4
+ When I try to parse the file with "Udp" class
5
+ Then it should finish successfully
6
+ And the parsed data #1 have the following field and value:
7
+ | field | value |
8
+ | class | Pio::Udp |
9
+ | destination_mac | ff:ff:ff:ff:ff:ff |
10
+ | source_mac | 00:0b:82:01:fc:42 |
11
+ | ether_type | 2048 |
12
+ | ip_version | 4 |
13
+ | ip_header_length | 5 |
14
+ | ip_type_of_service | 0 |
15
+ | ip_total_length | 300 |
16
+ | ip_identifier | 43062 |
17
+ | ip_flag | 0 |
18
+ | ip_fragment | 0 |
19
+ | ip_ttl | 250 |
20
+ | ip_protocol | 17 |
21
+ | ip_header_checksum | 6027 |
22
+ | ip_source_address | 0.0.0.0 |
23
+ | ip_destination_address | 255.255.255.255 |
24
+ | ip_option | |
25
+ | udp_source_port | 68 |
26
+ | udp_destination_port | 67 |
27
+ | udp_length | 280 |
28
+ | udp_checksum | 22815 |
29
+ | udp_payload.length | 272 |
data/lib/pio.rb CHANGED
@@ -13,3 +13,4 @@ require 'pio/mac'
13
13
  require 'pio/packet_in'
14
14
  require 'pio/packet_out'
15
15
  require 'pio/port_status'
16
+ require 'pio/udp'
@@ -1,5 +1,5 @@
1
1
  require 'bindata'
2
- require 'pio/type/ethernet_header'
2
+ require 'pio/ethernet_header'
3
3
  require 'pio/type/ip_address'
4
4
  require 'pio/type/mac_address'
5
5
 
@@ -7,7 +7,7 @@ module Pio
7
7
  class Arp
8
8
  # ARP parser.
9
9
  class Format < BinData::Record
10
- extend Type::EthernetHeader
10
+ include EthernetHeader
11
11
 
12
12
  endian :big
13
13
 
@@ -31,8 +31,8 @@ module Pio
31
31
  source_mac: source_mac,
32
32
  ip_destination_address: ip_destination_address,
33
33
  ip_source_address: ip_source_address,
34
- udp_src_port: BOOTPS,
35
- udp_dst_port: BOOTPC,
34
+ udp_source_port: BOOTPS,
35
+ udp_destination_port: BOOTPC,
36
36
  dhcp: dhcp_data
37
37
  }
38
38
  end
@@ -27,8 +27,8 @@ module Pio
27
27
  source_mac: source_mac,
28
28
  ip_source_address: QUAD_ZERO_IP_ADDRESS,
29
29
  ip_destination_address: BROADCAST_IP_ADDRESS,
30
- udp_src_port: BOOTPC,
31
- udp_dst_port: BOOTPS,
30
+ udp_source_port: BOOTPC,
31
+ udp_destination_port: BOOTPS,
32
32
  dhcp: dhcp_data
33
33
  }
34
34
  end
@@ -1,92 +1,37 @@
1
- require 'pio/type/ethernet_header'
2
- require 'pio/type/ipv4_header'
3
- require 'pio/type/udp_header'
4
1
  require 'pio/dhcp/dhcp_field'
5
2
  require 'pio/dhcp/field_util'
6
- require 'pio/dhcp/csum_util'
3
+ require 'pio/ipv4_header'
4
+ require 'pio/ethernet_header'
5
+ require 'pio/udp_header'
7
6
 
8
7
  module Pio
9
8
  class Dhcp
10
9
  # Dhcp frame parser.
11
10
  class Frame < BinData::Record
12
- extend Type::EthernetHeader
13
- extend Type::IPv4Header
14
- extend Type::UdpHeader
15
11
  include FieldUtil
16
- include CsumUtil
17
12
 
18
- ETHER_TYPE_IP = 0x0800
19
- IP_PROTOCOL_UDP = 17
20
- IP_HEADER_LENGTH = 20
21
- UDP_HEADER_LENGTH = 8
22
- DHCP_OPTION_FIELD_LENGTH = 60
13
+ OPTION_FIELD_LENGTH = 60
23
14
 
24
- endian :big
15
+ include EthernetHeader
16
+ include IPv4Header
17
+ include UdpHeader
25
18
 
26
- ethernet_header ether_type: ETHER_TYPE_IP
27
- ipv4_header ip_protocol: IP_PROTOCOL_UDP,
28
- ip_header_checksum: -> { ip_sum },
29
- ip_total_length: -> { ip_len }
30
- udp_header udp_length: -> { udp_len },
31
- udp_checksum: -> { udp_sum }
19
+ endian :big
20
+ ethernet_header ether_type: EthernetHeader::ETHER_TYPE_IP
21
+ ipv4_header ip_protocol: IPv4Header::IP_PROTOCOL_UDP
22
+ udp_header
32
23
  dhcp_field :dhcp
33
-
34
- def ip_sum
35
- ~((ip_csum & 0xffff) + (ip_csum >> 16)) & 0xffff
36
- end
37
-
38
- def udp_sum
39
- ~((udp_csum & 0xffff) + (udp_csum >> 16)) & 0xffff
40
- end
41
-
42
- def ip_len
43
- IP_HEADER_LENGTH + udp_len
44
- end
45
-
46
- def udp_len
47
- UDP_HEADER_LENGTH + dhcp_len
48
- end
24
+ string :padding, read_length: 0, initial_value: :ff_and_padding
49
25
 
50
26
  def to_binary
51
- to_binary_s + trail_data
27
+ to_binary_s
52
28
  end
53
29
 
54
30
  private
55
31
 
56
- def dhcp_len
57
- dhcp.num_bytes + trail_data.bytesize
58
- end
59
-
60
- def dhcp_binary
61
- dhcp.to_binary_s
62
- end
63
-
64
- def should_padding?
65
- option_length < DHCP_OPTION_FIELD_LENGTH
66
- end
67
-
68
- def option_length
69
- dhcp.optional_tlvs.num_bytes + 1
70
- end
71
-
72
- def trail_data
73
- if should_padding?
74
- end_of_pdu + padding
75
- else
76
- end_of_pdu
77
- end
78
- end
79
-
80
- def end_of_pdu
81
- [0xff].pack('C*')
82
- end
83
-
84
- def padding
85
- [0x00].pack('C*') * padding_length
86
- end
87
-
88
- def padding_length
89
- DHCP_OPTION_FIELD_LENGTH - option_length
32
+ def ff_and_padding
33
+ padding_length = OPTION_FIELD_LENGTH - dhcp.optional_tlvs.num_bytes - 1
34
+ "\xFF" + (padding_length > 0 ? "\x00" * padding_length : '')
90
35
  end
91
36
  end
92
37
  end
@@ -22,8 +22,8 @@ module Pio
22
22
  def_delegators :@frame, :ip_header_checksum
23
23
  def_delegators :@frame, :ip_source_address
24
24
  def_delegators :@frame, :ip_destination_address
25
- def_delegators :@frame, :udp_src_port
26
- def_delegators :@frame, :udp_dst_port
25
+ def_delegators :@frame, :udp_source_port
26
+ def_delegators :@frame, :udp_destination_port
27
27
  def_delegators :@frame, :udp_length
28
28
  def_delegators :@frame, :udp_checksum
29
29
  def_delegators :@frame, :dhcp
@@ -0,0 +1,40 @@
1
+ require 'pio/type/mac_address'
2
+
3
+ # Base module
4
+ module Pio
5
+ # Adds ethernet_header macro.
6
+ module EthernetHeader
7
+ ETHER_TYPE_IP = 0x0800
8
+
9
+ # rubocop:disable AbcSize
10
+ def self.included(klass)
11
+ def klass.ethernet_header(options = {})
12
+ mac_address :destination_mac
13
+ mac_address :source_mac
14
+ uint16 :ether_type_internal, initial_value: options[:ether_type] || 0
15
+ bit3 :vlan_pcp_internal, onlyif: -> { vlan? }
16
+ bit1 :vlan_cfi, onlyif: -> { vlan? }
17
+ bit12 :vlan_vid_internal, onlyif: -> { vlan? }
18
+ uint16 :ether_type_vlan,
19
+ onlyif: -> { vlan? }, initial_value: options[:ether_type] || 0
20
+ end
21
+ end
22
+ # rubocop:enable AbcSize
23
+
24
+ def ether_type
25
+ ether_type_internal
26
+ end
27
+
28
+ def vlan_vid
29
+ vlan? ? vlan_vid_internal : 0xffff
30
+ end
31
+
32
+ def vlan_pcp
33
+ vlan? ? vlan_pcp_internal : 0
34
+ end
35
+
36
+ def vlan?
37
+ ether_type == 0x8100
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,4 @@
1
1
  require 'pio/match'
2
- require 'pio/type/ethernet_header'
3
2
 
4
3
  module Pio
5
4
  # OpenFlow 1.0 exact match
@@ -1,126 +1,52 @@
1
1
  require 'bindata'
2
- require 'pio/type/ethernet_header'
3
- require 'pio/type/ipv4_header'
2
+ require 'pio/ethernet_header'
3
+ require 'pio/ipv4_header'
4
4
 
5
5
  module Pio
6
6
  class Icmp
7
7
  # Icmp parser.
8
8
  class Format < BinData::Record
9
- PADDED_PACKET_LENGTH = 50
10
- MINIMUM_PACKET_LENGTH = 36
11
- MINIMUM_FRAME_LENGTH = 64
9
+ MINIMUM_IP_PACKET_LENGTH = 50
12
10
 
13
- extend Type::EthernetHeader
14
- extend Type::IPv4Header
11
+ include EthernetHeader
12
+ include IPv4Header
15
13
 
16
14
  endian :big
17
15
 
18
- ethernet_header ether_type: 0x0800
19
- ipv4_header ip_protocol: 1,
20
- ip_header_checksum: -> { ip_sum },
21
- ip_total_length: -> { ip_packet_length }
16
+ ethernet_header ether_type: EthernetHeader::ETHER_TYPE_IP
17
+ ipv4_header ip_protocol: IPv4Header::IP_PROTOCOL_ICMP
22
18
  uint8 :icmp_type
23
19
  uint8 :icmp_code, initial_value: 0
24
- uint16 :icmp_checksum, value: -> { icmp_sum }
20
+ uint16 :icmp_checksum, value: :calculate_icmp_checksum
25
21
  uint16 :icmp_identifier
26
22
  uint16 :icmp_sequence_number
27
- string :echo_data,
28
- read_length: -> { echo_data_length }
23
+ string :echo_data, read_length: :echo_data_read_length
24
+ string :padding, read_length: 0, initial_value: :icmp_padding_length
29
25
 
30
26
  def message_type
31
27
  icmp_type
32
28
  end
33
29
 
34
- def echo_data_length
35
- ip_total_length - (ip_header_length * 4 + 8)
36
- end
37
-
38
- def ip_packet_length
39
- icmpsize = (ip_header_length * 4) + (8 + echo_data.bytesize)
40
- if icmpsize < MINIMUM_PACKET_LENGTH
41
- PADDED_PACKET_LENGTH
42
- else
43
- icmpsize
44
- end
45
- end
46
-
47
- def icmp_sum
48
- ~((icmp_csum & 0xffff) + (icmp_csum >> 16)) & 0xffff
49
- end
50
-
51
- def ip_sum
52
- ~((ip_csum & 0xffff) + (ip_csum >> 16)) & 0xffff
53
- end
54
-
55
- def to_binary
56
- if num_bytes < MINIMUM_FRAME_LENGTH
57
- to_binary_s + "\000" * (MINIMUM_FRAME_LENGTH - num_bytes)
58
- else
59
- to_binary_s
60
- end
61
- end
30
+ alias_method :to_binary, :to_binary_s
62
31
 
63
32
  private
64
33
 
65
- def icmp_csum
66
- icmp_2bytewise_slices.reduce(0) do |acc, each|
67
- acc + each
68
- end
69
- end
70
-
71
- def ip_csum
72
- ipv4_header_2bytewise_slices.reduce(0) do |acc, each|
73
- acc + each
74
- end
75
- end
76
-
77
- def icmp_2bytewise_slices
78
- [
79
- icmp_type * 0x100 + icmp_code,
80
- icmp_identifier,
81
- icmp_sequence_number,
82
- *echo_data.unpack('n*')
83
- ]
84
- end
85
-
86
- def ipv4_header_2bytewise_slices
87
- [
88
- ipversion_ipheaderlength_iptypeofservice, ip_total_length,
89
- ip_identifier, ipflag_ipfragment,
90
- ipttl_ipproto,
91
- ip_source_address_upper,
92
- ip_source_address_lower,
93
- ip_destination_address_upper,
94
- ip_destination_address_lower
95
- ]
96
- end
97
-
98
- def ip_source_address_upper
99
- ip_source_address.get.to_i >> 16
100
- end
101
-
102
- def ip_source_address_lower
103
- ip_source_address.get.to_i & 0xffff
104
- end
105
-
106
- def ip_destination_address_upper
107
- ip_destination_address.get.to_i >> 16
108
- end
109
-
110
- def ip_destination_address_lower
111
- ip_destination_address.get.to_i & 0xffff
112
- end
113
-
114
- def ipversion_ipheaderlength_iptypeofservice
115
- ip_version << 12 | ip_header_length << 8 | ip_type_of_service
34
+ def calculate_icmp_checksum
35
+ sum = [icmp_type * 0x100 + icmp_code,
36
+ icmp_identifier,
37
+ icmp_sequence_number,
38
+ *echo_data.unpack('n*')].inject(:+)
39
+ ~((sum & 0xffff) + (sum >> 16)) & 0xffff
116
40
  end
117
41
 
118
- def ipflag_ipfragment
119
- ip_flag << 13 | ip_fragment
42
+ def echo_data_read_length
43
+ ip_total_length - (ip_header_length * 4) - 8
120
44
  end
121
45
 
122
- def ipttl_ipproto
123
- ip_ttl << 8 | ip_protocol
46
+ def icmp_padding_length
47
+ length = MINIMUM_IP_PACKET_LENGTH -
48
+ (ip_header_length * 4) - 8 - echo_data.length
49
+ length > 0 ? "\x00" * length : ''
124
50
  end
125
51
  end
126
52
  end