Class: Ethernet::FrameSocket

Inherits:
Object
  • Object
show all
Defined in:
lib/ethernet/frame_socket.rb

Overview

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

Instance Method Summary collapse

Constructor Details

#initialize(raw_socket_or_device, ether_type, mac_address = nil) ⇒ FrameSocket

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


17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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

Instance Method Details

#closeObject

Closes the underlying socket.



46
47
48
# File 'lib/ethernet/frame_socket.rb', line 46

def close
  @socket.close
end

#connect(mac_address) ⇒ Object

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


40
41
42
43
# 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) ⇒ Object

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.



88
89
90
91
92
93
94
# 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) ⇒ Object

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.



106
107
108
109
110
111
112
113
114
# 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) ⇒ Object

Sends an Ethernet II frame.

Args:

data:: the data bytes to be sent

Raises:

RuntimeError:: if connect wasn' previously called


57
58
59
60
# 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) ⇒ Object

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


70
71
72
73
74
75
76
# 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