]> 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 a70d585f6c965203e4ab95a9bcb74702891e0826..e5c3afa7d86da2cc7a427444bb64cfd591299d90 100644 (file)
@@ -127,50 +127,19 @@ static const char rcsid[] _U_ =
 #include <fcntl.h>
 #include <string.h>
 #include <limits.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 <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>
 #include <dirent.h>
 
 #include <netinet/in.h>
 #include <linux/if_ether.h>
 #include <net/if_arp.h>
 #include <poll.h>
 #include <dirent.h>
 
-#ifdef HAVE_LINUX_NET_TSTAMP_H
-#include <linux/net_tstamp.h>
-#include <linux/sockios.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>
-#include <linux/sockios.h>
-#endif /* HAVE_LINUX_ETHTOOL_H */
-
 #include "pcap-int.h"
 #include "pcap/sll.h"
 #include "pcap/vlan.h"
 #include "pcap-int.h"
 #include "pcap/sll.h"
 #include "pcap/vlan.h"
@@ -199,9 +168,12 @@ static const char rcsid[] _U_ =
 #include "pcap-can-linux.h"
 #endif
 
 #include "pcap-can-linux.h"
 #endif
 
+#if PCAP_SUPPORT_CANUSB
+#include "pcap-canusb-linux.h"
+#endif
+
 #ifdef PCAP_SUPPORT_NETFILTER
 #ifdef PCAP_SUPPORT_NETFILTER
-int netfilter_platform_finddevs(pcap_if_t **alldevsp, char *err_str);
-pcap_t *nflog_create(const char *device, char *ebuf);
+#include "pcap-netfilter-linux.h"
 #endif
 
 /*
 #endif
 
 /*
@@ -268,6 +240,46 @@ pcap_t *nflog_create(const char *device, char *ebuf);
 #include <linux/filter.h>
 #endif
 
 #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
 #ifndef HAVE_SOCKLEN_T
 typedef int            socklen_t;
 #endif
@@ -306,7 +318,7 @@ typedef int         socklen_t;
 /*
  * Prototypes for internal functions and methods.
  */
 /*
  * 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
 #ifdef HAVE_PF_PACKET_SOCKETS
 static short int map_packet_type_to_sll_type(short int);
 #endif
@@ -412,8 +424,15 @@ pcap_create(const char *device, char *ebuf)
        }
 #endif
 
        }
 #endif
 
+#if PCAP_SUPPORT_CANUSB
+  if (strstr(device, "canusb")) {
+    return canusb_create(device, ebuf);
+  }
+#endif
+
 #ifdef PCAP_SUPPORT_CAN
 #ifdef PCAP_SUPPORT_CAN
-       if (strstr(device, "can") || strstr(device, "vcan")) {
+       if ((strncmp(device, "can", 3) == 0 && isdigit(device[3])) ||
+           (strncmp(device, "vcan", 4) == 0 && isdigit(device[4]))) {
                return can_create(device, ebuf);
        }
 #endif
                return can_create(device, ebuf);
        }
 #endif
@@ -855,6 +874,18 @@ added:
        nanosleep(&delay, NULL);
 #endif
 
        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.
         */
        /*
         * Now configure the monitor interface up.
         */
@@ -1064,6 +1095,7 @@ static void       pcap_cleanup_linux( pcap_t *handle )
        int ret;
 #endif /* HAVE_LIBNL */
 #ifdef IW_MODE_MONITOR
        int ret;
 #endif /* HAVE_LIBNL */
 #ifdef IW_MODE_MONITOR
+       int oldflags;
        struct iwreq ireq;
 #endif /* IW_MODE_MONITOR */
 
        struct iwreq ireq;
 #endif /* IW_MODE_MONITOR */
 
@@ -1087,10 +1119,10 @@ static void     pcap_cleanup_linux( pcap_t *handle )
                            sizeof(ifr.ifr_name));
                        if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) {
                                fprintf(stderr,
                            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",
                                    "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) {
                                        /*
                        } else {
                                if (ifr.ifr_flags & IFF_PROMISC) {
                                        /*
@@ -1101,9 +1133,10 @@ static void      pcap_cleanup_linux( pcap_t *handle )
                                        if (ioctl(handle->fd, SIOCSIFFLAGS,
                                            &ifr) == -1) {
                                                fprintf(stderr,
                                        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",
                                                    "Please adjust manually.\n"
                                                    "Hint: This can't happen with Linux >= 2.2.0.\n",
+                                                   handle->md.device,
                                                    strerror(errno));
                                        }
                                }
                                                    strerror(errno));
                                        }
                                }
@@ -1137,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.
                         */
                         * 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]
                        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]
@@ -1147,9 +1203,23 @@ static void      pcap_cleanup_linux( pcap_t *handle )
                                 * Scientist, you've failed.
                                 */
                                fprintf(stderr,
                                 * 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",
                                    "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 */
                        }
                }
 #endif /* IW_MODE_MONITOR */
@@ -1550,32 +1620,40 @@ 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)
        }
 
 #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;
+
+                       bp -= VLAN_TAG_LEN;
+                       memmove(bp, bp + VLAN_TAG_LEN, handle->md.vlan_offset);
 
 
-               tag = (struct vlan_tag *)(bp + 2 * ETH_ALEN);
-               tag->vlan_tpid = htons(ETH_P_8021Q);
-               tag->vlan_tci = htons(aux->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;
+                       packet_len += VLAN_TAG_LEN;
+               }
        }
 #endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
 #endif /* HAVE_PF_PACKET_SOCKETS */
        }
 #endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
 #endif /* HAVE_PF_PACKET_SOCKETS */
@@ -1883,6 +1961,8 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf)
        DIR *sys_class_net_d;
        int fd;
        struct dirent *ent;
        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;
        char *p;
        char name[512]; /* XXX - pick a size */
        char *q, *saveq;
@@ -1890,8 +1970,20 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf)
        int ret = 1;
 
        sys_class_net_d = opendir("/sys/class/net");
        int ret = 1;
 
        sys_class_net_d = opendir("/sys/class/net");
-       if (sys_class_net_d == NULL && errno == ENOENT)
-               return (0);
+       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.
 
        /*
         * Create a socket from which to fetch interface information.
@@ -1915,10 +2007,41 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf)
                }
 
                /*
                }
 
                /*
-                * Ignore directories (".", "..", and any subdirectories).
+                * 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.)
                 */
                 */
-               if (ent->d_type == DT_DIR)
+               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;
                        continue;
+               }
 
                /*
                 * Get the interface name.
 
                /*
                 * Get the interface name.
@@ -2024,8 +2147,20 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf)
        int ret = 0;
 
        proc_net_f = fopen("/proc/net/dev", "r");
        int ret = 0;
 
        proc_net_f = fopen("/proc/net/dev", "r");
-       if (proc_net_f == NULL && errno == ENOENT)
-               return (0);
+       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.
 
        /*
         * Create a socket from which to fetch interface information.
@@ -2218,6 +2353,11 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf)
                return (-1);
 #endif
 
                return (-1);
 #endif
 
+#if PCAP_SUPPORT_CANUSB
+  if (canusb_platform_finddevs(alldevsp, errbuf) < 0)
+    return (-1);
+#endif
+
        return (0);
 }
 
        return (0);
 }
 
@@ -2447,6 +2587,51 @@ map_packet_type_to_sll_type(short int sll_pkttype)
 }
 #endif
 
 }
 #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
 /*
  *  Linux uses the ARP hardware type to identify the type of an
  *  interface. pcap uses the DLT_xxx constants for this. This
@@ -2465,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.
  */
  *
  *  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:
                /*
        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
                 * 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
@@ -2480,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).
                 *
                 * 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()",
                 * 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?
                 */
                 * 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 */
 
                }
                /* FALLTHROUGH */
 
@@ -2804,9 +3015,28 @@ activate_new(pcap_t *handle)
                socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
 
        if (sock_fd == -1) {
                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) );
                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. */
        }
 
        /* It seems the kernel supports the new interface. */
@@ -2876,7 +3106,7 @@ activate_new(pcap_t *handle)
                        close(sock_fd);
                        return arptype;
                }
                        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 ||
                if (handle->linktype == -1 ||
                    handle->linktype == DLT_LINUX_SLL ||
                    handle->linktype == DLT_LINUX_IRDA ||
@@ -2903,7 +3133,18 @@ activate_new(pcap_t *handle)
                        if (sock_fd == -1) {
                                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                                    "socket: %s", pcap_strerror(errno));
                        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;
 
                        }
                        handle->md.cooked = 1;
 
@@ -3054,6 +3295,24 @@ activate_new(pcap_t *handle)
        }
        handle->bufsize = handle->snapshot;
 
        }
        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;
 
        /* Save the socket FD in the pcap structure */
        handle->fd = sock_fd;
 
@@ -3792,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 "
                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;
                }
                                tp_mac, tp_snaplen, handle->bufsize);
                        return -1;
                }
@@ -3893,14 +4152,20 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback,
                }
 
 #ifdef HAVE_TPACKET2
                }
 
 #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;
                        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);
 
                        tag->vlan_tpid = htons(ETH_P_8021Q);
                        tag->vlan_tci = htons(h.h2->tp_vlan_tci);
 
@@ -4195,6 +4460,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
        monitor_type montype;
        int i;
        __u32 cmd;
        monitor_type montype;
        int i;
        __u32 cmd;
+       struct ifreq ifr;
+       int oldflags;
        int args[2];
        int channel;
 
        int args[2];
        int channel;
 
@@ -4204,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 */
        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.
        /*
         * Try to get all the Wireless Extensions private ioctls
         * supported by this device.
@@ -4227,187 +4501,189 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
                    device);
                return PCAP_ERROR;
        }
                    device);
                return PCAP_ERROR;
        }
-       if (errno == EOPNOTSUPP) {
+       if (errno != EOPNOTSUPP) {
                /*
                /*
-                * No private ioctls, so we assume that there's only one
-                * DLT_ for monitor mode.
+                * OK, it's not as if there are no private ioctls.
                 */
                 */
-               return 0;
-       }
-       if (errno != E2BIG) {
-               /*
-                * Failed.
-                */
-               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;
                                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;
                                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;
                                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;
                                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;
                                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;
                        }
                                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?
 
        /*
         * XXX - ipw3945?  islism?
@@ -4502,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);
         */
        strncpy(ireq.ifr_ifrn.ifrn_name, device,
            sizeof ireq.ifr_ifrn.ifrn_name);
@@ -4511,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.
        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;
        }
 
                return PCAP_ERROR_RFMON_NOTSUP;
        }
 
@@ -4673,6 +4978,32 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
                break;
        }
 
                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.
        /*
         * Note that we have to put the old mode back when we
         * close the device.
@@ -4724,8 +5055,13 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device)
 
 /*
  * Find out if we have any form of fragmentation/reassembly offloading.
 
 /*
  * 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".
  */
  */
-#ifdef SIOCETHTOOL
+#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)
 {
 static int
 iface_ethtool_ioctl(pcap_t *handle, int cmd, const char *cmdname)
 {
@@ -4737,7 +5073,7 @@ iface_ethtool_ioctl(pcap_t *handle, int cmd, const char *cmdname)
        eval.cmd = cmd;
        ifr.ifr_data = (caddr_t)&eval;
        if (ioctl(handle->fd, SIOCETHTOOL, &ifr) == -1) {
        eval.cmd = cmd;
        ifr.ifr_data = (caddr_t)&eval;
        if (ioctl(handle->fd, SIOCETHTOOL, &ifr) == -1) {
-               if (errno == EOPNOTSUPP) {
+               if (errno == EOPNOTSUPP || errno == EINVAL) {
                        /*
                         * OK, let's just return 0, which, in our
                         * case, either means "no, what we're asking
                        /*
                         * OK, let's just return 0, which, in our
                         * case, either means "no, what we're asking
@@ -4759,18 +5095,23 @@ iface_get_offload(pcap_t *handle)
 {
        int ret;
 
 {
        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 */
        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 */
        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,
        /*
         * XXX - will this cause large unsegmented packets to be
         * handed to PF_PACKET sockets on transmission?  If not,
@@ -4781,6 +5122,7 @@ iface_get_offload(pcap_t *handle)
                return -1;
        if (ret)
                return 1;       /* generic segmentation offloading on */
                return -1;
        if (ret)
                return 1;       /* generic segmentation offloading on */
+#endif
 
 #ifdef ETHTOOL_GFLAGS
        ret = iface_ethtool_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
 
 #ifdef ETHTOOL_GFLAGS
        ret = iface_ethtool_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
@@ -4840,7 +5182,18 @@ activate_old(pcap_t *handle)
        if (handle->fd == -1) {
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                         "socket: %s", pcap_strerror(errno));
        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 */
        }
 
        /* It worked - we are using the old interface */
@@ -4870,7 +5223,7 @@ activate_old(pcap_t *handle)
         * Try to find the DLT_ type corresponding to that
         * link-layer type.
         */
         * 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);
        if (handle->linktype == -1) {
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
                         "unknown arptype %d", arptype);
@@ -4998,6 +5351,12 @@ activate_old(pcap_t *handle)
         */
        handle->offset   = 0;
 
         */
        handle->offset   = 0;
 
+       /*
+        * SOCK_PACKET sockets don't supply information from
+        * stripped VLAN tags.
+        */
+       handle->md.vlan_offset = -1; /* unknown */
+
        return 1;
 }
 
        return 1;
 }
 
@@ -5204,13 +5563,19 @@ fix_offset(struct bpf_insn *p)
                 * header.
                 */
                p->k -= SLL_HDR_LEN;
                 * 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 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
                /*
                 * It's within the header, but it's not one of those
                 * fields; we can't do that in the kernel, so punt