module Ethernet::RawSocketFactory
Low-level socket creation functionality.
Low-level socket creation functionality.
Low-level socket creation functionality.
Public Class Methods
socket(eth_device, ether_type = nil)
click to toggle source
A raw socket sends and receives raw Ethernet
frames.
Args:
eth_device:: device name for the Ethernet card, e.g. 'eth0' ether_type:: only receive Ethernet frames with this protocol number
# File lib/ethernet/raw_socket_factory.rb, line 13 def self.socket(eth_device, ether_type = nil) # This method is redefined in platform-specific implementations. raise "Unsupported os #{Ethernet::Provisioning::OS}" end
Private Class Methods
bpf_pseudo_socket()
click to toggle source
Returns a BPF file descriptor that acts almost like a link-layer socket.
BPF means Berkeley Packet Filter, and works on FreeBSD-like kernels, including Darwin.
# File lib/ethernet/raw_socket_factory_darwin.rb, line 24 def bpf_pseudo_socket 3.times do Dir['/dev/bpf*'].sort.each do |name| begin s = File.open name, 'r+b' s.sync = true return s rescue Errno::EBUSY # Move to the next BPF device. end end end return nil end
htonl(long_integer)
click to toggle source
Converts a 32-bit integer from host-order to network-order.
# File lib/ethernet/raw_socket_factory.rb, line 26 def htonl(long_integer) [long_integer].pack('N').unpack('L').first end
htons(short_integer)
click to toggle source
Converts a 16-bit integer from host-order to network-order.
# File lib/ethernet/raw_socket_factory.rb, line 20 def htons(short_integer) [short_integer].pack('n').unpack('S').first end
set_bpf_eth_device(bpf, eth_device, ether_type)
click to toggle source
Binds a BPF file descriptor to a device and limits packet capture.
BPF means Berkeley Packet Filter, and works on FreeBSD-like kernels, including Darwin.
This method also sets flags so that the socket behaves as much as possible like a Linux PF_PACKET raw socket.
# File lib/ethernet/raw_socket_factory_darwin.rb, line 47 def set_bpf_eth_device(bpf, eth_device, ether_type) # BIOCSETIF in /usr/include/net/bpf.h # _IOW in /usr/include/sys/ioccom.h # struct ifreq in /usr/include/net/if.h bpf.ioctl 0x8020426C, [eth_device].pack('a32') # Receive packets as soon as they're available. # BIOCIMMEDIATE in /usr/include/net/bpf.h # _IOW in /usr/include/sys/ioccom.h bpf.ioctl 0x80044270, [1].pack('L') # Don't automatically set the Ethernet header. # BIOCSHDRCMPLT in /usr/include/net/bpf.h # _IOW in /usr/include/sys/ioccom.h bpf.ioctl 0x80044275, [1].pack('L') # Don't receive the packets that we sent ourselves. # BIOCSSEESENT in /usr/include/net/bpf.h # _IOW in /usr/include/sys/ioccom.h bpf.ioctl 0x80044275, [0].pack('L') # BPF filter programming constants in /usr/include/net/bpf.h if ether_type filter = [ # A <- packet Ethernet type [0x28, 0, 0, 12], # BPF_LD + BPF_H + BPF_ABS # if A == ether_type jump above next instruction [0x15, 1, 0, ether_type], # BPF_JMP + BPF_JEQ + BPF_K # drop packet (ret K = 0) [0x06, 0, 0, 0] # BPF_RET + BPF_K ] else filter = [] end ether_mac = Ethernet::Devices.mac eth_device filter += [ # A <- first byte of destination MAC address [0x30, 0, 0, 0], # BPF_LD + BPF_B + BPF_ABS # if A & 1 (multicast MAC address) jump above exact MAC match [0x45, 5, 0, 1], # BPF_JMP + BPF_JSET + BPF_K # A <- first 4 bytes of destination MAC addres [0x20, 0, 0, 0], # BPF_LD + BPF_W + BPF_ABS # if A != first 4 bytes of local MAC address jump to drop instruction [0x15, 0, 2, ether_mac.unpack('N').first], # BPF_JMP + BPF_JEQ + BPF_K # A <- last 2 bytes of destination MAC address [0x28, 0, 0, 4], # BPF_LD + BPF_H + BPF_ABS # if A == last 2 bytes of local MAC address jump above next instruction [0x15, 1, 0, ether_mac.unpack('@4n').first], # BPF_JMP + BPF_JEQ + BPF_K # drop packet (ret K = 0) [0x06, 0, 0, 0], # BPF_RET + BPF_K # A <- packet length [0x80, 0, 0, 0], # BPF_LD + BPF_W + BPF_LEN # ret A (accept the entire packet) [0x16, 0, 0, 0] # BPF_RET + BPF_A ] filter_code = filter.map { |i| i.pack('SCCL') }.join('') # struct bpf_program in /usr/include/net/bpf.h filter_code_ptr = FFI::MemoryPointer.new :char, filter_code.length + 1 filter_code_ptr.write_string filter_code if Ethernet::Provisioning::POINTER_SIZE == 8 pack_spec = 'QQ' else pack_spec = 'LL' end bpf_program = [filter.length, filter_code_ptr.address].pack pack_spec # BIOCSETF in /usr/include/net/bpf.h # _IOW in /usr/include/sys/iocom.h bpf.ioctl 0x80104267, bpf_program end