pio 0.3.0 → 0.4.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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +46 -12
  3. data/README.md +131 -116
  4. data/Rakefile +7 -92
  5. data/examples/arp_new.rb +16 -0
  6. data/examples/arp_read.rb +4 -0
  7. data/examples/dhcp_new.rb +30 -0
  8. data/examples/dhcp_read.rb +4 -0
  9. data/examples/icmp_new.rb +21 -0
  10. data/examples/icmp_read.rb +4 -0
  11. data/examples/lldp_new.rb +4 -0
  12. data/examples/lldp_read.rb +4 -0
  13. data/lib/pio.rb +6 -12
  14. data/lib/pio/arp.rb +7 -19
  15. data/lib/pio/arp/frame.rb +8 -12
  16. data/lib/pio/arp/message.rb +12 -25
  17. data/lib/pio/arp/reply.rb +30 -30
  18. data/lib/pio/arp/request.rb +30 -29
  19. data/lib/pio/dhcp.rb +58 -0
  20. data/lib/pio/dhcp/ack.rb +12 -0
  21. data/lib/pio/dhcp/boot_reply.rb +16 -0
  22. data/lib/pio/dhcp/boot_reply_options.rb +75 -0
  23. data/lib/pio/dhcp/boot_request.rb +16 -0
  24. data/lib/pio/dhcp/boot_request_options.rb +69 -0
  25. data/lib/pio/dhcp/common_options.rb +71 -0
  26. data/lib/pio/dhcp/csum_util.rb +83 -0
  27. data/lib/pio/dhcp/dhcp_field.rb +48 -0
  28. data/lib/pio/dhcp/dhcp_tlv_options.rb +84 -0
  29. data/lib/pio/dhcp/discover.rb +12 -0
  30. data/lib/pio/dhcp/field_util.rb +102 -0
  31. data/lib/pio/dhcp/frame.rb +95 -0
  32. data/lib/pio/dhcp/message.rb +79 -0
  33. data/lib/pio/dhcp/offer.rb +12 -0
  34. data/lib/pio/dhcp/optional_tlv.rb +74 -0
  35. data/lib/pio/dhcp/request.rb +12 -0
  36. data/lib/pio/dhcp/type/dhcp_client_id.rb +21 -0
  37. data/lib/pio/dhcp/type/dhcp_param_list.rb +22 -0
  38. data/lib/pio/dhcp/type/dhcp_string.rb +21 -0
  39. data/lib/pio/icmp.rb +7 -18
  40. data/lib/pio/icmp/frame.rb +38 -40
  41. data/lib/pio/icmp/message.rb +10 -61
  42. data/lib/pio/icmp/options.rb +25 -0
  43. data/lib/pio/icmp/reply.rb +34 -7
  44. data/lib/pio/icmp/request.rb +43 -7
  45. data/lib/pio/ipv4_address.rb +5 -8
  46. data/lib/pio/lldp.rb +22 -62
  47. data/lib/pio/lldp/chassis_id_tlv.rb +7 -13
  48. data/lib/pio/lldp/end_of_lldpdu_value.rb +3 -9
  49. data/lib/pio/lldp/frame.rb +6 -12
  50. data/lib/pio/lldp/management_address_value.rb +4 -10
  51. data/lib/pio/lldp/optional_tlv.rb +5 -10
  52. data/lib/pio/lldp/options.rb +37 -0
  53. data/lib/pio/lldp/organizationally_specific_value.rb +2 -8
  54. data/lib/pio/lldp/port_description_value.rb +2 -8
  55. data/lib/pio/lldp/port_id_tlv.rb +6 -12
  56. data/lib/pio/lldp/system_capabilities_value.rb +2 -8
  57. data/lib/pio/lldp/system_description_value.rb +2 -8
  58. data/lib/pio/lldp/system_name_value.rb +2 -8
  59. data/lib/pio/lldp/ttl_tlv.rb +5 -11
  60. data/lib/pio/mac.rb +4 -9
  61. data/lib/pio/message_type_selector.rb +22 -0
  62. data/lib/pio/options.rb +65 -0
  63. data/lib/pio/parse_error.rb +6 -0
  64. data/lib/pio/type/ethernet_header.rb +3 -2
  65. data/lib/pio/type/ip_address.rb +4 -9
  66. data/lib/pio/type/ipv4_header.rb +12 -17
  67. data/lib/pio/type/mac_address.rb +5 -10
  68. data/lib/pio/type/udp_header.rb +18 -0
  69. data/lib/pio/version.rb +3 -8
  70. data/pio.gemspec +12 -10
  71. data/spec/pio/arp/reply/options_spec.rb +145 -0
  72. data/spec/pio/arp/reply_spec.rb +77 -113
  73. data/spec/pio/arp/request/options_spec.rb +115 -0
  74. data/spec/pio/arp/request_spec.rb +74 -96
  75. data/spec/pio/arp_spec.rb +71 -105
  76. data/spec/pio/dhcp/ack_spec.rb +189 -0
  77. data/spec/pio/dhcp/discover_spec.rb +165 -0
  78. data/spec/pio/dhcp/offer_spec.rb +189 -0
  79. data/spec/pio/dhcp/request_spec.rb +173 -0
  80. data/spec/pio/dhcp_spec.rb +609 -0
  81. data/spec/pio/icmp/reply_spec.rb +102 -95
  82. data/spec/pio/icmp/request_spec.rb +86 -78
  83. data/spec/pio/icmp_spec.rb +153 -146
  84. data/spec/pio/ipv4_address_spec.rb +2 -7
  85. data/spec/pio/lldp/options_spec.rb +188 -0
  86. data/spec/pio/lldp_spec.rb +181 -208
  87. data/spec/pio/mac_spec.rb +3 -8
  88. data/spec/spec_helper.rb +4 -10
  89. metadata +69 -17
  90. data/.gitignore +0 -20
  91. data/.rspec +0 -3
  92. data/.rubocop.yml +0 -1
  93. data/.travis.yml +0 -13
  94. data/Gemfile +0 -37
  95. data/Guardfile +0 -24
  96. data/lib/pio/message_util.rb +0 -19
  97. data/lib/pio/type/config.reek +0 -4
  98. data/lib/pio/util.rb +0 -21
  99. data/pio.org +0 -668
  100. data/pio.org_archive +0 -943
  101. data/rubocop-todo.yml +0 -9
data/lib/pio/icmp.rb CHANGED
@@ -1,27 +1,16 @@
1
- # -*- coding: utf-8 -*-
2
- require 'rubygems'
3
- require 'bindata'
1
+ # encoding: utf-8
4
2
 
5
3
  require 'pio/icmp/frame'
6
- require 'pio/icmp/request'
7
4
  require 'pio/icmp/reply'
8
- require 'pio/util'
5
+ require 'pio/icmp/request'
6
+ require 'pio/message_type_selector'
9
7
 
8
+ # Packet parser and generator library.
10
9
  module Pio
11
10
  # Icmp parser and generator.
12
11
  class Icmp
13
- MESSAGE_TYPE = {
14
- Request::TYPE => Request,
15
- Reply::TYPE => Reply
16
- }
17
- class << self
18
- include Util
19
- end
12
+ extend MessageTypeSelector
13
+ message_type Request::TYPE => Request, Reply::TYPE => Reply
20
14
  end
15
+ ICMP = Icmp
21
16
  end
22
-
23
- ### Local variables:
24
- ### mode: Ruby
25
- ### coding: utf-8-unix
26
- ### indent-tabs-mode: nil
27
- ### End:
@@ -1,4 +1,6 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
+
3
+ require 'bindata'
2
4
  require 'pio/type/ethernet_header'
3
5
  require 'pio/type/ipv4_header'
4
6
 
@@ -14,18 +16,18 @@ module Pio
14
16
  extend Type::IPv4Header
15
17
 
16
18
  endian :big
17
- ethernet_header :ether_type => 0x0800
18
- ipv4_header :ip_protocol => 1,
19
- :ip_header_checksum => lambda { ip_sum },
20
- :ip_total_length => lambda { ip_packet_length }
19
+
20
+ ethernet_header ether_type: 0x0800
21
+ ipv4_header ip_protocol: 1,
22
+ ip_header_checksum: -> { ip_sum },
23
+ ip_total_length: -> { ip_packet_length }
21
24
  uint8 :icmp_type
22
- uint8 :icmp_code, :initial_value => 0
23
- uint16 :icmp_checksum, :value => lambda { icmp_sum }
24
- uint16 :icmp_identifier, :initial_value => 0x0100
25
- uint16 :icmp_sequence_number, :initial_value => 0x0001
25
+ uint8 :icmp_code, initial_value: 0
26
+ uint16 :icmp_checksum, value: -> { icmp_sum }
27
+ uint16 :icmp_identifier
28
+ uint16 :icmp_sequence_number
26
29
  string :echo_data,
27
- :initial_value => 'DEADBEEF',
28
- :read_length => lambda { echo_data_length }
30
+ read_length: -> { echo_data_length }
29
31
 
30
32
  def message_type
31
33
  icmp_type
@@ -48,16 +50,26 @@ module Pio
48
50
  ~((icmp_csum & 0xffff) + (icmp_csum >> 16)) & 0xffff
49
51
  end
50
52
 
53
+ def ip_sum
54
+ ~((ip_csum & 0xffff) + (ip_csum >> 16)) & 0xffff
55
+ end
56
+
57
+ def to_binary
58
+ if num_bytes < MINIMUM_FRAME_LENGTH
59
+ to_binary_s + "\000" * (MINIMUM_FRAME_LENGTH - num_bytes)
60
+ else
61
+ to_binary_s
62
+ end
63
+ end
64
+
65
+ private
66
+
51
67
  def icmp_csum
52
68
  icmp_2bytewise_slices.reduce(0) do |acc, each|
53
69
  acc + each
54
70
  end
55
71
  end
56
72
 
57
- def ip_sum
58
- ~((ip_csum & 0xffff) + (ip_csum >> 16)) & 0xffff
59
- end
60
-
61
73
  def ip_csum
62
74
  ipv4_header_2bytewise_slices.reduce(0) do |acc, each|
63
75
  acc + each
@@ -66,22 +78,22 @@ module Pio
66
78
 
67
79
  def icmp_2bytewise_slices
68
80
  [
69
- icmp_type * 0x100 + icmp_code,
70
- icmp_identifier,
71
- icmp_sequence_number,
72
- *echo_data.unpack('n*')
81
+ icmp_type * 0x100 + icmp_code,
82
+ icmp_identifier,
83
+ icmp_sequence_number,
84
+ *echo_data.unpack('n*')
73
85
  ]
74
86
  end
75
87
 
76
88
  def ipv4_header_2bytewise_slices
77
89
  [
78
- ipversion_ipheaderlength_iptypeofservice, ip_total_length,
79
- ip_identifier, ipflag_ipfragment,
80
- ipttl_ipproto,
81
- ip_source_address_upper,
82
- ip_source_address_lower,
83
- ip_destination_address_upper,
84
- ip_destination_address_lower
90
+ ipversion_ipheaderlength_iptypeofservice, ip_total_length,
91
+ ip_identifier, ipflag_ipfragment,
92
+ ipttl_ipproto,
93
+ ip_source_address_upper,
94
+ ip_source_address_lower,
95
+ ip_destination_address_upper,
96
+ ip_destination_address_lower
85
97
  ]
86
98
  end
87
99
 
@@ -112,20 +124,6 @@ module Pio
112
124
  def ipttl_ipproto
113
125
  ip_ttl << 8 | ip_protocol
114
126
  end
115
-
116
- def to_binary
117
- if num_bytes < MINIMUM_FRAME_LENGTH
118
- to_binary_s + "\000" * (MINIMUM_FRAME_LENGTH - num_bytes)
119
- else
120
- to_binary_s
121
- end
122
- end
123
127
  end
124
128
  end
125
129
  end
126
-
127
- ### Local variables:
128
- ### mode: Ruby
129
- ### coding: utf-8-unix
130
- ### indent-tabs-mode: nil
131
- ### End:
@@ -1,6 +1,6 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
+
2
3
  require 'pio/icmp/frame'
3
- require 'pio/message_util'
4
4
  require 'forwardable'
5
5
 
6
6
  module Pio
@@ -8,18 +8,6 @@ module Pio
8
8
  # Base class of Request, Reply, TTL Exceeded and destination unreachable.
9
9
  class Message
10
10
  extend Forwardable
11
- include MessageUtil
12
-
13
- def self.create_from(frame)
14
- message = allocate
15
- message.instance_variable_set :@frame, frame
16
- message
17
- end
18
-
19
- def initialize(options)
20
- @options = options
21
- @frame = Icmp::Frame.new(option_hash)
22
- end
23
11
 
24
12
  def_delegators :@frame, :destination_mac
25
13
  def_delegators :@frame, :source_mac
@@ -44,57 +32,18 @@ module Pio
44
32
  def_delegators :@frame, :echo_data
45
33
  def_delegators :@frame, :to_binary
46
34
 
47
- private
48
-
49
- # rubocop:disable MethodLength
50
- def default_options
51
- {
52
- :ip_type_of_service => @options[:ip_type_of_service],
53
- :ip_identifier => @options[:ip_identifier],
54
- :ip_flag => @options[:ip_flag],
55
- :ip_fragment => @options[:ip_fragment],
56
- :ip_ttl => @options[:ip_ttl],
57
- :icmp_type => self.class::TYPE,
58
- :icmp_code => @options[:icmp_code],
59
- :icmp_identifier => @options[:icmp_identifier],
60
- :icmp_sequence_number => @options[:icmp_sequence_number],
61
- :echo_data => @options[:echo_data]
62
- }
63
- end
64
- # rubocop:enable MethodLength
65
-
66
- def user_options
67
- @options.merge(
68
- :source_mac => @options[:source_mac],
69
- :destination_mac => @options[:destination_mac],
70
- :ip_source_address => @options[:ip_source_address],
71
- :ip_destination_address => @options[:ip_destination_address]
72
- )
35
+ def self.create_from(frame)
36
+ message = allocate
37
+ message.instance_variable_set :@frame, frame
38
+ message
73
39
  end
74
40
 
75
- def mandatory_options
76
- [
77
- :source_mac,
78
- :destination_mac,
79
- :ip_source_address,
80
- :ip_destination_address
81
- ]
82
- end
41
+ private_class_method :new
83
42
 
84
- def option_to_klass
85
- {
86
- :source_mac => Mac,
87
- :destination_mac => Mac,
88
- :ip_source_address => IPv4Address,
89
- :ip_destination_address => IPv4Address
90
- }
43
+ def initialize(user_options)
44
+ options = self.class.const_get(:Options).new(user_options)
45
+ @frame = Icmp::Frame.new(options.to_hash)
91
46
  end
92
47
  end
93
48
  end
94
49
  end
95
-
96
- ### Local variables:
97
- ### mode: Ruby
98
- ### coding: utf-8-unix
99
- ### indent-tabs-mode: nil
100
- ### End:
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require 'pio/options'
4
+
5
+ module Pio
6
+ class Icmp
7
+ # User options for creating an ICMP messages.
8
+ class Options < Pio::Options
9
+ DEFAULT_ECHO_DATA = ''.freeze
10
+
11
+ def to_hash
12
+ {
13
+ icmp_type: @type,
14
+ source_mac: @source_mac,
15
+ destination_mac: @destination_mac,
16
+ ip_source_address: @ip_source_address,
17
+ ip_destination_address: @ip_destination_address,
18
+ icmp_identifier: @identifier,
19
+ icmp_sequence_number: @sequence_number,
20
+ echo_data: @echo_data
21
+ }.freeze
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,17 +1,44 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
+
2
3
  require 'pio/icmp/message'
4
+ require 'pio/icmp/options'
5
+ require 'pio/mac'
3
6
 
4
7
  module Pio
5
8
  class Icmp
6
9
  # ICMP Reply packet generator
7
10
  class Reply < Message
8
11
  TYPE = 0
12
+ public_class_method :new
13
+
14
+ # User options for creating an ICMP Reply.
15
+ class Options < Pio::Icmp::Options
16
+ mandatory_option :source_mac
17
+ mandatory_option :destination_mac
18
+ mandatory_option :ip_source_address
19
+ mandatory_option :ip_destination_address
20
+ mandatory_option :identifier
21
+ mandatory_option :sequence_number
22
+ option :echo_data
23
+
24
+ # rubocop:disable MethodLength
25
+
26
+ def initialize(options)
27
+ validate options
28
+ @type = TYPE
29
+ @source_mac = Mac.new(options[:source_mac]).freeze
30
+ @destination_mac = Mac.new(options[:destination_mac]).freeze
31
+ @ip_source_address =
32
+ IPv4Address.new(options[:ip_source_address]).freeze
33
+ @ip_destination_address =
34
+ IPv4Address.new(options[:ip_destination_address]).freeze
35
+ @identifier = options[:identifier]
36
+ @sequence_number = options[:sequence_number]
37
+ @echo_data = options[:echo_data] || DEFAULT_ECHO_DATA
38
+ end
39
+
40
+ # rubocop:enable MethodLength
41
+ end
9
42
  end
10
43
  end
11
44
  end
12
-
13
- ### Local variables:
14
- ### mode: Ruby
15
- ### coding: utf-8-unix
16
- ### indent-tabs-mode: nil
17
- ### End:
@@ -1,17 +1,53 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
+
2
3
  require 'pio/icmp/message'
4
+ require 'pio/icmp/options'
5
+ require 'pio/mac'
3
6
 
4
7
  module Pio
5
8
  class Icmp
6
9
  # ICMP Request packet generator
7
10
  class Request < Message
8
11
  TYPE = 8
12
+ public_class_method :new
13
+
14
+ # User options for creating an ICMP Request.
15
+ class Options < Pio::Icmp::Options
16
+ DEFAULT_IDENTIFIER = 0x0100
17
+ DEFAULT_SEQUENCE_NUMBER = 0
18
+
19
+ mandatory_option :source_mac
20
+ mandatory_option :destination_mac
21
+ mandatory_option :ip_source_address
22
+ mandatory_option :ip_destination_address
23
+ option :identifier
24
+ option :sequence_number
25
+ option :echo_data
26
+
27
+ # rubocop:disable MethodLength
28
+
29
+ def initialize(options)
30
+ validate options
31
+ @type = TYPE
32
+ @source_mac = Mac.new(options[:source_mac]).freeze
33
+ @destination_mac = Mac.new(options[:destination_mac]).freeze
34
+ @ip_source_address =
35
+ IPv4Address.new(options[:ip_source_address]).freeze
36
+ @ip_destination_address =
37
+ IPv4Address.new(options[:ip_destination_address]).freeze
38
+ @identifier =
39
+ options[:icmp_identifier] ||
40
+ options[:identifier] ||
41
+ DEFAULT_IDENTIFIER
42
+ @sequence_number =
43
+ options[:icmp_sequence_number] ||
44
+ options[:sequence_number] ||
45
+ DEFAULT_SEQUENCE_NUMBER
46
+ @echo_data = options[:echo_data] || DEFAULT_ECHO_DATA
47
+ end
48
+
49
+ # rubocop:enable MethodLength
50
+ end
9
51
  end
10
52
  end
11
53
  end
12
-
13
- ### Local variables:
14
- ### mode: Ruby
15
- ### coding: utf-8-unix
16
- ### indent-tabs-mode: nil
17
- ### End:
@@ -1,4 +1,5 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
+
2
3
  require 'forwardable'
3
4
  require 'ipaddr'
4
5
 
@@ -55,13 +56,15 @@ module Pio
55
56
  #
56
57
  def_delegator :value, :to_range
57
58
 
59
+ def_delegator :value, :==
60
+
58
61
  #
59
62
  # @return [Number] prefix length of IPv4 address.
60
63
  #
61
64
  def prefixlen
62
65
  netmask = to_range.first.to_i ^ to_range.last.to_i
63
66
  if netmask > 0
64
- 32 - sprintf('%b', netmask).length
67
+ 32 - format('%b', netmask).length
65
68
  else
66
69
  32
67
70
  end
@@ -154,9 +157,3 @@ module Pio
154
157
  end
155
158
  end
156
159
  end
157
-
158
- ### Local variables:
159
- ### mode: Ruby
160
- ### coding: utf-8-unix
161
- ### indent-tabs-mode: nil
162
- ### End:
data/lib/pio/lldp.rb CHANGED
@@ -1,67 +1,16 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
+
2
3
  require 'English'
3
4
  require 'forwardable'
4
5
  require 'pio/lldp/frame'
6
+ require 'pio/lldp/options'
5
7
 
8
+ # Packet parser and generator library.
6
9
  module Pio
7
10
  # LLDP frame parser and generator.
8
11
  class Lldp
9
- # User options for creating an LLDP frame.
10
- class Options
11
- def initialize(options)
12
- @options = options
13
- fail TypeError, "Invalid DPID: #{ dpid.inspect }" unless dpid
14
- unless port_id
15
- fail TypeError, "Invalid port number: #{ port_id.inspect }"
16
- end
17
- end
18
-
19
- def to_hash
20
- {
21
- :destination_mac => Mac.new(destination_mac).to_a,
22
- :source_mac => Mac.new(source_mac).to_a,
23
- :chassis_id => dpid,
24
- :port_id => port_id
25
- }
26
- end
27
-
28
- private
29
-
30
- def dpid
31
- @options[:dpid]
32
- end
33
-
34
- def port_id
35
- @options[:port_number]
36
- end
37
-
38
- def destination_mac
39
- @options[:destination_mac] || '01:80:c2:00:00:0e'
40
- end
41
-
42
- def source_mac
43
- @options[:source_mac] || '01:02:03:04:05:06'
44
- end
45
- end
46
-
47
12
  extend Forwardable
48
13
 
49
- def self.read(raw_data)
50
- begin
51
- frame = Frame.read(raw_data)
52
- rescue
53
- raise Pio::ParseError, $ERROR_INFO.message
54
- end
55
-
56
- lldp = allocate
57
- lldp.instance_variable_set :@frame, frame
58
- lldp
59
- end
60
-
61
- def initialize(options)
62
- @frame = Frame.new(Options.new(options).to_hash)
63
- end
64
-
65
14
  def_delegator :@frame, :destination_mac
66
15
  def_delegator :@frame, :source_mac
67
16
  def_delegator :@frame, :ether_type
@@ -79,14 +28,25 @@ module Pio
79
28
  def_delegator :@frame, :management_address
80
29
  def_delegator :@frame, :organizationally_specific
81
30
 
31
+ def self.read(raw_data)
32
+ begin
33
+ frame = Frame.read(raw_data)
34
+ rescue
35
+ raise Pio::ParseError, $ERROR_INFO.message
36
+ end
37
+
38
+ lldp = allocate
39
+ lldp.instance_variable_set :@frame, frame
40
+ lldp
41
+ end
42
+
43
+ def initialize(options)
44
+ @frame = Frame.new(Options.new(options).to_hash)
45
+ end
46
+
82
47
  def to_binary
83
- @frame.to_binary_s + "\000" * ( 64 - @frame.num_bytes)
48
+ @frame.to_binary_s + "\000" * (64 - @frame.num_bytes)
84
49
  end
85
50
  end
51
+ LLDP = Lldp
86
52
  end
87
-
88
- ### Local variables:
89
- ### mode: Ruby
90
- ### coding: utf-8-unix
91
- ### indent-tabs-mode: nil
92
- ### End: