Module: Ethernet::RawSocketFactory

Defined in:
lib/ethernet/raw_socket_factory.rb,
lib/ethernet/raw_socket_factory_linux.rb,
lib/ethernet/raw_socket_factory_darwin.rb,
lib/ethernet/raw_socket_factory_darwin.rb

Overview

:nodoc: namespace

Defined Under Namespace

Classes: BpfSocketWrapper

Class Method Summary collapse

Class Method Details

.all_ethernet_protocolsObject

The protocol number for listening to all ethernet protocols.



31
32
33
# File 'lib/ethernet/raw_socket_factory_linux.rb', line 31

def all_ethernet_protocols
  3  # cat /usr/include/linux/if_ether.h | grep ETH_P_ALL
end

.bpf_pseudo_socketObject

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.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 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) ⇒ Object

Converts a 32-bit integer from host-order to network-order.



26
27
28
# File 'lib/ethernet/raw_socket_factory.rb', line 26

def htonl(long_integer)
  [long_integer].pack('N').unpack('L').first
end

.htons(short_integer) ⇒ Object

Converts a 16-bit integer from host-order to network-order.



20
21
22
# File 'lib/ethernet/raw_socket_factory.rb', line 20

def htons(short_integer)
  [short_integer].pack('n').unpack('S').first
end

.raw_address_familyObject

The AF / PF number for raw sockets.



37
38
39
# File 'lib/ethernet/raw_socket_factory_linux.rb', line 37

def raw_address_family
  17  # cat /usr/include/bits/socket.h | grep PF_PACKET
end

.set_bpf_eth_device(bpf, eth_device, ether_type) ⇒ Object

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.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# 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

.set_socket_eth_device(socket, eth_device, ether_type) ⇒ Object

Sets the Ethernet interface and protocol type for a socket.



20
21
22
23
24
25
26
27
# File 'lib/ethernet/raw_socket_factory_linux.rb', line 20

def set_socket_eth_device(socket, eth_device, ether_type)
  if_number = Ethernet::Devices.interface_index eth_device
  # struct sockaddr_ll in /usr/include/linux/if_packet.h
  socket_address = [raw_address_family, htons(ether_type), if_number,
                    0xFFFF, 0, 0, ''].pack 'SSISCCa8'
  socket.bind socket_address
  socket
end

.socket(eth_device, ether_type = nil) ⇒ Object

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


13
14
15
16
# 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