pio 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +36 -0
  4. data/examples/packet_out_new.rb +18 -0
  5. data/examples/packet_out_read.rb +6 -0
  6. data/features/packet_out_read.feature +5 -0
  7. data/lib/pio.rb +1 -0
  8. data/lib/pio/echo.rb +5 -6
  9. data/lib/pio/echo/message.rb +39 -13
  10. data/lib/pio/echo/reply.rb +1 -6
  11. data/lib/pio/echo/request.rb +3 -5
  12. data/lib/pio/enqueue.rb +62 -0
  13. data/lib/pio/features.rb +5 -15
  14. data/lib/pio/features/reply.rb +28 -48
  15. data/lib/pio/features/request.rb +11 -8
  16. data/lib/pio/hello.rb +24 -2
  17. data/lib/pio/monkey_patch/integer.rb +6 -0
  18. data/lib/pio/monkey_patch/integer/ranges.rb +22 -0
  19. data/lib/pio/open_flow.rb +1 -0
  20. data/lib/pio/open_flow/flags.rb +40 -26
  21. data/lib/pio/open_flow/message.rb +28 -0
  22. data/lib/pio/open_flow/parser.rb +22 -0
  23. data/lib/pio/open_flow/phy_port.rb +34 -50
  24. data/lib/pio/open_flow/port_number.rb +39 -0
  25. data/lib/pio/open_flow/type.rb +1 -0
  26. data/lib/pio/packet_in.rb +42 -9
  27. data/lib/pio/packet_out.rb +102 -0
  28. data/lib/pio/send_out_port.rb +74 -0
  29. data/lib/pio/set_eth_addr.rb +52 -0
  30. data/lib/pio/set_ip_addr.rb +49 -0
  31. data/lib/pio/set_ip_tos.rb +42 -0
  32. data/lib/pio/set_transport_port.rb +58 -0
  33. data/lib/pio/set_vlan.rb +37 -0
  34. data/lib/pio/set_vlan_priority.rb +18 -0
  35. data/lib/pio/set_vlan_vid.rb +30 -0
  36. data/lib/pio/strip_vlan_header.rb +19 -0
  37. data/lib/pio/type/ip_address.rb +11 -2
  38. data/lib/pio/type/ipv4_header.rb +1 -0
  39. data/lib/pio/type/mac_address.rb +2 -0
  40. data/lib/pio/version.rb +1 -1
  41. data/pio.gemspec +4 -4
  42. data/spec/pio/echo/reply_spec.rb +1 -4
  43. data/spec/pio/echo/request_spec.rb +5 -8
  44. data/spec/pio/enqueue_spec.rb +67 -0
  45. data/spec/pio/features/request_spec.rb +4 -4
  46. data/spec/pio/features_spec.rb +1 -2
  47. data/spec/pio/hello_spec.rb +1 -4
  48. data/spec/pio/packet_out_spec.rb +290 -0
  49. data/spec/pio/send_out_port_spec.rb +121 -0
  50. data/spec/pio/set_eth_dst_addr_spec.rb +28 -0
  51. data/spec/pio/set_eth_src_addr_spec.rb +28 -0
  52. data/spec/pio/set_ip_dst_addr_spec.rb +25 -0
  53. data/spec/pio/set_ip_src_addr_spec.rb +25 -0
  54. data/spec/pio/set_ip_tos_spec.rb +30 -0
  55. data/spec/pio/set_transport_dst_port_spec.rb +42 -0
  56. data/spec/pio/set_transport_src_port_spec.rb +42 -0
  57. data/spec/pio/set_vlan_priority_spec.rb +42 -0
  58. data/spec/pio/set_vlan_vid_spec.rb +42 -0
  59. data/spec/pio/strip_vlan_header_spec.rb +20 -0
  60. metadata +55 -13
  61. data/lib/pio/echo/format.rb +0 -22
  62. data/lib/pio/hello/format.rb +0 -18
  63. data/lib/pio/packet_in/format.rb +0 -55
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ddcd50819881ef3e84f8efe948b846b346527afc
4
- data.tar.gz: 8e66cab7abcc66b81908bc278fc4a0ac3b91519f
3
+ metadata.gz: 8b5745888688705d7d5a5cf305072b022821e9c2
4
+ data.tar.gz: 565161541fb6cf0090bce51b060478c455c3cc2b
5
5
  SHA512:
6
- metadata.gz: f12db68f58ac7b90230f5fd4fb190ec0d939ad9c2895305393066b08e37d8ad5aa3604558f179fc5d29f8b5c25beb26faa4697b3ecfdb8cda2c856d718ba9cb6
7
- data.tar.gz: 969ee297aa80eb58ad0e5f3a3aff999f49aea84eb2ef88db78058bc77aed050154faaa43eeb78530641bd336024efed1044589b8c8e7a12771c0d5be5bcd5b84
6
+ metadata.gz: 4af67cdf203efe01f2a6c6c1128adc142ca72015bbb2fb5d5d3448c1ea4700bb5eb30fb7ec5329ab3bef2f9bb0b0e678438825c75c6b31f0e0467b21966be9e6
7
+ data.tar.gz: 0b12f27bd5eed509ff5471cd4c0ea6d72b193fdfe9a645c979b5bf6b0910d290233988fb2fef0a9aa86dab0e7c9b0540d848ef1e638c8830ffc1ca7abc0dface
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.0 (1/6/2015)
4
+
5
+ ### New features
6
+ * [#103](https://github.com/trema/pio/pull/103): Added new class `Pio::PacketOut`.
7
+
8
+
3
9
  ## 0.9.0 (12/22/2014)
4
10
 
5
11
  ### New features
data/README.md CHANGED
@@ -22,6 +22,7 @@ supports the following packet formats:
22
22
  - Echo
23
23
  - Features
24
24
  - Packet-In
25
+ - Packet-Out
25
26
  - (…currently there are just a few formats supported but I'm sure this list will grow)
26
27
 
27
28
  ## Features Overview
@@ -284,6 +285,41 @@ like below:
284
285
  )
285
286
  packet_in.to_binary # => Packet-In message in binary format.
286
287
 
288
+ ### Packet-Out
289
+
290
+ To parse an OpenFlow 1.0 Packet-Out message, use the API
291
+ `Pio::PacketOut.read` and you can access each field of the parsed
292
+ Packet-Out message.
293
+
294
+ require 'pio'
295
+
296
+ packet_out = Pio::PacketOut.read(binary_data)
297
+ packet_out.actions.length # => 1
298
+ packet_out.actions[0] # => Pio::SendOutPort
299
+ packet_out.actions[0].port_number # => 2
300
+
301
+ Also you can use `Pio::PacketOut#new` to generate a Packet-Out message
302
+ like below:
303
+
304
+ require 'pio'
305
+
306
+ data_dump = [
307
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x01, 0x02, 0x03, 0x04,
308
+ 0x05, 0x06, 0x88, 0xcc, 0x02, 0x09, 0x07, 0x00, 0x00, 0x00,
309
+ 0x00, 0x00, 0x00, 0x01, 0x23, 0x04, 0x05, 0x07, 0x00, 0x00,
310
+ 0x00, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00,
311
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
312
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
313
+ 0x00, 0x00, 0x00, 0x00
314
+ ].pack('C*')
315
+
316
+ packet_out = Pio::PacketOut.new(transaction_id: 0x16,
317
+ buffer_id: 0xffffffff,
318
+ in_port: 0xffff,
319
+ actions: Pio::SendOutPort.new(2),
320
+ data: data_dump)
321
+ packet_out.to_binary # => Packet-Out message in binary format.
322
+
287
323
  ## Installation
288
324
 
289
325
  The simplest way to install Pio is to use [Bundler](http://gembundler.com/).
@@ -0,0 +1,18 @@
1
+ require 'pio'
2
+
3
+ data_dump = [
4
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x01, 0x02, 0x03, 0x04,
5
+ 0x05, 0x06, 0x88, 0xcc, 0x02, 0x09, 0x07, 0x00, 0x00, 0x00,
6
+ 0x00, 0x00, 0x00, 0x01, 0x23, 0x04, 0x05, 0x07, 0x00, 0x00,
7
+ 0x00, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00,
8
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10
+ 0x00, 0x00, 0x00, 0x00
11
+ ].pack('C*')
12
+
13
+ packet_out = Pio::PacketOut.new(transaction_id: 0x16,
14
+ buffer_id: 0xffffffff,
15
+ in_port: 0xffff,
16
+ actions: Pio::SendOutPort.new(2),
17
+ data: data_dump)
18
+ packet_out.to_binary # => Packet-Out message in binary format.
@@ -0,0 +1,6 @@
1
+ require 'pio'
2
+
3
+ packet_out = Pio::PacketOut.read(binary_data)
4
+ packet_out.actions.length # => 1
5
+ packet_out.actions[0] # => Pio::SendOutPort
6
+ packet_out.actions[0].port_number # => 2
@@ -0,0 +1,5 @@
1
+ Feature: Pio::PacketOut.read
2
+ Scenario: packet_out.raw
3
+ Given a packet data file "packet_out.raw"
4
+ When I try to parse the file with "PacketOut" class
5
+ Then it should finish successfully
data/lib/pio.rb CHANGED
@@ -9,3 +9,4 @@ require 'pio/icmp'
9
9
  require 'pio/lldp'
10
10
  require 'pio/mac'
11
11
  require 'pio/packet_in'
12
+ require 'pio/packet_out'
data/lib/pio/echo.rb CHANGED
@@ -1,15 +1,14 @@
1
- require 'pio/echo/format'
2
1
  require 'pio/echo/reply'
3
2
  require 'pio/echo/request'
4
- require 'pio/message_type_selector'
5
3
  require 'pio/open_flow'
4
+ require 'pio/open_flow/parser'
6
5
 
7
6
  module Pio
8
- # OpenFlow Echo Request and Reply message parser.
7
+ # OpenFlow 1.0 Echo Request and Reply message parser.
9
8
  class Echo
10
- extend MessageTypeSelector
9
+ KLASS = { Pio::OpenFlow::Type::ECHO_REQUEST => Pio::Echo::Request,
10
+ Pio::OpenFlow::Type::ECHO_REPLY => Pio::Echo::Reply }
11
11
 
12
- message_type Pio::OpenFlow::Type::ECHO_REQUEST => Request,
13
- Pio::OpenFlow::Type::ECHO_REPLY => Reply
12
+ extend Pio::OpenFlow::Parser
14
13
  end
15
14
  end
@@ -1,23 +1,49 @@
1
+ require 'pio/open_flow'
2
+
1
3
  module Pio
2
4
  class Echo
3
5
  # Base class of Echo request and reply.
4
6
  class Message < Pio::OpenFlow::Message
5
- # @reek This method smells of :reek:FeatureEnvy
6
- # rubocop:disable Metrics/MethodLength
7
+ # Message body of Echo
8
+ class Body < BinData::Record
9
+ endian :big
10
+
11
+ string :body
12
+
13
+ def length
14
+ body.length
15
+ end
16
+
17
+ def empty?
18
+ length == 0
19
+ end
20
+
21
+ def ==(other)
22
+ body == other
23
+ end
24
+ end
25
+
26
+ # rubocop:disable MethodLength
27
+ # rubocop:disable AbcSize
28
+ # This method smells of :reek:FeatureEnvy
7
29
  def initialize(user_options = {})
8
- options = if user_options.respond_to?(:to_i)
9
- { open_flow_header: { transaction_id: user_options.to_i } }
10
- elsif user_options.respond_to?(:fetch)
11
- transaction_id =
12
- user_options[:transaction_id] || user_options[:xid] || 0
13
- { open_flow_header: { transaction_id: transaction_id },
14
- body: user_options[:user_data] }
15
- else
16
- fail TypeError
17
- end
30
+ if user_options.respond_to?(:to_i)
31
+ options = { open_flow_header: { transaction_id: user_options.to_i } }
32
+ elsif user_options.respond_to?(:fetch)
33
+ header_options = { transaction_id: user_options[:transaction_id] ||
34
+ user_options[:xid] || 0 }
35
+ options = { open_flow_header: header_options,
36
+ body: { body: user_options[:user_data] } }
37
+ else
38
+ fail TypeError
39
+ end
40
+ if options[:open_flow_header][:transaction_id] >= 2**32
41
+ fail ArgumentError, 'Transaction ID >= 2**32'
42
+ end
18
43
  @format = self.class.const_get(:Format).new(options)
19
44
  end
20
- # rubocop:enable Metrics/MethodLength
45
+ # rubocop:enable AbcSize
46
+ # rubocop:enable MethodLength
21
47
  end
22
48
  end
23
49
  end
@@ -1,5 +1,3 @@
1
- require 'forwardable'
2
- require 'pio/echo/format'
3
1
  require 'pio/echo/message'
4
2
  require 'pio/open_flow'
5
3
 
@@ -7,10 +5,7 @@ module Pio
7
5
  class Echo
8
6
  # OpenFlow 1.0 Echo Reply message generator.
9
7
  class Reply < Message
10
- # OpenFlow 1.0 Echo reply message generator.
11
- class Format < Pio::Echo::Format
12
- default_parameters message_type_value: Pio::OpenFlow::Type::ECHO_REPLY
13
- end
8
+ def_format Pio::OpenFlow::Type::ECHO_REPLY
14
9
 
15
10
  # Creates an EchoReply OpenFlow message. This message can be
16
11
  # used to measure the bandwidth of a controller/switch
@@ -1,4 +1,3 @@
1
- require 'pio/echo/format'
2
1
  require 'pio/echo/message'
3
2
  require 'pio/open_flow'
4
3
 
@@ -6,10 +5,7 @@ module Pio
6
5
  class Echo
7
6
  # OpenFlow 1.0 Echo Request message.
8
7
  class Request < Message
9
- # OpenFlow 1.0 Echo request message generator.
10
- class Format < Pio::Echo::Format
11
- default_parameters message_type_value: Pio::OpenFlow::Type::ECHO_REQUEST
12
- end
8
+ def_format Pio::OpenFlow::Type::ECHO_REQUEST
13
9
 
14
10
  # Creates an EchoRequest OpenFlow message. This message can be
15
11
  # used to measure the bandwidth of a controller/switch
@@ -40,6 +36,8 @@ module Pio
40
36
  # timestamp to check latency, various lengths to measure
41
37
  # bandwidth or zero-size(nil) to verify liveness between the
42
38
  # switch and controller.
39
+ #
40
+ # rubocop:disable MethodLength
43
41
  end
44
42
  end
45
43
  end
@@ -0,0 +1,62 @@
1
+ require 'bindata'
2
+ require 'forwardable'
3
+ require 'pio/monkey_patch/integer'
4
+ require 'pio/open_flow'
5
+
6
+ module Pio
7
+ # An action to enqueue the packet on the specified queue attached to
8
+ # a port.
9
+ class Enqueue
10
+ # OpenFlow 1.0 OFPAT_ENQUEUE action format.
11
+ class Format < BinData::Record
12
+ endian :big
13
+
14
+ uint16 :type, value: 11
15
+ uint16 :message_length, value: 16
16
+ port_number :port_number
17
+ uint48 :padding
18
+ hide :padding
19
+ uint32 :queue_id
20
+ end
21
+
22
+ def self.read(raw_data)
23
+ enqueue = allocate
24
+ enqueue.instance_variable_set :@format, Format.read(raw_data)
25
+ enqueue
26
+ end
27
+
28
+ extend Forwardable
29
+
30
+ def_delegators :@format, :type
31
+ def_delegators :@format, :message_length
32
+ def_delegators :@format, :port_number
33
+ def_delegators :@format, :queue_id
34
+ def_delegator :@format, :to_binary_s, :to_binary
35
+
36
+ def initialize(user_options)
37
+ validate_port_number user_options
38
+ validate_queue_id user_options
39
+ @format = Format.new(user_options)
40
+ end
41
+
42
+ private
43
+
44
+ def validate_port_number(user_options)
45
+ port_number = user_options.fetch(:port_number)
46
+ if port_number.is_a?(Symbol) && port_number != :in_port
47
+ fail(ArgumentError,
48
+ ':port_number must be a valid physical port or :in_port')
49
+ end
50
+ rescue KeyError
51
+ raise ArgumentError, ':port_number is a mandatory option'
52
+ end
53
+
54
+ def validate_queue_id(user_options)
55
+ unless user_options.fetch(:queue_id).unsigned_32bit?
56
+ fail ArgumentError, ':queue_id must be an unsigned 32-bit integer'
57
+ end
58
+ rescue KeyError
59
+ raise ArgumentError, ':queue_id is a mandatory option'
60
+ end
61
+ end
62
+ end
data/lib/pio/features.rb CHANGED
@@ -1,24 +1,14 @@
1
1
  require 'pio/features/reply'
2
2
  require 'pio/features/request'
3
3
  require 'pio/open_flow'
4
- require 'pio/parse_error'
4
+ require 'pio/open_flow/parser'
5
5
 
6
6
  module Pio
7
- # OpenFlow 1.0 Features messages
7
+ # OpenFlow 1.0 Features Request and Reply message parser.
8
8
  class Features
9
- include Pio::OpenFlow::Type
9
+ KLASS = { Pio::OpenFlow::Type::FEATURES_REQUEST => Pio::Features::Request,
10
+ Pio::OpenFlow::Type::FEATURES_REPLY => Pio::Features::Reply }
10
11
 
11
- # @reek This method smells of :reek:TooManyStatements
12
- def self.read(raw_data)
13
- header = Pio::Type::OpenFlow::OpenFlowHeader.read(raw_data)
14
- klass = { FEATURES_REQUEST => Request,
15
- FEATURES_REPLY => Reply }.fetch(header.message_type)
16
- format = klass.const_get(:Format).read(raw_data)
17
- message = klass.allocate
18
- message.instance_variable_set :@format, format
19
- message
20
- rescue KeyError
21
- raise Pio::ParseError, 'Invalid features request/reply message.'
22
- end
12
+ extend Pio::OpenFlow::Parser
23
13
  end
24
14
  end
@@ -6,46 +6,36 @@ module Pio
6
6
  class Features
7
7
  # OpenFlow 1.0 Features Reply message
8
8
  class Reply < Pio::OpenFlow::Message
9
- # enum ofp_capabilities
10
- class Capabilities < BinData::Primitive
11
- extend Flags
12
-
13
- endian :big
14
-
15
- flags :capabilities,
16
- flow_stats: 1 << 0,
17
- table_stats: 1 << 1,
18
- port_stats: 1 << 2,
19
- stp: 1 << 3,
20
- reserved: 1 << 4,
21
- ip_reasm: 1 << 5,
22
- queue_stats: 1 << 6,
23
- arp_match_ip: 1 << 7
24
- end
25
-
26
- # enum ofp_action_type
27
- class Actions < BinData::Primitive
9
+ # Message body of features reply.
10
+ class Body < BinData::Record
28
11
  extend Flags
29
12
 
30
- endian :big
31
-
32
- flags :actions,
33
- output: 1 << 0,
34
- set_vlan_vid: 1 << 1,
35
- set_vlan_pcp: 1 << 2,
36
- strip_vlan: 1 << 3,
37
- set_dl_src: 1 << 4,
38
- set_dl_dst: 1 << 5,
39
- set_nw_src: 1 << 6,
40
- set_nw_dst: 1 << 7,
41
- set_nw_tos: 1 << 8,
42
- set_tp_src: 1 << 9,
43
- set_tp_dst: 1 << 10,
44
- enqueue: 1 << 11
45
- end
13
+ # enum ofp_capabilities
14
+ def_flags :capabilities,
15
+ [:flow_stats,
16
+ :table_stats,
17
+ :port_stats,
18
+ :stp,
19
+ :reserved,
20
+ :ip_reasm,
21
+ :queue_stats,
22
+ :arp_match_ip]
23
+
24
+ # enum ofp_action_type
25
+ def_flags :actions,
26
+ [:output,
27
+ :set_vlan_vid,
28
+ :set_vlan_pcp,
29
+ :strip_vlan,
30
+ :set_dl_src,
31
+ :set_dl_dst,
32
+ :set_nw_src,
33
+ :set_nw_dst,
34
+ :set_nw_tos,
35
+ :set_tp_src,
36
+ :set_tp_dst,
37
+ :enqueue]
46
38
 
47
- # Message body of features reply.
48
- class Body < BinData::Record
49
39
  endian :big
50
40
 
51
41
  uint64 :datapath_id
@@ -66,17 +56,7 @@ module Pio
66
56
  end
67
57
  end
68
58
 
69
- # OpenFlow 1.0 Features request message.
70
- class Format < BinData::Record
71
- include Pio::OpenFlow::Type
72
-
73
- endian :big
74
-
75
- open_flow_header :open_flow_header, message_type_value: FEATURES_REPLY
76
- virtual assert: -> { open_flow_header.message_type == FEATURES_REPLY }
77
-
78
- body :body
79
- end
59
+ def_format Pio::OpenFlow::Type::FEATURES_REPLY
80
60
 
81
61
  def datapath_id
82
62
  @format.body.datapath_id
@@ -6,20 +6,23 @@ module Pio
6
6
  class Features
7
7
  # OpenFlow 1.0 Features Request message
8
8
  class Request < Pio::OpenFlow::Message
9
- # OpenFlow 1.0 Features Request format
10
- class Format < BinData::Record
11
- include Pio::OpenFlow::Type
12
-
9
+ # Message body of Features Request
10
+ class Body < BinData::Record
13
11
  endian :big
14
12
 
15
- open_flow_header :open_flow_header, message_type_value: FEATURES_REQUEST
16
- virtual assert: -> { open_flow_header.message_type == FEATURES_REQUEST }
13
+ string :body # ignored
14
+
15
+ def length
16
+ 0
17
+ end
17
18
 
18
- def body
19
- ''
19
+ def empty?
20
+ true
20
21
  end
21
22
  end
22
23
 
24
+ def_format Pio::OpenFlow::Type::FEATURES_REQUEST
25
+
23
26
  # Creates a Features Request OpenFlow message.
24
27
  #
25
28
  # @overload initialize()