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, define_bit_fields_on, define_field, define_field_after, define_field_before, fields, #fields, #inspect, #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.



234
235
236
237
# File 'lib/packetgen/header/base.rb', line 234

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:



138
139
140
# File 'lib/packetgen/header/base.rb', line 138

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.

Examples:

Basic examples

# 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

Defining a binding on a field using a lambda.

# 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 }

Defining a binding using procs key

# 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



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/packetgen/header/base.rb', line 181

def self.bind(header_klass, args={})
  if @known_headers[header_klass].nil?
    bindings = Bindings.new
    @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

.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



217
218
219
220
221
222
223
224
# File 'lib/packetgen/header/base.rb', line 217

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)


144
145
146
147
# File 'lib/packetgen/header/base.rb', line 144

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



229
230
231
# File 'lib/packetgen/header/base.rb', line 229

def self.known_headers
  @known_headers
end

.protocol_nameString

Give protocol name for this class

Returns:

  • (String)

Since:

  • 2.0.0



201
202
203
204
205
206
207
208
209
210
# File 'lib/packetgen/header/base.rb', line 201

def self.protocol_name
  return @protocol_name if defined? @protocol_name

  classname = to_s
  @protocol_name = if classname.start_with?('PacketGen::Header')
                     classname.sub(/.*Header::/, '')
                   else
                     classname.sub(/.*::/, '')
                   end
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



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

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:



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

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

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

  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:



301
302
303
304
305
306
307
# File 'lib/packetgen/header/base.rb', line 301

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:



315
316
317
318
319
320
321
# File 'lib/packetgen/header/base.rb', line 315

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



249
250
251
252
253
# File 'lib/packetgen/header/base.rb', line 249

def method_name
  return @method_name if defined? @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)


259
260
261
# File 'lib/packetgen/header/base.rb', line 259

def parse?
  true
end

#protocol_nameString

Return header protocol name

Returns:

  • (String)


241
242
243
# File 'lib/packetgen/header/base.rb', line 241

def protocol_name
  self.class.protocol_name
end