class Mu::Pcap::Ethernet

Constants

ADDR_TO_BYTES
ETHERTYPE_802_1Q
ETHERTYPE_ARP
ETHERTYPE_IP
ETHERTYPE_IP6
ETHERTYPE_PPPOE_SESSION
FMT_HEADER
FMT_MAC
FMT_n
MAC_TEMPLATE
PPP_IP
PPP_IPV6

Attributes

dst[RW]
src[RW]
type[RW]
vlan[RW]

Public Class Methods

from_bytes(bytes) click to toggle source
# File lib/woolen_common/pcap/mu/pcap/ethernet.rb, line 46
def self.from_bytes bytes
    if bytes.length < 14
        raise ParseError, "Truncated Ethernet header: expected 14 bytes, got #{bytes.length} bytes"
    end

    dst = bytes.slice!(0, 6).unpack FMT_MAC
    dst = MAC_TEMPLATE % dst
    src = bytes.slice!(0, 6).unpack FMT_MAC
    src = MAC_TEMPLATE % src
    type = bytes.slice!(0, 2).unpack(FMT_n)[0]
    if type == ETHERTYPE_802_1Q
        @vlan = true
    else
        @vlan = false
    end
    while (type == ETHERTYPE_802_1Q)
        # Skip 4 bytes for 802.1q vlan tag field
        bytes.slice!(0, 2)
        type = bytes.slice!(0, 2).unpack(FMT_n)[0]
    end
    ethernet = Ethernet.new src, dst, type
    ethernet.vlan = @vlan
    ethernet.payload = bytes
    ethernet.payload_raw = bytes
    begin
        case type
            when ETHERTYPE_IP
                ethernet.payload = IPv4.from_bytes bytes
            when ETHERTYPE_IP6
                ethernet.payload = IPv6.from_bytes bytes
            when ETHERTYPE_PPPOE_SESSION
                # Remove PPPoE/PPP session layer
                ethernet.payload = bytes
                ethernet.remove_pppoe!
            else
                ethernet.payload = bytes
        end
    rescue ParseError => e
        Pcap.warning e
    end
    return ethernet
end
new(src=nil, dst=nil, type=0) click to toggle source
Calls superclass method
# File lib/woolen_common/pcap/mu/pcap/ethernet.rb, line 20
def initialize src=nil, dst=nil, type=0
    super()
    @src = src
    @dst = dst
    @type = type
    @vlan = false
end

Public Instance Methods

==(other) click to toggle source
Calls superclass method
# File lib/woolen_common/pcap/mu/pcap/ethernet.rb, line 154
def == other
    return super &&
        self.src == other.src &&
        self.dst == other.dst &&
        self.type == other.type
end
flow_id() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/ethernet.rb, line 34
def flow_id
    if not @payload or @payload.is_a? String
        return [:ethernet, @src, @dst, @type]
    else
        return @payload.flow_id
    end
end
ip?() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/ethernet.rb, line 89
def ip?
    return payload.is_a?(IP)
end
remove_pppoe!() click to toggle source

Remove the PPPoE and PPP headers. PPPoE is documented in RFC 2516.

# File lib/woolen_common/pcap/mu/pcap/ethernet.rb, line 109
def remove_pppoe!
    bytes = self.payload_raw

    # Remove PPPoE header
    Pcap.assert bytes.length >= 6, 'Truncated PPPoE header: ' +
        "expected at least 6 bytes, got #{bytes.length} bytes"
    version_type, code, session_id, length = bytes.unpack 'CCnn'
    version = version_type >> 4 & 0b1111
    type = version_type & 0b1111
    Pcap.assert version == 1, "Unknown PPPoE version: #{version}"
    Pcap.assert type == 1, "Unknown PPPoE type: #{type}"
    Pcap.assert code == 0, "Unknown PPPoE code: #{code}"
    bytes = bytes[6..-1]
    Pcap.assert bytes.length >= length, 'Truncated PPoE packet: ' +
        "expected #{length} bytes, got #{bytes.length} bytes"

    # Remove PPP header
    Pcap.assert bytes.length >= 2, 'Truncated PPP packet: ' +
        "expected at least bytes, got #{bytes.length} bytes"
    protocol_id, = bytes.unpack 'n'
    bytes = bytes[2..-1]
    case protocol_id
        when PPP_IP
            self.payload = IPv4.from_bytes bytes
            self.payload_raw = bytes
            self.type = ETHERTYPE_IP
        when PPP_IPV6
            self.payload = IPv6.from_bytes bytes
            self.payload_raw = bytes
            self.type = ETHERTYPE_IP6
        else
            # Failed.  Don't update payload or type.
            raise ParseError, "Unknown PPP protocol: 0x#{'%04x' % protocol_id}"
    end
end
to_s() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/ethernet.rb, line 145
def to_s
    if @payload.is_a? String
        payload = @payload.inspect
    else
        payload = @payload.to_s
    end
    return "ethernet(%s, %s, %d, %s)" % [@src, @dst, @type, payload]
end
vlan?() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/ethernet.rb, line 30
def vlan?
    vlan
end
write(io) click to toggle source
# File lib/woolen_common/pcap/mu/pcap/ethernet.rb, line 96
def write io
    dst_mac = ADDR_TO_BYTES[@dst] ||= @dst.split(':').inject('') { |m, b| m << b.to_i(16).chr }
    src_mac = ADDR_TO_BYTES[@src] ||= @src.split(':').inject('') { |m, b| m << b.to_i(16).chr }
    bytes = [dst_mac, src_mac, @type].pack(FMT_HEADER)
    io.write bytes
    if @payload.is_a? String
        io.write @payload
    else
        @payload.write io
    end
end