diff --git a/scapy/arch/__init__.py b/scapy/arch/__init__.py
index d76ba5b49615bbe8901e78758d9cf1012cab0a22..f8cb4bf91633b122a8bd1579058dd10faaa13e3b 100644
--- a/scapy/arch/__init__.py
+++ b/scapy/arch/__init__.py
@@ -68,14 +68,6 @@ elif SOLARIS:
     from scapy.arch.solaris import *
 elif WINDOWS:
     from scapy.arch.windows import *
-    # import only if parent is not route.py
-    # because compatibility.py will require route.py to work (through sendrecv.py)
-    parents = parent_function()
-    if len(parents) >= 3:
-        if not parents[2][1].endswith("route.py"):
-            from scapy.arch.windows.compatibility import *
-    else:
-        from scapy.arch.windows.compatibility import *
 
 if scapy.config.conf.iface is None:
     scapy.config.conf.iface = LOOPBACK_NAME
diff --git a/scapy/arch/bpf/supersocket.py b/scapy/arch/bpf/supersocket.py
index 41865be43a2dcc7da4d55e26c12cc239893e4e1e..e3a6b6d003662f94adb6b59269469bcced12031d 100644
--- a/scapy/arch/bpf/supersocket.py
+++ b/scapy/arch/bpf/supersocket.py
@@ -363,22 +363,19 @@ def bpf_select(fds_list, timeout=None):
 
     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
+        # Specific BPF sockets: get buffers status
+        if isBPFSocket(tmp_fd) and 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):
+    if 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/pcapdnet.py b/scapy/arch/pcapdnet.py
index 79c2a9291a81e3e0c502f55a3a29308435d67d4c..06a67a9ce6adc38bdf821777a7406c4e5a3b47f2 100644
--- a/scapy/arch/pcapdnet.py
+++ b/scapy/arch/pcapdnet.py
@@ -249,6 +249,10 @@ if conf.use_winpcapy:
               promisc = 0
           self.promisc = promisc
           self.ins = open_pcap(iface, 1600, self.promisc, 100)
+          # We need to have a different interface open because of an
+          # access violation in Npcap that occurs in multi-threading
+          # (see https://github.com/nmap/nmap/issues/982)
+          self.outs = open_pcap(iface, 1600, self.promisc, 100)
           try:
               ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1))
           except:
@@ -275,7 +279,7 @@ if conf.use_winpcapy:
           sx = str(x)
           if hasattr(x, "sent_time"):
               x.sent_time = time.time()
-          return self.ins.send(sx)
+          return self.outs.send(sx)
 
       def recv(self,x=MTU):
           ll = self.ins.datalink()
@@ -309,10 +313,12 @@ if conf.use_winpcapy:
           return p
   
       def close(self):
-          if hasattr(self, "ins"):
-              self.ins.close()
-          if hasattr(self, "outs"):
-              self.outs.close()
+          if not self.closed:
+              if hasattr(self, "ins"):
+                  self.ins.close()
+              if hasattr(self, "outs"):
+                  self.outs.close()
+          self.closed = True
 
   class L3pcapSocket(L2pcapSocket):
       desc = "read/write packets at layer 3 using only libpcap"
@@ -408,8 +414,8 @@ if conf.use_pcap:
                         return (s+0.000001*us), p
                 __next__ = next
                 def fileno(self):
-                    warning("fileno: pcapy API does not permit to get capure file descriptor. Bugs ahead! Press Enter to trigger packet reading")
-                    return 0
+                    raise RuntimeError("%s has no fileno. Please report this bug." %
+                                       self.__class__.__name__)
                 def __getattr__(self, attr):
                     return getattr(self.pcap, attr)
                 def __del__(self):
@@ -634,10 +640,12 @@ if conf.use_pcap and conf.use_dnet:
             return p
     
         def close(self):
-            if hasattr(self, "ins"):
-                del(self.ins)
-            if hasattr(self, "outs"):
-                del(self.outs)
+            if not self.closed:
+                if hasattr(self, "ins"):
+                    del(self.ins)
+                if hasattr(self, "outs"):
+                    del(self.outs)
+            self.closed = True
     
     class L2dnetSocket(SuperSocket):
         desc = "read/write packets at layer 2 using libdnet and libpcap"
@@ -704,10 +712,12 @@ if conf.use_pcap and conf.use_dnet:
             return p
     
         def close(self):
-            if hasattr(self, "ins"):
-                del(self.ins)
-            if hasattr(self, "outs"):
-                del(self.outs)
+            if not self.closed:
+                if hasattr(self, "ins"):
+                    del(self.ins)
+                if hasattr(self, "outs"):
+                    del(self.outs)
+            self.closed = True
 
     conf.L3socket=L3dnetSocket
     conf.L2socket=L2dnetSocket
diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py
index 43614e409b75b26d86ba03147dd9add7255efe04..154a667fdeb281e392d7678d3b1edaff55dd3f82 100755
--- a/scapy/arch/windows/__init__.py
+++ b/scapy/arch/windows/__init__.py
@@ -43,7 +43,7 @@ if not hasattr(socket, 'IPPROTO_GRE'):
 from scapy.arch import pcapdnet
 from scapy.arch.pcapdnet import *
 
-from scapy.consts import LOOPBACK_NAME
+import scapy.consts
 
 def is_new_release(ignoreVBS=False):
     if NEW_RELEASE and conf.prog.powershell is not None:
@@ -348,7 +348,7 @@ class NetworkInterface(object):
         try:
             if not self.ip:
                 self.ip=get_ip_from_name(data['name'])
-            if not self.ip and self.name == LOOPBACK_NAME:
+            if not self.ip and self.name == scapy.consts.LOOPBACK_NAME:
                 self.ip = "127.0.0.1"
             if not self.ip:
                 # No IP detected
@@ -423,7 +423,7 @@ class NetworkInterfaceDict(UserDict):
             except (KeyError, PcapNameNotFoundError):
                 pass
         
-        if len(self.data) == 0 and conf.use_winpcapy:
+        if not self.data and conf.use_winpcapy:
             _detect = pcap_service_status()
             def _ask_user():
                 if not conf.interactive:
@@ -456,7 +456,9 @@ class NetworkInterfaceDict(UserDict):
             self.remove_invalid_ifaces()
             # Replace LOOPBACK_INTERFACE
             try:
-                scapy.consts.LOOPBACK_INTERFACE = self.dev_from_name(LOOPBACK_NAME)
+                scapy.consts.LOOPBACK_INTERFACE = self.dev_from_name(
+                    scapy.consts.LOOPBACK_NAME,
+                )
             except:
                 pass
 
@@ -683,14 +685,14 @@ def in6_getifaddr():
 
 def _append_route6(routes, dpref, dp, nh, iface, lifaddr):
     cset = [] # candidate set (possible source addresses)
-    if iface.name == LOOPBACK_NAME:
+    if iface.name == scapy.consts.LOOPBACK_NAME:
         if dpref == '::':
             return
         cset = ['::1']
     else:
         devaddrs = (x for x in lifaddr if x[2] == iface)
         cset = scapy.utils6.construct_source_candidate_set(dpref, dp, devaddrs)
-    if len(cset) == 0:
+    if not cset:
         return
     # APPEND (DESTINATION, NETMASK, NEXT HOP, IFACE, CANDIDATS)
     routes.append((dpref, dp, nh, iface, cset))
@@ -743,7 +745,7 @@ def _read_routes6_7():
     index = 0
     for l in stdout.split('\n'):
         if not l.strip():
-            if len(current_object) == 0:
+            if not current_object:
                 continue
             
             if len(current_object) == len(regex_list):
@@ -834,10 +836,10 @@ def route_add_loopback(routes=None, ipv6=False, iflist=None):
     warning("This will completly mess up the routes. Testing purpose only !")
     # Add only if some adpaters already exist
     if ipv6:
-        if len(conf.route6.routes) == 0:
+        if not conf.route6.routes:
             return
     else:
-        if len(conf.route.routes) == 0:
+        if not conf.route.routes:
             return
     data = {}
     data['name'] = LOOPBACK_NAME
@@ -882,3 +884,16 @@ def route_add_loopback(routes=None, ipv6=False, iflist=None):
             routes.append(loopback_route6_custom)
         else:
             routes.append(loopback_route)
+
+
+if not conf.use_winpcapy:
+
+    class NotAvailableSocket(SuperSocket):
+        desc = "wpcap.dll missing"
+        def __init__(self, *args, **kargs):
+            raise RuntimeError("Sniffing and sending packets is not available: "
+                               "winpcap is not installed")
+
+    conf.L2socket = NotAvailableSocket
+    conf.L2listen = NotAvailableSocket
+    conf.L3socket = NotAvailableSocket
diff --git a/scapy/arch/windows/compatibility.py b/scapy/arch/windows/compatibility.py
deleted file mode 100644
index 0b4700c026323341001fcbd38ecd3874e91e7b4c..0000000000000000000000000000000000000000
--- a/scapy/arch/windows/compatibility.py
+++ /dev/null
@@ -1,248 +0,0 @@
-## This file is part of Scapy
-## See http://www.secdev.org/projects/scapy for more informations
-## Copyright (C) Philippe Biondi <phil@secdev.org>
-## This program is published under a GPLv2 license
-
-"""
-Instanciate part of the customizations needed to support Microsoft Windows.
-"""
-
-from __future__ import absolute_import
-from __future__ import print_function
-import itertools
-import os
-import re
-import socket
-import subprocess
-import sys
-import time
-
-from scapy.consts import LOOPBACK_NAME
-from scapy.config import conf,ConfClass
-from scapy.base_classes import Gen, SetGen
-import scapy.plist as plist
-from scapy.utils import PcapReader, tcpdump
-from scapy.arch.pcapdnet import PcapTimeoutElapsed
-from scapy.error import log_runtime
-from scapy.data import MTU, ETH_P_ARP,ETH_P_ALL
-import scapy.modules.six as six
-
-WINDOWS = True
-
-def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
-    if not isinstance(pkt, Gen):
-        pkt = SetGen(pkt)
-        
-    if verbose is None:
-        verbose = conf.verb
-    from scapy.sendrecv import debug
-    debug.recv = plist.PacketList([],"Unanswered")
-    debug.sent = plist.PacketList([],"Sent")
-    debug.match = plist.SndRcvList([])
-    nbrecv=0
-    ans = []
-    # do it here to fix random fields, so that parent and child have the same
-    all_stimuli = tobesent = [p for p in pkt]
-    notans = len(tobesent)
-
-    hsent={}
-    for i in tobesent:
-        h = i.hashret()
-        if h in hsent:
-            hsent[h].append(i)
-        else:
-            hsent[h] = [i]
-    if retry < 0:
-        retry = -retry
-        autostop=retry
-    else:
-        autostop=0
-
-
-    while retry >= 0:
-        found=0
-    
-        if timeout < 0:
-            timeout = None
-
-        pid=1
-        try:
-            if WINDOWS or pid == 0:
-                try:
-                    try:
-                        i = 0
-                        if verbose:
-                            print("Begin emission:")
-                        for p in tobesent:
-                            pks.send(p)
-                            i += 1
-                            time.sleep(inter)
-                        if verbose:
-                            print("Finished to send %i packets." % i)
-                    except SystemExit:
-                        pass
-                    except KeyboardInterrupt:
-                        pass
-                    except:
-                        log_runtime.exception("--- Error sending packets")
-                        log_runtime.info("--- Error sending packets")
-                finally:
-                    try:
-                        sent_times = [p.sent_time for p in all_stimuli if p.sent_time]
-                    except:
-                        pass
-            if WINDOWS or pid > 0:
-                # Timeout starts after last packet is sent (as in Unix version) 
-                if timeout:
-                    stoptime = time.time()+timeout
-                else:
-                    stoptime = 0
-                remaintime = None
-                try:
-                    try:
-                        while True:
-                            if stoptime:
-                                remaintime = stoptime-time.time()
-                                if remaintime <= 0:
-                                    break
-                            r = pks.recv(MTU)
-                            if r is None:
-                                continue
-                            ok = 0
-                            h = r.hashret()
-                            if h in hsent:
-                                hlst = hsent[h]
-                                for i, sentpkt in enumerate(hlst):
-                                    if r.answers(sentpkt):
-                                        ans.append((sentpkt, r))
-                                        if verbose > 1:
-                                            os.write(1, b"*")
-                                        ok = 1
-                                        if not multi:
-                                            del hlst[i]
-                                            notans -= 1
-                                        else:
-                                            if not hasattr(sentpkt, '_answered'):
-                                                notans -= 1
-                                            sentpkt._answered = 1
-                                        break
-                            if notans == 0 and not multi:
-                                break
-                            if not ok:
-                                if verbose > 1:
-                                    os.write(1, b".")
-                                nbrecv += 1
-                                if conf.debug_match:
-                                    debug.recv.append(r)
-                    except KeyboardInterrupt:
-                        if chainCC:
-                            raise
-                finally:
-                    if WINDOWS:
-                        for p,t in zip(all_stimuli, sent_times):
-                            p.sent_time = t
-        finally:
-            pass
-
-        remain = list(itertools.chain(*six.itervalues(hsent)))
-        if multi:
-            remain = [p for p in remain if not hasattr(p, '_answered')]
-            
-        if autostop and len(remain) > 0 and len(remain) != len(tobesent):
-            retry = autostop
-            
-        tobesent = remain
-        if len(tobesent) == 0:
-            break
-        retry -= 1
-        
-    if conf.debug_match:
-        debug.sent=plist.PacketList(remain[:],"Sent")
-        debug.match=plist.SndRcvList(ans[:])
-
-    #clean the ans list to delete the field _answered
-    if (multi):
-        for s,r in ans:
-            if hasattr(s, '_answered'):
-                del(s._answered)
-    
-    if verbose:
-        print("\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans))
-    return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered")
-
-
-import scapy.sendrecv as sendrecv
-sendrecv.sndrcv = sndrcv
-
-def sniff(count=0, store=1, offline=None, prn = None, stop_filter=None, lfilter=None, L2socket=None, timeout=None, *arg, **karg):
-    """Sniff packets
-sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets
-Select interface to sniff by setting conf.iface. Use show_interfaces() to see interface names.
-  count: number of packets to capture. 0 means infinity
-  store: whether to store sniffed packets or discard them
-    prn: function to apply to each packet. If something is returned,
-         it is displayed. Ex:
-         ex: prn = lambda x: x.summary()
- filter: provide a BPF filter
-lfilter: python function applied to each packet to determine
-         if further action may be done
-         ex: lfilter = lambda x: x.haslayer(Padding)
-offline: pcap file to read packets from, instead of sniffing them
-timeout: stop sniffing after a given time (default: None)
-L2socket: use the provided L2socket
-stop_filter: python function applied to each packet to determine
-             if we have to stop the capture after this packet
-             ex: stop_filter = lambda x: x.haslayer(TCP)
-    """
-    c = 0
-
-    if offline is None:
-        log_runtime.info('Sniffing on %s' % conf.iface)
-        if L2socket is None:
-            L2socket = conf.L2listen
-        s = L2socket(type=ETH_P_ALL, *arg, **karg)
-    else:
-        flt = karg.get('filter')
-        s = PcapReader(offline if flt is None else
-                       tcpdump(offline, args=["-w", "-", flt], getfd=True))
-    lst = []
-    if timeout is not None:
-        stoptime = time.time()+timeout
-    remain = None
-    while True:
-        try:
-            if timeout is not None:
-                remain = stoptime-time.time()
-                if remain <= 0:
-                    break
-
-            try:
-                p = s.recv(MTU)
-            except PcapTimeoutElapsed:
-                continue
-            if p is None:
-                break
-            if lfilter and not lfilter(p):
-                continue
-            if store:
-                lst.append(p)
-            c += 1
-            if prn:
-                r = prn(p)
-                if r is not None:
-                    print(r)
-            if stop_filter and stop_filter(p):
-                break
-            if 0 < count <= c:
-                break
-        except KeyboardInterrupt:
-            break
-    s.close()
-    return plist.PacketList(lst,"Sniffed")
-
-import scapy.sendrecv
-sendrecv.sniff = sniff
-
-# If wpcap.dll is not available
-if not (conf.use_winpcapy or conf.use_pcap or conf.use_dnet):
-    from scapy.arch.windows.disable_sendrecv import *
diff --git a/scapy/arch/windows/disable_sendrecv.py b/scapy/arch/windows/disable_sendrecv.py
deleted file mode 100644
index e9d514c6f2a341982711b90cc921dce6423fcc4d..0000000000000000000000000000000000000000
--- a/scapy/arch/windows/disable_sendrecv.py
+++ /dev/null
@@ -1,57 +0,0 @@
-## This file is part of Scapy
-## See http://www.secdev.org/projects/scapy for more informations
-## Copyright (C) Philippe Biondi <phil@secdev.org>
-## This program is published under a GPLv2 license
-
-"""
-When wpcap.dll is not available, replace all sendrecv functions that won't work.
-"""
-
-from scapy.error import log_runtime
-import scapy.sendrecv as sendrecv
-import scapy.config as conf
-from scapy.supersocket import SuperSocket
-
-def log_warning():
-    if conf.conf.interactive:
-        log_runtime.warning("Function not available (winpcap is not installed)")
-    else:
-        raise ImportError("Function not available (winpcap is not installed)")
-
-def not_available(*args, **kwargs):
-    log_warning()
-    return None
-
-class not_available_socket(SuperSocket):
-    desc = "wpcap.dll missing"
-    def __init__(self, type=None, promisc=None, filter=None, iface=None, nofilter=0):
-        log_warning()
-        return
-    def send(self, x):
-        return
-    def recv(self,x=None):
-        return
-    def nonblock_recv(self):
-        return
-    def close(self):
-        return
-    
-
-sendrecv.send = not_available
-sendrecv.sendp = not_available
-sendrecv.sendpfast = not_available
-sendrecv.sr = not_available
-sendrecv.sr1 = not_available
-sendrecv.srflood = not_available
-sendrecv.srloop = not_available
-sendrecv.srp = not_available
-sendrecv.srp1 = not_available
-sendrecv.srpflood = not_available
-sendrecv.srploop = not_available
-sendrecv.sniff = not_available
-sendrecv.sndrcv = not_available
-sendrecv.sndrcvflood = not_available
-sendrecv.tshark = not_available
-
-conf.L3socket=not_available_socket
-conf.L2socket=not_available_socket
diff --git a/scapy/config.py b/scapy/config.py
index 3408648b7cad0f6870f0da8cee0628307962043e..871c182cfc8e4641be0ccc29fc1f056dfb781d9e 100755
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -201,8 +201,12 @@ class CacheInstance(dict, object):
         self._timetable[item] = time.time()
         dict.__setitem__(self, item,v)
     def update(self, other):
-        dict.update(self, other)
-        self._timetable.update(other._timetable)
+        for key, value in other.iteritems():
+            # We only update an element from `other` either if it does
+            # not exist in `self` or if the entry in `self` is older.
+            if key not in self or self._timetable[key] < other._timetable[key]:
+                dict.__setitem__(self, key, value)
+                self._timetable[key] = other._timetable[key]
     def iteritems(self):
         if self.timeout is None:
             return six.iteritems(self.__dict__)
diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py
index 5e7e7978fa9eec2a48d192ca561de83a976122ac..ae6dc5c0b8db7b12cc4e507b2c968bf221891fc3 100644
--- a/scapy/layers/l2.py
+++ b/scapy/layers/l2.py
@@ -468,7 +468,7 @@ arpcachepoison(target, victim, [interval=60]) -> None
         while True:
             sendp(p, iface_hint=target)
             if conf.verb > 1:
-                os.write(1,".")
+                os.write(1, b".")
             time.sleep(interval)
     except KeyboardInterrupt:
         pass
diff --git a/scapy/plist.py b/scapy/plist.py
index a066f49088e488099e15e0ec648e0b1dcb0e5413..3301ed030d0dc21a9b492be7b06ac1f0e8c7a646 100644
--- a/scapy/plist.py
+++ b/scapy/plist.py
@@ -416,7 +416,7 @@ lfilter: truth function to apply to each packet to decide whether it will be dis
             cbb = c.bbox()
             c.text(cbb.left(),cbb.top()+1,r"\font\cmssfont=cmss12\cmssfont{Frame %i/%i}" % (i,l),[pyx.text.size.LARGE])
             if conf.verb >= 2:
-                os.write(1,".")
+                os.write(1, b".")
             d.append(pyx.document.page(c, paperformat=pyx.document.paperformat.A4,
                                        margin=1*pyx.unit.t_cm,
                                        fittosize=1))
diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py
index b404f88fe2aa3d83490dfb69cf2f1cc747624125..4503abcac26c5526c0cbd2841ba4c59885f78022 100644
--- a/scapy/sendrecv.py
+++ b/scapy/sendrecv.py
@@ -7,24 +7,27 @@
 Functions to send and receive packets.
 """
 
-from __future__ import absolute_import
-from __future__ import print_function
+from __future__ import absolute_import, print_function
 import errno
-import os, sys, time, subprocess
 import itertools
+import threading
+import os
 from select import select, error as select_error
+import subprocess
+import time
 
-from scapy.consts import DARWIN, FREEBSD, OPENBSD
-from scapy.data import *
+from scapy.consts import DARWIN, FREEBSD, OPENBSD, WINDOWS
+from scapy.data import ETH_P_ALL, MTU
 from scapy.config import conf
 from scapy.packet import Gen
 from scapy.utils import get_temp_file, PcapReader, tcpdump, wrpcap
 from scapy import plist
-from scapy.error import log_runtime, log_interactive, warning
+from scapy.error import log_runtime, log_interactive
 from scapy.base_classes import SetGen
 from scapy.supersocket import StreamSocket
 import scapy.modules.six as six
-from scapy.modules.six.moves import map, zip
+from scapy.modules.six.moves import map
+from scapy.modules.six import iteritems
 if conf.route is None:
     # unused import, only to initialize conf.route
     import scapy.route
@@ -44,12 +47,41 @@ class debug:
 ####################
 
 
+def _sndrcv_snd(pks, timeout, inter, verbose, tobesent, stopevent):
+    """Function used in the sending thread of sndrcv()"""
+    try:
+        i = 0
+        if verbose:
+            print("Begin emission:")
+        for p in tobesent:
+            pks.send(p)
+            i += 1
+            time.sleep(inter)
+        if verbose:
+            print("Finished to send %i packets." % i)
+    except SystemExit:
+        pass
+    except KeyboardInterrupt:
+        pass
+    except:
+        log_runtime.info("--- Error sending packets", exc_info=True)
+    if timeout is not None:
+        stopevent.wait(timeout)
+        stopevent.set()
+
 
+class _BreakException(Exception):
+    """A dummy exception used in _get_pkt() to get out of the infinite
+loop
 
-def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
+    """
+    pass
+
+
+def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False,
+           retry=0, multi=False):
     if not isinstance(pkt, Gen):
         pkt = SetGen(pkt)
-        
     if verbose is None:
         verbose = conf.verb
     debug.recv = plist.PacketList([],"Unanswered")
@@ -58,149 +90,103 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
     nbrecv=0
     ans = []
     # do it here to fix random fields, so that parent and child have the same
-    all_stimuli = tobesent = [p for p in pkt]
+    tobesent = [p for p in pkt]
     notans = len(tobesent)
 
     hsent={}
     for i in tobesent:
         h = i.hashret()
-        if h in hsent:
-            hsent[h].append(i)
-        else:
-            hsent[h] = [i]
+        hsent.setdefault(i.hashret(), []).append(i)
+
     if retry < 0:
         retry = -retry
-        autostop=retry
+        autostop = retry
     else:
-        autostop=0
-
+        autostop = 0
+
+    if WINDOWS:
+        def _get_pkt():
+            return pks.recv(MTU)
+    elif conf.use_bpf:
+        from scapy.arch.bpf.supersocket import bpf_select
+        def _get_pkt():
+            if bpf_select([pks]):
+                return pks.recv()
+    elif conf.use_pcap or (not isinstance(pks, StreamSocket)
+                           and (DARWIN or FREEBSD or OPENBSD)):
+        def _get_pkt():
+            res = pks.nonblock_recv()
+            if res is None:
+                time.sleep(0.05)
+            return res
+    else:
+        def _get_pkt():
+            try:
+                inp, _, _ = select([pks], [], [], 0.05)
+            except (IOError, select_error) as exc:
+                # select.error has no .errno attribute
+                if exc.args[0] != errno.EINTR:
+                    raise
+            else:
+                if inp:
+                    return pks.recv(MTU)
+            if stopevent.is_set():
+                raise _BreakException()
 
     while retry >= 0:
-        found=0
-    
         if timeout < 0:
             timeout = None
-            
-        rdpipe,wrpipe = os.pipe()
-        rdpipe=os.fdopen(rdpipe)
-        wrpipe=os.fdopen(wrpipe,"w")
+        stopevent = threading.Event()
+
+        thread = threading.Thread(
+            target=_sndrcv_snd,
+            args=(pks, timeout, inter, verbose, tobesent, stopevent),
+        )
+        thread.start()
 
-        pid=1
         try:
-            pid = os.fork()
-            if pid == 0:
-                try:
-                    sys.stdin.close()
-                    rdpipe.close()
-                    try:
-                        i = 0
-                        if verbose:
-                            print("Begin emission:")
-                        for p in tobesent:
-                            pks.send(p)
-                            i += 1
-                            time.sleep(inter)
-                        if verbose:
-                            print("Finished to send %i packets." % i)
-                    except SystemExit:
-                        pass
-                    except KeyboardInterrupt:
-                        pass
-                    except:
-                        log_runtime.exception("--- Error in child %i" % os.getpid())
-                        log_runtime.info("--- Error in child %i" % os.getpid())
-                finally:
-                    try:
-                        os.setpgrp() # Chance process group to avoid ctrl-C
-                        sent_times = [p.sent_time for p in all_stimuli if p.sent_time]
-                        six.moves.cPickle.dump( (conf.netcache,sent_times), wrpipe )
-                        wrpipe.close()
-                    except:
-                        pass
-            elif pid < 0:
-                log_runtime.error("fork error")
-            else:
-                wrpipe.close()
-                stoptime = 0
-                remaintime = None
-                inmask = [rdpipe,pks]
-                try:
-                    try:
-                        while True:
-                            if stoptime:
-                                remaintime = stoptime-time.time()
-                                if remaintime <= 0:
-                                    break
-                            r = None
-                            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 or OPENBSD):
-                                inp, out, err = select(inmask,[],[], 0.05)
-                                if len(inp) == 0 or pks in inp:
-                                    r = pks.nonblock_recv()
-                            else:
-                                inp = []
-                                try:
-                                    inp, out, err = select(inmask,[],[], remaintime)
-                                except (IOError, select_error) as exc:
-                                    # select.error has no .errno attribute
-                                    if exc.args[0] != errno.EINTR:
-                                        raise
-                                if len(inp) == 0:
-                                    break
-                                if pks in inp:
-                                    r = pks.recv(MTU)
-                            if rdpipe in inp:
-                                if timeout:
-                                    stoptime = time.time()+timeout
-                                del(inmask[inmask.index(rdpipe)])
-                            if r is None:
-                                continue
-                            ok = 0
-                            h = r.hashret()
-                            if h in hsent:
-                                hlst = hsent[h]
-                                for i, sentpkt in enumerate(hlst):
-                                    if r.answers(sentpkt):
-                                        ans.append((sentpkt, r))
-                                        if verbose > 1:
-                                            os.write(1, b"*")
-                                        ok = 1
-                                        if not multi:
-                                            del hlst[i]
-                                            notans -= 1
-                                        else:
-                                            if not hasattr(sentpkt, '_answered'):
-                                                notans -= 1
-                                            sentpkt._answered = 1
-                                        break
-                            if notans == 0 and not multi:
-                                break
-                            if not ok:
+            try:
+                while True:
+                    r = _get_pkt()
+                    if r is None:
+                        if stopevent.is_set():
+                            break
+                        continue
+                    ok = False
+                    h = r.hashret()
+                    if h in hsent:
+                        hlst = hsent[h]
+                        for i, sentpkt in enumerate(hlst):
+                            if r.answers(sentpkt):
+                                ans.append((sentpkt, r))
                                 if verbose > 1:
-                                    os.write(1, b".")
-                                nbrecv += 1
-                                if conf.debug_match:
-                                    debug.recv.append(r)
-                    except KeyboardInterrupt:
-                        if chainCC:
-                            raise
-                finally:
-                    try:
-                        nc,sent_times = six.moves.cPickle.load(rdpipe)
-                    except EOFError:
-                        warning("Child died unexpectedly. Packets may have not been sent %i"%os.getpid())
-                    else:
-                        conf.netcache.update(nc)
-                        for p,t in zip(all_stimuli, sent_times):
-                            p.sent_time = t
-                    os.waitpid(pid,0)
+                                    os.write(1, b"*")
+                                ok = True
+                                if not multi:
+                                    del hlst[i]
+                                    notans -= 1
+                                else:
+                                    if not hasattr(sentpkt, '_answered'):
+                                        notans -= 1
+                                    sentpkt._answered = 1
+                                break
+                    if notans == 0 and not multi:
+                        break
+                    if not ok:
+                        if verbose > 1:
+                            os.write(1, b".")
+                        nbrecv += 1
+                        if conf.debug_match:
+                            debug.recv.append(r)
+            except KeyboardInterrupt:
+                if chainCC:
+                    raise
+            except _BreakException:
+                pass
         finally:
-            if pid == 0:
-                os._exit(0)
+            stopevent.set()
+            thread.join()
+            pks.close()
 
         remain = list(itertools.chain(*six.itervalues(hsent)))
         if multi:
@@ -213,20 +199,20 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
         if len(tobesent) == 0:
             break
         retry -= 1
-        
+
     if conf.debug_match:
-        debug.sent=plist.PacketList(remain[:],"Sent")
+        debug.sent=plist.PacketList(remain[:], "Sent")
         debug.match=plist.SndRcvList(ans[:])
 
-    #clean the ans list to delete the field _answered
-    if (multi):
-        for s,r in ans:
-            if hasattr(s, '_answered'):
-                del(s._answered)
-    
+    # Clean the ans list to delete the field _answered
+    if multi:
+        for snd, _ in ans:
+            if hasattr(snd, '_answered'):
+                del snd._answered
+
     if verbose:
         print("\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans))
-    return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered")
+    return plist.SndRcvList(ans), plist.PacketList(remain, "Unanswered")
 
 
 def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, *args, **kargs):
@@ -352,9 +338,9 @@ iface:    listen answers only on the given interface"""
     if "timeout" not in kargs:
         kargs["timeout"] = -1
     s = conf.L3socket(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter)
-    a,b=sndrcv(s,x,*args,**kargs)
+    result = sndrcv(s, x, *args, **kargs)
     s.close()
-    return a,b
+    return result
 
 @conf.commands.register
 def sr1(x, promisc=None, filter=None, iface=None, nofilter=0, *args,**kargs):
@@ -370,10 +356,10 @@ iface:    listen answers only on the given interface"""
     if "timeout" not in kargs:
         kargs["timeout"] = -1
     s=conf.L3socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface)
-    a,b=sndrcv(s,x,*args,**kargs)
+    ans, _ = sndrcv(s, x, *args, **kargs)
     s.close()
-    if len(a) > 0:
-        return a[0][1]
+    if len(ans) > 0:
+        return ans[0][1]
     else:
         return None
 
@@ -393,9 +379,9 @@ iface:    work only on the given interface"""
     if iface is None and iface_hint is not None:
         iface = conf.route.route(iface_hint)[0]
     s = conf.L2socket(promisc=promisc, iface=iface, filter=filter, nofilter=nofilter, type=type)
-    a,b=sndrcv(s ,x,*args,**kargs)
+    result = sndrcv(s, x, *args, **kargs)
     s.close()
-    return a,b
+    return result
 
 @conf.commands.register
 def srp1(*args,**kargs):
@@ -410,9 +396,9 @@ filter:   provide a BPF filter
 iface:    work only on the given interface"""
     if "timeout" not in kargs:
         kargs["timeout"] = -1
-    a,b=srp(*args,**kargs)
-    if len(a) > 0:
-        return a[0][1]
+    ans, _ = srp(*args, **kargs)
+    if len(ans) > 0:
+        return ans[0][1]
     else:
         return None
 
@@ -438,7 +424,7 @@ def __sr_loop(srfunc, pkts, prn=lambda x:x[1].summary(), prnfail=lambda x:x.summ
             start = time.time()
             if verbose > 1:
                 print("\rsend...\r", end=' ')
-            res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=1, *args, **kargs)
+            res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=True, *args, **kargs)
             n += len(res[0])+len(res[1])
             r += len(res[0])
             if verbose > 1 and prn and len(res[0]) > 0:
@@ -574,94 +560,171 @@ iface:    listen answers only on the given interface"""
 
 
 @conf.commands.register
-def sniff(count=0, store=1, offline=None, prn=None, lfilter=None,
+def sniff(count=0, store=True, offline=None, prn=None, lfilter=None,
           L2socket=None, timeout=None, opened_socket=None,
           stop_filter=None, iface=None, *arg, **karg):
-    """Sniff packets
-sniff([count=0,] [prn=None,] [store=1,] [offline=None,]
-[lfilter=None,] + L2ListenSocket args) -> list of packets
+    """
+
+Sniff packets and return a list of packets.
+
+Arguments:
+
+  count: number of packets to capture. 0 means infinity.
 
-  count: number of packets to capture. 0 means infinity
   store: whether to store sniffed packets or discard them
-    prn: function to apply to each packet. If something is returned,
-         it is displayed. Ex:
-         ex: prn = lambda x: x.summary()
- filter: provide a BPF filter
-lfilter: python function applied to each packet to determine
-         if further action may be done
-         ex: lfilter = lambda x: x.haslayer(Padding)
-offline: pcap file to read packets from, instead of sniffing them
-timeout: stop sniffing after a given time (default: None)
-L2socket: use the provided L2socket
-opened_socket: provide an object ready to use .recv() on
-stop_filter: python function applied to each packet to determine
-             if we have to stop the capture after this packet
-             ex: stop_filter = lambda x: x.haslayer(TCP)
-iface: interface or list of interfaces (default: None for sniffing on all
-interfaces)
+
+  prn: function to apply to each packet. If something is returned, it
+      is displayed.
+
+      Ex: prn = lambda x: x.summary()
+
+  filter: BPF filter to apply.
+
+  lfilter: Python function applied to each packet to determine if
+      further action may be done.
+
+      Ex: lfilter = lambda x: x.haslayer(Padding)
+
+  offline: PCAP file (or list of PCAP files) to read packets from,
+      instead of sniffing them
+
+  timeout: stop sniffing after a given time (default: None).
+
+  L2socket: use the provided L2socket (default: use conf.L2listen).
+
+  opened_socket: provide an object (or a list of objects) ready to use
+      .recv() on.
+
+  stop_filter: Python function applied to each packet to determine if
+      we have to stop the capture after this packet.
+
+      Ex: stop_filter = lambda x: x.haslayer(TCP)
+
+  iface: interface or list of interfaces (default: None for sniffing
+      on all interfaces).
+
+The iface, offline and opened_socket parameters can be either an
+element, a list of elements, or a dict object mapping an element to a
+label (see examples below).
+
+Examples:
+
+  >>> sniff(filter="arp")
+
+  >>> sniff(lfilter=lambda pkt: ARP in pkt)
+
+  >>> sniff(iface="eth0", prn=Packet.summary)
+
+  >>> sniff(iface=["eth0", "mon0"],
+  ...       prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
+  ...                                   pkt.summary()))
+
+  >>> sniff(iface={"eth0": "Ethernet", "mon0": "Wifi"},
+  ...       prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
+  ...                                   pkt.summary()))
+
     """
     c = 0
-    label = {}
-    sniff_sockets = []
+    sniff_sockets = {}  # socket: label dict
     if opened_socket is not None:
-        sniff_sockets = [opened_socket]
-    else:
-        if offline is None:
-            if L2socket is None:
-                L2socket = conf.L2listen
-            if isinstance(iface, list):
-                for i in iface:
-                    s = L2socket(type=ETH_P_ALL, iface=i, *arg, **karg)
-                    label[s] = i
-                    sniff_sockets.append(s)
-            else:
-                sniff_sockets = [L2socket(type=ETH_P_ALL, iface=iface, *arg,
-                                           **karg)]
+        if isinstance(opened_socket, list):
+            sniff_sockets.update((s, "socket%d" % i)
+                                 for i, s in enumerate(opened_socket))
+        elif isinstance(opened_socket, dict):
+            sniff_sockets.update((s, label)
+                                 for s, label in iteritems(opened_socket))
+        else:
+            sniff_sockets[opened_socket] = "socket0"
+    if offline is not None:
+        flt = karg.get('filter')
+        if isinstance(offline, list):
+            sniff_sockets.update((PcapReader(
+                fname if flt is None else
+                tcpdump(fname, args=["-w", "-", flt], getfd=True)
+            ), fname) for fname in offline)
+        elif isinstance(offline, dict):
+            sniff_sockets.update((PcapReader(
+                fname if flt is None else
+                tcpdump(fname, args=["-w", "-", flt], getfd=True)
+            ), label) for fname, label in iteritems(offline))
         else:
-            flt = karg.get('filter')
-            sniff_sockets = [PcapReader(
+            sniff_sockets[PcapReader(
                 offline if flt is None else
                 tcpdump(offline, args=["-w", "-", flt], getfd=True)
-            )]
+            )] = offline
+    if not sniff_sockets or iface is not None:
+        if L2socket is None:
+            L2socket = conf.L2listen
+        if isinstance(iface, list):
+            sniff_sockets.update(
+                (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), ifname)
+                for ifname in iface
+            )
+        elif isinstance(iface, dict):
+            sniff_sockets.update(
+                (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), iflabel)
+                for ifname, iflabel in iteritems(iface)
+            )
+        else:
+            sniff_sockets[L2socket(type=ETH_P_ALL, iface=iface,
+                                   *arg, **karg)] = iface
     lst = []
     if timeout is not None:
         stoptime = time.time()+timeout
     remain = None
+    read_allowed_exceptions = ()
+    if conf.use_bpf:
+        from scapy.arch.bpf.supersocket import bpf_select
+        def _select(sockets):
+            return bpf_select(sockets, remain)
+    elif WINDOWS:
+        from scapy.arch.pcapdnet import PcapTimeoutElapsed
+        read_allowed_exceptions = (PcapTimeoutElapsed,)
+        def _select(sockets):
+            try:
+                return sockets
+            except PcapTimeoutElapsed:
+                return []
+    else:
+        def _select(sockets):
+            try:
+                return select(sockets, [], [], remain)[0]
+            except select_error as exc:
+                # Catch 'Interrupted system call' errors
+                if exc[0] == errno.EINTR:
+                    return []
+                raise
     try:
-        stop_event = False
-        while not stop_event:
+        while sniff_sockets:
             if timeout is not None:
                 remain = stoptime-time.time()
                 if remain <= 0:
                     break
-            if conf.use_bpf:
-                from scapy.arch.bpf.supersocket import bpf_select
-                ins = bpf_select(sniff_sockets, remain)
-            else:
-                ins, _, _ = select(sniff_sockets, [], [], remain)
+            ins = _select(sniff_sockets)
             for s in ins:
-                p = s.recv()
-                if p is None and offline is not None:
-                    stop_event = True
+                try:
+                    p = s.recv()
+                except read_allowed_exceptions:
+                    continue
+                if p is None:
+                    del sniff_sockets[s]
+                    break
+                if lfilter and not lfilter(p):
+                    continue
+                p.sniffed_on = sniff_sockets[s]
+                if store:
+                    lst.append(p)
+                c += 1
+                if prn:
+                    r = prn(p)
+                    if r is not None:
+                        print(r)
+                if stop_filter and stop_filter(p):
+                    sniff_sockets = []
+                    break
+                if 0 < count <= c:
+                    sniff_sockets = []
                     break
-                elif p is not None:
-                    if lfilter and not lfilter(p):
-                        continue
-                    if s in label:
-                        p.sniffed_on = label[s]
-                    if store:
-                        lst.append(p)
-                    c += 1
-                    if prn:
-                        r = prn(p)
-                        if r is not None:
-                            print(r)
-                    if stop_filter and stop_filter(p):
-                        stop_event = True
-                        break
-                    if 0 < count <= c:
-                        stop_event = True
-                        break
     except KeyboardInterrupt:
         pass
     if opened_socket is None:
@@ -671,76 +734,48 @@ interfaces)
 
 
 @conf.commands.register
-def bridge_and_sniff(if1, if2, count=0, store=1, offline=None, prn=None, 
-                     lfilter=None, L2socket=None, timeout=None,
-                     stop_filter=None, *args, **kargs):
-    """Forward traffic between two interfaces and sniff packets exchanged
-bridge_and_sniff([count=0,] [prn=None,] [store=1,] [offline=None,] 
-[lfilter=None,] + L2Socket args) -> list of packets
-
-  count: number of packets to capture. 0 means infinity
-  store: whether to store sniffed packets or discard them
-    prn: function to apply to each packet. If something is returned,
-         it is displayed. Ex:
-         ex: prn = lambda x: x.summary()
-lfilter: python function applied to each packet to determine
-         if further action may be done
-         ex: lfilter = lambda x: x.haslayer(Padding)
-timeout: stop sniffing after a given time (default: None)
-L2socket: use the provided L2socket
-stop_filter: python function applied to each packet to determine
-             if we have to stop the capture after this packet
-             ex: stop_filter = lambda x: x.haslayer(TCP)
+def bridge_and_sniff(if1, if2, prn=None, L2socket=None, *args, **kargs):
+    """Forward traffic between interfaces if1 and if2, sniff and return the
+exchanged packets.
+
+Arguments:
+
+  if1, if2: the interfaces to use
+
+  The other arguments are the same than for the function sniff(),
+      except for opened_socket, offline and iface that are ignored.
+      See help(sniff) for more.
+
     """
-    c = 0
+    for arg in ['opened_socket', 'offline', 'iface']:
+        if arg in kargs:
+            log_runtime.warning("Argument %s cannot be used in "
+                                "bridge_and_sniff() -- ignoring it.", arg)
+            del kargs[arg]
     if L2socket is None:
         L2socket = conf.L2socket
     s1 = L2socket(iface=if1)
     s2 = L2socket(iface=if2)
-    peerof={s1:s2,s2:s1}
-    label={s1:if1, s2:if2}
-    
-    lst = []
-    if timeout is not None:
-        stoptime = time.time()+timeout
-    remain = None
-    try:
-        stop_event = False
-        while not stop_event:
-            if timeout is not None:
-                remain = stoptime-time.time()
-                if remain <= 0:
-                    break
-            if conf.use_bpf:
-                from scapy.arch.bpf.supersocket import bpf_select
-                ins = bpf_select([s1, s2], remain)
-            else:
-                ins, _, _ = select([s1, s2], [], [], remain)
+    peers = {if1: s2, if2: s1}
+    def prn_send(pkt):
+        try:
+            sendsock = peers[pkt.sniffed_on]
+        except KeyError:
+            return
+        try:
+            sendsock.send(pkt.original)
+        except:
+            log_runtime.warning('Cannot forward packet [%s] received from %s',
+                                pkt.summary(), pkt.sniffed_on, exc_info=True)
+    if prn is None:
+        prn = prn_send
+    else:
+        prn_orig = prn
+        def prn(pkt):
+            prn_send(pkt)
+            return prn_orig(pkt)
 
-            for s in ins:
-                p = s.recv()
-                if p is not None:
-                    peerof[s].send(p.original)
-                    if lfilter and not lfilter(p):
-                        continue
-                    if store:
-                        p.sniffed_on = label[s]
-                        lst.append(p)
-                    c += 1
-                    if prn:
-                        r = prn(p)
-                        if r is not None:
-                            print(r)
-                    if stop_filter and stop_filter(p):
-                        stop_event = True
-                        break
-                    if 0 < count <= c:
-                        stop_event = True
-                        break
-    except KeyboardInterrupt:
-        pass
-    finally:
-        return plist.PacketList(lst,"Sniffed")
+    return sniff(opened_socket={s1: if1, s2: if2}, prn=prn, *args, **kargs)
 
 
 @conf.commands.register
diff --git a/scapy/utils6.py b/scapy/utils6.py
index 0759d6446afb37354548cbf4f66690ed3d1f075f..31bdb66c787d5b6a0e597c7789458eb1b52079f3 100644
--- a/scapy/utils6.py
+++ b/scapy/utils6.py
@@ -15,6 +15,7 @@ import socket
 import struct
 
 from scapy.config import conf
+import scapy.consts
 from scapy.data import *
 from scapy.utils import *
 from scapy.pton_ntop import *