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
@@ -0,0 +1,16 @@
1
+ require 'pio'
2
+
3
+ request = Pio::Arp::Request.new(
4
+ source_mac: '00:26:82:eb:ea:d1',
5
+ sender_protocol_address: '192.168.83.3',
6
+ target_protocol_address: '192.168.83.254'
7
+ )
8
+ request.to_binary # => Arp Request frame in binary format.
9
+
10
+ reply = Pio::Arp::Reply.new(
11
+ source_mac: '00:16:9d:1d:9c:c4',
12
+ destination_mac: '00:26:82:eb:ea:d1',
13
+ sender_protocol_address: '192.168.83.254',
14
+ target_protocol_address: '192.168.83.3'
15
+ )
16
+ reply.to_binary # => Arp Reply frame in binary format.
@@ -0,0 +1,4 @@
1
+ require 'pio'
2
+
3
+ arp = Pio::Arp.read(binary_data)
4
+ arp.source_mac.to_s # => '00:26:82:eb:ea:d1'
@@ -0,0 +1,30 @@
1
+ require 'pio'
2
+
3
+ discover = Pio::Dhcp::Discover.new(source_mac: '24:db:ac:41:e5:5b')
4
+ discover.to_binary # => DHCP Discover frame in binary format
5
+
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
+ )
13
+ offer.to_binary # => DHCP Offer frame in binary format
14
+
15
+ 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',
19
+ transaction_id: offer.transaction_id
20
+ )
21
+ request.to_binary # => DHCP Request frame in binary format
22
+
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
+ )
30
+ ack.to_binary # => DHCP Ack frame in binary format
@@ -0,0 +1,4 @@
1
+ require 'pio'
2
+
3
+ dhcp = Pio::Dhcp.read(binary_data)
4
+ dhcp.destination_mac.to_s # => 'ff:ff:ff:ff:ff:ff'
@@ -0,0 +1,21 @@
1
+ require 'pio'
2
+
3
+ request = Pio::Icmp::Request.new(
4
+ source_mac: '00:16:9d:1d:9c:c4',
5
+ destination_mac: '00:26:82:eb:ea:d1',
6
+ ip_source_address: '192.168.83.3',
7
+ ip_destination_address: '192.168.83.254'
8
+ )
9
+ request.to_binary # => ICMP Request frame in binary format.
10
+
11
+ reply = Pio::Icmp::Reply.new(
12
+ source_mac: '00:26:82:eb:ea:d1',
13
+ destination_mac: '00:16:9d:1d:9c:c4',
14
+ ip_source_address: '192.168.83.254',
15
+ ip_destination_address: '192.168.83.3',
16
+ # The ICMP Identifier and the ICMP Sequence number
17
+ # should be same as those of the request.
18
+ identifier: request.icmp_identifier,
19
+ sequence_number: request.icmp_sequence_number
20
+ )
21
+ reply.to_binary # => ICMP Reply frame in binary format.
@@ -0,0 +1,4 @@
1
+ require 'pio'
2
+
3
+ icmp = Pio::Icmp.read(binary_data)
4
+ icmp.source_mac.to_s # => '00:26:82:eb:ea:d1'
@@ -0,0 +1,4 @@
1
+ require 'pio'
2
+
3
+ lldp = Pio::Lldp.new(dpid: 0x123, port_number: 12)
4
+ lldp.to_binary # => LLDP frame in binary format.
@@ -0,0 +1,4 @@
1
+ require 'pio'
2
+
3
+ lldp = Pio::Lldp.read(binary_data)
4
+ lldp.ttl # => 120
data/lib/pio.rb CHANGED
@@ -1,15 +1,9 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
+
3
+ require 'pio/parse_error'
4
+
2
5
  require 'pio/arp'
3
6
  require 'pio/lldp'
4
7
  require 'pio/icmp'
5
-
6
- module Pio
7
- # Raised when the packet data is in wrong format.
8
- class ParseError < StandardError; end
9
- end
10
-
11
- ### Local variables:
12
- ### mode: Ruby
13
- ### coding: utf-8-unix
14
- ### indent-tabs-mode: nil
15
- ### End:
8
+ require 'pio/mac'
9
+ require 'pio/dhcp'
data/lib/pio/arp.rb CHANGED
@@ -1,28 +1,16 @@
1
- # -*- coding: utf-8 -*-
2
- require 'English'
3
- require 'rubygems'
4
- require 'bindata'
1
+ # encoding: utf-8
5
2
 
3
+ require 'pio/arp/frame'
6
4
  require 'pio/arp/request'
7
5
  require 'pio/arp/reply'
8
- require 'pio/util'
6
+ require 'pio/message_type_selector'
9
7
 
8
+ # Packet parser and generator library.
10
9
  module Pio
11
10
  # ARP parser and generator.
12
-
13
11
  class Arp
14
- MESSAGE_TYPE = {
15
- Request::OPERATION => Request,
16
- Reply::OPERATION => Reply
17
- }
18
- class << self
19
- include Util
20
- end
12
+ extend MessageTypeSelector
13
+ message_type Request::OPERATION => Request, Reply::OPERATION => Reply
21
14
  end
15
+ ARP = Arp
22
16
  end
23
-
24
- ### Local variables:
25
- ### mode: Ruby
26
- ### coding: utf-8-unix
27
- ### indent-tabs-mode: nil
28
- ### End:
data/lib/pio/arp/frame.rb CHANGED
@@ -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/ip_address'
4
6
  require 'pio/type/mac_address'
@@ -11,11 +13,11 @@ module Pio
11
13
 
12
14
  endian :big
13
15
 
14
- ethernet_header :ether_type => 0x0806
15
- uint16 :hardware_type, :value => 1
16
- uint16 :protocol_type, :value => 0x0800
17
- uint8 :hardware_length, :value => 6
18
- uint8 :protocol_length, :value => 4
16
+ ethernet_header ether_type: 0x0806
17
+ uint16 :hardware_type, value: 1
18
+ uint16 :protocol_type, value: 0x0800
19
+ uint8 :hardware_length, value: 6
20
+ uint8 :protocol_length, value: 4
19
21
  uint16 :operation
20
22
  mac_address :sender_hardware_address
21
23
  ip_address :sender_protocol_address
@@ -32,9 +34,3 @@ module Pio
32
34
  end
33
35
  end
34
36
  end
35
-
36
- ### Local variables:
37
- ### mode: Ruby
38
- ### coding: utf-8-unix
39
- ### indent-tabs-mode: nil
40
- ### End:
@@ -1,26 +1,13 @@
1
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
+
2
3
  require 'forwardable'
3
4
  require 'pio/arp/frame'
4
- require 'pio/ipv4_address'
5
- require 'pio/message_util'
6
5
 
7
6
  module Pio
8
7
  class Arp
9
8
  # Base class of ARP Request and Reply
10
9
  class Message
11
10
  extend Forwardable
12
- include MessageUtil
13
-
14
- def self.create_from(frame)
15
- message = allocate
16
- message.instance_variable_set :@frame, frame
17
- message
18
- end
19
-
20
- def initialize(options)
21
- @options = options
22
- @frame = Arp::Frame.new(option_hash)
23
- end
24
11
 
25
12
  def_delegators :@frame, :destination_mac
26
13
  def_delegators :@frame, :source_mac
@@ -36,17 +23,17 @@ module Pio
36
23
  def_delegators :@frame, :target_protocol_address
37
24
  def_delegators :@frame, :to_binary
38
25
 
39
- private
26
+ def self.create_from(frame)
27
+ message = allocate
28
+ message.instance_variable_set :@frame, frame
29
+ message
30
+ end
31
+
32
+ private_class_method :new
40
33
 
41
- def option_to_klass
42
- {
43
- :source_mac => Mac,
44
- :destination_mac => Mac,
45
- :sender_hardware_address => Mac,
46
- :target_hardware_address => Mac,
47
- :sender_protocol_address => IPv4Address,
48
- :target_protocol_address => IPv4Address,
49
- }
34
+ def initialize(user_options)
35
+ options = self.class.const_get(:Options).new(user_options.dup.freeze)
36
+ @frame = Arp::Frame.new(options.to_hash)
50
37
  end
51
38
  end
52
39
  end
data/lib/pio/arp/reply.rb CHANGED
@@ -1,45 +1,45 @@
1
- # -*- coding: utf-8 -*-
2
- require 'forwardable'
1
+ # encoding: utf-8
2
+
3
3
  require 'pio/arp/message'
4
4
  require 'pio/mac'
5
+ require 'pio/options'
5
6
 
6
7
  module Pio
7
8
  class Arp
8
9
  # ARP Reply packet generator
9
10
  class Reply < Message
10
11
  OPERATION = 2
12
+ public_class_method :new
11
13
 
12
- private
14
+ # User options for creating an Arp Reply.
15
+ class Options < Pio::Options
16
+ mandatory_option :source_mac
17
+ mandatory_option :destination_mac
18
+ mandatory_option :sender_protocol_address
19
+ mandatory_option :target_protocol_address
13
20
 
14
- def default_options
15
- {
16
- :operation => OPERATION,
17
- }
18
- end
21
+ def initialize(options)
22
+ validate options
23
+ @source_mac = Mac.new(options[:source_mac]).freeze
24
+ @destination_mac = Mac.new(options[:destination_mac]).freeze
25
+ @sender_protocol_address =
26
+ IPv4Address.new(options[:sender_protocol_address]).freeze
27
+ @target_protocol_address =
28
+ IPv4Address.new(options[:target_protocol_address]).freeze
29
+ end
19
30
 
20
- def user_options
21
- @options.merge(
22
- :sender_hardware_address => @options[:source_mac],
23
- :target_hardware_address => @options[:destination_mac]
24
- )
25
- end
26
-
27
- def mandatory_options
28
- [
29
- :source_mac,
30
- :destination_mac,
31
- :sender_hardware_address,
32
- :target_hardware_address,
33
- :sender_protocol_address,
34
- :target_protocol_address,
35
- ]
31
+ def to_hash
32
+ {
33
+ operation: OPERATION,
34
+ source_mac: @source_mac,
35
+ destination_mac: @destination_mac,
36
+ sender_hardware_address: @source_mac,
37
+ target_hardware_address: @destination_mac,
38
+ sender_protocol_address: @sender_protocol_address,
39
+ target_protocol_address: @target_protocol_address
40
+ }.freeze
41
+ end
36
42
  end
37
43
  end
38
44
  end
39
45
  end
40
-
41
- ### Local variables:
42
- ### mode: Ruby
43
- ### coding: utf-8-unix
44
- ### indent-tabs-mode: nil
45
- ### End:
@@ -1,45 +1,46 @@
1
- # -*- coding: utf-8 -*-
2
- require 'pio/arp/frame'
1
+ # encoding: utf-8
2
+
3
3
  require 'pio/arp/message'
4
4
  require 'pio/mac'
5
+ require 'pio/options'
5
6
 
6
7
  module Pio
7
8
  class Arp
8
9
  # ARP Request packet generator
9
10
  class Request < Message
10
11
  OPERATION = 1
12
+ public_class_method :new
11
13
 
12
- BROADCAST_MAC_ADDRESS = Mac.new(0xffffffffffff).to_a
13
- ALL_ZERO_MAC_ADDRESS = Mac.new(0).to_a
14
+ # User options for creating an Arp Request.
15
+ class Options < Pio::Options
16
+ mandatory_option :source_mac
17
+ mandatory_option :sender_protocol_address
18
+ mandatory_option :target_protocol_address
14
19
 
15
- private
20
+ BROADCAST_MAC = Mac.new(0xffffffffffff).freeze
21
+ ALL_ZERO_MAC = Mac.new(0).freeze
16
22
 
17
- def default_options
18
- {
19
- :operation => OPERATION,
20
- :destination_mac => BROADCAST_MAC_ADDRESS,
21
- :target_hardware_address => ALL_ZERO_MAC_ADDRESS
22
- }
23
- end
23
+ def initialize(options)
24
+ validate options
25
+ @source_mac = Mac.new(options[:source_mac]).freeze
26
+ @sender_protocol_address =
27
+ IPv4Address.new(options[:sender_protocol_address]).freeze
28
+ @target_protocol_address =
29
+ IPv4Address.new(options[:target_protocol_address]).freeze
30
+ end
24
31
 
25
- def user_options
26
- @options.merge :sender_hardware_address => @options[:source_mac]
27
- end
28
-
29
- def mandatory_options
30
- [
31
- :source_mac,
32
- :sender_hardware_address,
33
- :sender_protocol_address,
34
- :target_protocol_address,
35
- ]
32
+ def to_hash
33
+ {
34
+ operation: OPERATION,
35
+ source_mac: @source_mac,
36
+ destination_mac: BROADCAST_MAC,
37
+ sender_hardware_address: @source_mac,
38
+ target_hardware_address: ALL_ZERO_MAC,
39
+ sender_protocol_address: @sender_protocol_address,
40
+ target_protocol_address: @target_protocol_address
41
+ }.freeze
42
+ end
36
43
  end
37
44
  end
38
45
  end
39
46
  end
40
-
41
- ### Local variables:
42
- ### mode: Ruby
43
- ### coding: utf-8-unix
44
- ### indent-tabs-mode: nil
45
- ### End:
data/lib/pio/dhcp.rb ADDED
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ # Packet parser and generator library.
4
+ module Pio
5
+ # Dhcp parser and generator.
6
+ class Dhcp
7
+ MESSAGE_TYPE_TLV = 53
8
+ SERVER_IDENTIFIER_TLV = 54
9
+ CLIENT_IDENTIFIER_TLV = 61
10
+ RENEWAL_TIME_VALUE_TLV = 58
11
+ REBINDING_TIME_VALUE_TLV = 59
12
+ REQUESTED_IP_ADDRESS_TLV = 50
13
+ PARAMETERS_LIST_TLV = 55
14
+ IP_ADDRESS_LEASE_TIME_TLV = 51
15
+ SUBNET_MASK_TLV = 1
16
+ ROUTER_TLV = 3
17
+ NTP_SERVERS_TLV = 42
18
+ DNS_TLV = 6
19
+ END_OF_TLV = 255
20
+
21
+ PARAMETER_REQUEST_LIST =
22
+ [
23
+ SUBNET_MASK_TLV,
24
+ ROUTER_TLV,
25
+ DNS_TLV,
26
+ NTP_SERVERS_TLV
27
+ ]
28
+ end
29
+ DHCP = Dhcp
30
+ end
31
+
32
+ require 'bindata'
33
+ require 'pio/dhcp/discover'
34
+ require 'pio/dhcp/offer'
35
+ require 'pio/dhcp/request'
36
+ require 'pio/dhcp/ack'
37
+
38
+ module Pio
39
+ # Dhcp parser and generator.
40
+ class Dhcp
41
+ MESSAGE_TYPE = {
42
+ Discover::TYPE => Discover,
43
+ Offer::TYPE => Offer,
44
+ Request::TYPE => Request,
45
+ Ack::TYPE => Ack
46
+ }
47
+
48
+ def self.read(raw_data)
49
+ begin
50
+ frame = const_get('Frame').read(raw_data)
51
+ rescue
52
+ raise Pio::ParseError, $ERROR_INFO.message
53
+ end
54
+
55
+ const_get('MESSAGE_TYPE')[frame.message_type].create_from(frame)
56
+ end
57
+ end
58
+ end