diff --git a/.travis.yml b/.travis.yml
index 7ce3255817f177edb95ac6f233f236dd73599811..839733d2f63663b7de744f345ad9bb3b241bbecb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,6 +9,9 @@ matrix:
         - os: linux
           python: pypy
 
+        - os: osx
+          language: generic
+
         - os: osx
           language: generic
           env:
@@ -25,21 +28,23 @@ matrix:
           sudo: required
           python: 2.7
           env:
-            - TRAVIS_SUDO=sudo
-            - SCAPY_USE_PCAPDNET=yes
+            - TRAVIS_SUDO=sudo TEST_COMBINED_MODES=yes
 
         - os: linux
           sudo: required
           python: 2.7
           env:
-            - TRAVIS_SUDO=sudo
-            - TEST_COMBINED_MODES=yes
+            - TRAVIS_SUDO=sudo SCAPY_USE_PCAPDNET=yes
 
         - os: osx
           language: generic
           env:
             - TRAVIS_SUDO=sudo
-            - SCAPY_USE_PCAPDNET=yes
+
+        - os: osx
+          language: generic
+          env:
+            - TRAVIS_SUDO=sudo SCAPY_USE_PCAPDNET=yes
 
 install: bash .travis/install.sh
 
diff --git a/.travis/test.sh b/.travis/test.sh
index 62758444059e6ae91079f956f94c17952725ecf7..ef7e5121ca92a4ec9afe501f0f6f78eb41925756 100644
--- a/.travis/test.sh
+++ b/.travis/test.sh
@@ -4,22 +4,34 @@ python -c "from scapy.all import *; print conf"
 # Don't run tests that require root privileges
 if [ -z "$TRAVIS_SUDO" -o "$TRAVIS_SUDO" = "false" ]
 then
-  UT_FLAGS="-K netaccess "
+  UT_FLAGS="-K netaccess -K needs_root"
   TRAVIS_SUDO=""
 fi
 
 # Test AEAD modes in IPsec if available
 if [ "$TEST_COMBINED_MODES" != "yes" ]
 then
-  UT_FLAGS+="-K combined_modes "
+  UT_FLAGS+=" -K combined_modes "
 fi
 
 # Run unit tests
 cd test/
 
+if [ "$TRAVIS_OS_NAME" = "osx" ]
+then
+  if [ -z $SCAPY_USE_PCAPDNET ]
+  then
+    $TRAVIS_SUDO ./run_tests -q -F -t bpf.uts $UT_FLAGS || exit $?
+  fi
+fi
+
 for f in *.uts
 do
-  $TRAVIS_SUDO ./run_tests -f text -t $f $UT_FLAGS || exit $?
+  if [ "$f" = "bpf.uts" ]
+  then
+    continue
+  fi
+  $TRAVIS_SUDO ./run_tests -q -F -t $f $UT_FLAGS || exit $?
 done
 
 for f in ../scapy/contrib/*.uts
diff --git a/scapy/arch/__init__.py b/scapy/arch/__init__.py
index 29e9a69eccc52967dc7dbeaa7e627e4fae22e5a8..581092553e21a612466c24e7413ef82ca6dac441 100644
--- a/scapy/arch/__init__.py
+++ b/scapy/arch/__init__.py
@@ -44,6 +44,9 @@ def str2mac(s):
     return ("%02x:"*6)[:-1] % tuple(map(ord, s)) 
 
 
+if not scapy.config.conf.use_pcap and not scapy.config.conf.use_dnet:
+    from scapy.arch.bpf.core import get_if_raw_addr
+
 def get_if_addr(iff):
     return socket.inet_ntoa(get_if_raw_addr(iff))
     
@@ -63,6 +66,7 @@ def get_if_hwaddr(iff):
 # def attach_filter(s, filter, iface):
 # def set_promisc(s,iff,val=1):
 # def read_routes():
+# def read_routes6():
 # def get_if(iff,cmd):
 # def get_if_index(iff):
 
@@ -74,9 +78,16 @@ if LINUX:
         from scapy.arch.pcapdnet import *
 elif BSD:
     from scapy.arch.unix import read_routes, read_routes6, in6_getifaddr
-    scapy.config.conf.use_pcap = True
-    scapy.config.conf.use_dnet = True
-    from scapy.arch.pcapdnet import *
+
+    if scapy.config.conf.use_pcap or scapy.config.conf.use_dnet:
+        from scapy.arch.pcapdnet import *
+    else:
+        from scapy.arch.bpf.supersocket import L2bpfListenSocket, L2bpfSocket, L3bpfSocket
+        from scapy.arch.bpf.core import *
+        scapy.config.conf.use_bpf = True
+        scapy.config.conf.L2listen = L2bpfListenSocket
+        scapy.config.conf.L2socket = L2bpfSocket
+        scapy.config.conf.L3socket = L3bpfSocket
 elif SOLARIS:
     from scapy.arch.solaris import *
 elif WINDOWS:
diff --git a/scapy/arch/bpf/__init__.py b/scapy/arch/bpf/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c67498a55c5bb4a28741a0357ad994e8f96e746d
--- /dev/null
+++ b/scapy/arch/bpf/__init__.py
@@ -0,0 +1,5 @@
+# Guillaume Valadon <guillaume@valadon.net>
+
+"""
+Scapy *BSD native support
+"""
diff --git a/scapy/arch/bpf/consts.py b/scapy/arch/bpf/consts.py
new file mode 100644
index 0000000000000000000000000000000000000000..d4102d4da40e4341d698c6738a1f7a754c50a200
--- /dev/null
+++ b/scapy/arch/bpf/consts.py
@@ -0,0 +1,23 @@
+# Guillaume Valadon <guillaume@valadon.net>
+
+"""
+Scapy *BSD native support - constants
+"""
+
+
+from scapy.data import MTU
+
+
+SIOCGIFFLAGS = 0xc0206911
+BPF_BUFFER_LENGTH = MTU
+
+# From net/bpf.h
+BIOCIMMEDIATE = 0x80044270
+BIOCGSTATS = 0x4008426f
+BIOCPROMISC = 0x20004269
+BIOCSETIF = 0x8020426c
+BIOCSBLEN = 0xc0044266
+BIOCGBLEN = 0x40044266
+BIOCSETF = 0x80104267
+BIOCSHDRCMPLT = 0x80044275
+BIOCGDLT = 0x4004426a
diff --git a/scapy/arch/bpf/core.py b/scapy/arch/bpf/core.py
new file mode 100644
index 0000000000000000000000000000000000000000..99180699383d1aa526b964a230d8ba7ce897dec5
--- /dev/null
+++ b/scapy/arch/bpf/core.py
@@ -0,0 +1,223 @@
+# Guillaume Valadon <guillaume@valadon.net>
+
+"""
+Scapy *BSD native support - core
+"""
+
+from scapy.config import conf
+from scapy.error import Scapy_Exception
+from scapy.data import ARPHDR_LOOPBACK, ARPHDR_ETHER
+from scapy.arch.common import get_if
+from scapy.arch.consts import LOOPBACK_NAME
+from scapy.utils import warning
+
+from scapy.arch.bpf.consts import *
+
+import os
+import socket
+import fcntl
+import struct
+
+from ctypes import cdll, cast, pointer, POINTER, Structure
+from ctypes import c_uint, c_uint32, c_int, c_ulong, c_char_p, c_ushort, c_ubyte
+from ctypes.util import find_library
+
+
+# ctypes definitions
+
+LIBC = cdll.LoadLibrary(find_library("libc"))
+LIBC.ioctl.argtypes = [c_int, c_ulong, c_char_p]
+LIBC.ioctl.restype = c_int
+
+
+class bpf_insn(Structure):
+    """"The BPF instruction data structure"""
+    _fields_ = [("code", c_ushort),
+                ("jt", c_ubyte),
+                ("jf", c_ubyte),
+                ("k", c_uint32)]
+
+
+class bpf_program(Structure):
+    """"Structure for BIOCSETF"""
+    _fields_ = [("bf_len", c_uint),
+                ("bf_insns", POINTER(bpf_insn))]
+
+
+# Addresses manipulation functions
+
+def get_if_raw_addr(ifname):
+    """Returns the IPv4 address configured on 'ifname', packed with inet_pton."""
+
+    # Get ifconfig output
+    try:
+        fd = os.popen("%s %s" % (conf.prog.ifconfig, ifname))
+    except OSError, msg:
+        raise Scapy_Exception("Failed to execute ifconfig: (%s)" % msg)
+
+    # Get IPv4 addresses
+    addresses = [l for l in fd.readlines() if l.find("netmask") >= 0]
+    if not addresses:
+        raise Scapy_Exception("No IPv4 address found on %s !" % ifname)
+
+    # Pack the first address
+    address = addresses[0].split(' ')[1]
+    return socket.inet_pton(socket.AF_INET, address)
+
+
+def get_if_raw_hwaddr(ifname):
+    """Returns the packed MAC address configured on 'ifname'."""
+
+    NULL_MAC_ADDRESS = '\x00'*6
+
+    # Handle the loopback interface separately
+    if ifname == LOOPBACK_NAME:
+        return (ARPHDR_LOOPBACK, NULL_MAC_ADDRESS)
+
+    # Get ifconfig output
+    try:
+        fd = os.popen("%s %s" % (conf.prog.ifconfig, ifname))
+    except OSError, msg:
+        warning("Failed to execute ifconfig: (%s)" % msg)
+        raise Scapy_Exception("Failed to execute ifconfig: (%s)" % msg)
+
+    # Get MAC addresses
+    addresses = [l for l in fd.readlines() if l.find("ether") >= 0 or
+                                              l.find("lladdr") >= 0 or
+                                              l.find("address") >= 0]
+    if not addresses:
+        raise Scapy_Exception("No MAC address found on %s !" % ifname)
+
+    # Pack and return the MAC address
+    mac = addresses[0].split(' ')[1]
+    mac = [chr(int(b, 16)) for b in mac.split(':')]
+    return (ARPHDR_ETHER, ''.join(mac))
+
+
+# BPF specific functions
+
+def get_dev_bpf():
+    """Returns an opened BPF file object"""
+
+    # Get the first available BPF handle
+    for bpf in range(0, 8):
+        try:
+            fd = os.open("/dev/bpf%i" % bpf, os.O_RDWR)
+            return (fd, bpf)
+        except OSError, err:
+            continue
+
+    raise Scapy_Exception("No /dev/bpf handle is available !")
+
+
+def attach_filter(fd, iface, bpf_filter_string):
+    """Attach a BPF filter to the BPF file descriptor"""
+
+    # Retrieve the BPF byte code in decimal
+    command = "%s -i %s -ddd -s 1600 '%s'" % (conf.prog.tcpdump, iface, bpf_filter_string)
+    try:
+        f = os.popen(command)
+    except OSError, msg:
+        raise Scapy_Exception("Failed to execute tcpdump: (%s)" % msg)
+
+    # Convert the byte code to a BPF program structure
+    lines = f.readlines()
+    if lines == []:
+        raise Scapy_Exception("Got an empty BPF filter from tcpdump !")
+
+    # Allocate BPF instructions
+    size = int(lines[0])
+    bpf_insn_a = bpf_insn * size
+    bip = bpf_insn_a()
+
+    # Fill the BPF instruction structures with the byte code
+    lines = lines[1:]
+    for i in xrange(len(lines)):
+        values = [int(v) for v in lines[i].split()]
+        bip[i].code = c_ushort(values[0])
+        bip[i].jt = c_ubyte(values[1])
+        bip[i].jf = c_ubyte(values[2])
+        bip[i].k = c_uint(values[3])
+
+    # Create the BPF program and assign it to the interface
+    bp = bpf_program(size, bip)
+    ret = LIBC.ioctl(c_int(fd), BIOCSETF, cast(pointer(bp), c_char_p))
+    if ret < 0:
+        raise Scapy_Exception("Can't attach the BPF filter !")
+
+
+# Interface manipulation functions
+
+def get_if_list():
+    """Returns a list containing all network interfaces."""
+
+    # Get ifconfig output
+    try:
+        fd = os.popen("%s -a" % conf.prog.ifconfig)
+    except OSError, msg:
+        raise Scapy_Exception("Failed to execute ifconfig: (%s)" % msg)
+
+    # Get interfaces
+    interfaces = [line[:line.find(':')] for line in fd.readlines()
+                                        if ": flags" in line.lower()]
+    return interfaces
+
+
+def get_working_ifaces():
+    """
+    Returns an ordered list of interfaces that could be used with BPF.
+    Note: the order mimics pcap_findalldevs() behavior
+    """
+
+    # Only root is allowed to perform the following ioctl() call
+    if os.getuid() != 0:
+        return []
+
+    # Test all network interfaces
+    interfaces = []
+    for ifname in get_if_list():
+
+        # Unlike pcap_findalldevs(), we do not care of loopback interfaces.
+        if ifname == LOOPBACK_NAME:
+            continue
+
+        # Get interface flags
+        try:
+            result = get_if(ifname, SIOCGIFFLAGS)
+        except IOError, msg:
+            warning("ioctl(SIOCGIFFLAGS) failed on %s !" % ifname)
+            continue
+
+        # Convert flags
+        ifflags = struct.unpack("16xH14x", result)[0]
+        if ifflags & 0x1:  # IFF_UP
+
+            # Get a BPF handle
+            fd, _ = get_dev_bpf()
+            if fd is None:
+                raise Scapy_Exception("No /dev/bpf are available !")
+
+            # Check if the interface can be used
+            try:
+                fcntl.ioctl(fd, BIOCSETIF, struct.pack("16s16x", ifname))
+                interfaces.append((ifname, int(ifname[-1])))
+            except IOError, err:
+                pass
+
+            # Close the file descriptor
+            os.close(fd)
+
+    # Sort to mimic pcap_findalldevs() order
+    interfaces.sort(lambda (ifname_left, ifid_left),
+                        (ifname_right, ifid_right): ifid_left-ifid_right)
+    return interfaces
+
+
+def get_working_if():
+    """Returns the first interface than can be used with BPF"""
+
+    ifaces = get_working_ifaces()
+    if not ifaces:
+        # A better interface will be selected later using the routing table
+        return LOOPBACK_NAME
+    return ifaces[0][0]
diff --git a/scapy/arch/bpf/supersocket.py b/scapy/arch/bpf/supersocket.py
new file mode 100644
index 0000000000000000000000000000000000000000..a74d8f82e772f9143a929e1ec0efa97518893ec0
--- /dev/null
+++ b/scapy/arch/bpf/supersocket.py
@@ -0,0 +1,385 @@
+# Guillaume Valadon <guillaume@valadon.net>
+
+"""
+Scapy *BSD native support - BPF sockets
+"""
+
+from scapy.config import conf
+from scapy.error import Scapy_Exception
+from scapy.supersocket import SuperSocket
+from scapy.layers.l2 import Ether
+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.arch.consts import FREEBSD, OPENBSD, NETBSD
+from scapy.utils import warning
+
+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
+
+
+# SuperSockets definitions
+
+class _L2bpfSocket(SuperSocket):
+    """"Generic Scapy BPF Super Socket"""
+
+    desc = "read/write packets using BPF"
+    assigned_interface = None
+    fd_flags = None
+    ins = None
+    closed = False
+
+    def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0):
+
+        # SuperSocket mandatory variables
+        if promisc is None:
+            self.promisc = conf.sniff_promisc
+        else:
+            self.promisc = promisc
+
+        if iface is None:
+            self.iface = conf.iface
+        else:
+            self.iface = iface
+
+        # Get the BPF handle
+        (self.ins, self.dev_bpf) = get_dev_bpf()
+        self.outs = self.ins
+
+        # Set the BPF buffer length
+        try:
+            fcntl.ioctl(self.ins, BIOCSBLEN, struct.pack('I', BPF_BUFFER_LENGTH))
+        except IOError, err:
+            msg = "BIOCSBLEN failed on /dev/bpf%i" % self.dev_bpf
+            raise Scapy_Exception(msg)
+
+        # Assign the network interface to the BPF handle
+        try:
+            fcntl.ioctl(self.ins, BIOCSETIF, struct.pack("16s16x", self.iface))
+        except IOError, err:
+            msg = "BIOCSETIF failed on %s" % self.iface
+            raise Scapy_Exception(msg)
+        self.assigned_interface = self.iface
+
+        # Set the interface into promiscuous
+        if self.promisc:
+            self.set_promisc(1)
+
+        # Don't block on read
+        try:
+            fcntl.ioctl(self.ins, BIOCIMMEDIATE, struct.pack('I', 1))
+        except IOError, err:
+            msg = "BIOCIMMEDIATE failed on /dev/bpf%i" % self.dev_bpf
+            raise Scapy_Exception(msg)
+
+        # 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, err:
+            msg = "BIOCSHDRCMPLT failed on /dev/bpf%i" % self.dev_bpf
+            raise Scapy_Exception(msg)
+
+        # Configure the BPF filter
+        if not nofilter:
+            if conf.except_filter:
+                if filter:
+                    filter = "(%s) and not (%s)" % (filter, conf.except_filter)
+                else:
+                    filter = "not (%s)" % conf.except_filter
+            if filter is not None:
+                attach_filter(self.ins, self.iface, filter)
+
+        # Set the guessed packet class
+        self.guessed_cls = self.guess_cls()
+
+    def set_promisc(self, value):
+        """Set the interface in promiscuous mode"""
+
+        try:
+            fcntl.ioctl(self.ins, BIOCPROMISC, struct.pack('i', value))
+        except IOError, err:
+            msg = "Can't put your interface (%s) into promiscuous mode !" % self.iface
+            raise Scapy_Exception(msg)
+
+    def __del__(self):
+        """Close the file descriptor on delete"""
+        self.close()
+
+    def guess_cls(self):
+        """Guess the packet class that must be used on the interface"""
+
+        # Get the data link type
+        try:
+            ret = fcntl.ioctl(self.ins, BIOCGDLT, struct.pack('I', 0))
+            ret = struct.unpack('I', ret)[0]
+        except IOError, err:
+            warning("BIOCGDLT failed: unable to guess type. Using Ethernet !")
+            return Ether
+
+        # *BSD loopback interface
+        if OPENBSD and ret == 12:  # DTL_NULL on OpenBSD
+            return Loopback
+
+        # Retrieve the corresponding class
+        cls = conf.l2types.get(ret, None)
+        if cls is None:
+            cls = Ether
+            warning("Unable to guess type. Using Ethernet !")
+
+        return cls
+
+    def set_nonblock(self, set_flag=True):
+        """Set the non blocking flag on the socket"""
+
+        # Get the current flags
+        if self.fd_flags is None:
+            try:
+                self.fd_flags = fcntl.fcntl(self.ins, fcntl.F_GETFL)
+            except IOError, err:
+                warning("Can't get flags on this file descriptor !")
+                return
+
+        # Set the non blocking flag
+        if set_flag:
+            new_fd_flags = self.fd_flags | os.O_NONBLOCK
+        else:
+            new_fd_flags = self.fd_flags & ~os.O_NONBLOCK
+
+        try:
+            fcntl.fcntl(self.ins, fcntl.F_SETFL, new_fd_flags)
+            self.fd_flags = new_fd_flags
+        except:
+            warning("Can't set flags on this file descriptor !")
+
+    def get_stats(self):
+        """Get received / dropped statistics"""
+
+        try:
+            ret = fcntl.ioctl(self.ins, BIOCGSTATS, struct.pack("2I", 0, 0))
+            return struct.unpack("2I", ret)
+        except IOError, err:
+            warning("Unable to get stats from BPF !")
+            return (None, None)
+
+    def get_blen(self):
+        """Get the BPF buffer length"""
+
+        try:
+            ret = fcntl.ioctl(self.ins, BIOCGBLEN, struct.pack("I", 0))
+            return struct.unpack("I", ret)[0]
+        except IOError, err:
+            warning("Unable to get the BPF buffer length")
+            return
+
+    def fileno(self):
+        """Get the underlying file descriptor"""
+        return self.ins
+
+    def close(self):
+        """Close the Super Socket"""
+
+        if not self.closed and self.ins is not None:
+            os.close(self.ins)
+            self.closed = True
+            self.ins = None
+
+    def send(self, x):
+        """Dummy send method"""
+        raise Exception("Can't send anything with %s" % self.__name__)
+
+    def recv(self, x=BPF_BUFFER_LENGTH):
+        """Dummy recv method"""
+        raise Exception("Can't recv anything with %s" % self.__name__)
+
+
+class L2bpfListenSocket(_L2bpfSocket):
+    """"Scapy L2 BPF Listen Super Socket"""
+
+    received_frames = []
+
+    def buffered_frames(self):
+        """Return the number of frames in the buffer"""
+        return len(self.received_frames)
+
+    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
+
+    def bpf_align(self, 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>
+
+    def extract_frames(self, bpf_buffer):
+        """Extract all frames from the buffer and stored them in the received list."""
+
+        # Ensure that the BPF buffer contains at least the header
+        len_bb = len(bpf_buffer)
+        if len_bb < 20:  # Note: 20 == sizeof(struct bfp_hdr)
+            return
+
+        # Extract useful information from the BPF header
+        if FREEBSD or NETBSD:
+            # struct bpf_xhdr or struct bpf_hdr32
+            bh_tstamp_offset = 16
+        else:
+            # struct bpf_hdr
+            bh_tstamp_offset = 8
+
+        # Parse the BPF header
+        bh_caplen = struct.unpack('I', bpf_buffer[bh_tstamp_offset:bh_tstamp_offset+4])[0]
+        next_offset = bh_tstamp_offset + 4
+        bh_datalen = struct.unpack('I', bpf_buffer[next_offset:next_offset+4])[0]
+        next_offset += 4
+        bh_hdrlen = struct.unpack('H', bpf_buffer[next_offset:next_offset+2])[0]
+        if bh_datalen == 0:
+            return
+
+        # Get and store the Scapy object
+        frame_str = bpf_buffer[bh_hdrlen:bh_hdrlen+bh_caplen]
+        try:
+            pkt = self.guessed_cls(frame_str)
+        except:
+            if conf.debug_dissector:
+                raise
+            pkt = Raw(frame_str)
+        self.received_frames.append(pkt)
+
+        # Extract the next frame
+        end = self.bpf_align(bh_hdrlen, bh_caplen)
+        if (len_bb - end) >= 20:
+            self.extract_frames(bpf_buffer[end:])
+
+    def recv(self, x=BPF_BUFFER_LENGTH):
+        """Receive a frame from the network"""
+
+        if self.buffered_frames():
+            # 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, e:
+                if e.errno == errno.EAGAIN:
+                    return
+                else:
+                    warning("BPF recv(): %s" % e)
+                    return
+
+            # Extract all frames from the BPF buffer
+            self.extract_frames(bpf_buffer)
+            return self.get_frame()
+
+
+class L2bpfSocket(L2bpfListenSocket):
+    """"Scapy L2 BPF Super Socket"""
+
+    def send(self, x):
+        """Send a frame"""
+        return os.write(self.outs, str(x))
+
+    def nonblock_recv(self):
+        """Non blocking receive"""
+
+        if self.buffered_frames():
+            # 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
+
+
+class L3bpfSocket(L2bpfSocket):
+
+    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 = 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, err:
+                msg = "BIOCSETIF failed on %s" % iff
+                raise Scapy_Exception(msg)
+            self.assigned_interface = iff
+
+        # Build the frame
+        frame = str(self.guessed_cls()/pkt)
+        pkt.sent_time = time.time()
+
+        # Send the frame
+        L2bpfSocket.send(self, frame)
+
+
+# Sockets manipulation functions
+
+def isBPFSocket(obj):
+    """Return True is obj is a BPF Super Socket"""
+    return isinstance(obj, L2bpfListenSocket) or isinstance(obj, L2bpfListenSocket) or isinstance(obj, L3bpfSocket)
+
+
+def bpf_select(fds_list, timeout=None):
+    """A call to recv() can return several frames. This functions hides the fact
+       that some frames are read from the internal buffer."""
+
+    # Check file descriptors types
+    bpf_scks_buffered = list()
+    select_fds = list()
+
+    for tmp_fd in fds_list:
+
+        # Specific BPF sockets
+        if isBPFSocket(tmp_fd):
+            # Get buffers status
+            if tmp_fd.buffered_frames():
+                bpf_scks_buffered.append(tmp_fd)
+                continue
+
+        # Regular file descriptors or empty BPF buffer
+        select_fds.append(tmp_fd)
+
+    if len(select_fds):
+        # Call select for sockets with empty buffers
+        if timeout is None:
+            timeout = 0.05
+        ready_list, _, _ = select(select_fds, [], [], timeout)
+        return bpf_scks_buffered + ready_list
+
+    else:
+        return bpf_scks_buffered
diff --git a/scapy/arch/unix.py b/scapy/arch/unix.py
index 66400ad97985af7d1497a1b34a1ca5434bb92435..0e3428cbd0fcabac0ba53d2acf4c177274c0fc7d 100644
--- a/scapy/arch/unix.py
+++ b/scapy/arch/unix.py
@@ -16,7 +16,8 @@ import scapy.config
 import scapy.utils
 from scapy.utils6 import in6_getscope, construct_source_candidate_set
 from scapy.utils6 import in6_isvalid, in6_ismlladdr, in6_ismnladdr
-import scapy.arch
+from scapy.arch.consts import FREEBSD, NETBSD, OPENBSD, SOLARIS, LOOPBACK_NAME
+from scapy.arch import get_if_addr
 from scapy.config import conf
 
 
@@ -41,9 +42,9 @@ def _guess_iface_name(netif):
 
 
 def read_routes():
-    if scapy.arch.SOLARIS:
+    if SOLARIS:
         f=os.popen("netstat -rvn") # -f inet
-    elif scapy.arch.FREEBSD:
+    elif FREEBSD:
         f=os.popen("netstat -rnW") # -W to handle long interface names
     else:
         f=os.popen("netstat -rn") # -f inet
@@ -67,7 +68,7 @@ def read_routes():
             continue
         if not l:
             break
-        if scapy.arch.SOLARIS:
+        if SOLARIS:
             lspl = l.split()
             if len(lspl) == 10:
                 dest,mask,gw,netif,mxfrg,rtt,ref,flg = lspl[:8]
@@ -84,7 +85,7 @@ def read_routes():
             dest = 0L
             netmask = 0L
         else:
-            if scapy.arch.SOLARIS:
+            if SOLARIS:
                 netmask = scapy.utils.atol(mask)
             elif "/" in dest:
                 dest,netmask = dest.split("/")
@@ -97,7 +98,7 @@ def read_routes():
             gw = '0.0.0.0'
         if netif is not None:
             try:
-                ifaddr = scapy.arch.get_if_addr(netif)
+                ifaddr = get_if_addr(netif)
                 routes.append((dest,netmask,gw,netif,ifaddr))
             except OSError as exc:
                 if exc.message == 'Device not configured':
@@ -106,7 +107,7 @@ def read_routes():
                     # ignore it.
                     guessed_netif = _guess_iface_name(netif)
                     if guessed_netif is not None:
-                        ifaddr = scapy.arch.get_if_addr(guessed_netif)
+                        ifaddr = get_if_addr(guessed_netif)
                         routes.append((dest, netmask, gw, guessed_netif, ifaddr))
                     else:
                         warning("Could not guess partial interface name: %s" % netif)
@@ -184,7 +185,7 @@ def in6_getifaddr():
     """
 
     # List all network interfaces
-    if scapy.arch.OPENBSD:
+    if OPENBSD:
         try:
             f = os.popen("%s" % conf.prog.ifconfig)
         except OSError,msg:
@@ -244,7 +245,7 @@ def read_routes6():
 
         # Parse a route entry according to the operating system
         splitted_line = line.split()
-        if scapy.arch.OPENBSD or scapy.arch.NETBSD:
+        if OPENBSD or NETBSD:
             index = 5 + mtu_present + prio_present
             if len(splitted_line) < index:
                 warning("Not enough columns in route entry !")
@@ -312,14 +313,14 @@ def read_routes6():
             # Note: multicast routing is handled in Route6.route()
             continue
 
-        if scapy.arch.LOOPBACK_NAME in dev:
+        if LOOPBACK_NAME in dev:
             # Handle ::1 separately
             cset = ["::1"]
             next_hop = "::"
         else:
             # Get possible IPv6 source addresses
             devaddrs = filter(lambda x: x[2] == dev, lifaddr)
-            cset = construct_source_candidate_set(destination, destination_plen, devaddrs, scapy.arch.LOOPBACK_NAME)
+            cset = construct_source_candidate_set(destination, destination_plen, devaddrs, LOOPBACK_NAME)
 
         if len(cset):
             routes.append((destination, destination_plen, next_hop, dev, cset))
diff --git a/scapy/config.py b/scapy/config.py
index d66a6443a843b4075986ac2802a06ce727e78425..032ab871593b68fb75b8ec7a8441f18a08cee7e7 100755
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -367,6 +367,7 @@ contribs: a dict which can be used by contrib layers to store local configuratio
     emph = Emphasize()
     use_pcap = os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y")
     use_dnet = os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y")
+    use_bpf = False
     use_winpcapy = False
     ipv6_enabled = socket.has_ipv6
     ethertypes = ETHER_TYPES
diff --git a/scapy/data.py b/scapy/data.py
index 890b00af731584df723823f7204c031948edf29b..5a46311957afe11df02e8d0d090308766e32c0dd 100644
--- a/scapy/data.py
+++ b/scapy/data.py
@@ -30,6 +30,8 @@ ARPHDR_PPP = 512
 ARPHDR_LOOPBACK = 772
 ARPHDR_TUN = 65534
 
+# From net/bpf.h
+DLT_NULL = 0
 
 # From net/ipv6.h on Linux (+ Additions)
 IPV6_ADDR_UNICAST     = 0x01
diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py
index c83fff54538d05cbfab3cc4598dd1c8827a8ffab..00f78e3079b6c1711ce6e6767f89f817c9bac715 100644
--- a/scapy/layers/inet.py
+++ b/scapy/layers/inet.py
@@ -761,6 +761,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=2)
 bind_layers( IPerror,       IPerror,       frag=0, proto=4)
 bind_layers( IPerror,       ICMPerror,     frag=0, proto=1)
 bind_layers( IPerror,       TCPerror,      frag=0, proto=6)
diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py
index 124d913e75828aea3849266130bf05a56d039beb..86c63aea50b25b421b12fae2603a4f5a2576824b 100644
--- a/scapy/layers/inet6.py
+++ b/scapy/layers/inet6.py
@@ -3724,6 +3724,7 @@ conf.l2types.register(31, IPv6)
 
 bind_layers(Ether,     IPv6,     type = 0x86dd )
 bind_layers(CookedLinux, IPv6,   proto = 0x86dd )
+bind_layers(Loopback,  IPv6,     type = 0x1c )
 bind_layers(IPerror6,  TCPerror, nh = socket.IPPROTO_TCP )
 bind_layers(IPerror6,  UDPerror, nh = socket.IPPROTO_UDP )
 bind_layers(IPv6,      TCP,      nh = socket.IPPROTO_TCP )
diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py
index 4f491d299b7a7184ddfaae39d51b312f358f195d..c13bbc681a23a24f7bf84ae79c3d795159156dc9 100644
--- a/scapy/layers/l2.py
+++ b/scapy/layers/l2.py
@@ -16,7 +16,7 @@ from scapy.packet import *
 from scapy.ansmachine import *
 from scapy.plist import SndRcvList
 from scapy.fields import *
-from scapy.sendrecv import *
+from scapy.sendrecv import srp,srp1
 from scapy.arch import get_if_hwaddr
 from scapy.arch.consts import LOOPBACK_NAME
 from scapy.utils import inet_ntoa, inet_aton
@@ -603,10 +603,30 @@ class GRE(Packet):
         return p
 
 
+### *BSD loopback layer
+
+class LoIntEnumField(EnumField):
+    def __init__(self, name, default, enum):
+        EnumField.__init__(self, name, default, enum, "!I")
+
+    def m2i(self, pkt, x):
+        return x >> 24
+
+    def i2m(self, pkt, x):
+        return x << 24
+
+LOOPBACK_TYPES = { 0x2: "IPv4", 0x1c: "IPv6" }
+
+class Loopback(Packet):
+    """*BSD loopback layer"""
+
+    name = "Loopback"
+    fields_desc = [ LoIntEnumField("type", 0x2, LOOPBACK_TYPES) ]
+
+
 class Dot1AD(Dot1Q):
     name = '802_1AD'
 
-
 bind_layers( Dot3,          LLC,           )
 bind_layers( Ether,         LLC,           type=122)
 bind_layers( Ether,         LLC,           type=34928)
@@ -652,6 +672,7 @@ conf.l2types.register_num2layer(ARPHDR_LOOPBACK, Ether)
 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)
 
 conf.l3types.register(ETH_P_ARP, ARP)
 
diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py
index cf109f33478c3b0c8f9f41578dcc97c13ec9002c..5f6274b7deb46064f227106437a3ddd282adbb5e 100644
--- a/scapy/sendrecv.py
+++ b/scapy/sendrecv.py
@@ -127,7 +127,12 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
                                 if remaintime <= 0:
                                     break
                             r = None
-                            if not isinstance(pks, StreamSocket) and (FREEBSD or DARWIN):
+                            if conf.use_bpf:
+                                from scapy.arch.bpf.supersocket import bpf_select
+                                inp = bpf_select(inmask)
+                                if pks in inp:
+                                    r = pks.recv()
+                            elif not isinstance(pks, StreamSocket) and (FREEBSD or DARWIN):
                                 inp, out, err = select(inmask,[],[], 0.05)
                                 if len(inp) == 0 or pks in inp:
                                     r = pks.nonblock_recv()
@@ -488,7 +493,13 @@ def sndrcvflood(pks, pkt, prn=lambda (s,r):r.summary(), chainCC=0, store=1, uniq
 
     try:
         while 1:
-            readyr,readys,_ = select([rsock],[ssock],[])
+            if conf.use_bpf:
+                from scapy.arch.bpf.supersocket import bpf_select
+                readyr = bpf_select([rsock])
+                _, readys, _ = select([], [ssock], [])
+            else:
+                readyr, readys, _ = select([rsock], [ssock], [])
+
             if ssock in readys:
                 pks.send(packets_to_send.next())
                 
@@ -605,8 +616,12 @@ interfaces)
                 remain = stoptime-time.time()
                 if remain <= 0:
                     break
-            sel = select(sniff_sockets, [], [], remain)
-            for s in sel[0]:
+            if conf.use_bpf:
+                from scapy.arch.bpf.supersocket import bpf_select
+                ins = bpf_select(sniff_sockets, remain)
+            else:
+                ins, _, _ = select(sniff_sockets, [], [], remain)
+            for s in ins:
                 p = s.recv()
                 if p is None and offline is not None:
                     stop_event = True
@@ -678,7 +693,12 @@ stop_filter: python function applied to each packet to determine
                 remain = stoptime-time.time()
                 if remain <= 0:
                     break
-            ins, outs, errs = select([s1, s2], [], [], remain)
+            if conf.use_bpf:
+                from scapy.arch.bpf.supersocket import bpf_select
+                ins = bpf_select([s1, s2], remain)
+            else:
+                ins, _, _ = select([s1, s2], [], [], remain)
+
             for s in ins:
                 p = s.recv()
                 if p is not None:
diff --git a/test/bpf.uts b/test/bpf.uts
new file mode 100644
index 0000000000000000000000000000000000000000..13ca7d27dcdbbe12dc25fb4e30c2d45a79b9554e
--- /dev/null
+++ b/test/bpf.uts
@@ -0,0 +1,144 @@
+% Regression tests for Scapy BPF mode
+
+# More informations at http://www.secdev.org/projects/UTscapy/
+
+
+############
+############
++ Addresses manipulation functions
+
+= Get the packet IPv4 address configured on conf.iface
+
+get_if_raw_addr(conf.iface)
+
+
+= Get the packed MAC address of conf.iface
+
+get_if_raw_hwaddr(conf.iface)
+
+= Get the packed MAC address of LOOPBACK_NAME
+
+get_if_raw_hwaddr(LOOPBACK_NAME) == (ARPHDR_LOOPBACK, '\x00'*6) 
+
+
+############
+############
++ BPF related functions
+
+= Get a BPF handler
+~ needs_root
+
+from scapy.arch.bpf.supersocket import get_dev_bpf
+fd, _ = get_dev_bpf()
+
+= Attach a BPF filter
+~ needs_root
+
+from scapy.arch.bpf.supersocket import attach_filter
+attach_filter(fd, conf.iface, "arp or icmp")
+
+
+= Get network interfaces list
+
+iflist = get_if_list()
+len(iflist) > 0
+
+
+= Get working network interfaces
+~ needs_root
+
+from scapy.arch.bpf.core import get_working_ifaces
+ifworking = get_working_ifaces()
+len(ifworking)
+            
+from scapy.arch.bpf.core import get_working_if
+len(ifworking) and get_working_if() == ifworking[0][0]
+
+
+= Misc functions
+~ needs_root
+
+from scapy.arch.bpf.supersocket import isBPFSocket, bpf_select
+isBPFSocket(L2bpfListenSocket()) and isBPFSocket(L2bpfSocket()) and isBPFSocket(L3bpfSocket())
+
+l = bpf_select([L2bpfSocket()])
+l = bpf_select([L2bpfSocket(), sys.stdin.fileno()])
+
+
+############
+############
++ BPF sockets
+
+= L2bpfListenSocket - initialization variants
+~ needs_root
+
+L2bpfListenSocket()
+L2bpfListenSocket(iface=conf.iface)
+L2bpfListenSocket(promisc=True)
+L2bpfListenSocket(filter="icmp")
+L2bpfListenSocket(iface=conf.iface, promisc=True, filter="icmp")
+
+
+= L2bpfListenSocket - set_*()
+~ needs_root
+
+s = L2bpfListenSocket()
+s.set_promisc(0)
+s.set_nonblock(1)
+s.set_promisc(0)
+s.close()
+
+s = L2bpfListenSocket()
+s.set_nonblock(set_flag=False)
+s.set_nonblock(set_flag=True)
+s.set_nonblock(set_flag=False)
+s.close()
+
+
+= L2bpfListenSocket - get_*()
+~ needs_root
+
+s = L2bpfListenSocket()
+blen = s.get_blen()
+blen > 0 and type(blen) == int
+s.close()
+
+s = L2bpfListenSocket()
+stats = s.get_stats()
+len(stats) == 2 and type(stats) == tuple
+s.close()
+
+
+= L2bpfListenSocket - other methods
+~ needs_root
+
+s = L2bpfListenSocket()
+type(s.fileno()) == int
+s.close()
+
+s = L2bpfListenSocket()
+guessed = s.guess_cls()
+issubclass(guessed, Packet)
+s.close()
+
+
+= L2bpfSocket - nonblock_recv()
+~ needs_root
+
+s = L2bpfSocket()
+s.nonblock_recv()
+s.close()
+
+
+= L*bpfSocket - send()        
+~ needs_root
+                              
+s = L2bpfSocket()             
+s.send(Ether()/IP(dst="8.8.8.8")/ICMP())
+                              
+s = L3bpfSocket()             
+s.send(IP(dst="8.8.8.8")/ICMP())
+                              
+s = L3bpfSocket()             
+s.assigned_interface = LOOPBACK_NAME
+s.send(IP(dst="8.8.8.8")/ICMP())
diff --git a/test/regression.uts b/test/regression.uts
index 9bbaae07a73b2900f16ae7795128e6742936f37d..eda698d9b47f348e858d635d037cd2fe57634837 100644
--- a/test/regression.uts
+++ b/test/regression.uts
@@ -2645,6 +2645,11 @@ conf.route6.routes=[
 (                                '::',   0, 'fe80::20f:34ff:fe8a:8aa1', 'eth0', ['2001:db8:0:4444:20f:1fff:feca:4650', '2002:db8:0:4444:20f:1fff:feca:4650'])
 ]
 conf.route6.route("2002::1") == ('eth0', '2002:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') and conf.route6.route("2001::1") == ('eth0', '2001:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') and conf.route6.route("fe80::20f:1fff:feab:4870") == ('eth0', 'fe80::20f:1fff:feca:4650', '::') and conf.route6.route("::1") == ('lo', '::1', '::') and conf.route6.route("::") == ('eth0', '2001:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1')
+conf.route6.resync()
+if not len(conf.route6.routes):
+    # IPv6 seems disabled. Force a route to ::1
+    conf.route6.routes.append(("::1", 128, "::", LOOPBACK_NAME, ["::1"]))
+    True
 
 
 # There are many other to do.
@@ -4561,7 +4566,7 @@ assert isinstance(pkt, DNS) and isinstance(pkt.payload, NoPayload)
 import mock
 import StringIO
 
-@mock.patch("scapy.arch.get_if_addr")
+@mock.patch("scapy.arch.unix.get_if_addr")
 @mock.patch("scapy.arch.unix.os")
 def test_osx_netstat_truncated(mock_os, mock_get_if_addr):
     """Test read_routes() on OS X 10.? with a long interface name"""
@@ -4580,7 +4585,7 @@ default            link#11            UCSI            1        0 bridge1
     # Mocked file descriptors
     def se_popen(command):
         """Perform specific side effects"""
-        if command == "netstat -rn":
+        if command.startswith("netstat -rn"):
 	    return StringIO.StringIO(netstat_output)
         elif command == "ifconfig -l":
 	    ret = StringIO.StringIO(ifconfig_output)
@@ -4807,7 +4812,7 @@ test_freebsd_10_2()
 
 = OpenBSD 5.5
 
-@mock.patch("scapy.arch.OPENBSD")
+@mock.patch("scapy.arch.unix.OPENBSD")
 @mock.patch("scapy.arch.unix.in6_getifaddr")
 @mock.patch("scapy.arch.unix.os")
 def test_openbsd_5_5(mock_os, mock_in6_getifaddr, mock_openbsd):
@@ -4864,7 +4869,7 @@ test_openbsd_5_5()
 
 = NetBSD 7.0
 
-@mock.patch("scapy.arch.NETBSD")
+@mock.patch("scapy.arch.unix.NETBSD")
 @mock.patch("scapy.arch.unix.in6_getifaddr")
 @mock.patch("scapy.arch.unix.os")
 def test_netbsd_7_0(mock_os, mock_in6_getifaddr, mock_netbsd):