pio 0.6.0 → 0.7.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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +56 -22
  4. data/examples/dhcp_new.rb +22 -18
  5. data/examples/features_new.rb +14 -0
  6. data/examples/features_read.rb +4 -0
  7. data/features/arp_read.feature +4 -4
  8. data/features/dhcp_read.feature +2 -2
  9. data/features/echo_read.feature +5 -0
  10. data/features/features_read.feature +10 -0
  11. data/features/hello_read.feature +5 -0
  12. data/features/icmp_read.feature +2 -2
  13. data/features/lldp_read.feature +4 -4
  14. data/features/{pcap → packet_data}/arp-storm.pcap +0 -0
  15. data/features/{pcap → packet_data}/arp.pcap +0 -0
  16. data/features/{pcap → packet_data}/dhcp.pcap +0 -0
  17. data/features/packet_data/echo.raw +0 -0
  18. data/features/packet_data/features_reply.raw +0 -0
  19. data/features/packet_data/features_request.raw +0 -0
  20. data/features/packet_data/hello.raw +0 -0
  21. data/features/{pcap → packet_data}/icmp.pcap +0 -0
  22. data/features/{pcap → packet_data}/lldp.detailed.pcap +0 -0
  23. data/features/{pcap → packet_data}/lldp.minimal.pcap +0 -0
  24. data/features/step_definitions/packet_data_steps.rb +32 -0
  25. data/features/step_definitions/pending_steps.rb +5 -0
  26. data/lib/pio.rb +1 -0
  27. data/lib/pio/arp.rb +1 -1
  28. data/lib/pio/arp/{frame.rb → format.rb} +2 -2
  29. data/lib/pio/arp/message.rb +2 -2
  30. data/lib/pio/dhcp/dhcp_field.rb +3 -3
  31. data/lib/pio/dhcp/frame.rb +6 -6
  32. data/lib/pio/dhcp/optional_tlv.rb +3 -3
  33. data/lib/pio/echo.rb +4 -11
  34. data/lib/pio/echo/format.rb +5 -5
  35. data/lib/pio/echo/message.rb +13 -4
  36. data/lib/pio/echo/reply.rb +29 -0
  37. data/lib/pio/echo/request.rb +29 -0
  38. data/lib/pio/features.rb +18 -0
  39. data/lib/pio/features/format.rb +18 -0
  40. data/lib/pio/features/message.rb +14 -0
  41. data/lib/pio/features/reply.rb +73 -0
  42. data/lib/pio/features/request.rb +63 -0
  43. data/lib/pio/hello.rb +40 -9
  44. data/lib/pio/icmp.rb +1 -1
  45. data/lib/pio/icmp/{frame.rb → format.rb} +2 -2
  46. data/lib/pio/icmp/message.rb +2 -2
  47. data/lib/pio/icmp/request.rb +30 -14
  48. data/lib/pio/message_type_selector.rb +4 -7
  49. data/lib/pio/type/ethernet_header.rb +0 -2
  50. data/lib/pio/type/ipv4_header.rb +0 -1
  51. data/lib/pio/type/open_flow.rb +34 -0
  52. data/lib/pio/type/udp_header.rb +0 -1
  53. data/lib/pio/version.rb +1 -1
  54. data/pio.gemspec +2 -2
  55. data/spec/pio/dhcp/ack_spec.rb +1 -1
  56. data/spec/pio/dhcp_spec.rb +2 -2
  57. data/spec/pio/echo/reply_spec.rb +69 -4
  58. data/spec/pio/echo/request_spec.rb +48 -10
  59. data/spec/pio/echo_spec.rb +8 -0
  60. data/spec/pio/features/reply_spec.rb +30 -0
  61. data/spec/pio/features/request_spec.rb +70 -0
  62. data/spec/pio/features_spec.rb +78 -0
  63. data/spec/pio/hello_spec.rb +35 -6
  64. data/spec/spec_helper.rb +3 -0
  65. metadata +70 -40
  66. data/features/step_definitions/pcap_steps.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 24250e762b41db6e78d190ece32de1a57bbba89c
4
- data.tar.gz: df8706edd1b12d95750bb240b21ea93021b09d1d
3
+ metadata.gz: 9f5e9be92c12989ecc7bfe94c76a5db47bb5304e
4
+ data.tar.gz: dc6414ddb0c09cc48489295b5d4c746cfda522e9
5
5
  SHA512:
6
- metadata.gz: 008fe84cace5c6050f941180a8122dbcfbcdb475936efef993002dc33b6f939149d000c7d5955c9396defaef49a6e6a85e6d6c5f1ca460fa50b772e8ed5ae182
7
- data.tar.gz: af12f5c7bca4549a0c2ce34531def8ece2d530b88ef091763f84c782d0fbaa5c8081936982c074325391e4bed7d43b22b7b014362e6a9f72ac76ae04e2b95f2d
6
+ metadata.gz: a5b3da4c83662a1d20002dde48e926c71becb41f22c8efc4151a7966dcc75566333c17cb9d6f14805741d521914bc98caf97c940c7add564a7fb676aec876ed9
7
+ data.tar.gz: 081cc576cb1481899bd8733462e914f7644ccd1e51138bece41ff93851f432c63c6c56f4872519d297366b9e8c1524d237d40eaeff06f3cd8ab27a92b4df1bac
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0 (4/22/2014)
4
+
5
+ ### New features
6
+ * Added new class `Pio::Features`, `Pio::Features::Request` and `Pio::Features::Reply`
7
+
8
+ ### Changes
9
+ * Renamed `rake PCAP='foo.pcap' dump_pcap` => `rake PACKET_FILE='foo.pcap' dump`
10
+
11
+
3
12
  ## 0.6.0 (4/15/2014)
4
13
 
5
14
  ### New features
data/README.md CHANGED
@@ -18,7 +18,8 @@ supports the following packet formats:
18
18
  - DHCP
19
19
  - OpenFlow 1.0
20
20
  - Hello
21
- - Echo Request and Reply
21
+ - Echo
22
+ - Features
22
23
  - (…currently there are just a few formats supported but I'm sure this list will grow)
23
24
 
24
25
  ## Features Overview
@@ -132,33 +133,37 @@ Also you can use `Pio::Dhcp::Discover#new`,
132
133
 
133
134
  require 'pio'
134
135
 
135
- discover = Pio::Dhcp::Discover.new(source_mac: '24:db:ac:41:e5:5b')
136
+ dhcp_client_mac_address = '24:db:ac:41:e5:5b'
137
+
138
+ dhcp_server_options =
139
+ {
140
+ source_mac: '00:26:82:eb:ea:d1',
141
+ destination_mac: '24:db:ac:41:e5:5b',
142
+ ip_source_address: '192.168.0.100',
143
+ ip_destination_address: '192.168.0.1'
144
+ }
145
+
146
+ # Client side
147
+ discover = Pio::Dhcp::Discover.new(source_mac: dhcp_client_mac_address)
136
148
  discover.to_binary # => DHCP Discover frame in binary format
137
149
 
138
- offer = Pio::Dhcp::Offer.new(
139
- source_mac: '00:26:82:eb:ea:d1',
140
- destination_mac: '24:db:ac:41:e5:5b',
141
- ip_source_address: '192.168.0.100',
142
- ip_destination_address: '192.168.0.1',
143
- transaction_id: discover.transaction_id
144
- )
150
+ # Server side
151
+ offer = Pio::Dhcp::Offer.new(dhcp_server_options
152
+ .merge(transaction_id: discover.transaction_id))
145
153
  offer.to_binary # => DHCP Offer frame in binary format
146
154
 
155
+ # Client side
147
156
  request = Pio::Dhcp::Request.new(
148
- source_mac: '24:db:ac:41:e5:5b',
149
- server_identifier: '192.168.0.100',
150
- requested_ip_address: '192.168.0.1',
157
+ source_mac: dhcp_client_mac_address,
158
+ server_identifier: dhcp_server_options[:ip_source_address],
159
+ requested_ip_address: dhcp_server_options[:ip_destination_address],
151
160
  transaction_id: offer.transaction_id
152
161
  )
153
162
  request.to_binary # => DHCP Request frame in binary format
154
163
 
155
- ack = Pio::Dhcp::Ack.new(
156
- source_mac: '00:26:82:eb:ea:d1',
157
- destination_mac: '24:db:ac:41:e5:5b',
158
- ip_source_address: '192.168.0.100',
159
- ip_destination_address: '192.168.0.1',
160
- transaction_id: request.transaction_id
161
- )
164
+ # Server side
165
+ ack = Pio::Dhcp::Ack.new(dhcp_server_options
166
+ .merge(transaction_id: request.transaction_id))
162
167
  ack.to_binary # => DHCP Ack frame in binary format
163
168
 
164
169
  ### Hello
@@ -179,10 +184,10 @@ below:
179
184
  hello = Pio::Hello.new(transaction_id: 123)
180
185
  hello.to_binary # => HELLO message in binary format.
181
186
 
182
- ## Echo
187
+ ### Echo
183
188
 
184
- To parse an OpenFlow 1.0 ECHO message, use the API `Pio::Echo.read`
185
- and you can access each field of the parsed ECHO message.
189
+ To parse an OpenFlow 1.0 Echo message, use the API `Pio::Echo.read`
190
+ and you can access each field of the parsed Echo message.
186
191
 
187
192
  require 'pio'
188
193
 
@@ -202,6 +207,35 @@ generate an Echo Request/Reply message like below:
202
207
  reply = Pio::Echo::Reply.new(xid: request.xid)
203
208
  reply.to_binary # => ECHO Reply message in binary format.
204
209
 
210
+ ### Features
211
+
212
+ To parse an OpenFlow 1.0 Features message, use the API
213
+ `Pio::Features.read` and you can access each field of the parsed
214
+ Features message.
215
+
216
+ require 'pio'
217
+
218
+ features = Pio::Features.read(binary_data)
219
+ features.xid # => 123
220
+
221
+ Also you can use `Pio::Features::Request#new` or `Pio::Features::Reply#new` to
222
+ generate an Features Request/Reply message like below:
223
+
224
+ require 'pio'
225
+
226
+ request = Pio::Features::Request.new
227
+ request.to_binary # => Features Request message in binary format.
228
+
229
+ # The Features xid (transaction_id)
230
+ # should be same as that of the request.
231
+ reply = Pio::Features::Reply.new(xid: request.xid,
232
+ dpid: 0x123,
233
+ n_buffers: 0x100,
234
+ n_tables: 0xfe,
235
+ capabilities: 0xc7,
236
+ actions: 0xfff)
237
+ reply.to_binary # => Features Reply message in binary format.
238
+
205
239
  ## Installation
206
240
 
207
241
  The simplest way to install Pio is to use [Bundler](http://gembundler.com/).
@@ -1,30 +1,34 @@
1
1
  require 'pio'
2
2
 
3
- discover = Pio::Dhcp::Discover.new(source_mac: '24:db:ac:41:e5:5b')
3
+ dhcp_client_mac_address = '24:db:ac:41:e5:5b'
4
+
5
+ dhcp_server_options =
6
+ {
7
+ source_mac: '00:26:82:eb:ea:d1',
8
+ destination_mac: '24:db:ac:41:e5:5b',
9
+ ip_source_address: '192.168.0.100',
10
+ ip_destination_address: '192.168.0.1'
11
+ }
12
+
13
+ # Client side
14
+ discover = Pio::Dhcp::Discover.new(source_mac: dhcp_client_mac_address)
4
15
  discover.to_binary # => DHCP Discover frame in binary format
5
16
 
6
- offer = Pio::Dhcp::Offer.new(
7
- source_mac: '00:26:82:eb:ea:d1',
8
- destination_mac: '24:db:ac:41:e5:5b',
9
- ip_source_address: '192.168.0.100',
10
- ip_destination_address: '192.168.0.1',
11
- transaction_id: discover.transaction_id
12
- )
17
+ # Server side
18
+ offer = Pio::Dhcp::Offer.new(dhcp_server_options
19
+ .merge(transaction_id: discover.transaction_id))
13
20
  offer.to_binary # => DHCP Offer frame in binary format
14
21
 
22
+ # Client side
15
23
  request = Pio::Dhcp::Request.new(
16
- source_mac: '24:db:ac:41:e5:5b',
17
- server_identifier: '192.168.0.100',
18
- requested_ip_address: '192.168.0.1',
24
+ source_mac: dhcp_client_mac_address,
25
+ server_identifier: dhcp_server_options[:ip_source_address],
26
+ requested_ip_address: dhcp_server_options[:ip_destination_address],
19
27
  transaction_id: offer.transaction_id
20
28
  )
21
29
  request.to_binary # => DHCP Request frame in binary format
22
30
 
23
- ack = Pio::Dhcp::Ack.new(
24
- source_mac: '00:26:82:eb:ea:d1',
25
- destination_mac: '24:db:ac:41:e5:5b',
26
- ip_source_address: '192.168.0.100',
27
- ip_destination_address: '192.168.0.1',
28
- transaction_id: request.transaction_id
29
- )
31
+ # Server side
32
+ ack = Pio::Dhcp::Ack.new(dhcp_server_options
33
+ .merge(transaction_id: request.transaction_id))
30
34
  ack.to_binary # => DHCP Ack frame in binary format
@@ -0,0 +1,14 @@
1
+ require 'pio'
2
+
3
+ request = Pio::Features::Request.new
4
+ request.to_binary # => Features Request message in binary format.
5
+
6
+ # The Features xid (transaction_id)
7
+ # should be same as that of the request.
8
+ reply = Pio::Features::Reply.new(xid: request.xid,
9
+ dpid: 0x123,
10
+ n_buffers: 0x100,
11
+ n_tables: 0xfe,
12
+ capabilities: 0xc7,
13
+ actions: 0xfff)
14
+ reply.to_binary # => Features Reply message in binary format.
@@ -0,0 +1,4 @@
1
+ require 'pio'
2
+
3
+ features = Pio::Features.read(binary_data)
4
+ features.xid # => 123
@@ -1,10 +1,10 @@
1
1
  Feature: Pio::Arp.read
2
2
  Scenario: arp.pcap
3
- Given a pcap file "arp.pcap"
4
- When I try to parse the pcap file with "Arp" class
3
+ Given a packet data file "arp.pcap"
4
+ When I try to parse the file with "Arp" class
5
5
  Then it should finish successfully
6
6
 
7
7
  Scenario: arp-storm.pcap
8
- Given a pcap file "arp-storm.pcap"
9
- When I try to parse the pcap file with "Arp" class
8
+ Given a packet data file "arp-storm.pcap"
9
+ When I try to parse the file with "Arp" class
10
10
  Then it should finish successfully
@@ -1,5 +1,5 @@
1
1
  Feature: Pio::Dhcp.read
2
2
  Scenario: dhcp.pcap
3
- Given a pcap file "dhcp.pcap"
4
- When I try to parse the pcap file with "Dhcp" class
3
+ Given a packet data file "dhcp.pcap"
4
+ When I try to parse the file with "Dhcp" class
5
5
  Then it should finish successfully
@@ -0,0 +1,5 @@
1
+ Feature: Pio::Echo.read
2
+ Scenario: echo.raw
3
+ Given a packet data file "echo.raw"
4
+ When I try to parse the file with "Echo" class
5
+ Then it should finish successfully
@@ -0,0 +1,10 @@
1
+ Feature: Pio::Features.read
2
+ Scenario: features_request.raw
3
+ Given a packet data file "features_request.raw"
4
+ When I try to parse the file with "Features" class
5
+ Then it should finish successfully
6
+
7
+ Scenario: features_reply.raw
8
+ Given a packet data file "features_reply.raw"
9
+ When I try to parse the file with "Features" class
10
+ Then it should finish successfully
@@ -0,0 +1,5 @@
1
+ Feature: Pio::Hello.read
2
+ Scenario: hello.raw
3
+ Given a packet data file "hello.raw"
4
+ When I try to parse the file with "Hello" class
5
+ Then it should finish successfully
@@ -1,5 +1,5 @@
1
1
  Feature: Pio::Icmp.read
2
2
  Scenario: icmp.pcap
3
- Given a pcap file "icmp.pcap"
4
- When I try to parse the pcap file with "Icmp" class
3
+ Given a packet data file "icmp.pcap"
4
+ When I try to parse the file with "Icmp" class
5
5
  Then it should finish successfully
@@ -1,10 +1,10 @@
1
1
  Feature: Pio::Lldp.read
2
2
  Scenario: lldp.minimal.pcap
3
- Given a pcap file "lldp.minimal.pcap"
4
- When I try to parse the pcap file with "Lldp" class
3
+ Given a packet data file "lldp.minimal.pcap"
4
+ When I try to parse the file with "Lldp" class
5
5
  Then it should finish successfully
6
6
 
7
7
  Scenario: lldp.detailed.pcap
8
- Given a pcap file "lldp.detailed.pcap"
9
- When I try to parse the pcap file with "Lldp" class
8
+ Given a packet data file "lldp.detailed.pcap"
9
+ When I try to parse the file with "Lldp" class
10
10
  Then it should finish successfully
File without changes
File without changes
File without changes
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ Given(/^a packet data file "(.*?)"$/) do |name|
4
+ path = File.expand_path(File.join(File.dirname(__FILE__),
5
+ '..', 'packet_data', name))
6
+ case File.extname(name)
7
+ when '.raw'
8
+ @raw = path
9
+ when '.pcap'
10
+ @pcap = path
11
+ else
12
+ fail "Unsupported file extension: #{name}"
13
+ end
14
+ end
15
+
16
+ When(/^I try to parse the file with "(.*?)" class$/) do |parser|
17
+ parser_klass = Pio.const_get(parser)
18
+ if @raw
19
+ parser_klass.read IO.read(@raw)
20
+ elsif @pcap
21
+ File.open(@pcap) do |file|
22
+ pcap = Pio::Pcap::Frame.read(file)
23
+ pcap.records.each { |each| parser_klass.read each.data }
24
+ end
25
+ else
26
+ fail 'Packet data file is not specified.'
27
+ end
28
+ end
29
+
30
+ Then(/^it should finish successfully$/) do
31
+ # Noop.
32
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ Given(/^PENDING/) do
4
+ pending
5
+ end
data/lib/pio.rb CHANGED
@@ -5,6 +5,7 @@ require 'pio/parse_error'
5
5
  require 'pio/arp'
6
6
  require 'pio/dhcp'
7
7
  require 'pio/echo'
8
+ require 'pio/features'
8
9
  require 'pio/hello'
9
10
  require 'pio/icmp'
10
11
  require 'pio/lldp'
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'pio/arp/frame'
3
+ require 'pio/arp/format'
4
4
  require 'pio/arp/request'
5
5
  require 'pio/arp/reply'
6
6
  require 'pio/message_type_selector'
@@ -7,8 +7,8 @@ require 'pio/type/mac_address'
7
7
 
8
8
  module Pio
9
9
  class Arp
10
- # ARP frame parser.
11
- class Frame < BinData::Record
10
+ # ARP parser.
11
+ class Format < BinData::Record
12
12
  extend Type::EthernetHeader
13
13
 
14
14
  endian :big
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'forwardable'
4
- require 'pio/arp/frame'
4
+ require 'pio/arp/format'
5
5
 
6
6
  module Pio
7
7
  class Arp
@@ -33,7 +33,7 @@ module Pio
33
33
 
34
34
  def initialize(user_options)
35
35
  options = self.class.const_get(:Options).new(user_options.dup.freeze)
36
- @frame = Arp::Frame.new(options.to_hash)
36
+ @frame = Arp::Format.new(options.to_hash)
37
37
  end
38
38
  end
39
39
  end
@@ -40,9 +40,9 @@ module Pio
40
40
  length: 128
41
41
  uint32be :magic_cookie,
42
42
  value: MAGIC_COOKIE
43
- array :optional_tlvs,
44
- type: :optional_tlv,
45
- read_until: -> { element.end_of_dhcptlv? }
43
+ array :optional_tlvs,
44
+ type: :optional_tlv,
45
+ read_until: -> { element.end_of_dhcptlv? }
46
46
  end
47
47
  end
48
48
  end
@@ -26,12 +26,12 @@ module Pio
26
26
  endian :big
27
27
 
28
28
  ethernet_header ether_type: ETHER_TYPE_IP
29
- ipv4_header ip_protocol: IP_PROTOCOL_UDP,
30
- ip_header_checksum: -> { ip_sum },
31
- ip_total_length: -> { ip_len }
32
- udp_header udp_length: -> { udp_len },
33
- udp_checksum: -> { udp_sum }
34
- dhcp_field :dhcp
29
+ ipv4_header ip_protocol: IP_PROTOCOL_UDP,
30
+ ip_header_checksum: -> { ip_sum },
31
+ ip_total_length: -> { ip_len }
32
+ udp_header udp_length: -> { udp_len },
33
+ udp_checksum: -> { udp_sum }
34
+ dhcp_field :dhcp
35
35
 
36
36
  def ip_sum
37
37
  ~((ip_csum & 0xffff) + (ip_csum >> 16)) & 0xffff
@@ -16,9 +16,9 @@ module Pio
16
16
  bit8 :tlv_type
17
17
  bit8 :tlv_info_length,
18
18
  onlyif: -> { !(end_of_dhcpdu?) }
19
- choice :tlv_value,
20
- onlyif: -> { !(end_of_dhcpdu?) },
21
- selection: :chooser do
19
+ choice :tlv_value,
20
+ onlyif: -> { !(end_of_dhcpdu?) },
21
+ selection: :chooser do
22
22
  uint8 Dhcp::MESSAGE_TYPE_TLV
23
23
  ip_address Dhcp::SERVER_IDENTIFIER_TLV
24
24
  uint32be Dhcp::RENEWAL_TIME_VALUE_TLV