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
*/
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);
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)
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
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;
}
}
*
* 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.
*/
/* 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;
}
/*
* 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);
* 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)) {
* 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()".
#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
*/
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,
* 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.
/* 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;
}
}
{
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.
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;
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;
}
* 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;
*/
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
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;
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;
h.h1->tp_sec,
h.h1->tp_usec,
0,
+ 0,
0);
if (ret == 1) {
pkts++;
#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++;
#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++;
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.
*
*/
#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;
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;
}
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)
#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)
* 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)
#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)
* 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)
* 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;
* "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;
}
}
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;
}