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 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