pio 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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