packetgen 3.1.4 → 3.2.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/bin/pgconsole +1 -0
  4. data/lib/packetgen.rb +19 -3
  5. data/lib/packetgen/capture.rb +30 -9
  6. data/lib/packetgen/config.rb +15 -9
  7. data/lib/packetgen/deprecation.rb +1 -1
  8. data/lib/packetgen/header/asn1_base.rb +19 -9
  9. data/lib/packetgen/header/base.rb +68 -70
  10. data/lib/packetgen/header/dhcpv6/duid.rb +3 -1
  11. data/lib/packetgen/header/dhcpv6/option.rb +4 -12
  12. data/lib/packetgen/header/dns/name.rb +18 -7
  13. data/lib/packetgen/header/dns/qdsection.rb +1 -1
  14. data/lib/packetgen/header/dns/question.rb +2 -0
  15. data/lib/packetgen/header/dot11.rb +25 -38
  16. data/lib/packetgen/header/dot11/data.rb +28 -34
  17. data/lib/packetgen/header/dot1x.rb +1 -14
  18. data/lib/packetgen/header/eap.rb +14 -17
  19. data/lib/packetgen/header/eth.rb +5 -6
  20. data/lib/packetgen/header/http/headers.rb +4 -2
  21. data/lib/packetgen/header/http/request.rb +37 -18
  22. data/lib/packetgen/header/http/response.rb +11 -5
  23. data/lib/packetgen/header/http/verbs.rb +1 -1
  24. data/lib/packetgen/header/igmpv3/group_record.rb +2 -0
  25. data/lib/packetgen/header/ip.rb +27 -26
  26. data/lib/packetgen/header/ip/addr.rb +3 -1
  27. data/lib/packetgen/header/ip/option.rb +4 -4
  28. data/lib/packetgen/header/ipv6/addr.rb +2 -0
  29. data/lib/packetgen/header/mldv2/mcast_address_record.rb +2 -0
  30. data/lib/packetgen/header/ospfv2/ls_request.rb +2 -0
  31. data/lib/packetgen/header/ospfv2/lsa.rb +13 -3
  32. data/lib/packetgen/header/ospfv2/lsa_header.rb +2 -1
  33. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +2 -0
  34. data/lib/packetgen/header/ospfv3/ls_request.rb +2 -0
  35. data/lib/packetgen/header/ospfv3/lsa.rb +9 -3
  36. data/lib/packetgen/header/ospfv3/lsa_header.rb +2 -1
  37. data/lib/packetgen/header/snmp.rb +3 -2
  38. data/lib/packetgen/header/tcp.rb +1 -20
  39. data/lib/packetgen/header/tcp/option.rb +8 -6
  40. data/lib/packetgen/inspect.rb +1 -17
  41. data/lib/packetgen/packet.rb +10 -6
  42. data/lib/packetgen/pcapng.rb +11 -11
  43. data/lib/packetgen/pcapng/block.rb +15 -2
  44. data/lib/packetgen/pcapng/epb.rb +22 -15
  45. data/lib/packetgen/pcapng/file.rb +166 -81
  46. data/lib/packetgen/pcapng/idb.rb +7 -9
  47. data/lib/packetgen/pcapng/shb.rb +35 -28
  48. data/lib/packetgen/pcapng/spb.rb +16 -12
  49. data/lib/packetgen/pcapng/unknown_block.rb +3 -11
  50. data/lib/packetgen/pcaprub_wrapper.rb +25 -11
  51. data/lib/packetgen/types.rb +1 -0
  52. data/lib/packetgen/types/abstract_tlv.rb +3 -1
  53. data/lib/packetgen/types/array.rb +17 -10
  54. data/lib/packetgen/types/cstring.rb +56 -19
  55. data/lib/packetgen/types/enum.rb +4 -0
  56. data/lib/packetgen/types/fieldable.rb +65 -0
  57. data/lib/packetgen/types/fields.rb +180 -113
  58. data/lib/packetgen/types/int.rb +15 -1
  59. data/lib/packetgen/types/int_string.rb +8 -0
  60. data/lib/packetgen/types/length_from.rb +18 -10
  61. data/lib/packetgen/types/oui.rb +2 -0
  62. data/lib/packetgen/types/string.rb +58 -7
  63. data/lib/packetgen/types/tlv.rb +2 -0
  64. data/lib/packetgen/unknown_packet.rb +84 -0
  65. data/lib/packetgen/utils.rb +6 -7
  66. data/lib/packetgen/version.rb +1 -1
  67. metadata +18 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2adf2b07c2d2d334e4f0ae6e4e7b49571d4d436788bef51df6cf7c1a5aaba9c8
4
- data.tar.gz: 8997321a31379ea6952ab42a4642d98a717587ed777652ebdb9e25b5928cc8d4
3
+ metadata.gz: 8f3faa715cd36e918df79355267c50690bdde15f1566e23947d26890e7b89a82
4
+ data.tar.gz: d374447f230500b0c5be84b225d3a5576f019afc4870dc6512c659bc2e069d67
5
5
  SHA512:
6
- metadata.gz: b5af6e0d04f8bb03af01d63cde3879a6a13c8a78477811be2b95fee5c597820f4714f8ac8b69f4aa8f04d3d5b81c5e25b5787815ee2237a8c3329721b5dc7895
7
- data.tar.gz: 6eebb203f8dfde49505987d16778c1ecbc81c463d6ac8b5f005e2c6f486166ba0cceabd848d3b3aeebe867b138993171026e8dcacfb04c84a126bc149ff5c616
6
+ metadata.gz: de7911facea630eaa2d3d4afd2ae15e1cbd927241ebf0c63ec096c64eac31c58e519d379476763c50fc1b4290029cec4b14dbf9e96ae9aa05dc06701cf45af32
7
+ data.tar.gz: 69881fc66b4af80177bdad4b5b2daa4bd28259596bbf8a36112998aa9a8c117c7c9898180372635fcc1b47376e2fab4fb5cece88be5c000277cdce825fb03168
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
 
2
2
  [![Gem Version](https://badge.fury.io/rb/packetgen.svg)](https://badge.fury.io/rb/packetgen)
3
- [![Build Status](https://travis-ci.org/sdaubert/packetgen.svg?branch=master)](https://travis-ci.org/sdaubert/packetgen)
4
3
 
5
4
  # PacketGen
6
5
 
data/bin/pgconsole CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'packetgen'
4
5
  require 'packetgen/config'
data/lib/packetgen.rb CHANGED
@@ -32,6 +32,7 @@ module PacketGen
32
32
  attr_reader :hdr
33
33
 
34
34
  def initialize(prev_hdr, hdr)
35
+ super()
35
36
  @prev_hdr = prev_hdr
36
37
  @hdr = hdr
37
38
  end
@@ -66,7 +67,7 @@ module PacketGen
66
67
  # @yieldparam [Packet] packet
67
68
  # @return [Array<Packet>]
68
69
  def self.capture(**kwargs)
69
- Packet.capture(kwargs) { |packet| yield packet if block_given? }
70
+ Packet.capture(**kwargs) { |packet| yield packet if block_given? }
70
71
  end
71
72
 
72
73
  # Shortcut for {Packet.read}
@@ -94,7 +95,21 @@ module PacketGen
94
95
  # Get default network interface (ie. first non-loopback declared interface)
95
96
  # @return [String]
96
97
  def self.default_iface
97
- Interfacez.default
98
+ return @default_iface if defined? @default_iface
99
+
100
+ @default_iface = Interfacez.raw_interface_addresses.each do |iface|
101
+ next unless iface.broadaddr
102
+ next unless Interfacez.ipv4_address_of(iface.name)
103
+ next unless Interfacez.ipv6_address_of(iface.name)
104
+
105
+ break iface.name
106
+ end
107
+ end
108
+
109
+ # Get loopback network interface
110
+ # @return [String]
111
+ def self.loopback_iface
112
+ Interfacez.loopback
98
113
  end
99
114
 
100
115
  # Shortcut to get a header class
@@ -114,11 +129,12 @@ module PacketGen
114
129
  end
115
130
 
116
131
  require 'packetgen/deprecation'
117
- require 'packetgen/types'
118
132
  require 'packetgen/inspect'
133
+ require 'packetgen/types'
119
134
  require 'packetgen/pcapng'
120
135
  require 'packetgen/pcap'
121
136
  require 'packetgen/packet'
137
+ require 'packetgen/unknown_packet'
122
138
  require 'packetgen/capture'
123
139
  require 'packetgen/inject'
124
140
  require 'packetgen/proto'
@@ -13,7 +13,9 @@ module PacketGen
13
13
  class Capture
14
14
  private
15
15
 
16
- attr_reader :filter, :cap_thread, :snaplen, :promisc
16
+ attr_reader :filter, :cap_thread, :snaplen, :promisc, :monitor
17
+
18
+ # rubocop:disable Metrics/ParameterLists
17
19
 
18
20
  public
19
21
 
@@ -41,14 +43,18 @@ module PacketGen
41
43
  # yielding. Default: +true+
42
44
  # @param [Integer] snaplen maximum number of bytes to capture for
43
45
  # each packet.
46
+ # @param [Boolean] monitor enable or disable monitor mode on interface (if supported by +iface+).
44
47
  # @since 2.0.0 remove old 1.x API
45
48
  # @since 3.0.0 arguments are kwargs and no more a hash
46
- def initialize(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil)
47
- @iface = iface || Interfacez.default || Interfacez.loopback
49
+ # @since 3.1.5 add monitor argument
50
+ # @author Sylvain Daubert
51
+ # @author optix2000 - add monitor argument
52
+ def initialize(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil)
53
+ @iface = iface || PacketGen.default_iface || PacketGen.loopback_iface
48
54
 
49
55
  @packets = []
50
56
  @raw_packets = []
51
- set_options iface, max, timeout, filter, promisc, parse, snaplen
57
+ set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
52
58
  end
53
59
 
54
60
  # Start capture
@@ -56,8 +62,11 @@ module PacketGen
56
62
  # @yieldparam [Packet,String] packet if a block is given, yield each
57
63
  # captured packet (Packet or raw data String, depending on +:parse+ option)
58
64
  # @since 3.0.0 arguments are kwargs and no more a hash
59
- def start(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, &block)
60
- set_options iface, max, timeout, filter, promisc, parse, snaplen
65
+ # @since 3.1.5 add monitor argument
66
+ # @author Sylvain Daubert
67
+ # @author optix2000 - add monitor argument
68
+ def start(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil, &block)
69
+ set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
61
70
 
62
71
  @cap_thread = Thread.new do
63
72
  PCAPRUBWrapper.capture(**capture_args) do |packet_data|
@@ -79,7 +88,10 @@ module PacketGen
79
88
 
80
89
  private
81
90
 
82
- def set_options(iface, max, timeout, filter, promisc, parse, snaplen)
91
+ # rubocop:disable Metrics/CyclomaticComplexity
92
+ # rubocop:disable Metrics/PerceivedComplexity
93
+
94
+ def set_options(iface, max, timeout, filter, promisc, parse, snaplen, monitor)
83
95
  @max = max if max
84
96
  @filter = filter unless filter.nil?
85
97
  @timeout = timeout unless timeout.nil?
@@ -87,10 +99,15 @@ module PacketGen
87
99
  @snaplen = snaplen unless snaplen.nil?
88
100
  @parse = parse unless parse.nil?
89
101
  @iface = iface unless iface.nil?
102
+ @monitor = monitor unless monitor.nil?
90
103
  end
91
104
 
105
+ # rubocop:enable Metrics/CyclomaticComplexity
106
+ # rubocop:enable Metrics/PerceivedComplexity
107
+ # rubocop:enable Metrics/ParameterLists
108
+
92
109
  def capture_args
93
- h = { iface: iface, filter: filter }
110
+ h = { iface: iface, filter: filter, monitor: monitor }
94
111
  h[:snaplen] = snaplen unless snaplen.nil?
95
112
  h[:promisc] = promisc unless promisc.nil?
96
113
  h
@@ -105,7 +122,11 @@ module PacketGen
105
122
  def add_packet(data, &block)
106
123
  raw_packets << data
107
124
  if @parse
108
- packet = Packet.parse(data)
125
+ begin
126
+ packet = Packet.parse(data)
127
+ rescue ParseError
128
+ packet = UnknownPacket.new.parse(data)
129
+ end
109
130
  packets << packet
110
131
  block&.call(packet)
111
132
  elsif block
@@ -22,37 +22,43 @@ module PacketGen
22
22
  attr_reader :default_iface
23
23
 
24
24
  def initialize
25
- @default_iface = Interfacez.default || Interfacez.loopback
25
+ @default_iface = PacketGen.default_iface || PacketGen.loopback_iface
26
26
  @hwaddr = {}
27
27
  @ipaddr = {}
28
28
  @ip6addr = {}
29
29
 
30
- Interfacez.all do |iface_name|
31
- @hwaddr[iface_name] = Interfacez.mac_address_of(iface_name)
32
- @ipaddr[iface_name] = Interfacez.ipv4_address_of(iface_name)
33
- @ip6addr[iface_name] = Interfacez.ipv6_addresses_of(iface_name)
34
- end
30
+ initialize_local_addresses
35
31
  end
36
32
 
37
33
  # Get MAC address for given network interface
38
34
  # @param [String,nil] iface network interface. If +nil+, use default one.
39
35
  # @return [String]
40
36
  def hwaddr(iface=nil)
41
- @hwaddr[iface || @default_iface]
37
+ @hwaddr[iface || default_iface]
42
38
  end
43
39
 
44
40
  # Get IP address for given network interface
45
41
  # @param [String,nil] iface network interface. If +nil+, use default one.
46
42
  # @return [String]
47
43
  def ipaddr(iface=nil)
48
- @ipaddr[iface || @default_iface]
44
+ @ipaddr[iface || default_iface]
49
45
  end
50
46
 
51
47
  # Get IPv6 addresses for given network interface
52
48
  # @param [String,nil] iface network interface. If +nil+, use default one.
53
49
  # @return [Array<String>]
54
50
  def ip6addr(iface=nil)
55
- @ip6addr[iface || @default_iface]
51
+ @ip6addr[iface || default_iface]
52
+ end
53
+
54
+ private
55
+
56
+ def initialize_local_addresses
57
+ Interfacez.all do |iface_name|
58
+ @hwaddr[iface_name] = Interfacez.mac_address_of(iface_name)
59
+ @ipaddr[iface_name] = Interfacez.ipv4_address_of(iface_name)
60
+ @ip6addr[iface_name] = Interfacez.ipv6_addresses_of(iface_name)
61
+ end
56
62
  end
57
63
  end
58
64
  end
@@ -31,7 +31,7 @@ module PacketGen
31
31
  complete_deprecated_method_name = "#{base_name}#{deprecated_method}"
32
32
  complete_new_method_name = "#{base_name}#{new_method}" unless new_method.nil?
33
33
 
34
- file, line = caller(2..2).split(':')[0, 2]
34
+ file, line = caller(2..2).first.split(':')[0, 2]
35
35
  message = +"#{file}:#{line}: #{complete_deprecated_method_name} is deprecated"
36
36
  message << " in favor of #{complete_new_method_name}" unless new_method.nil?
37
37
  message << '. ' << self.removed(remove_version)
@@ -19,14 +19,20 @@ module PacketGen
19
19
  class ASN1Base < RASN1::Model
20
20
  include Headerable
21
21
 
22
- # Define some methods from given ASN.1 fields to mimic {Base} attributes
23
- # @param [Array<Symbol>] attributes
24
- # @return [void]
25
- def self.define_attributes(*attributes)
26
- @attributes = attributes
27
- attributes.each do |attr|
28
- class_eval "def #{attr}; @elements[:#{attr}].value; end\n" \
29
- "def #{attr}=(v); @elements[:#{attr}].value = v; end"
22
+ class <<self
23
+ # Define some methods from given ASN.1 fields to mimic {Base} attributes
24
+ # @param [Array<Symbol>] attributes
25
+ # @return [void]
26
+ def define_attributes(*attributes)
27
+ @attributes = attributes
28
+ attributes.each do |attr|
29
+ class_eval "def #{attr}; @elements[:#{attr}].value; end\n" \
30
+ "def #{attr}=(v); @elements[:#{attr}].value = v; end"
31
+ end
32
+ end
33
+
34
+ def known_headers
35
+ @known_headers ||= {}.freeze
30
36
  end
31
37
  end
32
38
 
@@ -37,7 +43,11 @@ module PacketGen
37
43
  # @param [String] str
38
44
  # @return [ASN1Base] self
39
45
  def read(str)
40
- parse(str, ber: true)
46
+ begin
47
+ parse(str, ber: true)
48
+ rescue RASN1::ASN1Error
49
+ # suppress exception to allow guessing
50
+ end
41
51
  self
42
52
  end
43
53
 
@@ -99,10 +99,8 @@ module PacketGen
99
99
 
100
100
  # each iterator
101
101
  # @return [void]
102
- def each
103
- @bindings.each do |b|
104
- yield b
105
- end
102
+ def each(&block)
103
+ @bindings.each(&block)
106
104
  end
107
105
 
108
106
  # @return [Boolean]
@@ -144,74 +142,74 @@ module PacketGen
144
142
  klass.class_eval { @known_headers = {} }
145
143
  end
146
144
 
147
- # Bind a upper header to current one.
148
- # @param [Class] header_klass header class to bind to current class
149
- # @param [Hash] args current class fields and their value when +header_klass+
150
- # is embedded in current class.
151
- #
152
- # Given value may be a lambda, whose alone argument is the value extracted
153
- # from header field (or +nil+ when lambda is used to set field while adding
154
- # a header).
155
- #
156
- # Special key +procs+ may be used to set 2 lambdas, the former to set
157
- # fields, the latter to check bindings. This may be used when multiple and
158
- # non-trivial checks should be made.
159
- # @return [void]
160
- # @example Basic examples
161
- # # Bind Header2 to Header1 when field1 from Header1 has a value of 42
162
- # Header1.bind Header2, field1: 42
163
- # # Bind Header3 to Header1 when field1 from Header1 has a value of 43
164
- # # and field2 has value 43 or 44
165
- # Header1.bind Header3, field1: 43, field2: 43
166
- # Header1.bind Header3, field1: 43, field2: 44
167
- # @example Defining a binding on a field using a lambda.
168
- # # Bind Header4 to Header1 when field1 from Header1 has a value
169
- # # greater or equal to 44. When adding a Header2 to a Header1
170
- # # with Packet#add, force value to 44.
171
- # Header1.bind Header4, field1: ->(v) { v.nil? ? 44 : v >= 44 }
172
- # @example Defining a binding using procs key
173
- # # Bind Header5 to Header1 when field1 from Header1 has a value of 41
174
- # # and first two bytes of header1's body are null.
175
- # # When adding a Header2 to a Header1 with Packet#add, force value to 44.
176
- # Header1.bind Header5, procs: [->(hdr) { hdr.field1 = 41 }
177
- # ->(hdr) { hdr.field1 == 41 && hdr.body[0..1] == "\x00\x00" }]
178
- # @since 2.7.0
179
- def self.bind(header_klass, args={})
180
- if @known_headers[header_klass].nil?
181
- bindings = Bindings.new
182
- @known_headers[header_klass] = bindings
183
- else
184
- bindings = @known_headers[header_klass]
185
- end
186
- bindings.new_set
187
- args.each do |key, value|
188
- bindings << if key == :procs
189
- ProcBinding.new(value)
190
- else
191
- Binding.new(key, value)
192
- end
193
- end
194
- end
145
+ class <<self
146
+ # @api private
147
+ # Get known headers
148
+ # @return [Hash] keys: header classes, values: hashes
149
+ attr_reader :known_headers
195
150
 
196
- # Helper method to calculate length of +hdr+ and set its +length+ field.
197
- # To be used by +#calc_length+ in Base subclasses.
198
- # @param [Base] hdr
199
- # @param [Boolean] header_in_size if +true+ header is included in length,
200
- # if +false+, only +body+ is taken into account
201
- def self.calculate_and_set_length(hdr, header_in_size: true)
202
- length = if header_in_size
203
- hdr.sz
204
- else
205
- hdr[:body].sz
206
- end
207
- hdr.length = length
208
- end
151
+ # Bind a upper header to current one.
152
+ # @param [Class] header_klass header class to bind to current class
153
+ # @param [Hash] args current class fields and their value when +header_klass+
154
+ # is embedded in current class.
155
+ #
156
+ # Given value may be a lambda, whose alone argument is the value extracted
157
+ # from header field (or +nil+ when lambda is used to set field while adding
158
+ # a header).
159
+ #
160
+ # Special key +procs+ may be used to set 2 lambdas, the former to set
161
+ # fields, the latter to check bindings. This may be used when multiple and
162
+ # non-trivial checks should be made.
163
+ # @return [void]
164
+ # @example Basic examples
165
+ # # Bind Header2 to Header1 when field1 from Header1 has a value of 42
166
+ # Header1.bind Header2, field1: 42
167
+ # # Bind Header3 to Header1 when field1 from Header1 has a value of 43
168
+ # # and field2 has value 43 or 44
169
+ # Header1.bind Header3, field1: 43, field2: 43
170
+ # Header1.bind Header3, field1: 43, field2: 44
171
+ # @example Defining a binding on a field using a lambda.
172
+ # # Bind Header4 to Header1 when field1 from Header1 has a value
173
+ # # greater or equal to 44. When adding a Header2 to a Header1
174
+ # # with Packet#add, force value to 44.
175
+ # Header1.bind Header4, field1: ->(v) { v.nil? ? 44 : v >= 44 }
176
+ # @example Defining a binding using procs key
177
+ # # Bind Header5 to Header1 when field1 from Header1 has a value of 41
178
+ # # and first two bytes of header1's body are null.
179
+ # # When adding a Header2 to a Header1 with Packet#add, force value to 44.
180
+ # Header1.bind Header5, procs: [->(hdr) { hdr.field1 = 41 }
181
+ # ->(hdr) { hdr.field1 == 41 && hdr.body[0..1] == "\x00\x00" }]
182
+ # @since 2.7.0
183
+ def bind(header_klass, args={})
184
+ if @known_headers[header_klass].nil?
185
+ bindings = Bindings.new
186
+ @known_headers[header_klass] = bindings
187
+ else
188
+ bindings = @known_headers[header_klass]
189
+ end
190
+ bindings.new_set
191
+ args.each do |key, value|
192
+ bindings << if key == :procs
193
+ ProcBinding.new(value)
194
+ else
195
+ Binding.new(key, value)
196
+ end
197
+ end
198
+ end
209
199
 
210
- # @api private
211
- # Get known headers
212
- # @return [Hash] keys: header classes, values: hashes
213
- def self.known_headers
214
- @known_headers
200
+ # Helper method to calculate length of +hdr+ and set its +length+ field.
201
+ # To be used by +#calc_length+ in Base subclasses.
202
+ # @param [Base] hdr
203
+ # @param [Boolean] header_in_size if +true+ header is included in length,
204
+ # if +false+, only +body+ is taken into account
205
+ def calculate_and_set_length(hdr, header_in_size: true)
206
+ length = if header_in_size
207
+ hdr.sz
208
+ else
209
+ hdr[:body].sz
210
+ end
211
+ hdr.length = length
212
+ end
215
213
  end
216
214
 
217
215
  # @see Types::Fields#initialize
@@ -11,6 +11,8 @@ module PacketGen
11
11
  # @abstract Base class for DUID (DHCP Unique ID)
12
12
  # @author Sylvain Daubert
13
13
  class DUID < Types::Fields
14
+ include Types::Fieldable
15
+
14
16
  TYPES = {
15
17
  'DUID-LLT' => 1,
16
18
  'DUID-EN' => 2,
@@ -34,7 +36,7 @@ module PacketGen
34
36
  # @param [String] str
35
37
  # @return [DUID]
36
38
  def read(str)
37
- if self.class == DUID
39
+ if self.instance_of?(DUID)
38
40
  super
39
41
  case type
40
42
  when 1
@@ -17,6 +17,8 @@ module PacketGen
17
17
  # field to replace it by specific option field(s).
18
18
  # @author Sylvain Daubert
19
19
  class Option < Types::Fields
20
+ include Types::Fieldable
21
+
20
22
  # @!attribute type
21
23
  # 16-bit option type
22
24
  # @return [Integer]
@@ -85,7 +87,7 @@ module PacketGen
85
87
  # Get human-readable {#type}
86
88
  # @return [String]
87
89
  def human_type
88
- if self.class == Option
90
+ if self.instance_of?(Option)
89
91
  "option#{type}"
90
92
  else
91
93
  self.class.to_s.sub(/.*::/, '')
@@ -230,17 +232,7 @@ module PacketGen
230
232
 
231
233
  # @!attribute options
232
234
  # @return [RequestedOptions]
233
- define_field :options, RequestedOptions
234
-
235
- # Populate object from +str+
236
- # @param [String] str
237
- # @return [self]
238
- def read(str)
239
- self[:type].read str[0, 2]
240
- self[:length].read str[2, 2]
241
- self[:options].read str[4, self.length]
242
- self
243
- end
235
+ define_field :options, RequestedOptions, builder: ->(h, t) { t.new(length_from: h[:length]) }
244
236
 
245
237
  # Get human-readable data
246
238
  # @return [String]