pio 0.9.0 → 0.10.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 (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()