]> The Tcpdump Group git mirrors - libpcap/blobdiff - pcap-linux.c
Merge pull request #358 from bonsaiviking/config-packet-ring
[libpcap] / pcap-linux.c
index f42f23c4fd7b944b8ab7f041492f99995f65d760..74d6b43b08a8454aeda529dd59baa33267edb63c 100644 (file)
@@ -368,6 +368,12 @@ static void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h,
     const u_char *bytes);
 #endif
 
+#ifdef TP_STATUS_VLAN_TPID_VALID
+# define VLAN_TPID(hdr, hv)    (((hv)->tp_vlan_tpid || ((hdr)->tp_status & TP_STATUS_VLAN_TPID_VALID)) ? (hv)->tp_vlan_tpid : ETH_P_8021Q)
+#else
+# define VLAN_TPID(hdr, hv)    ETH_P_8021Q
+#endif
+
 /*
  * Wrap some ioctl calls
  */
@@ -384,6 +390,9 @@ 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 */
+#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
+static int     iface_ethtool_get_ts_info(pcap_t *handle, char *ebuf);
+#endif
 static int     iface_get_offload(pcap_t *handle);
 static int     iface_bind_old(int fd, const char *device, char *ebuf);
 
@@ -411,28 +420,15 @@ pcap_create_interface(const char *device, char *ebuf)
 
        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.
+        * See what time stamp types we support.
         */
-       handle->tstamp_type_count = 3;
-       handle->tstamp_type_list = malloc(3 * sizeof(u_int));
-       if (handle->tstamp_type_list == NULL) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
-                   pcap_strerror(errno));
+       if (iface_ethtool_get_ts_info(handle, ebuf) == -1) {
                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
 
 #if defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS)
@@ -1496,6 +1492,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
        int                     packet_len, caplen;
        struct pcap_pkthdr      pcap_header;
 
+        struct bpf_aux_data     aux_data;
 #ifdef HAVE_PF_PACKET_SOCKETS
        /*
         * If this is a cooked device, leave extra room for a
@@ -1676,9 +1673,14 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
                        memmove(bp, bp + VLAN_TAG_LEN, handlep->vlan_offset);
 
                        tag = (struct vlan_tag *)(bp + handlep->vlan_offset);
-                       tag->vlan_tpid = htons(ETH_P_8021Q);
+                       tag->vlan_tpid = htons(VLAN_TPID(aux, aux));
                        tag->vlan_tci = htons(aux->tp_vlan_tci);
 
+                        /* store vlan tci to bpf_aux_data struct for userland bpf filter */
+#if defined(TP_STATUS_VLAN_VALID)
+                        aux_data.vlan_tag = htons(aux->tp_vlan_tci) & 0x0fff;
+                        aux_data.vlan_tag_present = (aux->tp_status & TP_STATUS_VLAN_VALID);
+#endif
                        packet_len += VLAN_TAG_LEN;
                }
        }
@@ -1712,8 +1714,8 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
         *
         * We currently handle this by making a copy of the filter
         * program, fixing all "ret" instructions with non-zero
-        * operands to have an operand of 65535 so that the filter
-        * doesn't truncate the packet, and supplying that modified
+        * operands to have an operand of MAXIMUM_SNAPLEN so that the
+        * filter doesn't truncate the packet, and supplying that modified
         * filter to the kernel.
         */
 
@@ -1723,9 +1725,8 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
 
        /* Run the packet filter if not using kernel filter */
        if (handlep->filter_in_userland && handle->fcode.bf_insns) {
-               if (bpf_filter(handle->fcode.bf_insns, bp,
-                               packet_len, caplen) == 0)
-               {
+               if (bpf_filter_with_aux_data(handle->fcode.bf_insns, bp,
+                   packet_len, caplen, &aux_data) == 0) {
                        /* rejected by filter */
                        return 0;
                }
@@ -2321,7 +2322,8 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf)
        /*
         * Add the "any" device.
         */
-       if (pcap_add_if(alldevsp, "any", 0, any_descr, errbuf) < 0)
+       if (pcap_add_if(alldevsp, "any", IFF_UP|IFF_RUNNING,
+           any_descr, errbuf) < 0)
                return (-1);
 
        return (0);
@@ -2387,12 +2389,13 @@ pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter,
                 * of different size. Pointed out by Sebastian
                 *
                 * Oh, and we also need to fix it up so that all "ret"
-                * instructions with non-zero operands have 65535 as the
-                * operand if we're not capturing in memory-mapped modee,
-                * and so that, if we're in cooked mode, all memory-reference
-                * instructions use special magic offsets in references to
-                * the link-layer header and assume that the link-layer
-                * payload begins at 0; "fix_program()" will do that.
+                * instructions with non-zero operands have MAXIMUM_SNAPLEN
+                * as the operand if we're not capturing in memory-mapped
+                * mode, and so that, if we're in cooked mode, all memory-
+                * reference instructions use special magic offsets in
+                * references to the link-layer header and assume that the
+                * link-layer payload begins at 0; "fix_program()" will do
+                * that.
                 */
                switch (fix_program(handle, &fcode, is_mmapped)) {
 
@@ -2478,8 +2481,14 @@ pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter,
         * calling "pcap_setfilter()".  Otherwise, the kernel filter may
         * filter out packets that would pass the new userland filter.
         */
-       if (handlep->filter_in_userland)
-               reset_kernel_filter(handle);
+       if (handlep->filter_in_userland) {
+               if (reset_kernel_filter(handle) == -1) {
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "can't remove kernel filter: %s",
+                           pcap_strerror(errno));
+                       err = -2;       /* fatal error */
+               }
+       }
 
        /*
         * Free up the copy of the filter that was made by "fix_program()".
@@ -3014,6 +3023,10 @@ activate_new(pcap_t *handle)
 #endif
        int                     err = 0;
        struct packet_mreq      mr;
+#ifdef SO_BPF_EXTENSIONS
+       int                     bpf_extensions;
+       socklen_t               len = sizeof(bpf_extensions);
+#endif
 
        /*
         * Open a socket with protocol family packet. If the
@@ -3344,6 +3357,23 @@ activate_new(pcap_t *handle)
         */
        handle->fd = sock_fd;
 
+#ifdef SO_BPF_EXTENSIONS
+       /*
+        * Can we generate special code for VLAN checks?
+        * (XXX - what if we need the special code but it's not supported
+        * by the OS?  Is that possible?)
+        */
+       if (getsockopt(sock_fd, SOL_SOCKET, SO_BPF_EXTENSIONS,
+           &bpf_extensions, &len) == 0) {
+               if (bpf_extensions >= SKF_AD_VLAN_TAG_PRESENT) {
+                       /*
+                        * Yes, we can.  Request that we do so.
+                        */
+                       handle->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING;
+               }
+       }
+#endif /* SO_BPF_EXTENSIONS */
+
        return 1;
 #else /* HAVE_PF_PACKET_SOCKETS */
        strlcpy(ebuf,
@@ -3732,10 +3762,16 @@ create_ring(pcap_t *handle, int *status)
                 * We pick a "frame" size of 128K to leave enough
                 * room for at least one reasonably-sized packet
                 * in the "frame". */
-               req.tp_frame_size = 131072;
+               req.tp_frame_size = MAXIMUM_SNAPLEN;
                req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size;
                break;
 #endif
+       default:
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                   "Internal error: unknown TPACKET_ value %u",
+                   handlep->tp_version);
+               *status = PCAP_ERROR;
+               return -1;
        }
 
        /* compute the minumum block size that will handle this frame. 
@@ -3949,13 +3985,14 @@ destroy_ring(pcap_t *handle)
        /* tell the kernel to destroy the ring*/
        struct tpacket_req req;
        memset(&req, 0, sizeof(req));
-       setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING,
+       /* do not test for setsockopt failure, as we can't recover from any error */
+       (void)setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING,
                                (void *) &req, sizeof(req));
 
        /* if ring is mapped, unmap it*/
        if (handlep->mmapbuf) {
                /* do not test for mmap failure, as we can't recover from any error */
-               munmap(handlep->mmapbuf, handlep->mmapbuflen);
+               (void)munmap(handlep->mmapbuf, handlep->mmapbuflen);
                handlep->mmapbuf = NULL;
        }
 }
@@ -4018,6 +4055,13 @@ pcap_setnonblock_mmap(pcap_t *p, int nonblock, char *errbuf)
 {
        struct pcap_linux *handlep = p->priv;
 
+       /*
+        * Set the file descriptor to non-blocking mode, as we use
+        * it for sending packets.
+        */
+       if (pcap_setnonblock_fd(p, nonblock, errbuf) == -1)
+               return -1;
+
        /*
         * Map each value to their corresponding negation to
         * preserve the timeout value provided with pcap_set_timeout.
@@ -4195,7 +4239,8 @@ static int pcap_handle_packet_mmap(
                unsigned int tp_sec,
                unsigned int tp_usec,
                int tp_vlan_tci_valid,
-               __u16 tp_vlan_tci)
+               __u16 tp_vlan_tci,
+               __u16 tp_vlan_tpid)
 {
        struct pcap_linux *handlep = handle->priv;
        unsigned char *bp;
@@ -4206,7 +4251,7 @@ static int pcap_handle_packet_mmap(
        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;
        }
@@ -4221,22 +4266,9 @@ static int pcap_handle_packet_mmap(
         * the filter when the ring became empty, but it can possibly
         * happen a lot later... */
        bp = frame + tp_mac;
-       if (handlep->filter_in_userland && handle->fcode.bf_insns &&
-                       (bpf_filter(handle->fcode.bf_insns, bp,
-                               tp_len, tp_snaplen) == 0))
-               return 0;
-
-       sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen);
-       if (!linux_check_direction(handle, sll))
-               return 0;
-
-       /* get required packet info from ring header */
-       pcaphdr.ts.tv_sec = tp_sec;
-       pcaphdr.ts.tv_usec = tp_usec;
-       pcaphdr.caplen = tp_snaplen;
-       pcaphdr.len = tp_len;
 
        /* if required build in place the sll header*/
+       sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen);
        if (handlep->cooked) {
                struct sll_header *hdrp;
 
@@ -4249,7 +4281,7 @@ static int pcap_handle_packet_mmap(
                 */
                bp -= SLL_HDR_LEN;
 
-               /*/*
+               /*
                 * Let's make sure that's past the end of
                 * the tpacket header, i.e. >=
                 * ((u_char *)thdr + TPACKET_HDRLEN), so we
@@ -4274,7 +4306,30 @@ static int pcap_handle_packet_mmap(
                hdrp->sll_halen = htons(sll->sll_halen);
                memcpy(hdrp->sll_addr, sll->sll_addr, SLL_ADDRLEN);
                hdrp->sll_protocol = sll->sll_protocol;
+       }
+
+       if (handlep->filter_in_userland && handle->fcode.bf_insns) {
+               struct bpf_aux_data aux_data;
+
+               aux_data.vlan_tag = tp_vlan_tci & 0x0fff;
+               aux_data.vlan_tag_present = tp_vlan_tci_valid;
+
+               if (bpf_filter_with_aux_data(handle->fcode.bf_insns, bp,
+                   tp_len, tp_snaplen, &aux_data) == 0)
+                       return 0;
+       }
 
+       if (!linux_check_direction(handle, sll))
+               return 0;
+
+       /* get required packet info from ring header */
+       pcaphdr.ts.tv_sec = tp_sec;
+       pcaphdr.ts.tv_usec = tp_usec;
+       pcaphdr.caplen = tp_snaplen;
+       pcaphdr.len = tp_len;
+
+       /* if required build in place the sll header*/
+       if (handlep->cooked) {
                /* update packet len */
                pcaphdr.caplen += SLL_HDR_LEN;
                pcaphdr.len += SLL_HDR_LEN;
@@ -4291,7 +4346,7 @@ static int pcap_handle_packet_mmap(
                memmove(bp, bp + VLAN_TAG_LEN, handlep->vlan_offset);
 
                tag = (struct vlan_tag *)(bp + handlep->vlan_offset);
-               tag->vlan_tpid = htons(ETH_P_8021Q);
+               tag->vlan_tpid = htons(tp_vlan_tpid);
                tag->vlan_tci = htons(tp_vlan_tci);
 
                pcaphdr.caplen += VLAN_TAG_LEN;
@@ -4351,6 +4406,7 @@ pcap_read_linux_mmap_v1(pcap_t *handle, int max_packets, pcap_handler callback,
                                h.h1->tp_sec,
                                h.h1->tp_usec,
                                0,
+                               0,
                                0);
                if (ret == 1) {
                        pkts++;
@@ -4429,7 +4485,8 @@ pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback,
 #else
                                h.h2->tp_vlan_tci != 0,
 #endif
-                               h.h2->tp_vlan_tci);
+                               h.h2->tp_vlan_tci,
+                               VLAN_TPID(h.h2, h.h2));
                if (ret == 1) {
                        pkts++;
                        handlep->packets_read++;
@@ -4530,7 +4587,8 @@ again:
 #else
                                        tp3_hdr->hv1.tp_vlan_tci != 0,
 #endif
-                                       tp3_hdr->hv1.tp_vlan_tci);
+                                       tp3_hdr->hv1.tp_vlan_tci,
+                                       VLAN_TPID(tp3_hdr, &tp3_hdr->hv1));
                        if (ret == 1) {
                                pkts++;
                                handlep->packets_read++;
@@ -5445,6 +5503,120 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device)
        return 0;
 }
 
+#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
+/*
+ * Map SOF_TIMESTAMPING_ values to PCAP_TSTAMP_ values.
+ */
+static const struct {
+       int soft_timestamping_val;
+       int pcap_tstamp_val;
+} sof_ts_type_map[3] = {
+       { SOF_TIMESTAMPING_SOFTWARE, PCAP_TSTAMP_HOST },
+       { SOF_TIMESTAMPING_SYS_HARDWARE, PCAP_TSTAMP_ADAPTER },
+       { SOF_TIMESTAMPING_RAW_HARDWARE, PCAP_TSTAMP_ADAPTER_UNSYNCED }
+};
+#define NUM_SOF_TIMESTAMPING_TYPES     (sizeof sof_ts_type_map / sizeof sof_ts_type_map[0])
+
+static void
+iface_set_default_ts_types(pcap_t *handle)
+{
+       int i;
+
+       handle->tstamp_type_count = NUM_SOF_TIMESTAMPING_TYPES;
+       handle->tstamp_type_list = malloc(NUM_SOF_TIMESTAMPING_TYPES * sizeof(u_int));
+       for (i = 0; i < NUM_SOF_TIMESTAMPING_TYPES; i++)
+               handle->tstamp_type_list[i] = sof_ts_type_map[i].pcap_tstamp_val;
+}
+
+#ifdef ETHTOOL_GET_TS_INFO
+/*
+ * Get a list of time stamping capabilities.
+ */
+static int
+iface_ethtool_get_ts_info(pcap_t *handle, char *ebuf)
+{
+       int fd;
+       struct ifreq ifr;
+       struct ethtool_ts_info info;
+       int num_ts_types;
+       int i, j;
+
+       /*
+        * This doesn't apply to the "any" device; you have to ask
+        * specific devices for their capabilities, so just default
+        * to saying we support all of them.
+        */
+       if (strcmp(handle->opt.source, "any") == 0) {
+               iface_set_default_ts_types(handle);
+               return 0;
+       }
+
+       /*
+        * Create a socket from which to fetch time stamping capabilities.
+        */
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
+                   "socket for SIOCETHTOOL(ETHTOOL_GET_TS_INFO): %s", pcap_strerror(errno));
+               return -1;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       strlcpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name));
+       memset(&info, 0, sizeof(info));
+       info.cmd = ETHTOOL_GET_TS_INFO;
+       ifr.ifr_data = (caddr_t)&info;
+       if (ioctl(fd, SIOCETHTOOL, &ifr) == -1) {
+               close(fd);
+               if (errno == EOPNOTSUPP || errno == EINVAL) {
+                       /*
+                        * OK, let's just return all the possible time
+                        * stamping types.
+                        */
+                       iface_set_default_ts_types(handle);
+                       return 0;
+               }
+               snprintf(ebuf, PCAP_ERRBUF_SIZE,
+                   "%s: SIOCETHTOOL(ETHTOOL_GET_TS_INFO) ioctl failed: %s", handle->opt.source,
+                   strerror(errno));
+               return -1;
+       }
+       close(fd);
+
+       num_ts_types = 0;
+       for (i = 0; i < NUM_SOF_TIMESTAMPING_TYPES; i++) {
+               if (info.so_timestamping & sof_ts_type_map[i].soft_timestamping_val)
+                       num_ts_types++;
+       }
+       handle->tstamp_type_count = num_ts_types;
+       if (num_ts_types != 0) {
+               handle->tstamp_type_list = malloc(num_ts_types * sizeof(u_int));
+               for (i = 0, j = 0; i < NUM_SOF_TIMESTAMPING_TYPES; i++) {
+                       if (info.so_timestamping & sof_ts_type_map[i].soft_timestamping_val) {
+                               handle->tstamp_type_list[j] = sof_ts_type_map[i].pcap_tstamp_val;
+                               j++;
+                       }
+               }
+       } else
+               handle->tstamp_type_list = NULL;
+
+       return 0;
+}
+#else /* ETHTOOL_GET_TS_INFO */
+static int
+iface_ethtool_get_ts_info(pcap_t *handle, char *ebuf _U_)
+{
+       /*
+        * We don't have an ioctl to use to ask what's supported,
+        * so say we support everything.
+        */
+       iface_set_default_ts_types(handle);
+       return 0;
+}
+#endif /* ETHTOOL_GET_TS_INFO */
+
+#endif /* defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) */
+
 /*
  * Find out if we have any form of fragmentation/reassembly offloading.
  *
@@ -5455,7 +5627,7 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device)
  */
 #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)
+iface_ethtool_flag_ioctl(pcap_t *handle, int cmd, const char *cmdname)
 {
        struct ifreq    ifr;
        struct ethtool_value eval;
@@ -5476,7 +5648,7 @@ iface_ethtool_ioctl(pcap_t *handle, int cmd, const char *cmdname)
                        return 0;
                }
                snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   "%s: SIOETHTOOL(%s) ioctl failed: %s", handle->opt.source,
+                   "%s: SIOCETHTOOL(%s) ioctl failed: %s", handle->opt.source,
                    cmdname, strerror(errno));
                return -1;
        }
@@ -5489,7 +5661,7 @@ iface_get_offload(pcap_t *handle)
        int ret;
 
 #ifdef ETHTOOL_GTSO
-       ret = iface_ethtool_ioctl(handle, ETHTOOL_GTSO, "ETHTOOL_GTSO");
+       ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GTSO, "ETHTOOL_GTSO");
        if (ret == -1)
                return -1;
        if (ret)
@@ -5497,7 +5669,7 @@ iface_get_offload(pcap_t *handle)
 #endif
 
 #ifdef ETHTOOL_GUFO
-       ret = iface_ethtool_ioctl(handle, ETHTOOL_GUFO, "ETHTOOL_GUFO");
+       ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GUFO, "ETHTOOL_GUFO");
        if (ret == -1)
                return -1;
        if (ret)
@@ -5510,7 +5682,7 @@ iface_get_offload(pcap_t *handle)
         * handed to PF_PACKET sockets on transmission?  If not,
         * this need not be checked.
         */
-       ret = iface_ethtool_ioctl(handle, ETHTOOL_GGSO, "ETHTOOL_GGSO");
+       ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GGSO, "ETHTOOL_GGSO");
        if (ret == -1)
                return -1;
        if (ret)
@@ -5518,7 +5690,7 @@ iface_get_offload(pcap_t *handle)
 #endif
 
 #ifdef ETHTOOL_GFLAGS
-       ret = iface_ethtool_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
+       ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
        if (ret == -1)
                return -1;
        if (ret & ETH_FLAG_LRO)
@@ -5531,7 +5703,7 @@ iface_get_offload(pcap_t *handle)
         * handed to PF_PACKET sockets on receipt?  If not,
         * this need not be checked.
         */
-       ret = iface_ethtool_ioctl(handle, ETHTOOL_GGRO, "ETHTOOL_GGRO");
+       ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GGRO, "ETHTOOL_GGRO");
        if (ret == -1)
                return -1;
        if (ret)
@@ -5892,18 +6064,19 @@ fix_program(pcap_t *handle, struct sock_fprog *fcode, int is_mmapped)
                                         * Yes - if the value to be returned,
                                         * i.e. the snapshot length, is
                                         * anything other than 0, make it
-                                        * 65535, so that the packet is
-                                        * truncated by "recvfrom()",
+                                        * MAXIMUM_SNAPLEN, so that the packet
+                                        * is truncated by "recvfrom()",
                                         * not by the filter.
                                         *
                                         * XXX - there's nothing we can
                                         * easily do if it's getting the
                                         * value from the accumulator; we'd
                                         * have to insert code to force
-                                        * non-zero values to be 65535.
+                                        * non-zero values to be
+                                        * MAXIMUM_SNAPLEN.
                                         */
                                        if (p->k != 0)
-                                               p->k = 65535;
+                                               p->k = MAXIMUM_SNAPLEN;
                                }
                        }
                        break;
@@ -6036,20 +6209,40 @@ set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode)
                 * "nothing more to be read" error).
                 */
                save_mode = fcntl(handle->fd, F_GETFL, 0);
-               if (save_mode != -1 &&
-                   fcntl(handle->fd, F_SETFL, save_mode | O_NONBLOCK) >= 0) {
-                       while (recv(handle->fd, &drain, sizeof drain,
-                              MSG_TRUNC) >= 0)
-                               ;
-                       save_errno = errno;
-                       fcntl(handle->fd, F_SETFL, save_mode);
-                       if (save_errno != EAGAIN) {
-                               /* Fatal error */
-                               reset_kernel_filter(handle);
-                               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
-                                "recv: %s", pcap_strerror(save_errno));
-                               return -2;
-                       }
+               if (save_mode == -1) {
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "can't get FD flags when changing filter: %s",
+                           pcap_strerror(errno));
+                       return -2;
+               }
+               if (fcntl(handle->fd, F_SETFL, save_mode | O_NONBLOCK) < 0) {
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "can't set nonblocking mode when changing filter: %s",
+                           pcap_strerror(errno));
+                       return -2;
+               }
+               while (recv(handle->fd, &drain, sizeof drain, MSG_TRUNC) >= 0)
+                       ;
+               save_errno = errno;
+               if (save_errno != EAGAIN) {
+                       /*
+                        * Fatal error.
+                        *
+                        * If we can't restore the mode or reset the
+                        * kernel filter, there's nothing we can do.
+                        */
+                       (void)fcntl(handle->fd, F_SETFL, save_mode);
+                       (void)reset_kernel_filter(handle);
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "recv failed when changing filter: %s",
+                           pcap_strerror(save_errno));
+                       return -2;
+               }
+               if (fcntl(handle->fd, F_SETFL, save_mode) == -1) {
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "can't restore FD flags when changing filter: %s",
+                           pcap_strerror(save_errno));
+                       return -2;
                }
        }
 
@@ -6072,11 +6265,16 @@ set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode)
                save_errno = errno;
 
                /*
-                * XXX - if this fails, we're really screwed;
-                * we have the total filter on the socket,
-                * and it won't come off.  What do we do then?
+                * If this fails, we're really screwed; we have the
+                * total filter on the socket, and it won't come off.
+                * Report it as a fatal error.
                 */
-               reset_kernel_filter(handle);
+               if (reset_kernel_filter(handle) == -1) {
+                       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "can't remove kernel total filter: %s",
+                           pcap_strerror(errno));
+                       return -2;      /* fatal error */
+               }
 
                errno = save_errno;
        }