pio 0.3.0 → 0.4.0

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