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
-
.all_ethernet_protocols ⇒ Object
The protocol number for listening to all ethernet protocols.
-
.bpf_pseudo_socket ⇒ Object
Returns a BPF file descriptor that acts almost like a link-layer socket.
-
.htonl(long_integer) ⇒ Object
Converts a 32-bit integer from host-order to network-order.
-
.htons(short_integer) ⇒ Object
Converts a 16-bit integer from host-order to network-order.
-
.raw_address_family ⇒ Object
The AF / PF number for raw sockets.
-
.set_bpf_eth_device(bpf, eth_device, ether_type) ⇒ Object
Binds a BPF file descriptor to a device and limits packet capture.
-
.set_socket_eth_device(socket, eth_device, ether_type) ⇒ Object
Sets the Ethernet interface and protocol type for a socket.
-
.socket(eth_device, ether_type = nil) ⇒ Object
A raw socket sends and receives raw Ethernet frames.
Class Method Details
.all_ethernet_protocols ⇒ Object
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_socket ⇒ Object
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_family ⇒ Object
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 |