1# Guillaume Valadon <guillaume@valadon.net> 2 3""" 4Scapy *BSD native support - BPF sockets 5""" 6 7import errno 8import fcntl 9import os 10from select import select 11import struct 12import time 13 14from scapy.arch.bpf.core import get_dev_bpf, attach_filter 15from scapy.arch.bpf.consts import BIOCGBLEN, BIOCGDLT, BIOCGSTATS, \ 16 BIOCIMMEDIATE, BIOCPROMISC, BIOCSBLEN, BIOCSETIF, BIOCSHDRCMPLT, \ 17 BPF_BUFFER_LENGTH 18from scapy.config import conf 19from scapy.consts import FREEBSD, NETBSD 20from scapy.data import ETH_P_ALL 21from scapy.error import Scapy_Exception, warning 22from scapy.supersocket import SuperSocket 23from scapy.compat import raw 24 25 26if FREEBSD or NETBSD: 27 BPF_ALIGNMENT = 8 # sizeof(long) 28else: 29 BPF_ALIGNMENT = 4 # sizeof(int32_t) 30 31 32# SuperSockets definitions 33 34class _L2bpfSocket(SuperSocket): 35 """"Generic Scapy BPF Super Socket""" 36 37 desc = "read/write packets using BPF" 38 assigned_interface = None 39 fd_flags = None 40 ins = None 41 closed = False 42 43 def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0): 44 45 # SuperSocket mandatory variables 46 if promisc is None: 47 self.promisc = conf.sniff_promisc 48 else: 49 self.promisc = promisc 50 51 if iface is None: 52 self.iface = conf.iface 53 else: 54 self.iface = iface 55 56 # Get the BPF handle 57 (self.ins, self.dev_bpf) = get_dev_bpf() 58 self.outs = self.ins 59 60 # Set the BPF buffer length 61 try: 62 fcntl.ioctl(self.ins, BIOCSBLEN, struct.pack('I', BPF_BUFFER_LENGTH)) 63 except IOError: 64 raise Scapy_Exception("BIOCSBLEN failed on /dev/bpf%i" % 65 self.dev_bpf) 66 67 # Assign the network interface to the BPF handle 68 try: 69 fcntl.ioctl(self.ins, BIOCSETIF, struct.pack("16s16x", self.iface.encode())) 70 except IOError: 71 raise Scapy_Exception("BIOCSETIF failed on %s" % self.iface) 72 self.assigned_interface = self.iface 73 74 # Set the interface into promiscuous 75 if self.promisc: 76 self.set_promisc(1) 77 78 # Don't block on read 79 try: 80 fcntl.ioctl(self.ins, BIOCIMMEDIATE, struct.pack('I', 1)) 81 except IOError: 82 raise Scapy_Exception("BIOCIMMEDIATE failed on /dev/bpf%i" % 83 self.dev_bpf) 84 85 # Scapy will provide the link layer source address 86 # Otherwise, it is written by the kernel 87 try: 88 fcntl.ioctl(self.ins, BIOCSHDRCMPLT, struct.pack('i', 1)) 89 except IOError: 90 raise Scapy_Exception("BIOCSHDRCMPLT failed on /dev/bpf%i" % 91 self.dev_bpf) 92 93 # Configure the BPF filter 94 if not nofilter: 95 if conf.except_filter: 96 if filter: 97 filter = "(%s) and not (%s)" % (filter, conf.except_filter) 98 else: 99 filter = "not (%s)" % conf.except_filter 100 if filter is not None: 101 attach_filter(self.ins, self.iface, filter) 102 103 # Set the guessed packet class 104 self.guessed_cls = self.guess_cls() 105 106 def set_promisc(self, value): 107 """Set the interface in promiscuous mode""" 108 109 try: 110 fcntl.ioctl(self.ins, BIOCPROMISC, struct.pack('i', value)) 111 except IOError: 112 raise Scapy_Exception("Cannot set promiscuous mode on interface " 113 "(%s)!" % self.iface) 114 115 def __del__(self): 116 """Close the file descriptor on delete""" 117 # When the socket is deleted on Scapy exits, __del__ is 118 # sometimes called "too late", and self is None 119 if self is not None: 120 self.close() 121 122 def guess_cls(self): 123 """Guess the packet class that must be used on the interface""" 124 125 # Get the data link type 126 try: 127 ret = fcntl.ioctl(self.ins, BIOCGDLT, struct.pack('I', 0)) 128 ret = struct.unpack('I', ret)[0] 129 except IOError: 130 cls = conf.default_l2 131 warning("BIOCGDLT failed: unable to guess type. Using %s !", 132 cls.name) 133 return cls 134 135 # Retrieve the corresponding class 136 try: 137 return conf.l2types[ret] 138 except KeyError: 139 cls = conf.default_l2 140 warning("Unable to guess type (type %i). Using %s", ret, cls.name) 141 142 def set_nonblock(self, set_flag=True): 143 """Set the non blocking flag on the socket""" 144 145 # Get the current flags 146 if self.fd_flags is None: 147 try: 148 self.fd_flags = fcntl.fcntl(self.ins, fcntl.F_GETFL) 149 except IOError: 150 warning("Cannot get flags on this file descriptor !") 151 return 152 153 # Set the non blocking flag 154 if set_flag: 155 new_fd_flags = self.fd_flags | os.O_NONBLOCK 156 else: 157 new_fd_flags = self.fd_flags & ~os.O_NONBLOCK 158 159 try: 160 fcntl.fcntl(self.ins, fcntl.F_SETFL, new_fd_flags) 161 self.fd_flags = new_fd_flags 162 except: 163 warning("Can't set flags on this file descriptor !") 164 165 def get_stats(self): 166 """Get received / dropped statistics""" 167 168 try: 169 ret = fcntl.ioctl(self.ins, BIOCGSTATS, struct.pack("2I", 0, 0)) 170 return struct.unpack("2I", ret) 171 except IOError: 172 warning("Unable to get stats from BPF !") 173 return (None, None) 174 175 def get_blen(self): 176 """Get the BPF buffer length""" 177 178 try: 179 ret = fcntl.ioctl(self.ins, BIOCGBLEN, struct.pack("I", 0)) 180 return struct.unpack("I", ret)[0] 181 except IOError: 182 warning("Unable to get the BPF buffer length") 183 return 184 185 def fileno(self): 186 """Get the underlying file descriptor""" 187 return self.ins 188 189 def close(self): 190 """Close the Super Socket""" 191 192 if not self.closed and self.ins is not None: 193 os.close(self.ins) 194 self.closed = True 195 self.ins = None 196 197 def send(self, x): 198 """Dummy send method""" 199 raise Exception("Can't send anything with %s" % self.__name__) 200 201 def recv(self, x=BPF_BUFFER_LENGTH): 202 """Dummy recv method""" 203 raise Exception("Can't recv anything with %s" % self.__name__) 204 205 206class L2bpfListenSocket(_L2bpfSocket): 207 """"Scapy L2 BPF Listen Super Socket""" 208 209 received_frames = [] 210 211 def buffered_frames(self): 212 """Return the number of frames in the buffer""" 213 return len(self.received_frames) 214 215 def get_frame(self): 216 """Get a frame or packet from the received list""" 217 if self.received_frames: 218 return self.received_frames.pop(0) 219 220 @staticmethod 221 def bpf_align(bh_h, bh_c): 222 """Return the index to the end of the current packet""" 223 224 # from <net/bpf.h> 225 return ((bh_h + bh_c)+(BPF_ALIGNMENT-1)) & ~(BPF_ALIGNMENT-1) 226 227 def extract_frames(self, bpf_buffer): 228 """Extract all frames from the buffer and stored them in the received list.""" 229 230 # Ensure that the BPF buffer contains at least the header 231 len_bb = len(bpf_buffer) 232 if len_bb < 20: # Note: 20 == sizeof(struct bfp_hdr) 233 return 234 235 # Extract useful information from the BPF header 236 if FREEBSD or NETBSD: 237 # struct bpf_xhdr or struct bpf_hdr32 238 bh_tstamp_offset = 16 239 else: 240 # struct bpf_hdr 241 bh_tstamp_offset = 8 242 243 # Parse the BPF header 244 bh_caplen = struct.unpack('I', bpf_buffer[bh_tstamp_offset:bh_tstamp_offset+4])[0] 245 next_offset = bh_tstamp_offset + 4 246 bh_datalen = struct.unpack('I', bpf_buffer[next_offset:next_offset+4])[0] 247 next_offset += 4 248 bh_hdrlen = struct.unpack('H', bpf_buffer[next_offset:next_offset+2])[0] 249 if bh_datalen == 0: 250 return 251 252 # Get and store the Scapy object 253 frame_str = bpf_buffer[bh_hdrlen:bh_hdrlen+bh_caplen] 254 try: 255 pkt = self.guessed_cls(frame_str) 256 except: 257 if conf.debug_dissector: 258 raise 259 pkt = conf.raw_layer(frame_str) 260 self.received_frames.append(pkt) 261 262 # Extract the next frame 263 end = self.bpf_align(bh_hdrlen, bh_caplen) 264 if (len_bb - end) >= 20: 265 self.extract_frames(bpf_buffer[end:]) 266 267 def recv(self, x=BPF_BUFFER_LENGTH): 268 """Receive a frame from the network""" 269 270 if self.buffered_frames(): 271 # Get a frame from the buffer 272 return self.get_frame() 273 274 # Get data from BPF 275 try: 276 bpf_buffer = os.read(self.ins, x) 277 except EnvironmentError as exc: 278 if exc.errno != errno.EAGAIN: 279 warning("BPF recv()", exc_info=True) 280 return 281 282 # Extract all frames from the BPF buffer 283 self.extract_frames(bpf_buffer) 284 return self.get_frame() 285 286 287class L2bpfSocket(L2bpfListenSocket): 288 """"Scapy L2 BPF Super Socket""" 289 290 def send(self, x): 291 """Send a frame""" 292 return os.write(self.outs, raw(x)) 293 294 def nonblock_recv(self): 295 """Non blocking receive""" 296 297 if self.buffered_frames(): 298 # Get a frame from the buffer 299 return self.get_frame() 300 301 # Set the non blocking flag, read from the socket, and unset the flag 302 self.set_nonblock(True) 303 pkt = L2bpfListenSocket.recv(self) 304 self.set_nonblock(False) 305 return pkt 306 307 308class L3bpfSocket(L2bpfSocket): 309 310 def get_frame(self): 311 """Get a frame or packet from the received list""" 312 pkt = super(L3bpfSocket, self).get_frame() 313 if pkt is not None: 314 return pkt.payload 315 316 def send(self, pkt): 317 """Send a packet""" 318 319 # Use the routing table to find the output interface 320 iff = pkt.route()[0] 321 if iff is None: 322 iff = conf.iface 323 324 # Assign the network interface to the BPF handle 325 if self.assigned_interface != iff: 326 try: 327 fcntl.ioctl(self.outs, BIOCSETIF, struct.pack("16s16x", iff.encode())) 328 except IOError: 329 raise Scapy_Exception("BIOCSETIF failed on %s" % iff) 330 self.assigned_interface = iff 331 332 # Build the frame 333 frame = raw(self.guessed_cls()/pkt) 334 pkt.sent_time = time.time() 335 336 # Send the frame 337 L2bpfSocket.send(self, frame) 338 339 340# Sockets manipulation functions 341 342def isBPFSocket(obj): 343 """Return True is obj is a BPF Super Socket""" 344 return isinstance(obj, L2bpfListenSocket) or isinstance(obj, L2bpfListenSocket) or isinstance(obj, L3bpfSocket) 345 346 347def bpf_select(fds_list, timeout=None): 348 """A call to recv() can return several frames. This functions hides the fact 349 that some frames are read from the internal buffer.""" 350 351 # Check file descriptors types 352 bpf_scks_buffered = list() 353 select_fds = list() 354 355 for tmp_fd in fds_list: 356 357 # Specific BPF sockets: get buffers status 358 if isBPFSocket(tmp_fd) and tmp_fd.buffered_frames(): 359 bpf_scks_buffered.append(tmp_fd) 360 continue 361 362 # Regular file descriptors or empty BPF buffer 363 select_fds.append(tmp_fd) 364 365 if select_fds: 366 # Call select for sockets with empty buffers 367 if timeout is None: 368 timeout = 0.05 369 ready_list, _, _ = select(select_fds, [], [], timeout) 370 return bpf_scks_buffered + ready_list 371 else: 372 return bpf_scks_buffered 373