Class: PacketGen::Header::Base Abstract

Inherits:
Types::Fields show all
Defined in:
lib/packetgen/header/base.rb

Overview

This class is abstract.

Base class for all header types. Subclasses may define magic methods:

  • #calc_checksum, which computes header checksum,

  • #calc_length, which computes header length,

  • #parse?,

  • #reply!, which inverts needed fields to forge a response.

Author:

  • Sylvain Daubert

Defined Under Namespace

Classes: Binding, Bindings, ProcBinding

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Types::Fields

#[], #[]=, #bits_on, #body=, define_bit_fields_on, define_field, define_field_after, define_field_before, delete_field, fields, #fields, #force_binary, #inspect, #is_optional?, #is_present?, #offset_of, #optional?, #optional_fields, #present?, #read, remove_bit_fields_on, remove_field, #sz, #to_h, #to_s, update_field

Constructor Details

#initialize(options = {}) ⇒ Base

Returns a new instance of Base.



288
289
290
291
# File 'lib/packetgen/header/base.rb', line 288

def initialize(options={})
  @packet = options.delete(:packet) if options.key?(:packet)
  super
end

Instance Attribute Details

#packetPacket?

Reference on packet which owns this header

Returns:



162
163
164
# File 'lib/packetgen/header/base.rb', line 162

def packet
  @packet
end

Class Method Details

.bind(header_klass, args = {}) ⇒ void

This method returns an undefined value.

Bind a upper header to current one.

# Bind Header2 to Header1 when field1 from Header1 has a value of 42
Header1.bind Header2, field1: 42
# Bind Header3 to Header1 when field1 from Header1 has a value of 43
# and field2 has value 43 or 44
Header1.bind Header3, field1: 43, field2: 43
Header1.bind Header3, field1: 43, field2: 44
# Bind Header4 to Header1 when field1 from Header1 has a value
# greater or equal to 44. When adding a Header2 to a Header1
# with Packet#add, force value to 44.
Header1.bind Header4, field1: ->(v) { v.nil? ? 44 : v >= 44 }
# Bind Header5 to Header1 when field1 from Header1 has a value of 41
# and first two bytes of header1's body are null.
# When adding a Header2 to a Header1 with Packet#add, force value to 44.
Header1.bind Header5, procs: [->(hdr) { hdr.field1 = 41 }
                              ->(hdr) { hdr.field1 == 41 && hdr.body[0..1] == "\x00\x00" }]

Parameters:

  • header_klass (Class)

    header class to bind to current class

  • args (Hash) (defaults to: {})

    current class fields and their value when header_klass is embedded in current class.

    Given value may be a lambda, whose alone argument is the value extracted from header field (or nil when lambda is used to set field while adding a header).

    Special key procs may be used to set 2 lambdas, the former to set fields, the latter to check bindings. This may be used when multiple and non-trivial checks should be made.

Since:

  • 2.7.0



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/packetgen/header/base.rb', line 242

def self.bind(header_klass, args={})
  if @known_headers[header_klass].nil?
    bindings = Bindings.new(:newstyle)
    @known_headers[header_klass] = bindings
  else
    bindings = @known_headers[header_klass]
  end
  bindings.new_set
  args.each do |key, value|
    bindings << if key == :procs
                  ProcBinding.new(value)
                else
                  Binding.new(key, value)
                end
  end
end

.bind_header(header_klass, args = {}) ⇒ void

Deprecated.

Use bind instead.

This method returns an undefined value.

Bind a upper header to current class

Header1.bind_header Header2, field1: 43
Header1.bind_header Header3, field1: 43, field2: 43
Header1.bind_header Header4, op: :and, field1: 43, field2: 43
Header1.bind_header Header5, field1: ->(v) { v.nil? ? 128 : v > 127 }
Header1.bind_header Header6, procs: [->(hdr) { hdr.field1 = 1 }
                                     ->(hdr) { hdr.field1 == 1 && hdr.body[0..1] == "\x00\x00" }]

Parameters:

  • header_klass (Class)

    header class to bind to current class

  • args (Hash) (defaults to: {})

    current class fields and their value when header_klass is embedded in current class. Given value may be a lambda, whose alone argument is the value extracted from header field (or nil when lambda is used to set field while adding a header).

    If multiple fields are given, a special key :op may be given to set parse operation on this binding. By default, :op is :or (at least one binding must match to parse it). It also may be set to :and (all bindings must match to parse it).

    Special key procs may be used to set 2 lambdas, the former to set fields, the latter to check bindings. This may be used when multiple and non-trivial checks should be made.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/packetgen/header/base.rb', line 195

def self.bind_header(header_klass, args={})
  Deprecation.deprecated(self, __method__, 'bind', klass_method: true)
  op = args.delete(:op) || :or
  if @known_headers[header_klass].nil? || @known_headers[header_klass].op != op
    bindings = Bindings.new(op)
    @known_headers[header_klass] = bindings
  else
    bindings = @known_headers[header_klass]
  end
  args.each do |key, value|
    bindings << if key == :procs
                  ProcBinding.new(value)
                else
                  Binding.new(key, value)
                end
  end
end

.calculate_and_set_length(hdr, header_in_size: true) ⇒ Object

Helper method to calculate length of hdr and set its length field. To be used by #calc_length in Base subclasses.

Parameters:

  • hdr (Base)
  • header_in_size (Boolean) (defaults to: true)

    if true header is included in length, if false, only body is taken into account



271
272
273
274
275
276
277
278
# File 'lib/packetgen/header/base.rb', line 271

def self.calculate_and_set_length(hdr, header_in_size: true)
  length = if header_in_size
             hdr.sz
           else
             hdr.body.sz
           end
  hdr.length = length
end

.inherited(klass) ⇒ void

This method returns an undefined value.

On inheritage, create @known_header class variable

Parameters:

  • klass (Class)


167
168
169
170
# File 'lib/packetgen/header/base.rb', line 167

def self.inherited(klass)
  super
  klass.class_eval { @known_headers = {} }
end

.known_headersHash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get known headers

Returns:

  • (Hash)

    keys: header classes, values: hashes



283
284
285
# File 'lib/packetgen/header/base.rb', line 283

def self.known_headers
  @known_headers
end

.protocol_nameString

Give protocol name for this class

Returns:

  • (String)

Since:

  • 2.0.0



262
263
264
# File 'lib/packetgen/header/base.rb', line 262

def self.protocol_name
  new.protocol_name
end

Instance Method Details

#added_to_packet(packet) ⇒ void

This method is abstract.

This method is called when a header is added to a packet. This base method does nothing but may be overriden by subclasses.

This method returns an undefined value.

Parameters:

  • packet (Packet)

    packet to which self is added

Since:

  • 2.1.4



340
# File 'lib/packetgen/header/base.rb', line 340

def added_to_packet(packet) end

#header_id(header) ⇒ Integer

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get header id in packet headers array

Parameters:

Returns:

  • (Integer)

Raises:

  • FormatError header not in a packet



347
348
349
350
351
352
353
354
# File 'lib/packetgen/header/base.rb', line 347

def header_id(header)
  raise FormatError, "header of type #{header.class} not in a packet" if packet.nil?
  id = packet.headers.index(header)
  if id.nil?
    raise FormatError, "header of type #{header.class} not in packet #{packet}"
  end
  id
end

#ip_header(header) ⇒ Header

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get IP or IPv6 previous header from header

Parameters:

Returns:

Raises:

  • FormatError no IP or IPv6 header previous header in packet

  • FormatError header not in a packet



362
363
364
365
366
367
# File 'lib/packetgen/header/base.rb', line 362

def ip_header(header)
  hid = header_id(header)
  iph = packet.headers[0...hid].reverse.find { |h| h.is_a?(IP) || h.is_a?(IPv6) }
  raise FormatError, 'no IP or IPv6 header in packet' if iph.nil?
  iph
end

#ll_header(header) ⇒ Header

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get link layer header from given header

Parameters:

Returns:

Raises:

  • FormatError no link layer header in packet

  • FormatError header not in a packet



375
376
377
378
379
380
# File 'lib/packetgen/header/base.rb', line 375

def ll_header(header)
  hid = header_id(header)
  llh = packet.headers[0...hid].reverse.find { |h| h.is_a?(Eth) || h.is_a?(Dot11) }
  raise FormatError, 'no link layer header in packet' if llh.nil?
  llh
end

#method_nameString

return header method name

Returns:

  • (String)

Since:

  • 2.0.0

  • 2.8.6 permit multiple nesting levels



310
311
312
313
314
# File 'lib/packetgen/header/base.rb', line 310

def method_name
  return @method_name if @method_name

  @method_name = protocol_name.downcase.gsub(/::/, '_')
end

#parse?Boolean

This method is abstract.

Should be redefined by subclasses. This method should check invariant fields from header.

Call by Packet#parse when guessing first header to check if header is correct

Returns:

  • (Boolean)


320
321
322
# File 'lib/packetgen/header/base.rb', line 320

def parse?
  true
end

#protocol_nameString

Return header protocol name

Returns:

  • (String)


295
296
297
298
299
300
301
302
303
304
# File 'lib/packetgen/header/base.rb', line 295

def protocol_name
  return @protocol_name if @protocol_name

  classname = self.class.to_s
  @protocol_name = if classname.start_with?('PacketGen::Header')
                     classname.sub(/.*Header::/, '')
                   else
                     classname.sub(/.*::/, '')
                   end
end