packetgen 3.1.5 → 3.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/bin/pgconsole +1 -0
  3. data/lib/packetgen.rb +2 -2
  4. data/lib/packetgen/capture.rb +9 -0
  5. data/lib/packetgen/header/base.rb +68 -70
  6. data/lib/packetgen/header/dhcpv6/duid.rb +3 -1
  7. data/lib/packetgen/header/dhcpv6/option.rb +3 -1
  8. data/lib/packetgen/header/dns/name.rb +18 -7
  9. data/lib/packetgen/header/dns/question.rb +2 -0
  10. data/lib/packetgen/header/dot11.rb +23 -6
  11. data/lib/packetgen/header/dot11/data.rb +9 -5
  12. data/lib/packetgen/header/eap.rb +3 -2
  13. data/lib/packetgen/header/eth.rb +4 -8
  14. data/lib/packetgen/header/http/headers.rb +3 -4
  15. data/lib/packetgen/header/http/request.rb +32 -17
  16. data/lib/packetgen/header/http/response.rb +1 -1
  17. data/lib/packetgen/header/http/verbs.rb +1 -1
  18. data/lib/packetgen/header/igmpv3/group_record.rb +2 -0
  19. data/lib/packetgen/header/ip.rb +27 -26
  20. data/lib/packetgen/header/ip/addr.rb +2 -3
  21. data/lib/packetgen/header/ip/option.rb +4 -4
  22. data/lib/packetgen/header/ipv6/addr.rb +1 -2
  23. data/lib/packetgen/header/mldv2/mcast_address_record.rb +2 -0
  24. data/lib/packetgen/header/ospfv2/ls_request.rb +2 -0
  25. data/lib/packetgen/header/ospfv2/lsa.rb +6 -0
  26. data/lib/packetgen/header/ospfv2/lsa_header.rb +2 -1
  27. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +2 -0
  28. data/lib/packetgen/header/ospfv3/ls_request.rb +2 -0
  29. data/lib/packetgen/header/ospfv3/lsa.rb +2 -0
  30. data/lib/packetgen/header/ospfv3/lsa_header.rb +2 -1
  31. data/lib/packetgen/header/snmp.rb +3 -2
  32. data/lib/packetgen/header/tcp/option.rb +8 -6
  33. data/lib/packetgen/packet.rb +7 -3
  34. data/lib/packetgen/pcapng.rb +11 -11
  35. data/lib/packetgen/pcapng/block.rb +15 -2
  36. data/lib/packetgen/pcapng/epb.rb +22 -15
  37. data/lib/packetgen/pcapng/file.rb +164 -81
  38. data/lib/packetgen/pcapng/idb.rb +7 -9
  39. data/lib/packetgen/pcapng/shb.rb +35 -28
  40. data/lib/packetgen/pcapng/spb.rb +16 -12
  41. data/lib/packetgen/pcapng/unknown_block.rb +3 -11
  42. data/lib/packetgen/pcaprub_wrapper.rb +8 -8
  43. data/lib/packetgen/types.rb +1 -0
  44. data/lib/packetgen/types/abstract_tlv.rb +2 -3
  45. data/lib/packetgen/types/array.rb +15 -9
  46. data/lib/packetgen/types/cstring.rb +38 -17
  47. data/lib/packetgen/types/fieldable.rb +65 -0
  48. data/lib/packetgen/types/fields.rb +91 -56
  49. data/lib/packetgen/types/int.rb +2 -2
  50. data/lib/packetgen/types/int_string.rb +7 -2
  51. data/lib/packetgen/types/length_from.rb +18 -10
  52. data/lib/packetgen/types/oui.rb +1 -2
  53. data/lib/packetgen/types/string.rb +45 -8
  54. data/lib/packetgen/types/tlv.rb +1 -2
  55. data/lib/packetgen/utils.rb +2 -2
  56. data/lib/packetgen/version.rb +1 -1
  57. metadata +13 -12
  58. data/lib/packetgen/inspectable.rb +0 -20
@@ -6,6 +6,8 @@
6
6
  # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
7
7
  # This program is published under MIT license.
8
8
 
9
+ # rubocop:disable Metrics/ClassLength
10
+
9
11
  module PacketGen
10
12
  # An object of type {Packet} handles a network packet. This packet may contain
11
13
  # multiple protocol headers, starting from MAC layer or from Network (OSI) layer.
@@ -77,10 +79,10 @@ module PacketGen
77
79
  # @yieldparam [Packet,String] packet if a block is given, yield each
78
80
  # captured packet (Packet or raw data String, depending on +:parse+ option)
79
81
  # @return [Array<Packet>] captured packet
80
- def self.capture(**kwargs)
81
- capture = Capture.new(kwargs)
82
+ def self.capture(**kwargs, &block)
83
+ capture = Capture.new(**kwargs)
82
84
  if block_given?
83
- capture.start { |packet| yield packet }
85
+ capture.start(&block)
84
86
  else
85
87
  capture.start
86
88
  end
@@ -522,5 +524,7 @@ module PacketGen
522
524
  end
523
525
  end
524
526
 
527
+ # rubocop:enable Metrics/ClassLength
528
+
525
529
  require_relative 'headerable'
526
530
  require_relative 'header'
@@ -13,13 +13,13 @@ module PacketGen
13
13
  # @author Sylvain Daubert
14
14
  module PcapNG
15
15
  # Section Header Block type number
16
- SHB_TYPE = Types::Int32.new(0x0A0D0D0A, :little)
16
+ SHB_TYPE = Types::Int32.new(0x0A0D0D0A, :little).freeze
17
17
  # Interface Description Block type number
18
- IDB_TYPE = Types::Int32.new(1, :little)
18
+ IDB_TYPE = Types::Int32.new(1, :little).freeze
19
19
  # Simple Packet Block type number
20
- SPB_TYPE = Types::Int32.new(3, :little)
20
+ SPB_TYPE = Types::Int32.new(3, :little).freeze
21
21
  # Enhanced Packet Block type number
22
- EPB_TYPE = Types::Int32.new(6, :little)
22
+ EPB_TYPE = Types::Int32.new(6, :little).freeze
23
23
 
24
24
  # IEEE 802.3 Ethernet (10Mb, 100Mb, 1000Mb, and up)
25
25
  LINKTYPE_ETHERNET = 1
@@ -46,10 +46,10 @@ module PacketGen
46
46
  end
47
47
  end
48
48
 
49
- require_relative 'pcapng/block.rb'
50
- require_relative 'pcapng/unknown_block.rb'
51
- require_relative 'pcapng/shb.rb'
52
- require_relative 'pcapng/idb.rb'
53
- require_relative 'pcapng/epb.rb'
54
- require_relative 'pcapng/spb.rb'
55
- require_relative 'pcapng/file.rb'
49
+ require_relative 'pcapng/block'
50
+ require_relative 'pcapng/unknown_block'
51
+ require_relative 'pcapng/shb'
52
+ require_relative 'pcapng/idb'
53
+ require_relative 'pcapng/epb'
54
+ require_relative 'pcapng/spb'
55
+ require_relative 'pcapng/file'
@@ -49,7 +49,9 @@ module PacketGen
49
49
  # @return [void]
50
50
  def pad_field(*fields)
51
51
  fields.each do |field|
52
- @fields[field] << "\x00" * (4 - (@fields[field].sz % 4)) unless (@fields[field].sz % 4).zero?
52
+ obj = @fields[field]
53
+ pad_size = (obj.sz % 4).zero? ? 0 : (4 - (obj.sz % 4))
54
+ obj << "\x00" * pad_size
53
55
  end
54
56
  end
55
57
 
@@ -59,7 +61,7 @@ module PacketGen
59
61
  # Must be called by all subclass #initialize method.
60
62
  # @param [:little, :big] endian
61
63
  # @return [:little, :big] returns endian
62
- def set_endianness(endian)
64
+ def endianness(endian)
63
65
  raise ArgumentError, "unknown endianness for #{self.class}" unless %i[little big].include?(endian)
64
66
 
65
67
  @endian = endian
@@ -76,6 +78,17 @@ module PacketGen
76
78
 
77
79
  StringIO.new(force_binary(str_or_io.to_s))
78
80
  end
81
+
82
+ def remove_padding(io, data_len)
83
+ data_pad_len = (4 - (data_len % 4)) % 4
84
+ io.read data_pad_len
85
+ data_pad_len
86
+ end
87
+
88
+ def read_blocklen2_and_check(io)
89
+ self[:block_len2].read io.read(4)
90
+ check_len_coherency
91
+ end
79
92
  end
80
93
  end
81
94
  end
@@ -73,7 +73,7 @@ module PacketGen
73
73
  # @option options [Integer] :block_len2 block total length
74
74
  def initialize(options={})
75
75
  super
76
- set_endianness(options[:endian] || :little)
76
+ endianness(options[:endian] || :little)
77
77
  recalc_block_len
78
78
  self.type = options[:type] || PcapNG::EPB_TYPE.to_i
79
79
  end
@@ -85,22 +85,13 @@ module PacketGen
85
85
  io = to_io(str_or_io)
86
86
  return self if io.eof?
87
87
 
88
- self[:type].read io.read(4)
89
- self[:block_len].read io.read(4)
90
- self[:interface_id].read io.read(4)
91
- self[:tsh].read io.read(4)
92
- self[:tsl].read io.read(4)
93
- self[:cap_len].read io.read(4)
94
- self[:orig_len].read io.read(4)
88
+ %i[type block_len interface_id tsh tsl cap_len orig_len].each do |attr|
89
+ self[attr].read io.read(self[attr].sz)
90
+ end
95
91
  self[:data].read io.read(self.cap_len)
96
- data_pad_len = (4 - (self[:cap_len].to_i % 4)) % 4
97
- io.read data_pad_len
98
- options_len = self.block_len - self.cap_len - data_pad_len
99
- options_len -= MIN_SIZE
100
- self[:options].read io.read(options_len)
101
- self[:block_len2].read io.read(4)
92
+ read_options(io)
93
+ read_blocklen2_and_check(io)
102
94
 
103
- check_len_coherency
104
95
  self
105
96
  end
106
97
 
@@ -110,6 +101,16 @@ module PacketGen
110
101
  Time.at((self.tsh << 32 | self.tsl) * ts_resol)
111
102
  end
112
103
 
104
+ # Set timestamp from a Time object
105
+ # @param [Time] time
106
+ # @return [Time] time
107
+ def timestamp=(time)
108
+ tstamp = (time.to_r / ts_resol).to_i
109
+ self.tsh = (tstamp & 0xffffffff00000000) >> 32
110
+ self.tsl = tstamp & 0xffffffff
111
+ time
112
+ end
113
+
113
114
  # Return the object as a String
114
115
  # @return [String]
115
116
  def to_s
@@ -127,6 +128,12 @@ module PacketGen
127
128
  @interface.ts_resol
128
129
  end
129
130
  end
131
+
132
+ def read_options(io)
133
+ data_pad_len = remove_padding(io, self.cap_len)
134
+ options_len = self.block_len - self.cap_len - data_pad_len - MIN_SIZE
135
+ self[:options].read io.read(options_len)
136
+ end
130
137
  end
131
138
  end
132
139
  end
@@ -22,9 +22,9 @@ module PacketGen
22
22
 
23
23
  # @private
24
24
  BLOCK_TYPES = Hash[
25
- PcapNG.constants(false).select { |c| c.to_s =~ /_TYPE/ }.map do |c|
25
+ PcapNG.constants(false).select { |c| c.to_s.include?('_TYPE') }.map do |c|
26
26
  type_value = PcapNG.const_get(c).to_i
27
- klass = PcapNG.const_get(c.to_s[0..-6]) # use delete_suffix('_TYPE') when support for Ruby 2.4 will stop
27
+ klass = PcapNG.const_get(c.to_s[0..-6]) # @todo use delete_suffix('_TYPE') when support for Ruby 2.4 will stop
28
28
  [type_value, klass]
29
29
  end
30
30
  ].freeze
@@ -68,20 +68,13 @@ module PacketGen
68
68
  def readfile(fname, &blk)
69
69
  raise ArgumentError, "cannot read file #{fname}" unless ::File.readable?(fname)
70
70
 
71
- ::File.open(fname, 'rb') do |f|
72
- parse_section(f) until f.eof?
73
- end
74
-
71
+ ::File.open(fname, 'rb') { |f| parse_section(f) until f.eof? }
75
72
  return unless blk
76
73
 
77
74
  count = 0
78
- @sections.each do |section|
79
- section.interfaces.each do |intf|
80
- intf.packets.each do |pkt|
81
- count += 1
82
- yield pkt
83
- end
84
- end
75
+ each_packet_with_interface do |pkt, _itf|
76
+ count += 1
77
+ yield pkt
85
78
  end
86
79
  count
87
80
  end
@@ -98,12 +91,10 @@ module PacketGen
98
91
  # @return [Integer] number of packets
99
92
  # @raise [ArgumentError] cannot read +fname+
100
93
  def read_packet_bytes(fname, &blk)
101
- count = 0
102
94
  packets = [] unless blk
103
95
 
104
- readfile(fname) do |packet|
96
+ count = readfile(fname) do |packet|
105
97
  if blk
106
- count += 1
107
98
  yield packet.data.to_s, packet.interface.link_type
108
99
  else
109
100
  packets << packet.data.to_s
@@ -124,19 +115,11 @@ module PacketGen
124
115
  # @return [Integer] number of packets
125
116
  # @raise [ArgumentError] cannot read +fname+
126
117
  def read_packets(fname, &blk)
127
- count = 0
128
118
  packets = [] unless blk
129
119
 
130
- read_packet_bytes(fname) do |packet, link_type|
131
- first_header = KNOWN_LINK_TYPES[link_type]
132
- parsed_pkt = if first_header.nil?
133
- # unknown link type, try to guess
134
- Packet.parse(packet)
135
- else
136
- Packet.parse(packet, first_header: first_header)
137
- end
120
+ count = read_packet_bytes(fname) do |packet, link_type|
121
+ parsed_pkt = parse_packet(packet, link_type)
138
122
  if blk
139
- count += 1
140
123
  yield parsed_pkt
141
124
  else
142
125
  packets << parsed_pkt
@@ -158,6 +141,8 @@ module PacketGen
158
141
  @sections.clear
159
142
  end
160
143
 
144
+ # @deprecated
145
+ # Prefer use of {#to_a} or {#to_h}.
161
146
  # Translates a {File} into an array of packets.
162
147
  # @param [Hash] options
163
148
  # @option options [String] :file if given, object is cleared and filename
@@ -167,37 +152,59 @@ module PacketGen
167
152
  # as value. There is one hash per packet.
168
153
  # @return [Array<Packet>,Array<Hash>]
169
154
  def file_to_array(options={})
170
- Deprecation.deprecated_option(self.class, __method__, :filename) if options[:filename]
171
- Deprecation.deprecated_option(self.class, __method__, :keep_ts) if options[:keep_ts]
155
+ Deprecation.deprecated(self.class, __method__)
172
156
 
173
- filename = options[:filename] || options[:file]
174
- reread filename
157
+ file = options[:file] || options[:filename]
158
+ reread file
175
159
 
176
160
  ary = []
177
- @sections.each do |section|
178
- section.interfaces.each do |itf|
179
- blk = if options[:keep_timestamps] || options[:keep_ts]
180
- proc { |pkt| { pkt.timestamp => pkt.data.to_s } }
181
- else
182
- proc { |pkt| pkt.data.to_s }
183
- end
184
- ary.concat(itf.packets.map(&blk))
185
- end
161
+ blk = if options[:keep_timestamps] || options[:keep_ts]
162
+ proc { |pkt| { pkt.timestamp => pkt.data.to_s } }
163
+ else
164
+ proc { |pkt| pkt.data.to_s }
165
+ end
166
+ each_packet_with_interface do |pkt, _itf|
167
+ ary << blk.call(pkt)
168
+ end
169
+
170
+ ary
171
+ end
172
+
173
+ # Translates a {File} into an array of packets.
174
+ # @return [Array<Packet>]
175
+ # @since 3.1.6
176
+ def to_a
177
+ ary = []
178
+ each_packet_with_interface do |pkt, itf|
179
+ ary << parse_packet(pkt.data.to_s, itf.link_type)
186
180
  end
181
+
187
182
  ary
188
183
  end
189
184
 
185
+ # Translates a {File} into a hash with timestamps as keys.
186
+ # @note Only packets from {EPB} sections are extracted, as {SPB} ones do not have timestamp.
187
+ # @return [Hash{Time => Packet}]
188
+ # @since 3.1.6
189
+ def to_h
190
+ hsh = {}
191
+ each_packet_with_interface do |pkt, itf|
192
+ next if pkt.is_a?(SPB)
193
+
194
+ hsh[pkt.timestamp] = parse_packet(pkt.data.to_s, itf.link_type)
195
+ end
196
+
197
+ hsh
198
+ end
199
+
190
200
  # Writes the {File} to a file.
191
201
  # @param [Hash] options
192
202
  # @option options [Boolean] :append (default: +false+) if set to +true+,
193
203
  # the packets are appended to the file, rather than overwriting it
194
204
  # @return [Array] array of 2 elements: filename and size written
205
+ # @todo for 4.0, replace +options+ by +append+ kwarg
195
206
  def to_file(filename, options={})
196
- mode = if options[:append] && ::File.exist?(filename)
197
- 'ab'
198
- else
199
- 'wb'
200
- end
207
+ mode = (options[:append] && ::File.exist?(filename)) ? 'ab' : 'wb'
201
208
  ::File.open(filename, mode) { |f| f.write(self.to_s) }
202
209
  [filename, self.to_s.size]
203
210
  end
@@ -217,6 +224,7 @@ module PacketGen
217
224
  self.to_file(filename.to_s, append: true)
218
225
  end
219
226
 
227
+ # @deprecated Prefer use of {#read_array} or {#read_hash}.
220
228
  # @overload array_to_file(ary)
221
229
  # Update {File} object with packets.
222
230
  # @param [Array] ary as generated by {#file_to_array} or Array of Packet objects.
@@ -238,12 +246,10 @@ module PacketGen
238
246
  filename, ary, ts, ts_inc, append = array_to_file_options(options)
239
247
 
240
248
  section = create_new_shb_section
241
- ts_resol = section.interfaces.last.ts_resol
242
249
 
243
- ts_add_val = 0 # value to add to ts in Array case
244
250
  ary.each do |pkt|
245
- classify_block(section, epb_from_pkt(pkt, section.endian, ts, ts_resol, ts_add_val))
246
- ts_add_val += ts_inc
251
+ classify_block(section, epb_from_pkt(pkt, section, ts))
252
+ ts += ts_inc
247
253
  end
248
254
 
249
255
  if filename
@@ -253,6 +259,52 @@ module PacketGen
253
259
  end
254
260
  end
255
261
 
262
+ # Update current object from an array of packets
263
+ # @param [Array<Packet>] packets
264
+ # @param [Time, nil] timestamp initial timestamp, used for first packet
265
+ # @param [Numeric, nil] ts_inc timestamp increment, in seconds, to increment
266
+ # initial timestamp for each packet
267
+ # @return [void]
268
+ # @note if +timestamp+ and/or +ts_inc+ are nil, {SPB} sections are created
269
+ # for each packet, else {EPB} ones are used
270
+ # @since 3.1.6
271
+ def read_array(packets, timestamp: nil, ts_inc: nil)
272
+ ts = timestamp
273
+ section = create_new_shb_section
274
+ packets.each do |pkt|
275
+ block = create_block_from_pkt(pkt, section, ts, ts_inc)
276
+ classify_block(section, block)
277
+ ts = update_ts(ts, ts_inc)
278
+ end
279
+ end
280
+
281
+ # Update current object from a hash of packets and timestamps
282
+ # @param [Hash{Time => Packet}] hsh
283
+ # @return [void]
284
+ # @since 3.1.6
285
+ def read_hash(hsh)
286
+ section = create_new_shb_section
287
+ hsh.each do |ts, pkt|
288
+ block = create_block_from_pkt(pkt, section, ts, 0)
289
+ classify_block(section, block)
290
+ end
291
+ end
292
+
293
+ # @return [String]
294
+ # @since 3.1.6
295
+ def inspect
296
+ str = +''
297
+ sections.each do |section|
298
+ str << section.inspect
299
+ section.interfaces.each do |itf|
300
+ str << itf.inspect
301
+ itf.packets.each { |block| str << block.inspect }
302
+ end
303
+ end
304
+
305
+ str
306
+ end
307
+
256
308
  private
257
309
 
258
310
  # Parse a section. A section is made of at least a SHB. It than may contain
@@ -260,8 +312,7 @@ module PacketGen
260
312
  # @param [IO] io
261
313
  # @return [void]
262
314
  def parse_section(io)
263
- shb = SHB.new
264
- shb = parse_shb(shb, io)
315
+ shb = parse_shb(SHB.new, io)
265
316
  raise InvalidFileError, 'no Section header found' unless shb.is_a?(SHB)
266
317
 
267
318
  to_parse = if shb.section_len.to_i != 0xffffffffffffffff
@@ -294,17 +345,18 @@ module PacketGen
294
345
  # @param [SHB] shb header of current section
295
346
  # @return [Block]
296
347
  def parse(type, io, shb)
297
- klass = if BLOCK_TYPES.key?(type.to_i)
298
- BLOCK_TYPES[type.to_i]
299
- else
300
- UnknownBlock
301
- end
302
-
303
- block = klass.new(endian: shb.endian)
348
+ block = guess_block_type(type).new(endian: shb.endian)
304
349
  classify_block shb, block
305
350
  block.read(io)
306
351
  end
307
352
 
353
+ # Guess class to use from type
354
+ # @param [Types::Int] type
355
+ # @return [Block]
356
+ def guess_block_type(type)
357
+ BLOCK_TYPES.key?(type.to_i) ? BLOCK_TYPES[type.to_i] : UnknownBlock
358
+ end
359
+
308
360
  # Classify block from its type
309
361
  # @param [SHB] shb header of current section
310
362
  # @param [Block] block block to classify
@@ -315,16 +367,11 @@ module PacketGen
315
367
  @sections << block
316
368
  when IDB
317
369
  shb << block
318
- block.section = shb
319
- when EPB
320
- shb.interfaces[block.interface_id] << block
321
- block.interface = shb.interfaces[block.interface_id]
322
- when SPB
323
- shb.interfaces[0] << block
324
- block.interface = shb.interfaces[0]
370
+ when SPB, EPB
371
+ ifid = block.is_a?(EPB) ? block.interface_id : 0
372
+ shb.interfaces[ifid] << block
325
373
  else
326
- shb.unknown_blocks << block
327
- block.section = shb
374
+ shb.add_unknown_block(block)
328
375
  end
329
376
  end
330
377
 
@@ -341,9 +388,9 @@ module PacketGen
341
388
 
342
389
  # Extract and check options for #array_to_file
343
390
  def array_to_file_options_from_hash(options)
344
- Deprecation.deprecated_option(self.class, :array_to_file, :filename) if options[:filename]
345
- Deprecation.deprecated_option(self.class, :array_to_file, :arr) if options[:arr]
346
- Deprecation.deprecated_option(self.class, :array_to_file, :ts) if options[:ts]
391
+ %i[filename arr ts].each do |deprecated_opt|
392
+ Deprecation.deprecated_option(self.class, :array_to_file, deprecated_opt) if options[deprecated_opt]
393
+ end
347
394
 
348
395
  filename = options[:filename] || options[:file]
349
396
  ary = options[:array] || options[:arr]
@@ -368,10 +415,8 @@ module PacketGen
368
415
  # Compute tsh and tsl from ts
369
416
  def calc_ts(timeslot, ts_resol)
370
417
  this_ts = (timeslot / ts_resol).to_i
371
- this_tsh = this_ts >> 32
372
- this_tsl = this_ts & 0xffffffff
373
418
 
374
- [this_tsh, this_tsl]
419
+ [this_ts >> 32, this_ts & 0xffffffff]
375
420
  end
376
421
 
377
422
  def reread(filename)
@@ -381,18 +426,34 @@ module PacketGen
381
426
  readfile filename
382
427
  end
383
428
 
384
- def epb_from_pkt(pkt, endian, ts, ts_resol, ts_add_val)
385
- case pkt
386
- when Hash
387
- this_ts = pkt.keys.first.to_i
388
- this_data = pkt.values.first.to_s
429
+ def create_block_from_pkt(pkt, section, timestamp, ts_inc)
430
+ if timestamp.nil? || ts_inc.nil?
431
+ spb_from_pkt(pkt, section)
389
432
  else
390
- this_ts = (ts + ts_add_val).to_i
391
- this_data = pkt.to_s
433
+ epb_from_pkt(pkt, section, timestamp)
392
434
  end
435
+ end
436
+
437
+ def spb_from_pkt(pkt, section)
438
+ pkt_s = pkt.to_s
439
+ size = pkt_s.size
440
+ SPB.new(endian: section.endian,
441
+ block_len: size,
442
+ orig_len: size,
443
+ data: pkt_s)
444
+ end
445
+
446
+ # @todo remove hash case when #array_to_file will be removed
447
+ def epb_from_pkt(pkt, section, timestamp)
448
+ this_ts, this_data = case pkt
449
+ when Hash
450
+ [pkt.keys.first.to_i, pkt.values.first.to_s]
451
+ else
452
+ [timestamp.to_r, pkt.to_s]
453
+ end
393
454
  this_cap_len = this_data.size
394
- this_tsh, this_tsl = calc_ts(this_ts, ts_resol)
395
- EPB.new(endian: endian,
455
+ this_tsh, this_tsl = calc_ts(this_ts, section.interfaces.last.ts_resol)
456
+ EPB.new(endian: section.endian,
396
457
  interface_id: 0,
397
458
  tsh: this_tsh,
398
459
  tsl: this_tsl,
@@ -400,6 +461,28 @@ module PacketGen
400
461
  orig_len: this_cap_len,
401
462
  data: this_data)
402
463
  end
464
+
465
+ def update_ts(timestamp, ts_inc)
466
+ return nil if timestamp.nil? || ts_inc.nil?
467
+
468
+ timestamp + ts_inc
469
+ end
470
+
471
+ # Iterate over each xPB with its associated interface
472
+ # @return [void]
473
+ # @yieldparam [String] xpb
474
+ # @yieldparam [IDB] itf
475
+ def each_packet_with_interface
476
+ sections.each do |section|
477
+ section.interfaces.each do |itf|
478
+ itf.packets.each { |xpb| yield xpb, itf }
479
+ end
480
+ end
481
+ end
482
+
483
+ def parse_packet(data, link_type)
484
+ Packet.parse(data, first_header: KNOWN_LINK_TYPES[link_type])
485
+ end
403
486
  end
404
487
  end
405
488
  end