packetgen 3.2.0 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/bin/pgconsole +3 -3
  4. data/lib/packetgen/header/arp.rb +24 -13
  5. data/lib/packetgen/header/asn1_base.rb +2 -2
  6. data/lib/packetgen/header/base.rb +1 -1
  7. data/lib/packetgen/header/dhcpv6/option.rb +11 -13
  8. data/lib/packetgen/header/dns/opt.rb +2 -2
  9. data/lib/packetgen/header/dns/rr.rb +61 -28
  10. data/lib/packetgen/header/dns.rb +13 -6
  11. data/lib/packetgen/header/dot11/management.rb +2 -5
  12. data/lib/packetgen/header/dot11.rb +1 -1
  13. data/lib/packetgen/header/eap.rb +1 -1
  14. data/lib/packetgen/header/eth.rb +1 -1
  15. data/lib/packetgen/header/http/response.rb +43 -23
  16. data/lib/packetgen/header/igmp.rb +1 -1
  17. data/lib/packetgen/header/ip/option.rb +2 -1
  18. data/lib/packetgen/header/ipv6/addr.rb +3 -0
  19. data/lib/packetgen/header/mdns.rb +19 -25
  20. data/lib/packetgen/header/mld.rb +1 -1
  21. data/lib/packetgen/header/ospfv2/lsa_header.rb +2 -4
  22. data/lib/packetgen/header/ospfv2.rb +1 -1
  23. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +14 -5
  24. data/lib/packetgen/header/ospfv3/lsa.rb +1 -1
  25. data/lib/packetgen/header/ospfv3/lsa_header.rb +2 -4
  26. data/lib/packetgen/header/ospfv3.rb +1 -1
  27. data/lib/packetgen/header/snmp.rb +20 -14
  28. data/lib/packetgen/header/tcp/option.rb +1 -1
  29. data/lib/packetgen/header/tcp.rb +12 -5
  30. data/lib/packetgen/header/tftp.rb +15 -9
  31. data/lib/packetgen/inspect.rb +15 -9
  32. data/lib/packetgen/packet.rb +48 -2
  33. data/lib/packetgen/pcapng/file.rb +13 -13
  34. data/lib/packetgen/pcapng.rb +1 -0
  35. data/lib/packetgen/pcaprub_wrapper.rb +0 -4
  36. data/lib/packetgen/types/abstract_tlv.rb +1 -1
  37. data/lib/packetgen/types/array.rb +8 -1
  38. data/lib/packetgen/types/fields.rb +19 -19
  39. data/lib/packetgen/types/int.rb +7 -0
  40. data/lib/packetgen/types/oui.rb +1 -1
  41. data/lib/packetgen/types/tlv.rb +17 -9
  42. data/lib/packetgen/utils.rb +55 -21
  43. data/lib/packetgen/version.rb +1 -1
  44. data/lib/packetgen.rb +3 -3
  45. metadata +3 -3
@@ -78,17 +78,15 @@ module PacketGen
78
78
  # Compute and set Fletcher-16 checksum on LSA
79
79
  # @return [Integer]
80
80
  def calc_checksum
81
- bytes = to_s[2..-1].unpack('C*')
82
-
83
81
  c0 = c1 = 0
84
- bytes.each do |byte|
82
+ to_s[2..-1].unpack('C*').each do |byte|
85
83
  c0 += byte
86
84
  c1 += c0
87
85
  end
88
86
  c0 %= 255
89
87
  c1 %= 255
90
88
 
91
- x = ((sz - 16 - 1) * c0 - c1) % 255
89
+ x = ((sz - 17) * c0 - c1) % 255
92
90
  x += 255 if x <= 0
93
91
  y = 255 * 2 - c0 - x
94
92
  y -= 255 if y > 255
@@ -145,7 +145,7 @@ module PacketGen
145
145
  # directly called
146
146
  def added_to_packet(packet)
147
147
  ospf_idx = packet.headers.size
148
- packet.instance_eval "def ospfize(**kwargs) @headers[#{ospf_idx}].ospfize(**kwargs); end"
148
+ packet.instance_eval "def ospfize(**kwargs) @headers[#{ospf_idx}].ospfize(**kwargs); end" # def ospfize(**kwargs) @headers[2].ospfize(**kwargs); end
149
149
  end
150
150
 
151
151
  # Compute checksum and set +checksum+ field
@@ -286,20 +286,11 @@ module PacketGen
286
286
  def inspect
287
287
  str = super
288
288
  str << Inspect.shift_level
289
- if self[:data].chosen.nil?
290
- str << Inspect::FMT_ATTR % [self[:data].type, :data, '']
291
- else
292
- data = self[:data]
293
- str << Inspect::FMT_ATTR % [data.type, :data, data.chosen_value.type]
294
- str << Inspect.dashed_line('ASN.1 content')
295
- str << data.chosen_value.inspect(1)
296
- begin
297
- str << Inspect.inspect_body(self[:message].to_der, 'ASN.1 DER')
298
- rescue StandardError => e
299
- raise unless e.message.match?(/TAG.*not handled/)
300
- end
301
- str
302
- end
289
+ str << if self[:data].chosen.nil?
290
+ Inspect::FMT_ATTR % [self[:data].type, :data, '']
291
+ else
292
+ inspect_data
293
+ end
303
294
  end
304
295
 
305
296
  # @api private
@@ -315,6 +306,21 @@ module PacketGen
315
306
 
316
307
  packet.udp.sport = packet.udp.dport
317
308
  end
309
+
310
+ private
311
+
312
+ def inspect_data
313
+ data = self[:data]
314
+ str = Inspect::FMT_ATTR % [data.type, :data, data.chosen_value.type]
315
+ str << Inspect.dashed_line('ASN.1 content')
316
+ str << data.chosen_value.inspect(1)
317
+ begin
318
+ str << Inspect.inspect_body(self[:message].to_der, 'ASN.1 DER')
319
+ rescue StandardError => e
320
+ raise unless e.message.match?(/TAG.*not handled/)
321
+ end
322
+ str
323
+ end
318
324
  end
319
325
 
320
326
  self.add_class SNMP
@@ -43,7 +43,7 @@ module PacketGen
43
43
  # @!attribute value
44
44
  # @return [Integer,String] option value
45
45
  define_field :value, Types::String, optional: ->(h) { h.length? && h.length > 2 },
46
- builder: ->(h, t) { t.new(length_from: -> { h.length - 2 }) }
46
+ builder: ->(h, t) { t.new(length_from: -> { h.length - 2 }) }
47
47
 
48
48
  # @param [hash] options
49
49
  # @option options [Integer] :kind
@@ -216,11 +216,7 @@ module PacketGen
216
216
  doff = Inspect.int_dec_hex(data_offset, 1)
217
217
  str << shift << Inspect::FMT_ATTR % ['', 'data_offset', doff]
218
218
  str << shift << Inspect::FMT_ATTR % ['', 'reserved', reserved]
219
- flags = +''
220
- %w[ns cwr ece urg ack psh rst syn fin].each do |fl|
221
- flags << (send("flag_#{fl}?") ? fl[0].upcase : '.')
222
- end
223
- str << shift << Inspect::FMT_ATTR % ['', 'flags', flags]
219
+ str << shift << Inspect::FMT_ATTR % ['', 'flags', flags2string]
224
220
  end
225
221
  end
226
222
 
@@ -231,6 +227,17 @@ module PacketGen
231
227
  self[:sport], self[:dport] = self[:dport], self[:sport]
232
228
  self
233
229
  end
230
+
231
+ private
232
+
233
+ def flags2string
234
+ flags = +''
235
+ %w[ns cwr ece urg ack psh rst syn fin].each do |fl|
236
+ flags << (send("flag_#{fl}?") ? fl[0].upcase : '.')
237
+ end
238
+
239
+ flags
240
+ end
234
241
  end
235
242
 
236
243
  self.add_class TCP
@@ -102,22 +102,18 @@ module PacketGen
102
102
  client_tid = packet.udp.sport
103
103
  server_tid = nil
104
104
  ary.each do |pkt|
105
+ next unless pkt.is?('UDP')
106
+
105
107
  if server_tid.nil?
106
- next unless pkt.is?('UDP') && (pkt.udp.dport == client_tid)
108
+ next unless pkt.udp.dport == client_tid
107
109
 
108
110
  server_tid = pkt.udp.sport
109
111
  else
110
- next unless pkt.is?('UDP')
111
-
112
112
  tids = [server_tid, client_tid]
113
113
  ports = [pkt.udp.sport, pkt.udp.dport]
114
114
  next unless (tids - ports).empty?
115
115
  end
116
- tftp = Packet.parse(pkt.body, first_header: 'TFTP')
117
- udp_dport = pkt.udp.dport
118
- pkt.encapsulate tftp
119
- # need to fix it as #encapsulate force it to 69
120
- pkt.udp.dport = udp_dport
116
+ decode_tftp_packet(pkt)
121
117
  end
122
118
  end
123
119
 
@@ -135,7 +131,17 @@ module PacketGen
135
131
  def added_to_packet(packet)
136
132
  return if packet.respond_to? :tftp
137
133
 
138
- packet.instance_eval("def tftp(arg=nil); header(#{self.class}, arg); end")
134
+ packet.instance_eval("def tftp(arg=nil); header(#{self.class}, arg); end") # def tftp(arg=nil); header(TFTP, arg); end
135
+ end
136
+
137
+ private
138
+
139
+ def decode_tftp_packet(pkt)
140
+ tftp = Packet.parse(pkt.body, first_header: 'TFTP')
141
+ udp_dport = pkt.udp.dport
142
+ pkt.encapsulate tftp
143
+ # need to fix it as #encapsulate force it to 69
144
+ pkt.udp.dport = udp_dport
139
145
  end
140
146
 
141
147
  # TFTP Read Request header
@@ -12,6 +12,8 @@ module PacketGen
12
12
  module Inspect
13
13
  # Maximum number of characters on a line for INSPECT
14
14
  MAX_WIDTH = 70
15
+ # @private
16
+ SEPARATOR = ('-' * MAX_WIDTH << "\n").freeze
15
17
 
16
18
  # Format to inspect attribute
17
19
  FMT_ATTR = "%14s %16s: %s\n"
@@ -105,19 +107,23 @@ module PacketGen
105
107
  return '' if body.nil? || body.empty?
106
108
 
107
109
  str = dashed_line(name, 2)
108
- str << (0..15).to_a.map { |v| ' %02d' % v }.join << "\n"
109
- str << '-' * MAX_WIDTH << "\n"
110
+ 0.upto(15) { |v| str << ' %02d' % v }
111
+ str << "\n" << SEPARATOR
110
112
  unless body.empty?
111
113
  (body.size / 16 + 1).times do |i|
112
- octets = body.to_s[i * 16, 16].unpack('C*')
113
- o_str = octets.map { |v| ' %02x' % v }.join
114
- str << o_str
115
- str << ' ' * (3 * 16 - o_str.size) unless o_str.size >= 3 * 16
116
- str << ' ' << octets.map { |v| v < 128 && v > 31 ? v.chr : '.' }.join
117
- str << "\n"
114
+ str << self.convert_body_slice(body.to_s[i * 16, 16])
118
115
  end
119
116
  end
120
- str << '-' * MAX_WIDTH << "\n"
117
+ str << SEPARATOR
118
+ end
119
+
120
+ # @private
121
+ def self.convert_body_slice(bslice)
122
+ octets = bslice.unpack('C*')
123
+ str = octets.map { |v| ' %02x' % v }.join
124
+ str << ' ' * (48 - str.size) unless str.size >= 48
125
+ str << ' ' << octets.map { |v| (32..127).cover?(v) ? v.chr : '.' }.join
126
+ str << "\n"
121
127
  end
122
128
  end
123
129
  end
@@ -51,6 +51,9 @@ module PacketGen
51
51
  # Get packet headers, ordered as they appear in the packet.
52
52
  # @return [Array<Header::Base>]
53
53
  attr_reader :headers
54
+ # Activaye or deactivate header cache (activated by default)
55
+ # @return [Boolean]
56
+ attr_accessor :cache_headers
54
57
 
55
58
  # Create a new Packet
56
59
  # @param [String] protocol base protocol for packet
@@ -121,6 +124,8 @@ module PacketGen
121
124
  # @private
122
125
  def initialize
123
126
  @headers = []
127
+ @header_cache = {}
128
+ @cache_headers = true
124
129
  end
125
130
 
126
131
  # Add a protocol header in packet.
@@ -166,7 +171,7 @@ module PacketGen
166
171
  # @raise [ArgumentError] unknown protocol
167
172
  def is?(protocol)
168
173
  klass = check_protocol protocol
169
- headers.any? { |h| h.is_a? klass }
174
+ headers.any?(klass)
170
175
  end
171
176
 
172
177
  # Recalculate all packet checksums
@@ -271,6 +276,7 @@ module PacketGen
271
276
  headers.delete(hdr)
272
277
  add_header(next_hdr, previous_header: prev_hdr) if prev_hdr && next_hdr
273
278
  end
279
+ invalidate_header_cache
274
280
  rescue ArgumentError => e
275
281
  raise FormatError, e.message
276
282
  end
@@ -356,6 +362,7 @@ module PacketGen
356
362
  headers.each do |header|
357
363
  add_magic_header_method header
358
364
  end
365
+ invalidate_header_cache
359
366
  end
360
367
 
361
368
  # Give first header of packet
@@ -409,7 +416,7 @@ module PacketGen
409
416
  # @return [Header::Base]
410
417
  def header(klass, arg)
411
418
  layer = arg.is_a?(Integer) ? arg : 1
412
- header = headers.select { |h| h.is_a? klass }[layer - 1]
419
+ header = find_header(klass, layer)
413
420
  return header unless arg.is_a? Hash
414
421
 
415
422
  arg.each do |key, value|
@@ -421,6 +428,19 @@ module PacketGen
421
428
  header
422
429
  end
423
430
 
431
+ # Get header from cache, or find it in packet
432
+ # @param [Class] klass
433
+ # @param [Integer] layer
434
+ # @return [Header::Base]
435
+ def find_header(klass, layer)
436
+ header = fetch_header_from_cache(klass, layer)
437
+ return header if header
438
+
439
+ header = headers.select { |h| h.is_a? klass }[layer - 1]
440
+ add_header_to_cache(header, klass, layer)
441
+ header
442
+ end
443
+
424
444
  # check if protocol is known
425
445
  # @param [String] protocol
426
446
  # @raise [ArgumentError] unknown protocol
@@ -438,6 +458,7 @@ module PacketGen
438
458
  # @return [void]
439
459
  # @raise [BindingError]
440
460
  def add_header(header, previous_header: nil, parsing: false)
461
+ invalidate_header_cache
441
462
  prev_header = previous_header || last_header
442
463
  add_to_previous_header(prev_header, header, parsing) if prev_header
443
464
 
@@ -521,6 +542,31 @@ module PacketGen
521
542
 
522
543
  nil
523
544
  end
545
+
546
+ def invalidate_header_cache
547
+ return unless cache_headers
548
+
549
+ @header_cache = {}
550
+ end
551
+
552
+ # Fetch header from cache if authorized
553
+ # @param [Class] klass
554
+ # @param [Integer] layer
555
+ # @return [Header::Base,nil]
556
+ def fetch_header_from_cache(klass, layer)
557
+ @header_cache.fetch(klass, []).fetch(layer - 1, nil) if cache_headers
558
+ end
559
+
560
+ # Add header to cache if authorized
561
+ # @param [Class] klass
562
+ # @param [Integer] layer
563
+ # @return [Header::Base,nil]
564
+ def add_header_to_cache(header, klass, layer)
565
+ return unless cache_headers
566
+
567
+ @header_cache[klass] ||= []
568
+ @header_cache[klass][layer - 1] = header
569
+ end
524
570
  end
525
571
  end
526
572
 
@@ -9,7 +9,7 @@ module PacketGen
9
9
  module PcapNG
10
10
  # PcapNG::File is a complete Pcap-NG file handler.
11
11
  # @author Sylvain Daubert
12
- class File
12
+ class File # rubocop:disable Metrics/ClassLength
13
13
  # Known link types
14
14
  KNOWN_LINK_TYPES = {
15
15
  LINKTYPE_ETHERNET => 'Eth',
@@ -21,13 +21,11 @@ module PacketGen
21
21
  }.freeze
22
22
 
23
23
  # @private
24
- BLOCK_TYPES = Hash[
25
- PcapNG.constants(false).select { |c| c.to_s.include?('_TYPE') }.map do |c|
26
- type_value = PcapNG.const_get(c).to_i
27
- klass = PcapNG.const_get(c.to_s[0..-6]) # @todo use delete_suffix('_TYPE') when support for Ruby 2.4 will stop
28
- [type_value, klass]
29
- end
30
- ].freeze
24
+ BLOCK_TYPES = PcapNG.constants(false).select { |c| c.to_s.include?('_TYPE') }.map do |c|
25
+ type_value = PcapNG.const_get(c).to_i
26
+ klass = PcapNG.const_get(c.to_s.delete_suffix('_TYPE'))
27
+ [type_value, klass]
28
+ end.to_h.freeze
31
29
 
32
30
  # Get file sections
33
31
  # @return [Array]
@@ -204,7 +202,7 @@ module PacketGen
204
202
  # @return [Array] array of 2 elements: filename and size written
205
203
  # @todo for 4.0, replace +options+ by +append+ kwarg
206
204
  def to_file(filename, options={})
207
- mode = (options[:append] && ::File.exist?(filename)) ? 'ab' : 'wb'
205
+ mode = options[:append] && ::File.exist?(filename) ? 'ab' : 'wb'
208
206
  ::File.open(filename, mode) { |f| f.write(self.to_s) }
209
207
  [filename, self.to_s.size]
210
208
  end
@@ -317,12 +315,12 @@ module PacketGen
317
315
  shb = parse_shb(SHB.new, io)
318
316
  raise InvalidFileError, 'no Section header found' unless shb.is_a?(SHB)
319
317
 
320
- to_parse = if shb.section_len.to_i != 0xffffffffffffffff
321
- # Section length is defined
322
- StringIO.new(io.read(shb.section_len.to_i))
323
- else
318
+ to_parse = if shb.section_len.to_i == 0xffffffffffffffff
324
319
  # section length is undefined
325
320
  io
321
+ else
322
+ # Section length is defined
323
+ StringIO.new(io.read(shb.section_len.to_i))
326
324
  end
327
325
 
328
326
  until to_parse.eof?
@@ -388,6 +386,7 @@ module PacketGen
388
386
  end
389
387
  end
390
388
 
389
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
391
390
  # Extract and check options for #array_to_file
392
391
  def array_to_file_options_from_hash(options)
393
392
  %i[filename arr ts].each do |deprecated_opt|
@@ -404,6 +403,7 @@ module PacketGen
404
403
 
405
404
  [filename, ary, ts, ts_inc, append]
406
405
  end
406
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
407
407
 
408
408
  def create_new_shb_section
409
409
  section = SHB.new
@@ -41,6 +41,7 @@ module PacketGen
41
41
 
42
42
  # Base error class for PcapNG
43
43
  class Error < PacketGen::Error; end
44
+
44
45
  # Invalid PcapNG file error
45
46
  class InvalidFileError < Error; end
46
47
  end
@@ -38,8 +38,6 @@ module PacketGen
38
38
  pcap.activate
39
39
  end
40
40
 
41
- # rubocop:disable Metrics/ParameterLists
42
-
43
41
  # Capture packets from a network interface
44
42
  # @param [String] iface interface name
45
43
  # @param [Integer] snaplen
@@ -57,8 +55,6 @@ module PacketGen
57
55
  pcap.each(&block)
58
56
  end
59
57
 
60
- # rubocop:enable Metrics/ParameterLists
61
-
62
58
  # Inject given data onto wire
63
59
  # @param [String] iface interface name
64
60
  # @param [String] data to inject
@@ -60,7 +60,7 @@ module PacketGen
60
60
  class AbstractTLV < Types::Fields
61
61
  include Fieldable
62
62
 
63
- class <<self
63
+ class << self
64
64
  # @return [Hash]
65
65
  attr_accessor :aliases
66
66
  attr_accessor :header_in_length
@@ -62,7 +62,7 @@ module PacketGen
62
62
  HUMAN_SEPARATOR = ','
63
63
 
64
64
  # rubocop:disable Naming/AccessorMethodName
65
- class <<self
65
+ class << self
66
66
  # Get class set with {.set_of}.
67
67
  # @return [Class]
68
68
  # @since 3.0.0
@@ -154,6 +154,8 @@ module PacketGen
154
154
  self
155
155
  end
156
156
 
157
+ # rubocop:disable Metrics/CyclomaticComplexity
158
+
157
159
  # Populate object from a string
158
160
  # @param [String] str
159
161
  # @return [self]
@@ -171,6 +173,7 @@ module PacketGen
171
173
  end
172
174
  self
173
175
  end
176
+ # rubocop:enable Metrics/CyclomaticComplexity
174
177
 
175
178
  # Get size in bytes
176
179
  # @return [Integer]
@@ -228,18 +231,22 @@ module PacketGen
228
231
  class ArrayOfInt8 < Array
229
232
  set_of Int8
230
233
  end
234
+
231
235
  # Specialized array to handle serie of {Int16}.
232
236
  class ArrayOfInt16 < Array
233
237
  set_of Int16
234
238
  end
239
+
235
240
  # Specialized array to handle serie of {Int16le}.
236
241
  class ArrayOfInt16le < Array
237
242
  set_of Int16le
238
243
  end
244
+
239
245
  # Specialized array to handle serie of {Int32}.
240
246
  class ArrayOfInt32 < Types::Array
241
247
  set_of Types::Int32
242
248
  end
249
+
243
250
  # Specialized array to handle serie of {Int32le}.
244
251
  class ArrayOfInt32le < Types::Array
245
252
  set_of Types::Int32le
@@ -115,7 +115,7 @@ module PacketGen
115
115
  # @private bit field definitions
116
116
  @bit_fields = {}
117
117
 
118
- class <<self
118
+ class << self
119
119
  # Get field definitions for this class.
120
120
  # @return [Hash]
121
121
  # @since 3.1.0
@@ -282,7 +282,7 @@ module PacketGen
282
282
  type = field_defs[attr].type
283
283
  raise TypeError, "#{attr} is not a PacketGen::Types::Int" unless type < Types::Int
284
284
 
285
- total_size = type.new.width * 8
285
+ total_size = type.new.nbits
286
286
  idx = total_size - 1
287
287
 
288
288
  until args.empty?
@@ -358,15 +358,15 @@ module PacketGen
358
358
  clear_mask = compute_clear_mask(total_size, field_mask)
359
359
 
360
360
  class_eval <<-METHODS
361
- def #{name}?
362
- val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
363
- val != 0
364
- end
365
- def #{name}=(v)
366
- val = v ? 1 : 0
367
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
368
- self[:#{attr}].value |= val << #{shift}
369
- end
361
+ def #{name}? # def bit?
362
+ val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift} # val = (self[:attr}].to_i & 1}) >> 1
363
+ val != 0 # val != 0
364
+ end # end
365
+ def #{name}=(v) # def bit=(v)
366
+ val = v ? 1 : 0 # val = v ? 1 : 0
367
+ self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask} # self[:attr].value = self[:attr].to_i & 0xfffd
368
+ self[:#{attr}].value |= val << #{shift} # self[:attr].value |= val << 1
369
+ end # end
370
370
  METHODS
371
371
  end
372
372
 
@@ -375,13 +375,13 @@ module PacketGen
375
375
  clear_mask = compute_clear_mask(total_size, field_mask)
376
376
 
377
377
  class_eval <<-METHODS
378
- def #{name}
379
- (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
380
- end
381
- def #{name}=(v)
382
- self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
383
- self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
384
- end
378
+ def #{name} # def multibit
379
+ (self[:#{attr}].to_i & #{field_mask}) >> #{shift} # (self[:attr].to_i & 6) >> 1
380
+ end # end
381
+ def #{name}=(v) # def multibit=(v)
382
+ self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask} # self[:attr].value = self[:attr].to_i & 0xfff9
383
+ self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift} # self[:attr].value |= (v & 3) << 1
384
+ end # end
385
385
  METHODS
386
386
  end
387
387
 
@@ -523,7 +523,7 @@ module PacketGen
523
523
  # Return object as a hash
524
524
  # @return [Hash] keys: attributes, values: attribute values
525
525
  def to_h
526
- Hash[fields.map { |f| [f, @fields[f].to_human] }]
526
+ fields.map { |f| [f, @fields[f].to_human] }.to_h
527
527
  end
528
528
 
529
529
  # Get offset of given field in {Fields} structure.
@@ -89,6 +89,13 @@ module PacketGen
89
89
  format_str % [to_i.to_s, to_i]
90
90
  end
91
91
 
92
+ # Return the number of bits used to encode this Int
93
+ # @return [Integer]
94
+ # @since 3.2.1
95
+ def nbits
96
+ width * 8
97
+ end
98
+
92
99
  private
93
100
 
94
101
  def format_str
@@ -32,7 +32,7 @@ module PacketGen
32
32
  def from_human(str)
33
33
  return self if str.nil?
34
34
 
35
- bytes = str.split(/:/)
35
+ bytes = str.split(':')
36
36
  raise ArgumentError, 'not a OUI' unless bytes.size == 3
37
37
 
38
38
  self[:b2].read(bytes[0].to_i(16))
@@ -56,12 +56,8 @@ module PacketGen
56
56
  def initialize(options={})
57
57
  Deprecation.deprecated_class(self.class, AbstractTLV)
58
58
  super
59
- self[:type] = options[:t].new(self.type) if options[:t]
60
- self[:length] = options[:l].new(self.length) if options[:l]
61
- self[:value] = options[:v].new if options[:v]
62
- self.type = options[:type] if options[:type]
63
- self.value = options[:value] if options[:value]
64
- self.length = options[:length] if options[:length]
59
+ initialize_types(options)
60
+ initialize_values(options)
65
61
  end
66
62
 
67
63
  # Populate object from a binary string
@@ -137,9 +133,9 @@ module PacketGen
137
133
  else
138
134
  '%s'
139
135
  end
140
- @lenstr ||= "%-#{(2**(self[:length].width * 8) - 1).to_s.size}u"
141
- "#{name} type:#{@typestr} length:#{@lenstr} value:#{value.inspect}" % [human_type,
142
- length]
136
+ lenstr = "%-#{(2**self[:length].nbits - 1).to_s.size}u"
137
+ "#{name} type:#{@typestr} length:#{lenstr} value:#{value.inspect}" % [human_type,
138
+ length]
143
139
  end
144
140
 
145
141
  private
@@ -147,6 +143,18 @@ module PacketGen
147
143
  def human_types?
148
144
  self.class.const_defined? :TYPES
149
145
  end
146
+
147
+ def initialize_types(options)
148
+ self[:type] = options[:t].new(self.type) if options[:t]
149
+ self[:length] = options[:l].new(self.length) if options[:l]
150
+ self[:value] = options[:v].new if options[:v]
151
+ end
152
+
153
+ def initialize_values(options)
154
+ self.type = options[:type] if options[:type]
155
+ self.value = options[:value] if options[:value]
156
+ self.length = options[:length] if options[:length]
157
+ end
150
158
  end
151
159
  end
152
160
  end