class PacketGen::Types::AbstractTLV

This class is an abstract class to define type-length-value data.

This class supersedes {TLV} class, which is not well defined on some corner cases.

Usage

To simply define a new TLV class, do:

MyTLV = PacketGen::Types::AbstractTLV.create
MyTLV.define_type_enum 'one' => 1, 'two' => 2

This will define a new MyTLV class, subclass of {Fields}. This class will define 3 fields:

.define_type_enum is, here, necessary to define enum hash to be used for #type accessor, as this one is defined as an {Enum}.

This class may then be used as older {TLV} class:

tlv = MyTLV.new(type: 1, value: 'abcd')  # automagically set #length from value
tlv.type        #=> 1
tlv.human_type  #=> 'one'
tlv.length      #=> 4
tlv.value       #=> "abcd"

Advanced usage

Each field's type may be changed at generating TLV class:

MyTLV = PacketGen::Types::AbstractTLV.create(type_class: PacketGen::Types::Int16,
                                             length_class: PacketGen::Types::Int16,
                                             value_class: PacketGen::Header::IP::Addr)
tlv = MyTLV.new(type: 1, value: '1.2.3.4')
tlv.type        #=> 1
tlv.length      #=> 4
tlv.value       #=> '1.2.3.4'
tlv.to_s        #=> "\x00\x01\x00\x04\x01\x02\x03\x04"

Some aliases may also be defined. For example, to create a TLV type whose type field should be named code:

MyTLV = PacketGen::Types::AbstractTLV.create(type_class: PacketGen::Types::Int16,
                                             length_class: PacketGen::Types::Int16,
                                             aliases: { code: :type })
tlv = MyTLV.new(code: 1, value: 'abcd')
tlv.code        #=> 1
tlv.type        #=> 1
tlv.length      #=> 4
tlv.value       #=> 'abcd'

@author Sylvain Daubert @since 3.1.0 @since 3.1.1 add :aliases keyword to {#initialize}

Attributes

aliases[RW]

@return [Hash]

header_in_length[RW]

Public Class Methods

create(type_class: Int8Enum, length_class: Int8, value_class: String, aliases: {}, header_in_length: false) click to toggle source

Generate a TLV class @param [Class] type_class Class to use for type @param [Class] length_class Class to use for length @param [Class] value_class Class to use for value @param [Boolean] header_in_length if +true +, type and length fields are

included in length

@return [Class] @since 3.1.4 Add header_in_length parameter

# File lib/packetgen/types/abstract_tlv.rb, line 78
def self.create(type_class: Int8Enum, length_class: Int8, value_class: String,
                aliases: {}, header_in_length: false)
  raise Error, '.create cannot be called on a subclass of PacketGen::Types::AbstractTLV' unless self.equal? AbstractTLV

  klass = Class.new(self)
  klass.aliases = aliases
  klass.header_in_length = header_in_length

  if type_class < Enum
    klass.define_field :type, type_class, enum: {}
  else
    klass.define_field :type, type_class
  end
  klass.define_field :length, length_class
  klass.define_field :value, value_class

  aliases.each do |al, orig|
    klass.instance_eval do
      alias_method al, orig if klass.method_defined?(orig)
      alias_method :"#{al}=", :"#{orig}=" if klass.method_defined?(:"#{orig}=")
    end
  end

  klass
end
define_type_enum(hsh) click to toggle source

@abstract Should only be called on real TLV classes, created by {.create}. Set enum hash for {#type} field. @param [Hash] hsh enum hash @return [void]

# File lib/packetgen/types/abstract_tlv.rb, line 117
def self.define_type_enum(hsh)
  field_defs[:type][:enum].clear
  field_defs[:type][:enum].merge!(hsh)
end
new(options={}) click to toggle source

@abstract Should only be called on real TLV classes, created by {.create}. @param [Hash] options @option options [Integer] :type @option options [Integer] :length @option options [Object] :value

Calls superclass method PacketGen::Types::Fields::new
# File lib/packetgen/types/abstract_tlv.rb, line 127
def initialize(options={})
  @header_in_length = self.class.header_in_length
  self.class.aliases.each do |al, orig|
    options[orig] = options[al] if options.key?(al)
  end

  super
  # used #value= defined below, which set length if needed
  self.value = options[:value] if options[:value]
end

Public Instance Methods

human_type() click to toggle source

@abstract Should only be called on real TLV class instances. Get human-readable type @return [String]

# File lib/packetgen/types/abstract_tlv.rb, line 166
def human_type
  self[:type].to_human.to_s
end
read(str) click to toggle source

@abstract Should only be called on real TLV class instances. Populate object from a binary string @param [String] str @return [Fields] self

# File lib/packetgen/types/abstract_tlv.rb, line 142
def read(str)
  idx = 0
  self[:type].read str[idx, self[:type].sz]
  idx += self[:type].sz
  self[:length].read str[idx, self[:length].sz]
  idx += self[:length].sz
  self[:value].read str[idx, real_length]
  self
end
to_human() click to toggle source

@abstract Should only be called on real TLV class instances. @return [String]

# File lib/packetgen/types/abstract_tlv.rb, line 172
def to_human
  my_value = self[:value].is_a?(String) ? self[:value].inspect : self[:value].to_human
  'type:%s,length:%u,value:%s' % [human_type, length, my_value]
end
value=(val) click to toggle source

@abstract Should only be called on real TLV class instances. Set value. May set length if value is a {Types::String}. @param [::String,Integer] val @return [::String,Integer]

# File lib/packetgen/types/abstract_tlv.rb, line 156
def value=(val)
  self[:value].from_human val
  self.length = self[:value].sz
  self.length += self[:type].sz + self[:length].sz if @header_in_length
  val
end

Private Instance Methods

real_length() click to toggle source
# File lib/packetgen/types/abstract_tlv.rb, line 179
def real_length
  if @header_in_length
    self.length - self[:type].sz - self[:length].sz
  else
    self.length
  end
end