diff --git a/scapy/arch/bpf/supersocket.py b/scapy/arch/bpf/supersocket.py index d4f48235e3ceec4cd6dd68a32abcaa91183deed6..e0650020dbac4d421a85a5c78bfc9c1f87bd3639 100644 --- a/scapy/arch/bpf/supersocket.py +++ b/scapy/arch/bpf/supersocket.py @@ -4,24 +4,28 @@ Scapy *BSD native support - BPF sockets """ +import errno +import fcntl +import os +from select import select +import struct +import time + +from scapy.arch.bpf.core import get_dev_bpf, attach_filter +from scapy.arch.bpf.consts import BIOCGBLEN, BIOCGDLT, BIOCGSTATS, \ + BIOCIMMEDIATE, BIOCPROMISC, BIOCSBLEN, BIOCSETIF, BIOCSHDRCMPLT, \ + BPF_BUFFER_LENGTH from scapy.config import conf +from scapy.consts import FREEBSD, NETBSD +from scapy.data import ETH_P_ALL from scapy.error import Scapy_Exception, warning from scapy.supersocket import SuperSocket -from scapy.layers.inet import IP -from scapy.layers.inet6 import IPv6 -from scapy.packet import Raw -from scapy.data import ETH_P_ALL -from scapy.consts import FREEBSD, OPENBSD, NETBSD -from scapy.arch.bpf.core import get_dev_bpf, attach_filter -from scapy.arch.bpf.consts import * -import struct -import fcntl -import os -import time -import errno -from select import select +if FREEBSD or NETBSD: + BPF_ALIGNMENT = 8 # sizeof(long) +else: + BPF_ALIGNMENT = 4 # sizeof(int32_t) # SuperSockets definitions @@ -55,16 +59,15 @@ class _L2bpfSocket(SuperSocket): # Set the BPF buffer length try: fcntl.ioctl(self.ins, BIOCSBLEN, struct.pack('I', BPF_BUFFER_LENGTH)) - except IOError as err: - msg = "BIOCSBLEN failed on /dev/bpf%i" % self.dev_bpf - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("BIOCSBLEN failed on /dev/bpf%i" % + self.dev_bpf) # Assign the network interface to the BPF handle try: fcntl.ioctl(self.ins, BIOCSETIF, struct.pack("16s16x", self.iface)) - except IOError as err: - msg = "BIOCSETIF failed on %s" % self.iface - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("BIOCSETIF failed on %s" % self.iface) self.assigned_interface = self.iface # Set the interface into promiscuous @@ -74,17 +77,17 @@ class _L2bpfSocket(SuperSocket): # Don't block on read try: fcntl.ioctl(self.ins, BIOCIMMEDIATE, struct.pack('I', 1)) - except IOError as err: - msg = "BIOCIMMEDIATE failed on /dev/bpf%i" % self.dev_bpf - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("BIOCIMMEDIATE failed on /dev/bpf%i" % + self.dev_bpf) # Scapy will provide the link layer source address # Otherwise, it is written by the kernel try: fcntl.ioctl(self.ins, BIOCSHDRCMPLT, struct.pack('i', 1)) - except IOError as err: - msg = "BIOCSHDRCMPLT failed on /dev/bpf%i" % self.dev_bpf - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("BIOCSHDRCMPLT failed on /dev/bpf%i" % + self.dev_bpf) # Configure the BPF filter if not nofilter: @@ -104,13 +107,16 @@ class _L2bpfSocket(SuperSocket): try: fcntl.ioctl(self.ins, BIOCPROMISC, struct.pack('i', value)) - except IOError as err: - msg = "Can't put your interface (%s) into promiscuous mode !" % self.iface - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("Cannott set promiscuous mode on interface " + "(%s)!" % self.iface) def __del__(self): """Close the file descriptor on delete""" - self.close() + # When the socket is deleted on Scapy exits, __del__ is + # sometimes called "too late", and self is None + if self is not None: + self.close() def guess_cls(self): """Guess the packet class that must be used on the interface""" @@ -119,16 +125,12 @@ class _L2bpfSocket(SuperSocket): try: ret = fcntl.ioctl(self.ins, BIOCGDLT, struct.pack('I', 0)) ret = struct.unpack('I', ret)[0] - except IOError as err: + except IOError: cls = conf.default_l2 warning("BIOCGDLT failed: unable to guess type. Using %s !", cls.name) return cls - # *BSD loopback interface - if OPENBSD and ret == 12: # DTL_NULL on OpenBSD - return Loopback - # Retrieve the corresponding class try: return conf.l2types[ret] @@ -143,8 +145,8 @@ class _L2bpfSocket(SuperSocket): if self.fd_flags is None: try: self.fd_flags = fcntl.fcntl(self.ins, fcntl.F_GETFL) - except IOError as err: - warning("Can't get flags on this file descriptor !") + except IOError: + warning("Cannot get flags on this file descriptor !") return # Set the non blocking flag @@ -165,7 +167,7 @@ class _L2bpfSocket(SuperSocket): try: ret = fcntl.ioctl(self.ins, BIOCGSTATS, struct.pack("2I", 0, 0)) return struct.unpack("2I", ret) - except IOError as err: + except IOError: warning("Unable to get stats from BPF !") return (None, None) @@ -175,7 +177,7 @@ class _L2bpfSocket(SuperSocket): try: ret = fcntl.ioctl(self.ins, BIOCGBLEN, struct.pack("I", 0)) return struct.unpack("I", ret)[0] - except IOError as err: + except IOError: warning("Unable to get the BPF buffer length") return @@ -211,25 +213,15 @@ class L2bpfListenSocket(_L2bpfSocket): def get_frame(self): """Get a frame or packet from the received list""" - if self.received_frames: - pkt = self.received_frames.pop(0) - if isinstance(self, L3bpfSocket): - pkt = pkt.payload - return pkt - - return None + return self.received_frames.pop(0) - def bpf_align(self, bh_h, bh_c): + @staticmethod + def bpf_align(bh_h, bh_c): """Return the index to the end of the current packet""" - if FREEBSD or NETBSD: - BPF_ALIGNMENT = 8 # sizeof(long) - else: - BPF_ALIGNMENT = 4 # sizeof(int32_t) - - x = bh_h + bh_c - return ((x)+(BPF_ALIGNMENT-1)) & ~(BPF_ALIGNMENT-1) # from <net/bpf.h> + # from <net/bpf.h> + return ((bh_h + bh_c)+(BPF_ALIGNMENT-1)) & ~(BPF_ALIGNMENT-1) def extract_frames(self, bpf_buffer): """Extract all frames from the buffer and stored them in the received list.""" @@ -263,7 +255,7 @@ class L2bpfListenSocket(_L2bpfSocket): except: if conf.debug_dissector: raise - pkt = Raw(frame_str) + pkt = conf.raw_layer(frame_str) self.received_frames.append(pkt) # Extract the next frame @@ -278,20 +270,17 @@ class L2bpfListenSocket(_L2bpfSocket): # Get a frame from the buffer return self.get_frame() - else: - # Get data from BPF - try: - bpf_buffer = os.read(self.ins, x) - except EnvironmentError as e: - if e.errno == errno.EAGAIN: - return - else: - warning("BPF recv(): %s" % e) - return + # Get data from BPF + try: + bpf_buffer = os.read(self.ins, x) + except EnvironmentError as exc: + if exc.errno != errno.EAGAIN: + warning("BPF recv()", exc_info=True) + return - # Extract all frames from the BPF buffer - self.extract_frames(bpf_buffer) - return self.get_frame() + # Extract all frames from the BPF buffer + self.extract_frames(bpf_buffer) + return self.get_frame() class L2bpfSocket(L2bpfListenSocket): @@ -308,34 +297,35 @@ class L2bpfSocket(L2bpfListenSocket): # Get a frame from the buffer return self.get_frame() - else: - # Set the non blocking flag, read from the socket, and unset the flag - self.set_nonblock(True) - pkt = L2bpfListenSocket.recv(self) - self.set_nonblock(False) - return pkt + # Set the non blocking flag, read from the socket, and unset the flag + self.set_nonblock(True) + pkt = L2bpfListenSocket.recv(self) + self.set_nonblock(False) + return pkt class L3bpfSocket(L2bpfSocket): + def get_frame(self): + """Get a frame or packet from the received list""" + pkt = super(L3bpfSocket, self).get_frame() + if pkt is not None: + return pkt.payload + def send(self, pkt): """Send a packet""" # Use the routing table to find the output interface - if isinstance(pkt, IPv6): - iff, a, gw = conf.route6.route(pkt.dst) - if isinstance(pkt, IP): - iff, a, gw = conf.route.route(pkt.dst) - else: + iff = pkt.route()[0] + if iff is None: iff = conf.iface # Assign the network interface to the BPF handle if self.assigned_interface != iff: try: fcntl.ioctl(self.outs, BIOCSETIF, struct.pack("16s16x", iff)) - except IOError as err: - msg = "BIOCSETIF failed on %s" % iff - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("BIOCSETIF failed on %s" % iff) self.assigned_interface = iff # Build the frame diff --git a/scapy/error.py b/scapy/error.py index 793e220bb8a8acab44356881c68b7f96fc094696..d7273447d018e12e15534362ebf43fd3ffdfccf8 100644 --- a/scapy/error.py +++ b/scapy/error.py @@ -64,14 +64,13 @@ log_interactive = logging.getLogger("scapy.interactive") # logs in interactive log_loading = logging.getLogger("scapy.loading") # logs when loading Scapy -def warning(x, onlyOnce=None): +def warning(x, *args, **kargs): """ Prints a warning during runtime. onlyOnce - if True, the warning will never be printed again. """ - if onlyOnce: + if kargs.pop("onlyOnce", False): from scapy.config import conf conf.warning_next_only_once = True - log_runtime.warning(x) - + log_runtime.warning(x, *args, **kargs) diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py index 6c469416003fd4a38ecbb9ab0c3f870c4a9419ea..be474e653b4279436ecdf3b2abb2e15a3e740f11 100644 --- a/scapy/layers/inet.py +++ b/scapy/layers/inet.py @@ -18,7 +18,7 @@ from scapy.base_classes import Gen from scapy.data import * from scapy.layers.l2 import * from scapy.config import conf -from scapy.consts import WINDOWS +from scapy.consts import OPENBSD, WINDOWS from scapy.fields import * from scapy.packet import * from scapy.volatile import * @@ -796,6 +796,7 @@ bind_layers( Ether, IP, type=2048) bind_layers( CookedLinux, IP, proto=2048) bind_layers( GRE, IP, proto=2048) bind_layers( SNAP, IP, code=2048) +bind_layers( Loopback, IP, type=0) bind_layers( Loopback, IP, type=2) bind_layers( IPerror, IPerror, frag=0, proto=4) bind_layers( IPerror, ICMPerror, frag=0, proto=1) @@ -808,7 +809,9 @@ bind_layers( IP, UDP, frag=0, proto=17) bind_layers( IP, GRE, frag=0, proto=47) conf.l2types.register(101, IP) -conf.l2types.register_num2layer(12, IP) +if not OPENBSD: + # see scapy.layers.l2.py + conf.l2types.register_num2layer(12, IP) conf.l2types.register(DLT_IPV4, IP) conf.l3types.register(ETH_P_IP, IP) diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py index ae6dc5c0b8db7b12cc4e507b2c968bf221891fc3..d8f540d77bcb1e49499510a7763c0d4067337131 100644 --- a/scapy/layers/l2.py +++ b/scapy/layers/l2.py @@ -13,6 +13,7 @@ import os, struct, time, socket from scapy.base_classes import Net from scapy.config import conf +from scapy.consts import OPENBSD from scapy.data import * from scapy.packet import * from scapy.ansmachine import * @@ -447,6 +448,9 @@ conf.l2types.register_layer2num(ARPHDR_ETHER, Dot3) conf.l2types.register(144, CookedLinux) # called LINUX_IRDA, similar to CookedLinux conf.l2types.register(113, CookedLinux) conf.l2types.register(DLT_NULL, Loopback) +# Under OpenBSD, for some reason, DLT_NULL == 12 +if OPENBSD: + conf.l2types.register(12, Loopback) conf.l3types.register(ETH_P_ARP, ARP)