1# Guillaume Valadon <guillaume@valadon.net> 2 3""" 4Scapy *BSD native support - BPF sockets 5""" 6 7from ctypes import c_long, sizeof 8import errno 9import fcntl 10import os 11import platform 12from select import select 13import struct 14import time 15 16from scapy.arch.bpf.core import get_dev_bpf, attach_filter 17from scapy.arch.bpf.consts import BIOCGBLEN, BIOCGDLT, BIOCGSTATS, \ 18 BIOCIMMEDIATE, BIOCPROMISC, BIOCSBLEN, BIOCSETIF, BIOCSHDRCMPLT, \ 19 BPF_BUFFER_LENGTH, BIOCSDLT, DLT_IEEE802_11_RADIO 20from scapy.config import conf 21from scapy.consts import FREEBSD, NETBSD, DARWIN 22from scapy.data import ETH_P_ALL 23from scapy.error import Scapy_Exception, warning 24from scapy.interfaces import network_name 25from scapy.supersocket import SuperSocket 26from scapy.compat import raw 27from scapy.layers.l2 import Loopback 28 29 30if FREEBSD: 31 # On 32bit architectures long might be 32bit. 32 BPF_ALIGNMENT = sizeof(c_long) 33elif NETBSD: 34 BPF_ALIGNMENT = 8 # sizeof(long) 35else: 36 BPF_ALIGNMENT = 4 # sizeof(int32_t) 37 38 39# SuperSockets definitions 40 41class _L2bpfSocket(SuperSocket): 42 """"Generic Scapy BPF Super Socket""" 43 44 desc = "read/write packets using BPF" 45 nonblocking_socket = True 46 47 def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, 48 nofilter=0, monitor=False): 49 self.fd_flags = None 50 self.assigned_interface = None 51 52 # SuperSocket mandatory variables 53 if promisc is None: 54 self.promisc = conf.sniff_promisc 55 else: 56 self.promisc = promisc 57 58 self.iface = network_name(iface or conf.iface) 59 60 # Get the BPF handle 61 self.ins = None 62 (self.ins, self.dev_bpf) = get_dev_bpf() 63 self.outs = self.ins 64 65 # Set the BPF buffer length 66 try: 67 fcntl.ioctl(self.ins, BIOCSBLEN, struct.pack('I', BPF_BUFFER_LENGTH)) # noqa: E501 68 except IOError: 69 raise Scapy_Exception("BIOCSBLEN failed on /dev/bpf%i" % 70 self.dev_bpf) 71 72 # Assign the network interface to the BPF handle 73 try: 74 fcntl.ioctl(self.ins, BIOCSETIF, struct.pack("16s16x", self.iface.encode())) # noqa: E501 75 except IOError: 76 raise Scapy_Exception("BIOCSETIF failed on %s" % self.iface) 77 self.assigned_interface = self.iface 78 79 # Set the interface into promiscuous 80 if self.promisc: 81 self.set_promisc(1) 82 83 # Set the interface to monitor mode 84 # Note: - trick from libpcap/pcap-bpf.c - monitor_mode() 85 # - it only works on OS X 10.5 and later 86 if DARWIN and monitor: 87 dlt_radiotap = struct.pack('I', DLT_IEEE802_11_RADIO) 88 try: 89 fcntl.ioctl(self.ins, BIOCSDLT, dlt_radiotap) 90 except IOError: 91 raise Scapy_Exception("Can't set %s into monitor mode!" % 92 self.iface) 93 94 # Don't block on read 95 try: 96 fcntl.ioctl(self.ins, BIOCIMMEDIATE, struct.pack('I', 1)) 97 except IOError: 98 raise Scapy_Exception("BIOCIMMEDIATE failed on /dev/bpf%i" % 99 self.dev_bpf) 100 101 # Scapy will provide the link layer source address 102 # Otherwise, it is written by the kernel 103 try: 104 fcntl.ioctl(self.ins, BIOCSHDRCMPLT, struct.pack('i', 1)) 105 except IOError: 106 raise Scapy_Exception("BIOCSHDRCMPLT failed on /dev/bpf%i" % 107 self.dev_bpf) 108 109 # Configure the BPF filter 110 filter_attached = False 111 if not nofilter: 112 if conf.except_filter: 113 if filter: 114 filter = "(%s) and not (%s)" % (filter, conf.except_filter) 115 else: 116 filter = "not (%s)" % conf.except_filter 117 if filter is not None: 118 try: 119 attach_filter(self.ins, filter, self.iface) 120 filter_attached = True 121 except ImportError as ex: 122 warning("Cannot set filter: %s" % ex) 123 if NETBSD and filter_attached is False: 124 # On NetBSD, a filter must be attached to an interface, otherwise 125 # no frame will be received by os.read(). When no filter has been 126 # configured, Scapy uses a simple tcpdump filter that does nothing 127 # more than ensuring the length frame is not null. 128 filter = "greater 0" 129 try: 130 attach_filter(self.ins, filter, self.iface) 131 except ImportError as ex: 132 warning("Cannot set filter: %s" % ex) 133 134 # Set the guessed packet class 135 self.guessed_cls = self.guess_cls() 136 137 def set_promisc(self, value): 138 """Set the interface in promiscuous mode""" 139 140 try: 141 fcntl.ioctl(self.ins, BIOCPROMISC, struct.pack('i', value)) 142 except IOError: 143 raise Scapy_Exception("Cannot set promiscuous mode on interface " 144 "(%s)!" % self.iface) 145 146 def __del__(self): 147 """Close the file descriptor on delete""" 148 # When the socket is deleted on Scapy exits, __del__ is 149 # sometimes called "too late", and self is None 150 if self is not None: 151 self.close() 152 153 def guess_cls(self): 154 """Guess the packet class that must be used on the interface""" 155 156 # Get the data link type 157 try: 158 ret = fcntl.ioctl(self.ins, BIOCGDLT, struct.pack('I', 0)) 159 ret = struct.unpack('I', ret)[0] 160 except IOError: 161 cls = conf.default_l2 162 warning("BIOCGDLT failed: unable to guess type. Using %s !", 163 cls.name) 164 return cls 165 166 # Retrieve the corresponding class 167 try: 168 return conf.l2types[ret] 169 except KeyError: 170 cls = conf.default_l2 171 warning("Unable to guess type (type %i). Using %s", ret, cls.name) 172 173 def set_nonblock(self, set_flag=True): 174 """Set the non blocking flag on the socket""" 175 176 # Get the current flags 177 if self.fd_flags is None: 178 try: 179 self.fd_flags = fcntl.fcntl(self.ins, fcntl.F_GETFL) 180 except IOError: 181 warning("Cannot get flags on this file descriptor !") 182 return 183 184 # Set the non blocking flag 185 if set_flag: 186 new_fd_flags = self.fd_flags | os.O_NONBLOCK 187 else: 188 new_fd_flags = self.fd_flags & ~os.O_NONBLOCK 189 190 try: 191 fcntl.fcntl(self.ins, fcntl.F_SETFL, new_fd_flags) 192 self.fd_flags = new_fd_flags 193 except Exception: 194 warning("Can't set flags on this file descriptor !") 195 196 def get_stats(self): 197 """Get received / dropped statistics""" 198 199 try: 200 ret = fcntl.ioctl(self.ins, BIOCGSTATS, struct.pack("2I", 0, 0)) 201 return struct.unpack("2I", ret) 202 except IOError: 203 warning("Unable to get stats from BPF !") 204 return (None, None) 205 206 def get_blen(self): 207 """Get the BPF buffer length""" 208 209 try: 210 ret = fcntl.ioctl(self.ins, BIOCGBLEN, struct.pack("I", 0)) 211 return struct.unpack("I", ret)[0] 212 except IOError: 213 warning("Unable to get the BPF buffer length") 214 return 215 216 def fileno(self): 217 """Get the underlying file descriptor""" 218 return self.ins 219 220 def close(self): 221 """Close the Super Socket""" 222 223 if not self.closed and self.ins is not None: 224 os.close(self.ins) 225 self.closed = True 226 self.ins = None 227 228 def send(self, x): 229 """Dummy send method""" 230 raise Exception( 231 "Can't send anything with %s" % self.__class__.__name__ 232 ) 233 234 def recv_raw(self, x=BPF_BUFFER_LENGTH): 235 """Dummy recv method""" 236 raise Exception( 237 "Can't recv anything with %s" % self.__class__.__name__ 238 ) 239 240 @staticmethod 241 def select(sockets, remain=None): 242 """This function is called during sendrecv() routine to select 243 the available sockets. 244 """ 245 # sockets, None (means use the socket's recv() ) 246 return bpf_select(sockets, remain) 247 248 249class L2bpfListenSocket(_L2bpfSocket): 250 """"Scapy L2 BPF Listen Super Socket""" 251 252 def __init__(self, *args, **kwargs): 253 self.received_frames = [] 254 super(L2bpfListenSocket, self).__init__(*args, **kwargs) 255 256 def buffered_frames(self): 257 """Return the number of frames in the buffer""" 258 return len(self.received_frames) 259 260 def get_frame(self): 261 """Get a frame or packet from the received list""" 262 if self.received_frames: 263 return self.received_frames.pop(0) 264 else: 265 return None, None, None 266 267 @staticmethod 268 def bpf_align(bh_h, bh_c): 269 """Return the index to the end of the current packet""" 270 271 # from <net/bpf.h> 272 return ((bh_h + bh_c) + (BPF_ALIGNMENT - 1)) & ~(BPF_ALIGNMENT - 1) 273 274 def extract_frames(self, bpf_buffer): 275 """Extract all frames from the buffer and stored them in the received list.""" # noqa: E501 276 277 # Ensure that the BPF buffer contains at least the header 278 len_bb = len(bpf_buffer) 279 if len_bb < 20: # Note: 20 == sizeof(struct bfp_hdr) 280 return 281 282 # Extract useful information from the BPF header 283 if FREEBSD: 284 # Unless we set BIOCSTSTAMP to something different than 285 # BPF_T_MICROTIME, we will get bpf_hdr on FreeBSD, which means 286 # that we'll get a struct timeval, which is time_t, suseconds_t. 287 # On i386 time_t is 32bit so the bh_tstamp will only be 8 bytes. 288 # We really want to set BIOCSTSTAMP to BPF_T_NANOTIME and be 289 # done with this and it always be 16? 290 if platform.machine() == "i386": 291 # struct bpf_hdr 292 bh_tstamp_offset = 8 293 else: 294 # struct bpf_hdr (64bit time_t) or struct bpf_xhdr 295 bh_tstamp_offset = 16 296 elif NETBSD: 297 # struct bpf_hdr or struct bpf_hdr32 298 bh_tstamp_offset = 16 299 else: 300 # struct bpf_hdr 301 bh_tstamp_offset = 8 302 303 # Parse the BPF header 304 bh_caplen = struct.unpack('I', bpf_buffer[bh_tstamp_offset:bh_tstamp_offset + 4])[0] # noqa: E501 305 next_offset = bh_tstamp_offset + 4 306 bh_datalen = struct.unpack('I', bpf_buffer[next_offset:next_offset + 4])[0] # noqa: E501 307 next_offset += 4 308 bh_hdrlen = struct.unpack('H', bpf_buffer[next_offset:next_offset + 2])[0] # noqa: E501 309 if bh_datalen == 0: 310 return 311 312 # Get and store the Scapy object 313 frame_str = bpf_buffer[bh_hdrlen:bh_hdrlen + bh_caplen] 314 self.received_frames.append( 315 (self.guessed_cls, frame_str, None) 316 ) 317 318 # Extract the next frame 319 end = self.bpf_align(bh_hdrlen, bh_caplen) 320 if (len_bb - end) >= 20: 321 self.extract_frames(bpf_buffer[end:]) 322 323 def recv_raw(self, x=BPF_BUFFER_LENGTH): 324 """Receive a frame from the network""" 325 326 x = min(x, BPF_BUFFER_LENGTH) 327 328 if self.buffered_frames(): 329 # Get a frame from the buffer 330 return self.get_frame() 331 332 # Get data from BPF 333 try: 334 bpf_buffer = os.read(self.ins, x) 335 except EnvironmentError as exc: 336 if exc.errno != errno.EAGAIN: 337 warning("BPF recv_raw()", exc_info=True) 338 return None, None, None 339 340 # Extract all frames from the BPF buffer 341 self.extract_frames(bpf_buffer) 342 return self.get_frame() 343 344 345class L2bpfSocket(L2bpfListenSocket): 346 """"Scapy L2 BPF Super Socket""" 347 348 def send(self, x): 349 """Send a frame""" 350 return os.write(self.outs, raw(x)) 351 352 def nonblock_recv(self): 353 """Non blocking receive""" 354 355 if self.buffered_frames(): 356 # Get a frame from the buffer 357 return L2bpfListenSocket.recv(self) 358 359 # Set the non blocking flag, read from the socket, and unset the flag 360 self.set_nonblock(True) 361 pkt = L2bpfListenSocket.recv(self) 362 self.set_nonblock(False) 363 return pkt 364 365 366class L3bpfSocket(L2bpfSocket): 367 368 def recv(self, x=BPF_BUFFER_LENGTH): 369 """Receive on layer 3""" 370 r = SuperSocket.recv(self, x) 371 if r: 372 r.payload.time = r.time 373 return r.payload 374 return r 375 376 def send(self, pkt): 377 """Send a packet""" 378 379 # Use the routing table to find the output interface 380 iff = pkt.route()[0] 381 if iff is None: 382 iff = conf.iface 383 384 # Assign the network interface to the BPF handle 385 if self.assigned_interface != iff: 386 try: 387 fcntl.ioctl(self.outs, BIOCSETIF, struct.pack("16s16x", iff.encode())) # noqa: E501 388 except IOError: 389 raise Scapy_Exception("BIOCSETIF failed on %s" % iff) 390 self.assigned_interface = iff 391 392 # Build the frame 393 if self.guessed_cls == Loopback: 394 # bpf(4) man page (from macOS, but also for BSD): 395 # "A packet can be sent out on the network by writing to a bpf 396 # file descriptor. [...] Currently only writes to Ethernets and 397 # SLIP links are supported" 398 # 399 # Headers are only mentioned for reads, not writes. tuntaposx's tun 400 # device reports as a "loopback" device, but it does IP. 401 frame = raw(pkt) 402 else: 403 frame = raw(self.guessed_cls() / pkt) 404 405 pkt.sent_time = time.time() 406 407 # Send the frame 408 L2bpfSocket.send(self, frame) 409 410 411# Sockets manipulation functions 412 413def isBPFSocket(obj): 414 """Return True is obj is a BPF Super Socket""" 415 return isinstance( 416 obj, 417 (L2bpfListenSocket, L2bpfListenSocket, L3bpfSocket) 418 ) 419 420 421def bpf_select(fds_list, timeout=None): 422 """A call to recv() can return several frames. This functions hides the fact 423 that some frames are read from the internal buffer.""" 424 425 # Check file descriptors types 426 bpf_scks_buffered = list() 427 select_fds = list() 428 429 for tmp_fd in fds_list: 430 431 # Specific BPF sockets: get buffers status 432 if isBPFSocket(tmp_fd) and tmp_fd.buffered_frames(): 433 bpf_scks_buffered.append(tmp_fd) 434 continue 435 436 # Regular file descriptors or empty BPF buffer 437 select_fds.append(tmp_fd) 438 439 if select_fds: 440 # Call select for sockets with empty buffers 441 if timeout is None: 442 timeout = 0.05 443 ready_list, _, _ = select(select_fds, [], [], timeout) 444 return bpf_scks_buffered + ready_list 445 else: 446 return bpf_scks_buffered 447