packetgen 3.2.0 → 3.2.1

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