]> The Tcpdump Group git mirrors - libpcap/blobdiff - pcap-linux.c
Travis: Add .travis.yml (same as in version 1.4)
[libpcap] / pcap-linux.c
index d2f7c51e70c877efc52539d6e8ffe0425e8c6d7f..e5c3afa7d86da2cc7a427444bb64cfd591299d90 100644 (file)
@@ -122,39 +122,23 @@ static const char rcsid[] _U_ =
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <ctype.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <string.h>
 #include <limits.h>
+#include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <sys/utsname.h>
 #include <sys/mman.h>
 #include <linux/if.h>
+#include <linux/if_packet.h>
 #include <netinet/in.h>
 #include <linux/if_ether.h>
 #include <net/if_arp.h>
 #include <poll.h>
-
-/*
- * Got Wireless Extensions?
- */
-#ifdef HAVE_LINUX_WIRELESS_H
-#include <linux/wireless.h>
-#endif /* HAVE_LINUX_WIRELESS_H */
-
-/*
- * Got libnl?
- */
-#ifdef HAVE_LIBNL
-#include <linux/nl80211.h>
-
-#include <netlink/genl/genl.h>
-#include <netlink/genl/family.h>
-#include <netlink/genl/ctrl.h>
-#include <netlink/msg.h>
-#include <netlink/attr.h>
-#endif /* HAVE_LIBNL */
+#include <dirent.h>
 
 #include "pcap-int.h"
 #include "pcap/sll.h"
@@ -168,6 +152,10 @@ static const char rcsid[] _U_ =
 #include "pcap-septel.h"
 #endif /* HAVE_SEPTEL_API */
 
+#ifdef HAVE_SNF_API
+#include "pcap-snf.h"
+#endif /* HAVE_SNF_API */
+
 #ifdef PCAP_SUPPORT_USB
 #include "pcap-usb-linux.h"
 #endif
@@ -176,6 +164,18 @@ static const char rcsid[] _U_ =
 #include "pcap-bt-linux.h"
 #endif
 
+#ifdef PCAP_SUPPORT_CAN
+#include "pcap-can-linux.h"
+#endif
+
+#if PCAP_SUPPORT_CANUSB
+#include "pcap-canusb-linux.h"
+#endif
+
+#ifdef PCAP_SUPPORT_NETFILTER
+#include "pcap-netfilter-linux.h"
+#endif
+
 /*
  * If PF_PACKET is defined, we can use {SOCK_RAW,SOCK_DGRAM}/PF_PACKET
  * sockets rather than SOCK_PACKET sockets.
@@ -240,6 +240,46 @@ static const char rcsid[] _U_ =
 #include <linux/filter.h>
 #endif
 
+/*
+ * We need linux/sockios.h if we have linux/net_tstamp.h (for time stamp
+ * specification) or linux/ethtool.h (for ethtool ioctls to get offloading
+ * information).
+ */
+#if defined(HAVE_LINUX_NET_TSTAMP_H) || defined(HAVE_LINUX_ETHTOOL_H)
+#include <linux/sockios.h>
+#endif
+
+#ifdef HAVE_LINUX_NET_TSTAMP_H
+#include <linux/net_tstamp.h>
+#endif
+
+/*
+ * Got Wireless Extensions?
+ */
+#ifdef HAVE_LINUX_WIRELESS_H
+#include <linux/wireless.h>
+#endif /* HAVE_LINUX_WIRELESS_H */
+
+/*
+ * Got libnl?
+ */
+#ifdef HAVE_LIBNL
+#include <linux/nl80211.h>
+
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+#endif /* HAVE_LIBNL */
+
+/*
+ * Got ethtool support?
+ */
+#ifdef HAVE_LINUX_ETHTOOL_H
+#include <linux/ethtool.h>
+#endif
+
 #ifndef HAVE_SOCKLEN_T
 typedef int            socklen_t;
 #endif
@@ -278,14 +318,14 @@ typedef int               socklen_t;
 /*
  * Prototypes for internal functions and methods.
  */
-static void map_arphrd_to_dlt(pcap_t *, int, int);
+static void map_arphrd_to_dlt(pcap_t *, int, int, const char *, int);
 #ifdef HAVE_PF_PACKET_SOCKETS
 static short int map_packet_type_to_sll_type(short int);
 #endif
 static int pcap_activate_linux(pcap_t *);
 static int activate_old(pcap_t *);
 static int activate_new(pcap_t *);
-static int activate_mmap(pcap_t *);
+static int activate_mmap(pcap_t *, int *);
 static int pcap_can_set_rfmon_linux(pcap_t *);
 static int pcap_read_linux(pcap_t *, int, pcap_handler, u_char *);
 static int pcap_read_packet(pcap_t *, pcap_handler, u_char *);
@@ -305,7 +345,7 @@ union thdr {
 #define RING_GET_FRAME(h) (((union thdr **)h->buffer)[h->offset])
 
 static void destroy_ring(pcap_t *handle);
-static int create_ring(pcap_t *handle);
+static int create_ring(pcap_t *handle, int *status);
 static int prepare_tpacket_socket(pcap_t *handle);
 static void pcap_cleanup_linux_mmap(pcap_t *);
 static int pcap_read_linux_mmap(pcap_t *, int, pcap_handler , u_char *);
@@ -321,7 +361,7 @@ static void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h,
  */
 #ifdef HAVE_PF_PACKET_SOCKETS
 static int     iface_get_id(int fd, const char *device, char *ebuf);
-#endif
+#endif /* HAVE_PF_PACKET_SOCKETS */
 static int     iface_get_mtu(int fd, const char *device, char *ebuf);
 static int     iface_get_arptype(int fd, const char *device, char *ebuf);
 #ifdef HAVE_PF_PACKET_SOCKETS
@@ -332,6 +372,7 @@ static int  has_wext(int sock_fd, const char *device, char *ebuf);
 static int     enter_rfmon_mode(pcap_t *handle, int sock_fd,
     const char *device);
 #endif /* HAVE_PF_PACKET_SOCKETS */
+static int     iface_get_offload(pcap_t *handle);
 static int     iface_bind_old(int fd, const char *device, char *ebuf);
 
 #ifdef SO_ATTACH_FILTER
@@ -345,7 +386,7 @@ static struct sock_filter   total_insn
        = BPF_STMT(BPF_RET | BPF_K, 0);
 static struct sock_fprog       total_fcode
        = { 1, &total_insn };
-#endif
+#endif /* SO_ATTACH_FILTER */
 
 pcap_t *
 pcap_create(const char *device, char *ebuf)
@@ -370,70 +411,117 @@ pcap_create(const char *device, char *ebuf)
        }
 #endif /* HAVE_SEPTEL_API */
 
+#ifdef HAVE_SNF_API
+        handle = snf_create(device, ebuf);
+        if (strstr(device, "snf") || handle != NULL)
+               return handle;
+
+#endif /* HAVE_SNF_API */
+
 #ifdef PCAP_SUPPORT_BT
        if (strstr(device, "bluetooth")) {
                return bt_create(device, ebuf);
        }
 #endif
 
+#if PCAP_SUPPORT_CANUSB
+  if (strstr(device, "canusb")) {
+    return canusb_create(device, ebuf);
+  }
+#endif
+
+#ifdef PCAP_SUPPORT_CAN
+       if ((strncmp(device, "can", 3) == 0 && isdigit(device[3])) ||
+           (strncmp(device, "vcan", 4) == 0 && isdigit(device[4]))) {
+               return can_create(device, ebuf);
+       }
+#endif
+
 #ifdef PCAP_SUPPORT_USB
        if (strstr(device, "usbmon")) {
                return usb_create(device, ebuf);
        }
 #endif
 
+#ifdef PCAP_SUPPORT_NETFILTER
+       if (strncmp(device, "nflog", strlen("nflog")) == 0) {
+               return nflog_create(device, ebuf);
+       }
+#endif
+
        handle = pcap_create_common(device, ebuf);
        if (handle == NULL)
                return NULL;
 
        handle->activate_op = pcap_activate_linux;
        handle->can_set_rfmon_op = pcap_can_set_rfmon_linux;
+#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
+       /*
+        * We claim that we support:
+        *
+        *      software time stamps, with no details about their precision;
+        *      hardware time stamps, synced to the host time;
+        *      hardware time stamps, not synced to the host time.
+        *
+        * XXX - we can't ask a device whether it supports
+        * hardware time stamps, so we just claim all devices do.
+        */
+       handle->tstamp_type_count = 3;
+       handle->tstamp_type_list = malloc(3 * sizeof(u_int));
+       if (handle->tstamp_type_list == NULL) {
+               free(handle);
+               return NULL;
+       }
+       handle->tstamp_type_list[0] = PCAP_TSTAMP_HOST;
+       handle->tstamp_type_list[1] = PCAP_TSTAMP_ADAPTER;
+       handle->tstamp_type_list[2] = PCAP_TSTAMP_ADAPTER_UNSYNCED;
+#endif
+
        return handle;
 }
 
 #ifdef HAVE_LIBNL
 /*
-        *
-        * If interface {if} is a mac80211 driver, the file
-        * /sys/class/net/{if}/phy80211 is a symlink to
-        * /sys/class/ieee80211/{phydev}, for some {phydev}.
-        *
-        * On Fedora 9, with a 2.6.26.3-29 kernel, my Zydas stick, at
-        * least, has a "wmaster0" device and a "wlan0" device; the
-        * latter is the one with the IP address.  Both show up in
-        * "tcpdump -D" output.  Capturing on the wmaster0 device
-        * captures with 802.11 headers.
-        *
-        * airmon-ng searches through /sys/class/net for devices named
-        * monN, starting with mon0; as soon as one *doesn't* exist,
-        * it chooses that as the monitor device name.  If the "iw"
-        * command exists, it does "iw dev {if} interface add {monif}
-        * type monitor", where {monif} is the monitor device.  It
-        * then (sigh) sleeps .1 second, and then configures the
-        * device up.  Otherwise, if /sys/class/ieee80211/{phydev}/add_iface
-        * is a file, it writes {mondev}, without a newline, to that file,
-        * and again (sigh) sleeps .1 second, and then iwconfig's that
-        * device into monitor mode and configures it up.  Otherwise,
-        * you can't do monitor mode.
-        *
-        * All these devices are "glued" together by having the
-        * /sys/class/net/{device}/phy80211 links pointing to the same
-        * place, so, given a wmaster, wlan, or mon device, you can
-        * find the other devices by looking for devices with
-        * the same phy80211 link.
-        *
-        * To turn monitor mode off, delete the monitor interface,
-        * either with "iw dev {monif} interface del" or by sending
-        * {monif}, with no NL, down /sys/class/ieee80211/{phydev}/remove_iface
-        *
-        * Note: if you try to create a monitor device named "monN", and
-        * there's already a "monN" device, it fails, as least with
-        * the netlink interface (which is what iw uses), with a return
-        * value of -ENFILE.  (Return values are negative errnos.)  We
-        * could probably use that to find an unused device.
-        *
-        * Yes, you can have multiple monitor devices for a given
-        * physical device.
+ * If interface {if} is a mac80211 driver, the file
+ * /sys/class/net/{if}/phy80211 is a symlink to
+ * /sys/class/ieee80211/{phydev}, for some {phydev}.
+ *
+ * On Fedora 9, with a 2.6.26.3-29 kernel, my Zydas stick, at
+ * least, has a "wmaster0" device and a "wlan0" device; the
+ * latter is the one with the IP address.  Both show up in
+ * "tcpdump -D" output.  Capturing on the wmaster0 device
+ * captures with 802.11 headers.
+ *
+ * airmon-ng searches through /sys/class/net for devices named
+ * monN, starting with mon0; as soon as one *doesn't* exist,
+ * it chooses that as the monitor device name.  If the "iw"
+ * command exists, it does "iw dev {if} interface add {monif}
+ * type monitor", where {monif} is the monitor device.  It
+ * then (sigh) sleeps .1 second, and then configures the
+ * device up.  Otherwise, if /sys/class/ieee80211/{phydev}/add_iface
+ * is a file, it writes {mondev}, without a newline, to that file,
+ * and again (sigh) sleeps .1 second, and then iwconfig's that
+ * device into monitor mode and configures it up.  Otherwise,
+ * you can't do monitor mode.
+ *
+ * All these devices are "glued" together by having the
+ * /sys/class/net/{device}/phy80211 links pointing to the same
+ * place, so, given a wmaster, wlan, or mon device, you can
+ * find the other devices by looking for devices with
+ * the same phy80211 link.
+ *
+ * To turn monitor mode off, delete the monitor interface,
+ * either with "iw dev {monif} interface del" or by sending
+ * {monif}, with no NL, down /sys/class/ieee80211/{phydev}/remove_iface
+ *
+ * Note: if you try to create a monitor device named "monN", and
+ * there's already a "monN" device, it fails, as least with
+ * the netlink interface (which is what iw uses), with a return
+ * value of -ENFILE.  (Return values are negative errnos.)  We
+ * could probably use that to find an unused device.
+ *
+ * Yes, you can have multiple monitor devices for a given
+ * physical device.
 */
 
 /*
@@ -478,8 +566,41 @@ get_mac80211_phydev(pcap_t *handle, const char *device, char *phydev_path,
        return 1;
 }
 
+#ifdef HAVE_LIBNL_2_x
+#define get_nl_errmsg  nl_geterror
+#else
+/* libnl 2.x compatibility code */
+
+#define nl_sock nl_handle
+
+static inline struct nl_handle *
+nl_socket_alloc(void)
+{
+       return nl_handle_alloc();
+}
+
+static inline void
+nl_socket_free(struct nl_handle *h)
+{
+       nl_handle_destroy(h);
+}
+
+#define get_nl_errmsg  strerror
+
+static inline int
+__genl_ctrl_alloc_cache(struct nl_handle *h, struct nl_cache **cache)
+{
+       struct nl_cache *tmp = genl_ctrl_alloc_cache(h);
+       if (!tmp)
+               return -ENOMEM;
+       *cache = tmp;
+       return 0;
+}
+#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache
+#endif /* !HAVE_LIBNL_2_x */
+
 struct nl80211_state {
-       struct nl_handle *nl_handle;
+       struct nl_sock *nl_sock;
        struct nl_cache *nl_cache;
        struct genl_family *nl80211;
 };
@@ -487,23 +608,26 @@ struct nl80211_state {
 static int
 nl80211_init(pcap_t *handle, struct nl80211_state *state, const char *device)
 {
-       state->nl_handle = nl_handle_alloc();
-       if (!state->nl_handle) {
+       int err;
+
+       state->nl_sock = nl_socket_alloc();
+       if (!state->nl_sock) {
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                    "%s: failed to allocate netlink handle", device);
                return PCAP_ERROR;
        }
 
-       if (genl_connect(state->nl_handle)) {
+       if (genl_connect(state->nl_sock)) {
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                    "%s: failed to connect to generic netlink", device);
                goto out_handle_destroy;
        }
 
-       state->nl_cache = genl_ctrl_alloc_cache(state->nl_handle);
-       if (!state->nl_cache) {
+       err = genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache);
+       if (err < 0) {
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   "%s: failed to allocate generic netlink cache", device);
+                   "%s: failed to allocate generic netlink cache: %s",
+                   device, get_nl_errmsg(-err));
                goto out_handle_destroy;
        }
 
@@ -519,7 +643,7 @@ nl80211_init(pcap_t *handle, struct nl80211_state *state, const char *device)
 out_cache_free:
        nl_cache_free(state->nl_cache);
 out_handle_destroy:
-       nl_handle_destroy(state->nl_handle);
+       nl_socket_free(state->nl_sock);
        return PCAP_ERROR;
 }
 
@@ -528,7 +652,7 @@ nl80211_cleanup(struct nl80211_state *state)
 {
        genl_family_put(state->nl80211);
        nl_cache_free(state->nl_cache);
-       nl_handle_destroy(state->nl_handle);
+       nl_socket_free(state->nl_sock);
 }
 
 static int
@@ -556,12 +680,19 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
        NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, mondevice);
        NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR);
 
-       err = nl_send_auto_complete(state->nl_handle, msg);
+       err = nl_send_auto_complete(state->nl_sock, msg);
        if (err < 0) {
+#ifdef HAVE_LIBNL_2_x
+               if (err == -NLE_FAILURE) {
+#else
                if (err == -ENFILE) {
+#endif
                        /*
                         * Device not available; our caller should just
-                        * keep trying.
+                        * keep trying.  (libnl 2.x maps ENFILE to
+                        * NLE_FAILURE; it can also map other errors
+                        * to that, but there's not much we can do
+                        * about that.)
                         */
                        nlmsg_free(msg);
                        return 0;
@@ -572,17 +703,24 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
                         */
                        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                            "%s: nl_send_auto_complete failed adding %s interface: %s",
-                           device, mondevice, strerror(-err));
+                           device, mondevice, get_nl_errmsg(-err));
                        nlmsg_free(msg);
                        return PCAP_ERROR;
                }
        }
-       err = nl_wait_for_ack(state->nl_handle);
+       err = nl_wait_for_ack(state->nl_sock);
        if (err < 0) {
+#ifdef HAVE_LIBNL_2_x
+               if (err == -NLE_FAILURE) {
+#else
                if (err == -ENFILE) {
+#endif
                        /*
                         * Device not available; our caller should just
-                        * keep trying.
+                        * keep trying.  (libnl 2.x maps ENFILE to
+                        * NLE_FAILURE; it can also map other errors
+                        * to that, but there's not much we can do
+                        * about that.)
                         */
                        nlmsg_free(msg);
                        return 0;
@@ -593,7 +731,7 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
                         */
                        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                            "%s: nl_wait_for_ack failed adding %s interface: %s",
-                           device, mondevice, strerror(-err));
+                           device, mondevice, get_nl_errmsg(-err));
                        nlmsg_free(msg);
                        return PCAP_ERROR;
                }
@@ -636,47 +774,21 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
                    0, NL80211_CMD_DEL_INTERFACE, 0);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
 
-       err = nl_send_auto_complete(state->nl_handle, msg);
+       err = nl_send_auto_complete(state->nl_sock, msg);
        if (err < 0) {
-               if (err == -ENFILE) {
-                       /*
-                        * Device not available; our caller should just
-                        * keep trying.
-                        */
-                       nlmsg_free(msg);
-                       return 0;
-               } else {
-                       /*
-                        * Real failure, not just "that device is not
-                        * available.
-                        */
-                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
-                           "%s: nl_send_auto_complete failed deleting %s interface: %s",
-                           device, mondevice, strerror(-err));
-                       nlmsg_free(msg);
-                       return PCAP_ERROR;
-               }
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                   "%s: nl_send_auto_complete failed deleting %s interface: %s",
+                   device, mondevice, get_nl_errmsg(-err));
+               nlmsg_free(msg);
+               return PCAP_ERROR;
        }
-       err = nl_wait_for_ack(state->nl_handle);
+       err = nl_wait_for_ack(state->nl_sock);
        if (err < 0) {
-               if (err == -ENFILE) {
-                       /*
-                        * Device not available; our caller should just
-                        * keep trying.
-                        */
-                       nlmsg_free(msg);
-                       return 0;
-               } else {
-                       /*
-                        * Real failure, not just "that device is not
-                        * available.
-                        */
-                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
-                           "%s: nl_wait_for_ack failed adding %s interface: %s",
-                           device, mondevice, strerror(-err));
-                       nlmsg_free(msg);
-                       return PCAP_ERROR;
-               }
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                   "%s: nl_wait_for_ack failed adding %s interface: %s",
+                   device, mondevice, get_nl_errmsg(-err));
+               nlmsg_free(msg);
+               return PCAP_ERROR;
        }
 
        /*
@@ -762,6 +874,18 @@ added:
        nanosleep(&delay, NULL);
 #endif
 
+       /*
+        * If we haven't already done so, arrange to have
+        * "pcap_close_all()" called when we exit.
+        */
+       if (!pcap_do_addexit(handle)) {
+               /*
+                * "atexit()" failed; don't put the interface
+                * in rfmon mode, just give up.
+                */
+               return PCAP_ERROR_RFMON_NOTSUP;
+       }
+
        /*
         * Now configure the monitor interface up.
         */
@@ -890,7 +1014,14 @@ pcap_can_set_rfmon_linux(pcap_t *handle)
        return 0;
 }
 
-/* grabs the number of dropped packets by the interface from /proc/net/dev */
+/*
+ * Grabs the number of dropped packets by the interface from /proc/net/dev.
+ *
+ * XXX - what about /sys/class/net/{interface name}/rx_*?  There are
+ * individual devices giving, in ASCII, various rx_ and tx_ statistics.
+ *
+ * Or can we get them in binary form from netlink?
+ */
 static long int
 linux_if_drops(const char * if_name)
 {
@@ -964,6 +1095,7 @@ static void        pcap_cleanup_linux( pcap_t *handle )
        int ret;
 #endif /* HAVE_LIBNL */
 #ifdef IW_MODE_MONITOR
+       int oldflags;
        struct iwreq ireq;
 #endif /* IW_MODE_MONITOR */
 
@@ -987,10 +1119,10 @@ static void      pcap_cleanup_linux( pcap_t *handle )
                            sizeof(ifr.ifr_name));
                        if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) {
                                fprintf(stderr,
-                                   "Can't restore interface flags (SIOCGIFFLAGS failed: %s).\n"
+                                   "Can't restore interface %s flags (SIOCGIFFLAGS failed: %s).\n"
                                    "Please adjust manually.\n"
                                    "Hint: This can't happen with Linux >= 2.2.0.\n",
-                                   strerror(errno));
+                                   handle->md.device, strerror(errno));
                        } else {
                                if (ifr.ifr_flags & IFF_PROMISC) {
                                        /*
@@ -1001,9 +1133,10 @@ static void      pcap_cleanup_linux( pcap_t *handle )
                                        if (ioctl(handle->fd, SIOCSIFFLAGS,
                                            &ifr) == -1) {
                                                fprintf(stderr,
-                                                   "Can't restore interface flags (SIOCSIFFLAGS failed: %s).\n"
+                                                   "Can't restore interface %s flags (SIOCSIFFLAGS failed: %s).\n"
                                                    "Please adjust manually.\n"
                                                    "Hint: This can't happen with Linux >= 2.2.0.\n",
+                                                   handle->md.device,
                                                    strerror(errno));
                                        }
                                }
@@ -1037,6 +1170,29 @@ static void      pcap_cleanup_linux( pcap_t *handle )
                         * mode, this code cannot know that, so it'll take
                         * it out of rfmon mode.
                         */
+
+                       /*
+                        * First, take the interface down if it's up;
+                        * otherwise, we might get EBUSY.
+                        * If we get errors, just drive on and print
+                        * a warning if we can't restore the mode.
+                        */
+                       oldflags = 0;
+                       memset(&ifr, 0, sizeof(ifr));
+                       strncpy(ifr.ifr_name, handle->md.device,
+                           sizeof(ifr.ifr_name));
+                       if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) != -1) {
+                               if (ifr.ifr_flags & IFF_UP) {
+                                       oldflags = ifr.ifr_flags;
+                                       ifr.ifr_flags &= ~IFF_UP;
+                                       if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1)
+                                               oldflags = 0;   /* didn't set, don't restore */
+                               }
+                       }
+
+                       /*
+                        * Now restore the mode.
+                        */
                        strncpy(ireq.ifr_ifrn.ifrn_name, handle->md.device,
                            sizeof ireq.ifr_ifrn.ifrn_name);
                        ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1]
@@ -1047,9 +1203,23 @@ static void      pcap_cleanup_linux( pcap_t *handle )
                                 * Scientist, you've failed.
                                 */
                                fprintf(stderr,
-                                   "Can't restore interface wireless mode (SIOCSIWMODE failed: %s).\n"
+                                   "Can't restore interface %s wireless mode (SIOCSIWMODE failed: %s).\n"
                                    "Please adjust manually.\n",
-                                   strerror(errno));
+                                   handle->md.device, strerror(errno));
+                       }
+
+                       /*
+                        * Now bring the interface back up if we brought
+                        * it down.
+                        */
+                       if (oldflags != 0) {
+                               ifr.ifr_flags = oldflags;
+                               if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) {
+                                       fprintf(stderr,
+                                           "Can't bring interface %s back up (SIOCSIFFLAGS failed: %s).\n"
+                                           "Please adjust manually.\n",
+                                           handle->md.device, strerror(errno));
+                               }
                        }
                }
 #endif /* IW_MODE_MONITOR */
@@ -1137,34 +1307,46 @@ pcap_activate_linux(pcap_t *handle)
         * to be compatible with older kernels for a while so we are
         * trying both methods with the newer method preferred.
         */
-
-       if ((status = activate_new(handle)) == 1) {
+       status = activate_new(handle);
+       if (status < 0) {
+               /*
+                * Fatal error with the new way; just fail.
+                * status has the error return; if it's PCAP_ERROR,
+                * handle->errbuf has been set appropriately.
+                */
+               goto fail;
+       }
+       if (status == 1) {
                /*
                 * Success.
                 * Try to use memory-mapped access.
                 */
-               switch (activate_mmap(handle)) {
+               switch (activate_mmap(handle, &status)) {
 
                case 1:
-                       /* we succeeded; nothing more to do */
-                       return 0;
+                       /*
+                        * We succeeded.  status has been
+                        * set to the status to return,
+                        * which might be 0, or might be
+                        * a PCAP_WARNING_ value.
+                        */
+                       return status;
 
                case 0:
                        /*
                         * Kernel doesn't support it - just continue
                         * with non-memory-mapped access.
                         */
-                       status = 0;
                        break;
 
                case -1:
                        /*
-                        * We failed to set up to use it, or kernel
-                        * supports it, but we failed to enable it;
-                        * return an error.  handle->errbuf contains
-                        * an error message.
+                        * We failed to set up to use it, or the kernel
+                        * supports it, but we failed to enable it.
+                        * status has been set to the error status to
+                        * return and, if it's PCAP_ERROR, handle->errbuf
+                        * contains the error message.
                         */
-                       status = PCAP_ERROR;
                        goto fail;
                }
        }
@@ -1178,18 +1360,12 @@ pcap_activate_linux(pcap_t *handle)
                         */
                        goto fail;
                }
-       } else {
-               /*
-                * Fatal error with the new way; just fail.
-                * status has the error return; if it's PCAP_ERROR,
-                * handle->errbuf has been set appropriately.
-                */
-               goto fail;
        }
 
        /*
         * We set up the socket, but not with memory-mapped access.
         */
+       status = 0;
        if (handle->opt.buffer_size != 0) {
                /*
                 * Set the socket buffer size to the specified value.
@@ -1325,12 +1501,12 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
                 */
                if (handle->break_loop) {
                        /*
-                        * Yes - clear the flag that indicates that it
-                        * has, and return -2 as an indication that we
-                        * were told to break out of the loop.
+                        * Yes - clear the flag that indicates that it has,
+                        * and return PCAP_ERROR_BREAK as an indication that
+                        * we were told to break out of the loop.
                         */
                        handle->break_loop = 0;
-                       return -2;
+                       return PCAP_ERROR_BREAK;
                }
 
 #if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI)
@@ -1342,17 +1518,32 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
                        handle->bufsize - offset, MSG_TRUNC,
                        (struct sockaddr *) &from, &fromlen);
 #endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
-       } while (packet_len == -1 && (errno == EINTR || errno == ENETDOWN));
+       } while (packet_len == -1 && errno == EINTR);
 
        /* Check if an error occured */
 
        if (packet_len == -1) {
-               if (errno == EAGAIN)
+               switch (errno) {
+
+               case EAGAIN:
                        return 0;       /* no packet there */
-               else {
+
+               case ENETDOWN:
+                       /*
+                        * The device on which we're capturing went away.
+                        *
+                        * XXX - we should really return
+                        * PCAP_ERROR_IFACE_NOT_UP, but pcap_dispatch()
+                        * etc. aren't defined to return that.
+                        */
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                               "The interface went down");
+                       return PCAP_ERROR;
+
+               default:
                        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                                 "recvfrom: %s", pcap_strerror(errno));
-                       return -1;
+                       return PCAP_ERROR;
                }
        }
 
@@ -1429,34 +1620,42 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
        }
 
 #if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI)
-       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-               struct tpacket_auxdata *aux;
-               unsigned int len;
-               struct vlan_tag *tag;
-
-               if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)) ||
-                   cmsg->cmsg_level != SOL_PACKET ||
-                   cmsg->cmsg_type != PACKET_AUXDATA)
-                       continue;
+       if (handle->md.vlan_offset != -1) {
+               for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+                       struct tpacket_auxdata *aux;
+                       unsigned int len;
+                       struct vlan_tag *tag;
 
-               aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg);
-               if (aux->tp_vlan_tci == 0)
-                       continue;
+                       if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)) ||
+                           cmsg->cmsg_level != SOL_PACKET ||
+                           cmsg->cmsg_type != PACKET_AUXDATA)
+                               continue;
 
-               len = packet_len > iov.iov_len ? iov.iov_len : packet_len;
-               if (len < 2 * ETH_ALEN)
-                       break;
+                       aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg);
+#if defined(TP_STATUS_VLAN_VALID)
+                       if ((aux->tp_vlan_tci == 0) && !(aux->tp_status & TP_STATUS_VLAN_VALID))
+#else
+                       if (aux->tp_vlan_tci == 0) /* this is ambigious but without the
+                                               TP_STATUS_VLAN_VALID flag, there is
+                                               nothing that we can do */
+#endif
+                               continue;
 
-               bp -= VLAN_TAG_LEN;
-               memmove(bp, bp + VLAN_TAG_LEN, 2 * ETH_ALEN);
+                       len = packet_len > iov.iov_len ? iov.iov_len : packet_len;
+                       if (len < (unsigned int) handle->md.vlan_offset)
+                               break;
 
-               tag = (struct vlan_tag *)(bp + 2 * ETH_ALEN);
-               tag->vlan_tpid = htons(ETH_P_8021Q);
-               tag->vlan_tci = htons(aux->tp_vlan_tci);
+                       bp -= VLAN_TAG_LEN;
+                       memmove(bp, bp + VLAN_TAG_LEN, handle->md.vlan_offset);
 
-               packet_len += VLAN_TAG_LEN;
-       }
-#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
+                       tag = (struct vlan_tag *)(bp + handle->md.vlan_offset);
+                       tag->vlan_tpid = htons(ETH_P_8021Q);
+                       tag->vlan_tci = htons(aux->tp_vlan_tci);
+
+                       packet_len += VLAN_TAG_LEN;
+               }
+       }
+#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
 #endif /* HAVE_PF_PACKET_SOCKETS */
 
        /*
@@ -1510,7 +1709,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
        if (ioctl(handle->fd, SIOCGSTAMP, &pcap_header.ts) == -1) {
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                         "SIOCGSTAMP: %s", pcap_strerror(errno));
-               return -1;
+               return PCAP_ERROR;
        }
        pcap_header.caplen      = caplen;
        pcap_header.len         = packet_len;
@@ -1723,7 +1922,8 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
         *      "ps_drop" is not supported.
         *
         *      "ps_ifdrop" is supported. It will return the number
-        *      of drops the interface reports in /proc/net/dev
+        *      of drops the interface reports in /proc/net/dev,
+        *      if that is available.
         *
         *      "ps_recv" doesn't include packets not yet read from
         *      the kernel by libpcap.
@@ -1737,10 +1937,342 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
         
        stats->ps_recv = handle->md.packets_read;
        stats->ps_drop = 0;
-       stats->ps_ifdrop = handle->md.stat.ps_drop;
+       stats->ps_ifdrop = handle->md.stat.ps_ifdrop;
        return 0;
 }
 
+/*
+ * Get from "/sys/class/net" all interfaces listed there; if they're
+ * already in the list of interfaces we have, that won't add another
+ * instance, but if they're not, that'll add them.
+ *
+ * We don't bother getting any addresses for them; it appears you can't
+ * use SIOCGIFADDR on Linux to get IPv6 addresses for interfaces, and,
+ * although some other types of addresses can be fetched with SIOCGIFADDR,
+ * we don't bother with them for now.
+ *
+ * We also don't fail if we couldn't open "/sys/class/net"; we just leave
+ * the list of interfaces as is, and return 0, so that we can try
+ * scanning /proc/net/dev.
+ */
+static int
+scan_sys_class_net(pcap_if_t **devlistp, char *errbuf)
+{
+       DIR *sys_class_net_d;
+       int fd;
+       struct dirent *ent;
+       char subsystem_path[PATH_MAX+1];
+       struct stat statb;
+       char *p;
+       char name[512]; /* XXX - pick a size */
+       char *q, *saveq;
+       struct ifreq ifrflags;
+       int ret = 1;
+
+       sys_class_net_d = opendir("/sys/class/net");
+       if (sys_class_net_d == NULL) {
+               /*
+                * Don't fail if it doesn't exist at all.
+                */
+               if (errno == ENOENT)
+                       return (0);
+
+               /*
+                * Fail if we got some other error.
+                */
+               (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                   "Can't open /sys/class/net: %s", pcap_strerror(errno));
+               return (-1);
+       }
+
+       /*
+        * Create a socket from which to fetch interface information.
+        */
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                   "socket: %s", pcap_strerror(errno));
+               (void)closedir(sys_class_net_d);
+               return (-1);
+       }
+
+       for (;;) {
+               errno = 0;
+               ent = readdir(sys_class_net_d);
+               if (ent == NULL) {
+                       /*
+                        * Error or EOF; if errno != 0, it's an error.
+                        */
+                       break;
+               }
+
+               /*
+                * Ignore "." and "..".
+                */
+               if (strcmp(ent->d_name, ".") == 0 ||
+                   strcmp(ent->d_name, "..") == 0)
+                       continue;
+
+               /*
+                * Ignore plain files; they do not have subdirectories
+                * and thus have no attributes.
+                */
+               if (ent->d_type == DT_REG)
+                       continue;
+
+               /*
+                * Is there an "ifindex" file under that name?
+                * (We don't care whether it's a directory or
+                * a symlink; older kernels have directories
+                * for devices, newer kernels have symlinks to
+                * directories.)
+                */
+               snprintf(subsystem_path, sizeof subsystem_path,
+                   "/sys/class/net/%s/ifindex", ent->d_name);
+               if (lstat(subsystem_path, &statb) != 0) {
+                       /*
+                        * Stat failed.  Either there was an error
+                        * other than ENOENT, and we don't know if
+                        * this is an interface, or it's ENOENT,
+                        * and either some part of "/sys/class/net/{if}"
+                        * disappeared, in which case it probably means
+                        * the interface disappeared, or there's no
+                        * "ifindex" file, which means it's not a
+                        * network interface.
+                        */
+                       continue;
+               }
+
+               /*
+                * Get the interface name.
+                */
+               p = &ent->d_name[0];
+               q = &name[0];
+               while (*p != '\0' && isascii(*p) && !isspace(*p)) {
+                       if (*p == ':') {
+                               /*
+                                * This could be the separator between a
+                                * name and an alias number, or it could be
+                                * the separator between a name with no
+                                * alias number and the next field.
+                                *
+                                * If there's a colon after digits, it
+                                * separates the name and the alias number,
+                                * otherwise it separates the name and the
+                                * next field.
+                                */
+                               saveq = q;
+                               while (isascii(*p) && isdigit(*p))
+                                       *q++ = *p++;
+                               if (*p != ':') {
+                                       /*
+                                        * That was the next field,
+                                        * not the alias number.
+                                        */
+                                       q = saveq;
+                               }
+                               break;
+                       } else
+                               *q++ = *p++;
+               }
+               *q = '\0';
+
+               /*
+                * Get the flags for this interface, and skip it if
+                * it's not up.
+                */
+               strncpy(ifrflags.ifr_name, name, sizeof(ifrflags.ifr_name));
+               if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) {
+                       if (errno == ENXIO || errno == ENODEV)
+                               continue;
+                       (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                           "SIOCGIFFLAGS: %.*s: %s",
+                           (int)sizeof(ifrflags.ifr_name),
+                           ifrflags.ifr_name,
+                           pcap_strerror(errno));
+                       ret = -1;
+                       break;
+               }
+               if (!(ifrflags.ifr_flags & IFF_UP))
+                       continue;
+
+               /*
+                * Add an entry for this interface, with no addresses.
+                */
+               if (pcap_add_if(devlistp, name, ifrflags.ifr_flags, NULL,
+                   errbuf) == -1) {
+                       /*
+                        * Failure.
+                        */
+                       ret = -1;
+                       break;
+               }
+       }
+       if (ret != -1) {
+               /*
+                * Well, we didn't fail for any other reason; did we
+                * fail due to an error reading the directory?
+                */
+               if (errno != 0) {
+                       (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                           "Error reading /sys/class/net: %s",
+                           pcap_strerror(errno));
+                       ret = -1;
+               }
+       }
+
+       (void)close(fd);
+       (void)closedir(sys_class_net_d);
+       return (ret);
+}
+
+/*
+ * Get from "/proc/net/dev" all interfaces listed there; if they're
+ * already in the list of interfaces we have, that won't add another
+ * instance, but if they're not, that'll add them.
+ *
+ * See comments from scan_sys_class_net().
+ */
+static int
+scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf)
+{
+       FILE *proc_net_f;
+       int fd;
+       char linebuf[512];
+       int linenum;
+       char *p;
+       char name[512]; /* XXX - pick a size */
+       char *q, *saveq;
+       struct ifreq ifrflags;
+       int ret = 0;
+
+       proc_net_f = fopen("/proc/net/dev", "r");
+       if (proc_net_f == NULL) {
+               /*
+                * Don't fail if it doesn't exist at all.
+                */
+               if (errno == ENOENT)
+                       return (0);
+
+               /*
+                * Fail if we got some other error.
+                */
+               (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                   "Can't open /proc/net/dev: %s", pcap_strerror(errno));
+               return (-1);
+       }
+
+       /*
+        * Create a socket from which to fetch interface information.
+        */
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                   "socket: %s", pcap_strerror(errno));
+               (void)fclose(proc_net_f);
+               return (-1);
+       }
+
+       for (linenum = 1;
+           fgets(linebuf, sizeof linebuf, proc_net_f) != NULL; linenum++) {
+               /*
+                * Skip the first two lines - they're headers.
+                */
+               if (linenum <= 2)
+                       continue;
+
+               p = &linebuf[0];
+
+               /*
+                * Skip leading white space.
+                */
+               while (*p != '\0' && isascii(*p) && isspace(*p))
+                       p++;
+               if (*p == '\0' || *p == '\n')
+                       continue;       /* blank line */
+
+               /*
+                * Get the interface name.
+                */
+               q = &name[0];
+               while (*p != '\0' && isascii(*p) && !isspace(*p)) {
+                       if (*p == ':') {
+                               /*
+                                * This could be the separator between a
+                                * name and an alias number, or it could be
+                                * the separator between a name with no
+                                * alias number and the next field.
+                                *
+                                * If there's a colon after digits, it
+                                * separates the name and the alias number,
+                                * otherwise it separates the name and the
+                                * next field.
+                                */
+                               saveq = q;
+                               while (isascii(*p) && isdigit(*p))
+                                       *q++ = *p++;
+                               if (*p != ':') {
+                                       /*
+                                        * That was the next field,
+                                        * not the alias number.
+                                        */
+                                       q = saveq;
+                               }
+                               break;
+                       } else
+                               *q++ = *p++;
+               }
+               *q = '\0';
+
+               /*
+                * Get the flags for this interface, and skip it if
+                * it's not up.
+                */
+               strncpy(ifrflags.ifr_name, name, sizeof(ifrflags.ifr_name));
+               if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) {
+                       if (errno == ENXIO)
+                               continue;
+                       (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                           "SIOCGIFFLAGS: %.*s: %s",
+                           (int)sizeof(ifrflags.ifr_name),
+                           ifrflags.ifr_name,
+                           pcap_strerror(errno));
+                       ret = -1;
+                       break;
+               }
+               if (!(ifrflags.ifr_flags & IFF_UP))
+                       continue;
+
+               /*
+                * Add an entry for this interface, with no addresses.
+                */
+               if (pcap_add_if(devlistp, name, ifrflags.ifr_flags, NULL,
+                   errbuf) == -1) {
+                       /*
+                        * Failure.
+                        */
+                       ret = -1;
+                       break;
+               }
+       }
+       if (ret != -1) {
+               /*
+                * Well, we didn't fail for any other reason; did we
+                * fail due to an error reading the file?
+                */
+               if (ferror(proc_net_f)) {
+                       (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                           "Error reading /proc/net/dev: %s",
+                           pcap_strerror(errno));
+                       ret = -1;
+               }
+       }
+
+       (void)close(fd);
+       (void)fclose(proc_net_f);
+       return (ret);
+}
+
 /*
  * Description string for the "any" device.
  */
@@ -1749,29 +2281,83 @@ static const char any_descr[] = "Pseudo-device that captures on all interfaces";
 int
 pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf)
 {
+       int ret;
+
+       /*
+        * Read "/sys/class/net", and add to the list of interfaces all
+        * interfaces listed there that we don't already have, because,
+        * on Linux, SIOCGIFCONF reports only interfaces with IPv4 addresses,
+        * and even getifaddrs() won't return information about
+        * interfaces with no addresses, so you need to read "/sys/class/net"
+        * to get the names of the rest of the interfaces.
+        */
+       ret = scan_sys_class_net(alldevsp, errbuf);
+       if (ret == -1)
+               return (-1);    /* failed */
+       if (ret == 0) {
+               /*
+                * No /sys/class/net; try reading /proc/net/dev instead.
+                */
+               if (scan_proc_net_dev(alldevsp, errbuf) == -1)
+                       return (-1);
+       }
+
+       /*
+        * Add the "any" device.
+        */
        if (pcap_add_if(alldevsp, "any", 0, any_descr, errbuf) < 0)
                return (-1);
 
 #ifdef HAVE_DAG_API
+       /*
+        * Add DAG devices.
+        */
        if (dag_platform_finddevs(alldevsp, errbuf) < 0)
                return (-1);
 #endif /* HAVE_DAG_API */
 
 #ifdef HAVE_SEPTEL_API
+       /*
+        * Add Septel devices.
+        */
        if (septel_platform_finddevs(alldevsp, errbuf) < 0)
                return (-1);
 #endif /* HAVE_SEPTEL_API */
 
+#ifdef HAVE_SNF_API
+       if (snf_platform_finddevs(alldevsp, errbuf) < 0)
+               return (-1);
+#endif /* HAVE_SNF_API */
+
 #ifdef PCAP_SUPPORT_BT
+       /*
+        * Add Bluetooth devices.
+        */
        if (bt_platform_finddevs(alldevsp, errbuf) < 0)
                return (-1);
 #endif
 
 #ifdef PCAP_SUPPORT_USB
+       /*
+        * Add USB devices.
+        */
        if (usb_platform_finddevs(alldevsp, errbuf) < 0)
                return (-1);
 #endif
 
+#ifdef PCAP_SUPPORT_NETFILTER
+       /*
+        * Add netfilter devices.
+        */
+       if (netfilter_platform_finddevs(alldevsp, errbuf) < 0)
+               return (-1);
+#endif
+
+#if PCAP_SUPPORT_CANUSB
+  if (canusb_platform_finddevs(alldevsp, errbuf) < 0)
+    return (-1);
+#endif
+
        return (0);
 }
 
@@ -1867,6 +2453,30 @@ pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter,
                }
        }
 
+       /*
+        * NOTE: at this point, we've set both the "len" and "filter"
+        * fields of "fcode".  As of the 2.6.32.4 kernel, at least,
+        * those are the only members of the "sock_fprog" structure,
+        * so we initialize every member of that structure.
+        *
+        * If there is anything in "fcode" that is not initialized,
+        * it is either a field added in a later kernel, or it's
+        * padding.
+        *
+        * If a new field is added, this code needs to be updated
+        * to set it correctly.
+        *
+        * If there are no other fields, then:
+        *
+        *      if the Linux kernel looks at the padding, it's
+        *      buggy;
+        *
+        *      if the Linux kernel doesn't look at the padding,
+        *      then if some tool complains that we're passing
+        *      uninitialized data to the kernel, then the tool
+        *      is buggy and needs to understand that it's just
+        *      padding.
+        */
        if (can_filter_in_kernel) {
                if ((err = set_kernel_filter(handle, &fcode)) == 0)
                {
@@ -1942,7 +2552,6 @@ pcap_setdirection_linux(pcap_t *handle, pcap_direction_t d)
        return -1;
 }
 
-
 #ifdef HAVE_PF_PACKET_SOCKETS
 /*
  * Map the PACKET_ value to a LINUX_SLL_ value; we
@@ -1978,6 +2587,51 @@ map_packet_type_to_sll_type(short int sll_pkttype)
 }
 #endif
 
+static int
+is_wifi(int sock_fd
+#ifndef IW_MODE_MONITOR
+_U_
+#endif
+, const char *device)
+{
+       char *pathstr;
+       struct stat statb;
+#ifdef IW_MODE_MONITOR
+       char errbuf[PCAP_ERRBUF_SIZE];
+#endif
+
+       /*
+        * See if there's a sysfs wireless directory for it.
+        * If so, it's a wireless interface.
+        */
+       if (asprintf(&pathstr, "/sys/class/net/%s/wireless", device) == -1) {
+               /*
+                * Just give up here.
+                */
+               return 0;
+       }
+       if (stat(pathstr, &statb) == 0) {
+               free(pathstr);
+               return 1;
+       }
+       free(pathstr);
+
+#ifdef IW_MODE_MONITOR
+       /*
+        * OK, maybe it's not wireless, or maybe this kernel doesn't
+        * support sysfs.  Try the wireless extensions.
+        */
+       if (has_wext(sock_fd, device, errbuf) == 1) {
+               /*
+                * It supports the wireless extensions, so it's a Wi-Fi
+                * device.
+                */
+               return 1;
+       }
+#endif
+       return 0;
+}
+
 /*
  *  Linux uses the ARP hardware type to identify the type of an
  *  interface. pcap uses the DLT_xxx constants for this. This
@@ -1996,13 +2650,33 @@ map_packet_type_to_sll_type(short int sll_pkttype)
  *
  *  Sets the link type to -1 if unable to map the type.
  */
-static void map_arphrd_to_dlt(pcap_t *handle, int arptype, int cooked_ok)
+static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype,
+                             const char *device, int cooked_ok)
 {
+       static const char cdma_rmnet[] = "cdma_rmnet";
+
        switch (arptype) {
 
        case ARPHRD_ETHER:
                /*
-                * This is (presumably) a real Ethernet capture; give it a
+                * For various annoying reasons having to do with DHCP
+                * software, some versions of Android give the mobile-
+                * phone-network interface an ARPHRD_ value of
+                * ARPHRD_ETHER, even though the packets supplied by
+                * that interface have no link-layer header, and begin
+                * with an IP header, so that the ARPHRD_ value should
+                * be ARPHRD_NONE.
+                *
+                * Detect those devices by checking the device name, and
+                * use DLT_RAW for them.
+                */
+               if (strncmp(device, cdma_rmnet, sizeof cdma_rmnet - 1) == 0) {
+                       handle->linktype = DLT_RAW;
+                       return;
+               }
+       
+               /*
+                * Is this a real Ethernet device?  If so, give it a
                 * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so
                 * that an application can let you choose it, in case you're
                 * capturing DOCSIS traffic that a Cisco Cable Modem
@@ -2011,21 +2685,27 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, int cooked_ok)
                 * DOCSIS frames out on the wire inside the low-level
                 * Ethernet framing).
                 *
-                * XXX - are there any sorts of "fake Ethernet" that have
-                * ARPHRD_ETHER but that *shouldn't offer DLT_DOCSIS as
+                * XXX - are there any other sorts of "fake Ethernet" that
+                * have ARPHRD_ETHER but that shouldn't offer DLT_DOCSIS as
                 * a Cisco CMTS won't put traffic onto it or get traffic
                 * bridged onto it?  ISDN is handled in "activate_new()",
-                * as we fall back on cooked mode there; are there any
+                * as we fall back on cooked mode there, and we use
+                * is_wifi() to check for 802.11 devices; are there any
                 * others?
                 */
-               handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
-               /*
-                * If that fails, just leave the list empty.
-                */
-               if (handle->dlt_list != NULL) {
-                       handle->dlt_list[0] = DLT_EN10MB;
-                       handle->dlt_list[1] = DLT_DOCSIS;
-                       handle->dlt_count = 2;
+               if (!is_wifi(sock_fd, device)) {
+                       /*
+                        * It's not a Wi-Fi device; offer DOCSIS.
+                        */
+                       handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
+                       /*
+                        * If that fails, just leave the list empty.
+                        */
+                       if (handle->dlt_list != NULL) {
+                               handle->dlt_list[0] = DLT_EN10MB;
+                               handle->dlt_list[1] = DLT_DOCSIS;
+                               handle->dlt_count = 2;
+                       }
                }
                /* FALLTHROUGH */
 
@@ -2050,6 +2730,12 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, int cooked_ok)
        case ARPHRD_CHAOS:
                handle->linktype = DLT_CHAOS;
                break;
+#ifndef ARPHRD_CAN
+#define ARPHRD_CAN 280
+#endif
+       case ARPHRD_CAN:
+               handle->linktype = DLT_CAN_SOCKETCAN;
+               break;
 
 #ifndef ARPHRD_IEEE802_TR
 #define ARPHRD_IEEE802_TR 800  /* From Linux 2.4 */
@@ -2283,6 +2969,13 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, int cooked_ok)
                handle->linktype = DLT_RAW;
                break;
 
+#ifndef ARPHRD_IEEE802154
+#define ARPHRD_IEEE802154      804
+#endif
+       case ARPHRD_IEEE802154:
+               handle->linktype =  DLT_IEEE802_15_4_NOFCS;
+               break;
+
        default:
                handle->linktype = -1;
                break;
@@ -2322,9 +3015,28 @@ activate_new(pcap_t *handle)
                socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
 
        if (sock_fd == -1) {
+               if (errno == EINVAL || errno == EAFNOSUPPORT) {
+                       /*
+                        * We don't support PF_PACKET/SOCK_whatever
+                        * sockets; try the old mechanism.
+                        */
+                       return 0;
+               }
+
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s",
                         pcap_strerror(errno) );
-               return 0;       /* try old mechanism */
+               if (errno == EPERM || errno == EACCES) {
+                       /*
+                        * You don't have permission to open the
+                        * socket.
+                        */
+                       return PCAP_ERROR_PERM_DENIED;
+               } else {
+                       /*
+                        * Other error.
+                        */
+                       return PCAP_ERROR;
+               }
        }
 
        /* It seems the kernel supports the new interface. */
@@ -2394,7 +3106,7 @@ activate_new(pcap_t *handle)
                        close(sock_fd);
                        return arptype;
                }
-               map_arphrd_to_dlt(handle, arptype, 1);
+               map_arphrd_to_dlt(handle, sock_fd, arptype, device, 1);
                if (handle->linktype == -1 ||
                    handle->linktype == DLT_LINUX_SLL ||
                    handle->linktype == DLT_LINUX_IRDA ||
@@ -2421,7 +3133,18 @@ activate_new(pcap_t *handle)
                        if (sock_fd == -1) {
                                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                                    "socket: %s", pcap_strerror(errno));
-                               return PCAP_ERROR;
+                               if (errno == EPERM || errno == EACCES) {
+                                       /*
+                                        * You don't have permission to
+                                        * open the socket.
+                                        */
+                                       return PCAP_ERROR_PERM_DENIED;
+                               } else {
+                                       /*
+                                        * Other error.
+                                        */
+                                       return PCAP_ERROR;
+                               }
                        }
                        handle->md.cooked = 1;
 
@@ -2572,6 +3295,24 @@ activate_new(pcap_t *handle)
        }
        handle->bufsize = handle->snapshot;
 
+       /*
+        * Set the offset at which to insert VLAN tags.
+        */
+       switch (handle->linktype) {
+
+       case DLT_EN10MB:
+               handle->md.vlan_offset = 2 * ETH_ALEN;
+               break;
+
+       case DLT_LINUX_SLL:
+               handle->md.vlan_offset = 14;
+               break;
+
+       default:
+               handle->md.vlan_offset = -1; /* unknown */
+               break;
+       }
+
        /* Save the socket FD in the pcap structure */
        handle->fd = sock_fd;
 
@@ -2584,10 +3325,22 @@ activate_new(pcap_t *handle)
 #endif
 }
 
+#ifdef HAVE_PACKET_RING
+/*
+ * Attempt to activate with memory-mapped access.
+ *
+ * On success, returns 1, and sets *status to 0 if there are no warnings
+ * or to a PCAP_WARNING_ code if there is a warning.
+ *
+ * On failure due to lack of support for memory-mapped capture, returns
+ * 0.
+ *
+ * On error, returns -1, and sets *status to the appropriate error code;
+ * if that is PCAP_ERROR, sets handle->errbuf to the appropriate message.
+ */
 static int 
-activate_mmap(pcap_t *handle)
+activate_mmap(pcap_t *handle, int *status)
 {
-#ifdef HAVE_PACKET_RING
        int ret;
 
        /*
@@ -2599,7 +3352,8 @@ activate_mmap(pcap_t *handle)
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                         "can't allocate oneshot buffer: %s",
                         pcap_strerror(errno));
-               return PCAP_ERROR;
+               *status = PCAP_ERROR;
+               return -1;
        }
 
        if (handle->opt.buffer_size == 0) {
@@ -2607,20 +3361,38 @@ activate_mmap(pcap_t *handle)
                handle->opt.buffer_size = 2*1024*1024;
        }
        ret = prepare_tpacket_socket(handle);
-       if (ret != 1) {
+       if (ret == -1) {
                free(handle->md.oneshot_buffer);
+               *status = PCAP_ERROR;
                return ret;
        }
-       ret = create_ring(handle);
-       if (ret != 1) {
+       ret = create_ring(handle, status);
+       if (ret == 0) {
+               /*
+                * We don't support memory-mapped capture; our caller
+                * will fall back on reading from the socket.
+                */
                free(handle->md.oneshot_buffer);
-               return ret;
+               return 0;
+       }
+       if (ret == -1) {
+               /*
+                * Error attempting to enable memory-mapped capture;
+                * fail.  create_ring() has set *status.
+                */
+               free(handle->md.oneshot_buffer);
+               return -1;
        }
 
-       /* override some defaults and inherit the other fields from
-        * activate_new
-        * handle->offset is used to get the current position into the rx ring 
-        * handle->cc is used to store the ring size */
+       /*
+        * Success.  *status has been set either to 0 if there are no
+        * warnings or to a PCAP_WARNING_ value if there is a warning.
+        *
+        * Override some defaults and inherit the other fields from
+        * activate_new.
+        * handle->offset is used to get the current position into the rx ring.
+        * handle->cc is used to store the ring size.
+        */
        handle->read_op = pcap_read_linux_mmap;
        handle->cleanup_op = pcap_cleanup_linux_mmap;
        handle->setfilter_op = pcap_setfilter_linux_mmap;
@@ -2629,12 +3401,21 @@ activate_mmap(pcap_t *handle)
        handle->oneshot_callback = pcap_oneshot_mmap;
        handle->selectable_fd = handle->fd;
        return 1;
+}
 #else /* HAVE_PACKET_RING */
+static int 
+activate_mmap(pcap_t *handle _U_, int *status _U_)
+{
        return 0;
-#endif /* HAVE_PACKET_RING */
 }
+#endif /* HAVE_PACKET_RING */
 
 #ifdef HAVE_PACKET_RING
+/*
+ * Attempt to set the socket to version 2 of the memory-mapped header.
+ * Return 1 if we succeed or if we fail because version 2 isn't
+ * supported; return -1 on any other error, and set handle->errbuf.
+ */
 static int
 prepare_tpacket_socket(pcap_t *handle)
 {
@@ -2686,20 +3467,140 @@ prepare_tpacket_socket(pcap_t *handle)
        return 1;
 }
 
+/*
+ * Attempt to set up memory-mapped access.
+ *
+ * On success, returns 1, and sets *status to 0 if there are no warnings
+ * or to a PCAP_WARNING_ code if there is a warning.
+ *
+ * On failure due to lack of support for memory-mapped capture, returns
+ * 0.
+ *
+ * On error, returns -1, and sets *status to the appropriate error code;
+ * if that is PCAP_ERROR, sets handle->errbuf to the appropriate message.
+ */
 static int
-create_ring(pcap_t *handle)
+create_ring(pcap_t *handle, int *status)
 {
        unsigned i, j, frames_per_block;
        struct tpacket_req req;
+       socklen_t len;
+       unsigned int sk_type, tp_reserve, maclen, tp_hdrlen, netoff, macoff;
+       unsigned int frame_size;
 
-       /* Note that with large snapshot (say 64K) only a few frames 
-        * will be available in the ring even with pretty large ring size
-        * (and a lot of memory will be unused). 
-        * The snap len should be carefully chosen to achive best
-        * performance */
-       req.tp_frame_size = TPACKET_ALIGN(handle->snapshot +
-                                         TPACKET_ALIGN(handle->md.tp_hdrlen) +
-                                         sizeof(struct sockaddr_ll));
+       /*
+        * Start out assuming no warnings or errors.
+        */
+       *status = 0;
+
+       /* Note that with large snapshot length (say 64K, which is the default
+        * for recent versions of tcpdump, the value that "-s 0" has given
+        * for a long time with tcpdump, and the default in Wireshark/TShark),
+        * if we use the snapshot length to calculate the frame length,
+        * only a few frames will be available in the ring even with pretty
+        * large ring size (and a lot of memory will be unused).
+        *
+        * Ideally, we should choose a frame length based on the
+        * minimum of the specified snapshot length and the maximum
+        * packet size.  That's not as easy as it sounds; consider, for
+        * example, an 802.11 interface in monitor mode, where the
+        * frame would include a radiotap header, where the maximum
+        * radiotap header length is device-dependent.
+        *
+        * So, for now, we just do this for Ethernet devices, where
+        * there's no metadata header, and the link-layer header is
+        * fixed length.  We can get the maximum packet size by
+        * adding 18, the Ethernet header length plus the CRC length
+        * (just in case we happen to get the CRC in the packet), to
+        * the MTU of the interface; we fetch the MTU in the hopes
+        * that it reflects support for jumbo frames.  (Even if the
+        * interface is just being used for passive snooping, the driver
+        * might set the size of buffers in the receive ring based on
+        * the MTU, so that the MTU limits the maximum size of packets
+        * that we can receive.)
+        *
+        * We don't do that if segmentation/fragmentation or receive
+        * offload are enabled, so we don't get rudely surprised by
+        * "packets" bigger than the MTU. */
+       frame_size = handle->snapshot;
+       if (handle->linktype == DLT_EN10MB) {
+               int mtu;
+               int offload;
+
+               offload = iface_get_offload(handle);
+               if (offload == -1) {
+                       *status = PCAP_ERROR;
+                       return -1;
+               }
+               if (!offload) {
+                       mtu = iface_get_mtu(handle->fd, handle->opt.source,
+                           handle->errbuf);
+                       if (mtu == -1) {
+                               *status = PCAP_ERROR;
+                               return -1;
+                       }
+                       if (frame_size > mtu + 18)
+                               frame_size = mtu + 18;
+               }
+       }
+       
+       /* NOTE: calculus matching those in tpacket_rcv()
+        * in linux-2.6/net/packet/af_packet.c
+        */
+       len = sizeof(sk_type);
+       if (getsockopt(handle->fd, SOL_SOCKET, SO_TYPE, &sk_type, &len) < 0) {
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "getsockopt: %s", pcap_strerror(errno));
+               *status = PCAP_ERROR;
+               return -1;
+       }
+#ifdef PACKET_RESERVE
+       len = sizeof(tp_reserve);
+       if (getsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE, &tp_reserve, &len) < 0) {
+               if (errno != ENOPROTOOPT) {
+                       /*
+                        * ENOPROTOOPT means "kernel doesn't support
+                        * PACKET_RESERVE", in which case we fall back
+                        * as best we can.
+                        */
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "getsockopt: %s", pcap_strerror(errno));
+                       *status = PCAP_ERROR;
+                       return -1;
+               }
+               tp_reserve = 0; /* older kernel, reserve not supported */
+       }
+#else
+       tp_reserve = 0; /* older kernel, reserve not supported */
+#endif
+       maclen = (sk_type == SOCK_DGRAM) ? 0 : MAX_LINKHEADER_SIZE;
+               /* XXX: in the kernel maclen is calculated from
+                * LL_ALLOCATED_SPACE(dev) and vnet_hdr.hdr_len
+                * in:  packet_snd()           in linux-2.6/net/packet/af_packet.c
+                * then packet_alloc_skb()     in linux-2.6/net/packet/af_packet.c
+                * then sock_alloc_send_pskb() in linux-2.6/net/core/sock.c
+                * but I see no way to get those sizes in userspace,
+                * like for instance with an ifreq ioctl();
+                * the best thing I've found so far is MAX_HEADER in the kernel
+                * part of linux-2.6/include/linux/netdevice.h
+                * which goes up to 128+48=176; since pcap-linux.c defines
+                * a MAX_LINKHEADER_SIZE of 256 which is greater than that,
+                * let's use it.. maybe is it even large enough to directly
+                * replace macoff..
+                */
+       tp_hdrlen = TPACKET_ALIGN(handle->md.tp_hdrlen) + sizeof(struct sockaddr_ll) ;
+       netoff = TPACKET_ALIGN(tp_hdrlen + (maclen < 16 ? 16 : maclen)) + tp_reserve;
+               /* NOTE: AFAICS tp_reserve may break the TPACKET_ALIGN of
+                * netoff, which contradicts
+                * linux-2.6/Documentation/networking/packet_mmap.txt
+                * documenting that:
+                * "- Gap, chosen so that packet data (Start+tp_net)
+                * aligns to TPACKET_ALIGNMENT=16"
+                */
+               /* NOTE: in linux-2.6/include/linux/skbuff.h:
+                * "CPUs often take a performance hit
+                *  when accessing unaligned memory locations"
+                */
+       macoff = netoff - maclen;
+       req.tp_frame_size = TPACKET_ALIGN(macoff + frame_size);
        req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size;
 
        /* compute the minumum block size that will handle this frame. 
@@ -2710,7 +3611,110 @@ create_ring(pcap_t *handle)
        while (req.tp_block_size < req.tp_frame_size) 
                req.tp_block_size <<= 1;
 
-       frames_per_block = req.tp_block_size/req.tp_frame_size;
+       frames_per_block = req.tp_block_size/req.tp_frame_size;
+
+       /*
+        * PACKET_TIMESTAMP was added after linux/net_tstamp.h was,
+        * so we check for PACKET_TIMESTAMP.  We check for
+        * linux/net_tstamp.h just in case a system somehow has
+        * PACKET_TIMESTAMP but not linux/net_tstamp.h; that might
+        * be unnecessary.
+        *
+        * SIOCSHWTSTAMP was introduced in the patch that introduced
+        * linux/net_tstamp.h, so we don't bother checking whether
+        * SIOCSHWTSTAMP is defined (if your Linux system has
+        * linux/net_tstamp.h but doesn't define SIOCSHWTSTAMP, your
+        * Linux system is badly broken).
+        */
+#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
+       /*
+        * If we were told to do so, ask the kernel and the driver
+        * to use hardware timestamps.
+        *
+        * Hardware timestamps are only supported with mmapped
+        * captures.
+        */
+       if (handle->opt.tstamp_type == PCAP_TSTAMP_ADAPTER ||
+           handle->opt.tstamp_type == PCAP_TSTAMP_ADAPTER_UNSYNCED) {
+               struct hwtstamp_config hwconfig;
+               struct ifreq ifr;
+               int timesource;
+
+               /*
+                * Ask for hardware time stamps on all packets,
+                * including transmitted packets.
+                */
+               memset(&hwconfig, 0, sizeof(hwconfig));
+               hwconfig.tx_type = HWTSTAMP_TX_ON;
+               hwconfig.rx_filter = HWTSTAMP_FILTER_ALL;
+
+               memset(&ifr, 0, sizeof(ifr));
+               strcpy(ifr.ifr_name, handle->opt.source);
+               ifr.ifr_data = (void *)&hwconfig;
+
+               if (ioctl(handle->fd, SIOCSHWTSTAMP, &ifr) < 0) {
+                       switch (errno) {
+
+                       case EPERM:
+                               /*
+                                * Treat this as an error, as the
+                                * user should try to run this
+                                * with the appropriate privileges -
+                                * and, if they can't, shouldn't
+                                * try requesting hardware time stamps.
+                                */
+                               *status = PCAP_ERROR_PERM_DENIED;
+                               return -1;
+
+                       case EOPNOTSUPP:
+                               /*
+                                * Treat this as a warning, as the
+                                * only way to fix the warning is to
+                                * get an adapter that supports hardware
+                                * time stamps.  We'll just fall back
+                                * on the standard host time stamps.
+                                */
+                               *status = PCAP_WARNING_TSTAMP_TYPE_NOTSUP;
+                               break;
+
+                       default:
+                               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                                       "SIOCSHWTSTAMP failed: %s",
+                                       pcap_strerror(errno));
+                               *status = PCAP_ERROR;
+                               return -1;
+                       }
+               } else {
+                       /*
+                        * Well, that worked.  Now specify the type of
+                        * hardware time stamp we want for this
+                        * socket.
+                        */
+                       if (handle->opt.tstamp_type == PCAP_TSTAMP_ADAPTER) {
+                               /*
+                                * Hardware timestamp, synchronized
+                                * with the system clock.
+                                */
+                               timesource = SOF_TIMESTAMPING_SYS_HARDWARE;
+                       } else {
+                               /*
+                                * PCAP_TSTAMP_ADAPTER_UNSYNCED - hardware
+                                * timestamp, not synchronized with the
+                                * system clock.
+                                */
+                               timesource = SOF_TIMESTAMPING_RAW_HARDWARE;
+                       }
+                       if (setsockopt(handle->fd, SOL_PACKET, PACKET_TIMESTAMP,
+                               (void *)&timesource, sizeof(timesource))) {
+                               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, 
+                                       "can't set PACKET_TIMESTAMP: %s", 
+                                       pcap_strerror(errno));
+                               *status = PCAP_ERROR;
+                               return -1;
+                       }
+               }
+       }
+#endif /* HAVE_LINUX_NET_TSTAMP_H && PACKET_TIMESTAMP */
 
        /* ask the kernel to create the ring */
 retry:
@@ -2746,6 +3750,7 @@ retry:
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                    "can't create rx ring on packet socket: %s",
                    pcap_strerror(errno));
+               *status = PCAP_ERROR;
                return -1;
        }
 
@@ -2759,6 +3764,7 @@ retry:
 
                /* clear the allocated ring on error*/
                destroy_ring(handle);
+               *status = PCAP_ERROR;
                return -1;
        }
 
@@ -2771,6 +3777,7 @@ retry:
                    pcap_strerror(errno));
 
                destroy_ring(handle);
+               *status = PCAP_ERROR;
                return -1;
        }
 
@@ -2906,35 +3913,95 @@ pcap_get_ring_frame(pcap_t *handle, int status)
        return h.raw;
 }
 
+#ifndef POLLRDHUP
+#define POLLRDHUP 0
+#endif
+
 static int
 pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, 
                u_char *user)
 {
+       int timeout;
        int pkts = 0;
+       char c;
 
        /* wait for frames availability.*/
-       if ((handle->md.timeout >= 0) &&
-           !pcap_get_ring_frame(handle, TP_STATUS_USER)) {
+       if (!pcap_get_ring_frame(handle, TP_STATUS_USER)) {
                struct pollfd pollinfo;
                int ret;
 
                pollinfo.fd = handle->fd;
                pollinfo.events = POLLIN;
 
+               if (handle->md.timeout == 0)
+                       timeout = -1;   /* block forever */
+               else if (handle->md.timeout > 0)
+                       timeout = handle->md.timeout;   /* block for that amount of time */
+               else
+                       timeout = 0;    /* non-blocking mode - poll to pick up errors */
                do {
-                       /* poll() requires a negative timeout to wait forever */
-                       ret = poll(&pollinfo, 1, (handle->md.timeout > 0)?
-                                               handle->md.timeout: -1);
-                       if ((ret < 0) && (errno != EINTR)) {
+                       ret = poll(&pollinfo, 1, timeout);
+                       if (ret < 0 && errno != EINTR) {
                                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, 
-                                        "can't poll on packet socket fd %d: %d-%s",
-                                       handle->fd, errno, pcap_strerror(errno));
-                               return -1;
-                       }
+                                       "can't poll on packet socket: %s",
+                                       pcap_strerror(errno));
+                               return PCAP_ERROR;
+                       } else if (ret > 0 &&
+                           (pollinfo.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) {
+                               /*
+                                * There's some indication other than
+                                * "you can read on this descriptor" on
+                                * the descriptor.
+                                */
+                               if (pollinfo.revents & (POLLHUP | POLLRDHUP)) {
+                                       snprintf(handle->errbuf,
+                                               PCAP_ERRBUF_SIZE,
+                                               "Hangup on packet socket");
+                                       return PCAP_ERROR;
+                               }
+                               if (pollinfo.revents & POLLERR) {
+                                       /*
+                                        * A recv() will give us the
+                                        * actual error code.
+                                        *
+                                        * XXX - make the socket non-blocking?
+                                        */
+                                       if (recv(handle->fd, &c, sizeof c,
+                                           MSG_PEEK) != -1)
+                                               continue;       /* what, no error? */
+                                       if (errno == ENETDOWN) {
+                                               /*
+                                                * The device on which we're
+                                                * capturing went away.
+                                                *
+                                                * XXX - we should really return
+                                                * PCAP_ERROR_IFACE_NOT_UP,
+                                                * but pcap_dispatch() etc.
+                                                * aren't defined to return
+                                                * that.
+                                                */
+                                               snprintf(handle->errbuf,
+                                                       PCAP_ERRBUF_SIZE,
+                                                       "The interface went down");
+                                       } else {
+                                               snprintf(handle->errbuf,
+                                                       PCAP_ERRBUF_SIZE, 
+                                                       "Error condition on packet socket: %s",
+                                                       strerror(errno));
+                                       }
+                                       return PCAP_ERROR;
+                               }
+                               if (pollinfo.revents & POLLNVAL) {
+                                       snprintf(handle->errbuf,
+                                               PCAP_ERRBUF_SIZE, 
+                                               "Invalid polling request on packet socket");
+                                       return PCAP_ERROR;
+                               }
+                       }
                        /* check for break loop condition on interrupted syscall*/
                        if (handle->break_loop) {
                                handle->break_loop = 0;
-                               return -2;
+                               return PCAP_ERROR_BREAK;
                        }
                } while (ret < 0);
        }
@@ -2984,7 +4051,7 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback,
                if (tp_mac + tp_snaplen > handle->bufsize) {
                        snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, 
                                "corrupted frame on kernel ring mac "
-                               "offset %d + caplen %d > frame len %d", 
+                               "offset %u + caplen %u > frame len %d", 
                                tp_mac, tp_snaplen, handle->bufsize);
                        return -1;
                }
@@ -3085,14 +4152,20 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback,
                }
 
 #ifdef HAVE_TPACKET2
-               if (handle->md.tp_version == TPACKET_V2 && h.h2->tp_vlan_tci &&
-                   tp_snaplen >= 2 * ETH_ALEN) {
+               if ((handle->md.tp_version == TPACKET_V2) &&
+#if defined(TP_STATUS_VLAN_VALID)
+               (h.h2->tp_vlan_tci || (h.h2->tp_status & TP_STATUS_VLAN_VALID)) &&
+#else
+               h.h2->tp_vlan_tci &&
+#endif
+                   handle->md.vlan_offset != -1 &&
+                   tp_snaplen >= (unsigned int) handle->md.vlan_offset) {
                        struct vlan_tag *tag;
 
                        bp -= VLAN_TAG_LEN;
-                       memmove(bp, bp + VLAN_TAG_LEN, 2 * ETH_ALEN);
+                       memmove(bp, bp + VLAN_TAG_LEN, handle->md.vlan_offset);
 
-                       tag = (struct vlan_tag *)(bp + 2 * ETH_ALEN);
+                       tag = (struct vlan_tag *)(bp + handle->md.vlan_offset);
                        tag->vlan_tpid = htons(ETH_P_8021Q);
                        tag->vlan_tci = htons(h.h2->tp_vlan_tci);
 
@@ -3136,7 +4209,7 @@ skip:
                /* check for break loop condition*/
                if (handle->break_loop) {
                        handle->break_loop = 0;
-                       return -2;
+                       return PCAP_ERROR_BREAK;
                }
        }
        return pkts;
@@ -3387,6 +4460,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
        monitor_type montype;
        int i;
        __u32 cmd;
+       struct ifreq ifr;
+       int oldflags;
        int args[2];
        int channel;
 
@@ -3396,6 +4471,13 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
        err = has_wext(sock_fd, device, handle->errbuf);
        if (err <= 0)
                return err;     /* either it doesn't or the device doesn't even exist */
+       /*
+        * Start out assuming we have no private extensions to control
+        * radio metadata.
+        */
+       montype = MONITOR_WEXT;
+       cmd = 0;
+
        /*
         * Try to get all the Wireless Extensions private ioctls
         * supported by this device.
@@ -3419,187 +4501,189 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
                    device);
                return PCAP_ERROR;
        }
-       if (errno == EOPNOTSUPP) {
-               /*
-                * No private ioctls, so we assume that there's only one
-                * DLT_ for monitor mode.
-                */
-               return 0;
-       }
-       if (errno != E2BIG) {
+       if (errno != EOPNOTSUPP) {
                /*
-                * Failed.
+                * OK, it's not as if there are no private ioctls.
                 */
-               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   "%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno));
-               return PCAP_ERROR;
-       }
-       priv = malloc(ireq.u.data.length * sizeof (struct iw_priv_args));
-       if (priv == NULL) {
-               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
-                        "malloc: %s", pcap_strerror(errno));
-               return PCAP_ERROR;
-       }
-       ireq.u.data.pointer = (void *)priv;
-       if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) == -1) {
-               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   "%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno));
-               free(priv);
-               return PCAP_ERROR;
-       }
-
-       /*
-        * Look for private ioctls to turn monitor mode on or, if
-        * monitor mode is on, to set the header type.
-        */
-       montype = MONITOR_WEXT;
-       cmd = 0;
-       for (i = 0; i < ireq.u.data.length; i++) {
-               if (strcmp(priv[i].name, "monitor_type") == 0) {
-                       /*
-                        * Hostap driver, use this one.
-                        * Set monitor mode first.
-                        * You can set it to 0 to get DLT_IEEE80211,
-                        * 1 to get DLT_PRISM, 2 to get
-                        * DLT_IEEE80211_RADIO_AVS, and, with more
-                        * recent versions of the driver, 3 to get
-                        * DLT_IEEE80211_RADIO.
-                        */
-                       if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                               break;
-                       if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
-                               break;
-                       if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
-                               break;
-                       montype = MONITOR_HOSTAP;
-                       cmd = priv[i].cmd;
-                       break;
-               }
-               if (strcmp(priv[i].name, "set_prismhdr") == 0) {
-                       /*
-                        * Prism54 driver, use this one.
-                        * Set monitor mode first.
-                        * You can set it to 2 to get DLT_IEEE80211
-                        * or 3 or get DLT_PRISM.
-                        */
-                       if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                               break;
-                       if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
-                               break;
-                       if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
-                               break;
-                       montype = MONITOR_PRISM54;
-                       cmd = priv[i].cmd;
-                       break;
-               }
-               if (strcmp(priv[i].name, "forceprismheader") == 0) {
+               if (errno != E2BIG) {
                        /*
-                        * RT2570 driver, use this one.
-                        * Do this after turning monitor mode on.
-                        * You can set it to 1 to get DLT_PRISM or 2
-                        * to get DLT_IEEE80211.
+                        * Failed.
                         */
-                       if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                               break;
-                       if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
-                               break;
-                       if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
-                               break;
-                       montype = MONITOR_RT2570;
-                       cmd = priv[i].cmd;
-                       break;
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "%s: SIOCGIWPRIV: %s", device,
+                           pcap_strerror(errno));
+                       return PCAP_ERROR;
                }
-               if (strcmp(priv[i].name, "forceprism") == 0) {
-                       /*
-                        * RT73 driver, use this one.
-                        * Do this after turning monitor mode on.
-                        * Its argument is a *string*; you can
-                        * set it to "1" to get DLT_PRISM or "2"
-                        * to get DLT_IEEE80211.
-                        */
-                       if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_CHAR)
-                               break;
-                       if (priv[i].set_args & IW_PRIV_SIZE_FIXED)
-                               break;
-                       montype = MONITOR_RT73;
-                       cmd = priv[i].cmd;
-                       break;
+
+               /*
+                * OK, try to get the list of private ioctls.
+                */
+               priv = malloc(ireq.u.data.length * sizeof (struct iw_priv_args));
+               if (priv == NULL) {
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "malloc: %s", pcap_strerror(errno));
+                       return PCAP_ERROR;
                }
-               if (strcmp(priv[i].name, "prismhdr") == 0) {
-                       /*
-                        * One of the RTL8xxx drivers, use this one.
-                        * It can only be done after monitor mode
-                        * has been turned on.  You can set it to 1
-                        * to get DLT_PRISM or 0 to get DLT_IEEE80211.
-                        */
-                       if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                               break;
-                       if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
-                               break;
-                       if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
-                               break;
-                       montype = MONITOR_RTL8XXX;
-                       cmd = priv[i].cmd;
-                       break;
+               ireq.u.data.pointer = (void *)priv;
+               if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) == -1) {
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "%s: SIOCGIWPRIV: %s", device,
+                           pcap_strerror(errno));
+                       free(priv);
+                       return PCAP_ERROR;
                }
-               if (strcmp(priv[i].name, "rfmontx") == 0) {
-                       /*
-                        * RT2500 or RT61 driver, use this one.
-                        * It has one one-byte parameter; set
-                        * u.data.length to 1 and u.data.pointer to
-                        * point to the parameter.
-                        * It doesn't itself turn monitor mode on.
-                        * You can set it to 1 to allow transmitting
-                        * in monitor mode(?) and get DLT_IEEE80211,
-                        * or set it to 0 to disallow transmitting in
-                        * monitor mode(?) and get DLT_PRISM.
-                        */
-                       if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                               break;
-                       if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 2)
+
+               /*
+                * Look for private ioctls to turn monitor mode on or, if
+                * monitor mode is on, to set the header type.
+                */
+               for (i = 0; i < ireq.u.data.length; i++) {
+                       if (strcmp(priv[i].name, "monitor_type") == 0) {
+                               /*
+                                * Hostap driver, use this one.
+                                * Set monitor mode first.
+                                * You can set it to 0 to get DLT_IEEE80211,
+                                * 1 to get DLT_PRISM, 2 to get
+                                * DLT_IEEE80211_RADIO_AVS, and, with more
+                                * recent versions of the driver, 3 to get
+                                * DLT_IEEE80211_RADIO.
+                                */
+                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
+                                       break;
+                               if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
+                                       break;
+                               if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
+                                       break;
+                               montype = MONITOR_HOSTAP;
+                               cmd = priv[i].cmd;
                                break;
-                       montype = MONITOR_RT2500;
-                       cmd = priv[i].cmd;
-                       break;
-               }
-               if (strcmp(priv[i].name, "monitor") == 0) {
-                       /*
-                        * Either ACX100 or hostap, use this one.
-                        * It turns monitor mode on.
-                        * If it takes two arguments, it's ACX100;
-                        * the first argument is 1 for DLT_PRISM
-                        * or 2 for DLT_IEEE80211, and the second
-                        * argument is the channel on which to
-                        * run.  If it takes one argument, it's
-                        * HostAP, and the argument is 2 for
-                        * DLT_IEEE80211 and 3 for DLT_PRISM.
-                        *
-                        * If we see this, we don't quit, as this
-                        * might be a version of the hostap driver
-                        * that also supports "monitor_type".
-                        */
-                       if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
+                       }
+                       if (strcmp(priv[i].name, "set_prismhdr") == 0) {
+                               /*
+                                * Prism54 driver, use this one.
+                                * Set monitor mode first.
+                                * You can set it to 2 to get DLT_IEEE80211
+                                * or 3 or get DLT_PRISM.
+                                */
+                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
+                                       break;
+                               if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
+                                       break;
+                               if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
+                                       break;
+                               montype = MONITOR_PRISM54;
+                               cmd = priv[i].cmd;
                                break;
-                       if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
+                       }
+                       if (strcmp(priv[i].name, "forceprismheader") == 0) {
+                               /*
+                                * RT2570 driver, use this one.
+                                * Do this after turning monitor mode on.
+                                * You can set it to 1 to get DLT_PRISM or 2
+                                * to get DLT_IEEE80211.
+                                */
+                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
+                                       break;
+                               if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
+                                       break;
+                               if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
+                                       break;
+                               montype = MONITOR_RT2570;
+                               cmd = priv[i].cmd;
                                break;
-                       switch (priv[i].set_args & IW_PRIV_SIZE_MASK) {
-
-                       case 1:
-                               montype = MONITOR_PRISM;
+                       }
+                       if (strcmp(priv[i].name, "forceprism") == 0) {
+                               /*
+                                * RT73 driver, use this one.
+                                * Do this after turning monitor mode on.
+                                * Its argument is a *string*; you can
+                                * set it to "1" to get DLT_PRISM or "2"
+                                * to get DLT_IEEE80211.
+                                */
+                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_CHAR)
+                                       break;
+                               if (priv[i].set_args & IW_PRIV_SIZE_FIXED)
+                                       break;
+                               montype = MONITOR_RT73;
                                cmd = priv[i].cmd;
                                break;
-
-                       case 2:
-                               montype = MONITOR_ACX100;
+                       }
+                       if (strcmp(priv[i].name, "prismhdr") == 0) {
+                               /*
+                                * One of the RTL8xxx drivers, use this one.
+                                * It can only be done after monitor mode
+                                * has been turned on.  You can set it to 1
+                                * to get DLT_PRISM or 0 to get DLT_IEEE80211.
+                                */
+                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
+                                       break;
+                               if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
+                                       break;
+                               if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
+                                       break;
+                               montype = MONITOR_RTL8XXX;
                                cmd = priv[i].cmd;
                                break;
-
-                       default:
+                       }
+                       if (strcmp(priv[i].name, "rfmontx") == 0) {
+                               /*
+                                * RT2500 or RT61 driver, use this one.
+                                * It has one one-byte parameter; set
+                                * u.data.length to 1 and u.data.pointer to
+                                * point to the parameter.
+                                * It doesn't itself turn monitor mode on.
+                                * You can set it to 1 to allow transmitting
+                                * in monitor mode(?) and get DLT_IEEE80211,
+                                * or set it to 0 to disallow transmitting in
+                                * monitor mode(?) and get DLT_PRISM.
+                                */
+                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
+                                       break;
+                               if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 2)
+                                       break;
+                               montype = MONITOR_RT2500;
+                               cmd = priv[i].cmd;
                                break;
                        }
+                       if (strcmp(priv[i].name, "monitor") == 0) {
+                               /*
+                                * Either ACX100 or hostap, use this one.
+                                * It turns monitor mode on.
+                                * If it takes two arguments, it's ACX100;
+                                * the first argument is 1 for DLT_PRISM
+                                * or 2 for DLT_IEEE80211, and the second
+                                * argument is the channel on which to
+                                * run.  If it takes one argument, it's
+                                * HostAP, and the argument is 2 for
+                                * DLT_IEEE80211 and 3 for DLT_PRISM.
+                                *
+                                * If we see this, we don't quit, as this
+                                * might be a version of the hostap driver
+                                * that also supports "monitor_type".
+                                */
+                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
+                                       break;
+                               if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
+                                       break;
+                               switch (priv[i].set_args & IW_PRIV_SIZE_MASK) {
+
+                               case 1:
+                                       montype = MONITOR_PRISM;
+                                       cmd = priv[i].cmd;
+                                       break;
+
+                               case 2:
+                                       montype = MONITOR_ACX100;
+                                       cmd = priv[i].cmd;
+                                       break;
+
+                               default:
+                                       break;
+                               }
+                       }
                }
+               free(priv);
        }
-       free(priv);
 
        /*
         * XXX - ipw3945?  islism?
@@ -3694,7 +4778,29 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
        }
 
        /*
-        * First, turn monitor mode on.
+        * First, take the interface down if it's up; otherwise, we
+        * might get EBUSY.
+        */
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
+       if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                   "%s: Can't get flags: %s", device, strerror(errno));
+               return PCAP_ERROR;
+       }
+       oldflags = 0;
+       if (ifr.ifr_flags & IFF_UP) {
+               oldflags = ifr.ifr_flags;
+               ifr.ifr_flags &= ~IFF_UP;
+               if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "%s: Can't set flags: %s", device, strerror(errno));
+                       return PCAP_ERROR;
+               }
+       }
+
+       /*
+        * Then turn monitor mode on.
         */
        strncpy(ireq.ifr_ifrn.ifrn_name, device,
            sizeof ireq.ifr_ifrn.ifrn_name);
@@ -3703,7 +4809,14 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
        if (ioctl(sock_fd, SIOCSIWMODE, &ireq) == -1) {
                /*
                 * Scientist, you've failed.
+                * Bring the interface back up if we shut it down.
                 */
+               ifr.ifr_flags = oldflags;
+               if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "%s: Can't set flags: %s", device, strerror(errno));
+                       return PCAP_ERROR;
+               }
                return PCAP_ERROR_RFMON_NOTSUP;
        }
 
@@ -3865,6 +4978,32 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
                break;
        }
 
+       /*
+        * Now bring the interface back up if we brought it down.
+        */
+       if (oldflags != 0) {
+               ifr.ifr_flags = oldflags;
+               if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "%s: Can't set flags: %s", device, strerror(errno));
+
+                       /*
+                        * At least try to restore the old mode on the
+                        * interface.
+                        */
+                       if (ioctl(handle->fd, SIOCSIWMODE, &ireq) == -1) {
+                               /*
+                                * Scientist, you've failed.
+                                */
+                               fprintf(stderr,
+                                   "Can't restore interface wireless mode (SIOCSIWMODE failed: %s).\n"
+                                   "Please adjust manually.\n",
+                                   strerror(errno));
+                       }
+                       return PCAP_ERROR;
+               }
+       }
+
        /*
         * Note that we have to put the old mode back when we
         * close the device.
@@ -3914,6 +5053,112 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device)
        return 0;
 }
 
+/*
+ * Find out if we have any form of fragmentation/reassembly offloading.
+ *
+ * We do so using SIOCETHTOOL checking for various types of offloading;
+ * if SIOCETHTOOL isn't defined, or we don't have any #defines for any
+ * of the types of offloading, there's nothing we can do to check, so
+ * we just say "no, we don't".
+ */
+#if defined(SIOCETHTOOL) && (defined(ETHTOOL_GTSO) || defined(ETHTOOL_GUFO) || defined(ETHTOOL_GGSO) || defined(ETHTOOL_GFLAGS) || defined(ETHTOOL_GGRO))
+static int
+iface_ethtool_ioctl(pcap_t *handle, int cmd, const char *cmdname)
+{
+       struct ifreq    ifr;
+       struct ethtool_value eval;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name));
+       eval.cmd = cmd;
+       ifr.ifr_data = (caddr_t)&eval;
+       if (ioctl(handle->fd, SIOCETHTOOL, &ifr) == -1) {
+               if (errno == EOPNOTSUPP || errno == EINVAL) {
+                       /*
+                        * OK, let's just return 0, which, in our
+                        * case, either means "no, what we're asking
+                        * about is not enabled" or "all the flags
+                        * are clear (i.e., nothing is enabled)".
+                        */
+                       return 0;
+               }
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                   "%s: SIOETHTOOL(%s) ioctl failed: %s", handle->opt.source,
+                   cmdname, strerror(errno));
+               return -1;
+       }
+       return eval.data;       
+}
+
+static int
+iface_get_offload(pcap_t *handle)
+{
+       int ret;
+
+#ifdef ETHTOOL_GTSO
+       ret = iface_ethtool_ioctl(handle, ETHTOOL_GTSO, "ETHTOOL_GTSO");
+       if (ret == -1)
+               return -1;
+       if (ret)
+               return 1;       /* TCP segmentation offloading on */
+#endif
+
+#ifdef ETHTOOL_GUFO
+       ret = iface_ethtool_ioctl(handle, ETHTOOL_GUFO, "ETHTOOL_GUFO");
+       if (ret == -1)
+               return -1;
+       if (ret)
+               return 1;       /* UDP fragmentation offloading on */
+#endif
+
+#ifdef ETHTOOL_GGSO
+       /*
+        * XXX - will this cause large unsegmented packets to be
+        * handed to PF_PACKET sockets on transmission?  If not,
+        * this need not be checked.
+        */
+       ret = iface_ethtool_ioctl(handle, ETHTOOL_GGSO, "ETHTOOL_GGSO");
+       if (ret == -1)
+               return -1;
+       if (ret)
+               return 1;       /* generic segmentation offloading on */
+#endif
+
+#ifdef ETHTOOL_GFLAGS
+       ret = iface_ethtool_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
+       if (ret == -1)
+               return -1;
+       if (ret & ETH_FLAG_LRO)
+               return 1;       /* large receive offloading on */
+#endif
+
+#ifdef ETHTOOL_GGRO
+       /*
+        * XXX - will this cause large reassembled packets to be
+        * handed to PF_PACKET sockets on receipt?  If not,
+        * this need not be checked.
+        */
+       ret = iface_ethtool_ioctl(handle, ETHTOOL_GGRO, "ETHTOOL_GGRO");
+       if (ret == -1)
+               return -1;
+       if (ret)
+               return 1;       /* generic (large) receive offloading on */
+#endif
+
+       return 0;
+}
+#else /* SIOCETHTOOL */
+static int
+iface_get_offload(pcap_t *handle _U_)
+{
+       /*
+        * XXX - do we need to get this information if we don't
+        * have the ethtool ioctls?  If so, how do we do that?
+        */
+       return 0;
+}
+#endif /* SIOCETHTOOL */
+
 #endif /* HAVE_PF_PACKET_SOCKETS */
 
 /* ===== Functions to interface to the older kernels ================== */
@@ -3937,7 +5182,18 @@ activate_old(pcap_t *handle)
        if (handle->fd == -1) {
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                         "socket: %s", pcap_strerror(errno));
-               return PCAP_ERROR_PERM_DENIED;
+               if (errno == EPERM || errno == EACCES) {
+                       /*
+                        * You don't have permission to open the
+                        * socket.
+                        */
+                       return PCAP_ERROR_PERM_DENIED;
+               } else {
+                       /*
+                        * Other error.
+                        */
+                       return PCAP_ERROR;
+               }
        }
 
        /* It worked - we are using the old interface */
@@ -3967,7 +5223,7 @@ activate_old(pcap_t *handle)
         * Try to find the DLT_ type corresponding to that
         * link-layer type.
         */
-       map_arphrd_to_dlt(handle, arptype, 0);
+       map_arphrd_to_dlt(handle, handle->fd, arptype, device, 0);
        if (handle->linktype == -1) {
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                         "unknown arptype %d", arptype);
@@ -4095,6 +5351,12 @@ activate_old(pcap_t *handle)
         */
        handle->offset   = 0;
 
+       /*
+        * SOCK_PACKET sockets don't supply information from
+        * stripped VLAN tags.
+        */
+       handle->md.vlan_offset = -1; /* unknown */
+
        return 1;
 }
 
@@ -4301,13 +5563,19 @@ fix_offset(struct bpf_insn *p)
                 * header.
                 */
                p->k -= SLL_HDR_LEN;
+       } else if (p->k == 0) {
+               /*
+                * It's the packet type field; map it to the special magic
+                * kernel offset for that field.
+                */
+               p->k = SKF_AD_OFF + SKF_AD_PKTTYPE;
        } else if (p->k == 14) {
                /*
                 * It's the protocol field; map it to the special magic
                 * kernel offset for that field.
                 */
                p->k = SKF_AD_OFF + SKF_AD_PROTOCOL;
-       } else {
+       } else if ((bpf_int32)(p->k) > 0) {
                /*
                 * It's within the header, but it's not one of those
                 * fields; we can't do that in the kernel, so punt