Ethernet frame (layer 2, IEEE 802.3): Ruby parsing library

Ethernet frame is a OSI data link layer (layer 2) protocol data unit for Ethernet networks. In practice, many other networks and/or in-file dumps adopted the same format for encapsulation purposes.

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.8

References

This page hosts a formal specification of Ethernet frame (layer 2, IEEE 802.3) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for Ruby generated by Kaitai Struct depends on the Ruby runtime library. You have to install it before you can parse data.

The Ruby runtime library can be installed from RubyGems:

gem install kaitai-struct

Code

Parse a local file and get structure in memory:

data = EthernetFrame.from_file("path/to/local/file.Ethernet frame (layer 2, IEEE 802.3)")

Or parse structure from a string of bytes:

bytes = "\x00\x01\x02..."
data = EthernetFrame.new(Kaitai::Struct::Stream.new(bytes))

After that, one can get various attributes from the structure by invoking getter methods like:

data.dst_mac # => Destination MAC address
data.ether_type # => Ether type can be specied in several places in the frame. If
first location bears special marker (0x8100), then it is not the
real ether frame yet, an additional payload (`tci`) is expected
and real ether type is upcoming next.

Ruby source code to parse Ethernet frame (layer 2, IEEE 802.3)

ethernet_frame.rb

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

require 'kaitai/struct/struct'

unless Gem::Version.new(Kaitai::Struct::VERSION) >= Gem::Version.new('0.9')
  raise "Incompatible Kaitai Struct Ruby API: 0.9 or later is required, but you have #{Kaitai::Struct::VERSION}"
end


##
# Ethernet frame is a OSI data link layer (layer 2) protocol data unit
# for Ethernet networks. In practice, many other networks and/or
# in-file dumps adopted the same format for encapsulation purposes.
# @see https://ieeexplore.ieee.org/document/7428776 Source
class EthernetFrame < Kaitai::Struct::Struct

  ETHER_TYPE_ENUM = {
    2048 => :ether_type_enum_ipv4,
    2049 => :ether_type_enum_x_75_internet,
    2050 => :ether_type_enum_nbs_internet,
    2051 => :ether_type_enum_ecma_internet,
    2052 => :ether_type_enum_chaosnet,
    2053 => :ether_type_enum_x_25_level_3,
    2054 => :ether_type_enum_arp,
    33024 => :ether_type_enum_ieee_802_1q_tpid,
    34525 => :ether_type_enum_ipv6,
  }
  I__ETHER_TYPE_ENUM = ETHER_TYPE_ENUM.invert
  def initialize(_io, _parent = nil, _root = self)
    super(_io, _parent, _root)
    _read
  end

  def _read
    @dst_mac = @_io.read_bytes(6)
    @src_mac = @_io.read_bytes(6)
    @ether_type_1 = Kaitai::Struct::Stream::resolve_enum(ETHER_TYPE_ENUM, @_io.read_u2be)
    if ether_type_1 == :ether_type_enum_ieee_802_1q_tpid
      @tci = TagControlInfo.new(@_io, self, @_root)
    end
    if ether_type_1 == :ether_type_enum_ieee_802_1q_tpid
      @ether_type_2 = Kaitai::Struct::Stream::resolve_enum(ETHER_TYPE_ENUM, @_io.read_u2be)
    end
    case ether_type
    when :ether_type_enum_ipv4
      @_raw_body = @_io.read_bytes_full
      _io__raw_body = Kaitai::Struct::Stream.new(@_raw_body)
      @body = Ipv4Packet.new(_io__raw_body)
    when :ether_type_enum_ipv6
      @_raw_body = @_io.read_bytes_full
      _io__raw_body = Kaitai::Struct::Stream.new(@_raw_body)
      @body = Ipv6Packet.new(_io__raw_body)
    else
      @body = @_io.read_bytes_full
    end
    self
  end

  ##
  # Tag Control Information (TCI) is an extension of IEEE 802.1Q to
  # support VLANs on normal IEEE 802.3 Ethernet network.
  class TagControlInfo < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @priority = @_io.read_bits_int_be(3)
      @drop_eligible = @_io.read_bits_int_be(1) != 0
      @vlan_id = @_io.read_bits_int_be(12)
      self
    end

    ##
    # Priority Code Point (PCP) is used to specify priority for
    # different kinds of traffic.
    attr_reader :priority

    ##
    # Drop Eligible Indicator (DEI) specifies if frame is eligible
    # to dropping while congestion is detected for certain classes
    # of traffic.
    attr_reader :drop_eligible

    ##
    # VLAN Identifier (VID) specifies which VLAN this frame
    # belongs to.
    attr_reader :vlan_id
  end

  ##
  # Ether type can be specied in several places in the frame. If
  # first location bears special marker (0x8100), then it is not the
  # real ether frame yet, an additional payload (`tci`) is expected
  # and real ether type is upcoming next.
  def ether_type
    return @ether_type unless @ether_type.nil?
    @ether_type = (ether_type_1 == :ether_type_enum_ieee_802_1q_tpid ? ether_type_2 : ether_type_1)
    @ether_type
  end

  ##
  # Destination MAC address
  attr_reader :dst_mac

  ##
  # Source MAC address
  attr_reader :src_mac

  ##
  # Either ether type or TPID if it is a IEEE 802.1Q frame
  attr_reader :ether_type_1
  attr_reader :tci
  attr_reader :ether_type_2
  attr_reader :body
  attr_reader :_raw_body
end