class Ethernet::FrameSocket

Wraps an Ethernet socket and abstracts away the Ethernet II frame.

Public Class Methods

new(raw_socket_or_device, ether_type, mac_address = nil) click to toggle source

Creates a wrapper around a raw Ethernet socket.

Args:

raw_socket_or_device:: a raw Ethernet socket or a string containing an
                       Ethernet device name
ether_type:: 2-byte Ethernet packet type number
mac_address:: 6-byte MAC address for the Ethernet socket (optional if
              raw_socket_or_device is an Ethernet device name)

Raises:

RuntimeError:: if mac isn't exactly 6-bytes long
# File lib/ethernet/frame_socket.rb, line 17
def initialize(raw_socket_or_device, ether_type, mac_address = nil)
  check_mac mac_address if mac_address
  
  if raw_socket_or_device.respond_to? :to_str
    @source_mac = mac_address || Ethernet::Devices.mac(raw_socket_or_device)
    @socket = RawSocketFactory.socket raw_socket_or_device, ether_type
  else
    raise 'MAC address needed with raw socket' unless mac_address
    @source_mac = mac_address.dup
    @socket = raw_socket_or_device
  end
  
  @dest_mac = nil
  @ether_type = [ether_type].pack('n')
end

Public Instance Methods

close() click to toggle source

Closes the underlying socket.

# File lib/ethernet/frame_socket.rb, line 46
def close
  @socket.close
end
connect(mac_address) click to toggle source

Sets the destination MAC address for future calls to send.

Args:

mac:: 6-byte MAC address for the Ethernet socket

Raises:

RuntimeError:: if mac isn't exactly 6-bytes long
# File lib/ethernet/frame_socket.rb, line 40
def connect(mac_address)
  check_mac mac_address
  @dest_mac = mac_address
end
recv(buffer_size = 4096) click to toggle source

Receives an Ethernet II frame.

Args:

buffer_size:: optional maximum packet size argument passed to the raw
              socket's recv method

Returns the data and the source MAC address in the frame.

This will discard incoming frames that don’t match the MAC address that the socket is connected to, or the Ethernet packet type.

# File lib/ethernet/frame_socket.rb, line 88
def recv(buffer_size = 4096)
  raise "Not connected" unless @dest_mac
  loop do
    data, mac_address = recv_from buffer_size
    return data if @dest_mac == mac_address
  end
end
recv_from(buffer_size = 4096) click to toggle source

Receives an Ethernet II frame.

Args:

buffer_size:: optional maximum packet size argument passed to the raw
              socket's recv method

Returns the data in the frame.

This will discard incoming frames that don’t match the MAC address that the socket is connected to, or the Ethernet packet type.

# File lib/ethernet/frame_socket.rb, line 106
def recv_from(buffer_size = 4096)
  loop do
    packet = @socket.recv buffer_size
    next unless packet[12, 2] == @ether_type
    # The last part of the condition accepts multicast packets.
    next if packet[0, 6] != @source_mac && packet.unpack('C').first & 1 == 0
    return packet[14..-1], packet[6, 6]
  end
end
send(data, send_flags = 0) click to toggle source

Sends an Ethernet II frame.

Args:

data:: the data bytes to be sent

Raises:

RuntimeError:: if connect wasn' previously called
# File lib/ethernet/frame_socket.rb, line 57
def send(data, send_flags = 0)
  raise "Not connected" unless @dest_mac
  send_to @dest_mac, data, send_flags
end
send_to(mac_address, data, send_flags = 0) click to toggle source

Sends an Ethernet II frame.

Args:

mac_address:: the destination MAC address
data:: the data bytes to be sent

Raises:

RuntimeError:: if connect wasn' previously called
# File lib/ethernet/frame_socket.rb, line 70
def send_to(mac_address, data, send_flags = 0)
  check_mac mac_address

  padding = (data.length < 46) ? "\0" * (46 - data.length) : ''
  packet = [mac_address, @source_mac, @ether_type, data, padding].join
  @socket.send packet, send_flags
end

Private Instance Methods

check_mac(mac_address) click to toggle source

Raises an exception if the given MAC address is invalid.

# File lib/ethernet/frame_socket.rb, line 117
def check_mac(mac_address)
  raise "Invalid MAC address" unless mac_address.length == 6
end