class PacketGen::Header::Base

@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

Attributes

known_headers[R]

@api private Get known headers @return [Hash] keys: header classes, values: hashes

Public Class Methods

bind(header_klass, args={}) click to toggle source

Bind a upper header to current one. @param [Class] header_klass header class to bind to current class @param [Hash] args 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.

@return [void] @example 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

@example 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 }

@example 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" }]

@since 2.7.0

# File lib/packetgen/header/base.rb, line 183
def 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) click to toggle source

Helper method to calculate length of hdr and set its length field. To be used by #calc_length in Base subclasses. @param [Base] hdr @param [Boolean] header_in_size if true header is included in length,

if +false+, only +body+ is taken into account
# File lib/packetgen/header/base.rb, line 205
def 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) click to toggle source

@private On inheritage, create +@known_header+ class variable @param [Class] klass @return [void]

Calls superclass method
# File lib/packetgen/header/base.rb, line 140
def self.inherited(klass)
  super
  klass.class_eval { @known_headers = {} }
end
new(options={}) click to toggle source

@see Types::Fields#initialize

Calls superclass method
# File lib/packetgen/header/base.rb, line 216
def initialize(options={})
  @packet = options.delete(:packet) if options.key?(:packet)
  super
end

Public Instance Methods

header_id(header) click to toggle source

@api private Get header id in {Packet#headers} array @param [Header] header @return [Integer] @raise [FormatError] header not in a packet

# File lib/packetgen/header/base.rb, line 226
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) click to toggle source

@api private Get {IP} or {IPv6} previous header from header @param [Header] header @return [Header] @raise [FormatError] no IP or IPv6 header previous header in packet @raise [FormatError] header not in a packet

# File lib/packetgen/header/base.rb, line 241
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) click to toggle source

@api private Get link layer header from given header @param [Header] header @return [Header] @raise [FormatError] no link layer header in packet @raise [FormatError] header not in a packet

# File lib/packetgen/header/base.rb, line 255
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