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
@@ -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