1# Guillaume Valadon <guillaume@valadon.net> 2 3""" 4Scapy *BSD native support - core 5""" 6 7from __future__ import absolute_import 8from scapy.config import conf 9from scapy.error import Scapy_Exception, warning 10from scapy.data import ARPHDR_LOOPBACK, ARPHDR_ETHER 11from scapy.arch.common import get_if, get_bpf_pointer 12from scapy.consts import LOOPBACK_NAME 13 14from scapy.arch.bpf.consts import * 15 16import os 17import socket 18import fcntl 19import struct 20 21from ctypes import cdll, cast, pointer, POINTER, Structure 22from ctypes import c_int, c_ulong, c_char_p 23from ctypes.util import find_library 24from scapy.modules.six.moves import range 25 26 27# ctypes definitions 28 29LIBC = cdll.LoadLibrary(find_library("libc")) 30LIBC.ioctl.argtypes = [c_int, c_ulong, c_char_p] 31LIBC.ioctl.restype = c_int 32 33 34# Addresses manipulation functions 35 36def get_if_raw_addr(ifname): 37 """Returns the IPv4 address configured on 'ifname', packed with inet_pton.""" 38 39 # Get ifconfig output 40 try: 41 fd = os.popen("%s %s" % (conf.prog.ifconfig, ifname)) 42 except OSError as msg: 43 warning("Failed to execute ifconfig: (%s)", msg) 44 return b"\0\0\0\0" 45 46 # Get IPv4 addresses 47 addresses = [l for l in fd if l.find("netmask") >= 0] 48 if not addresses: 49 warning("No IPv4 address found on %s !", ifname) 50 return b"\0\0\0\0" 51 52 # Pack the first address 53 address = addresses[0].split(' ')[1] 54 return socket.inet_pton(socket.AF_INET, address) 55 56 57def get_if_raw_hwaddr(ifname): 58 """Returns the packed MAC address configured on 'ifname'.""" 59 60 NULL_MAC_ADDRESS = b'\x00'*6 61 62 # Handle the loopback interface separately 63 if ifname == LOOPBACK_NAME: 64 return (ARPHDR_LOOPBACK, NULL_MAC_ADDRESS) 65 66 # Get ifconfig output 67 try: 68 fd = os.popen("%s %s" % (conf.prog.ifconfig, ifname)) 69 except OSError as msg: 70 raise Scapy_Exception("Failed to execute ifconfig: (%s)" % msg) 71 72 # Get MAC addresses 73 addresses = [l for l in fd.readlines() if l.find("ether") >= 0 or 74 l.find("lladdr") >= 0 or 75 l.find("address") >= 0] 76 if not addresses: 77 raise Scapy_Exception("No MAC address found on %s !" % ifname) 78 79 # Pack and return the MAC address 80 mac = addresses[0].split(' ')[1] 81 mac = [chr(int(b, 16)) for b in mac.split(':')] 82 return (ARPHDR_ETHER, ''.join(mac)) 83 84 85# BPF specific functions 86 87def get_dev_bpf(): 88 """Returns an opened BPF file object""" 89 90 # Get the first available BPF handle 91 for bpf in range(0, 8): 92 try: 93 fd = os.open("/dev/bpf%i" % bpf, os.O_RDWR) 94 return (fd, bpf) 95 except OSError as err: 96 continue 97 98 raise Scapy_Exception("No /dev/bpf handle is available !") 99 100 101def attach_filter(fd, iface, bpf_filter_string): 102 """Attach a BPF filter to the BPF file descriptor""" 103 104 # Retrieve the BPF byte code in decimal 105 command = "%s -i %s -ddd -s 1600 '%s'" % (conf.prog.tcpdump, iface, bpf_filter_string) 106 try: 107 f = os.popen(command) 108 except OSError as msg: 109 raise Scapy_Exception("Failed to execute tcpdump: (%s)" % msg) 110 111 # Convert the byte code to a BPF program structure 112 lines = f.readlines() 113 if lines == []: 114 raise Scapy_Exception("Got an empty BPF filter from tcpdump !") 115 116 bp = get_bpf_pointer(lines) 117 # Assign the BPF program to the interface 118 ret = LIBC.ioctl(c_int(fd), BIOCSETF, cast(pointer(bp), c_char_p)) 119 if ret < 0: 120 raise Scapy_Exception("Can't attach the BPF filter !") 121 122 123# Interface manipulation functions 124 125def get_if_list(): 126 """Returns a list containing all network interfaces.""" 127 128 # Get ifconfig output 129 try: 130 fd = os.popen("%s -a" % conf.prog.ifconfig) 131 except OSError as msg: 132 raise Scapy_Exception("Failed to execute ifconfig: (%s)" % msg) 133 134 # Get interfaces 135 interfaces = [line[:line.find(':')] for line in fd.readlines() 136 if ": flags" in line.lower()] 137 return interfaces 138 139 140def get_working_ifaces(): 141 """ 142 Returns an ordered list of interfaces that could be used with BPF. 143 Note: the order mimics pcap_findalldevs() behavior 144 """ 145 146 # Only root is allowed to perform the following ioctl() call 147 if os.getuid() != 0: 148 return [] 149 150 # Test all network interfaces 151 interfaces = [] 152 for ifname in get_if_list(): 153 154 # Unlike pcap_findalldevs(), we do not care of loopback interfaces. 155 if ifname == LOOPBACK_NAME: 156 continue 157 158 # Get interface flags 159 try: 160 result = get_if(ifname, SIOCGIFFLAGS) 161 except IOError as msg: 162 warning("ioctl(SIOCGIFFLAGS) failed on %s !", ifname) 163 continue 164 165 # Convert flags 166 ifflags = struct.unpack("16xH14x", result)[0] 167 if ifflags & 0x1: # IFF_UP 168 169 # Get a BPF handle 170 fd, _ = get_dev_bpf() 171 if fd is None: 172 raise Scapy_Exception("No /dev/bpf are available !") 173 174 # Check if the interface can be used 175 try: 176 fcntl.ioctl(fd, BIOCSETIF, struct.pack("16s16x", ifname.encode())) 177 interfaces.append((ifname, int(ifname[-1]))) 178 except IOError as err: 179 pass 180 181 # Close the file descriptor 182 os.close(fd) 183 184 # Sort to mimic pcap_findalldevs() order 185 interfaces.sort(lambda ifname_left_and_ifid_left, 186 ifname_right_and_ifid_right: ifname_left_and_ifid_left[1]-ifname_right_and_ifid_right[1]) 187 return interfaces 188 189 190def get_working_if(): 191 """Returns the first interface than can be used with BPF""" 192 193 ifaces = get_working_ifaces() 194 if not ifaces: 195 # A better interface will be selected later using the routing table 196 return LOOPBACK_NAME 197 return ifaces[0][0] 198