* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Modifications: Added PACKET_MMAP support
* Added TPACKET_V3 support
- *
+ *
* based on previous works of:
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
+ * notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
#define _GNU_SOURCE
#ifdef HAVE_CONFIG_H
-#include "config.h"
+#include <config.h>
#endif
#include <errno.h>
# endif /* PACKET_HOST */
- /* check for memory mapped access avaibility. We assume every needed
+ /* check for memory mapped access avaibility. We assume every needed
* struct is defined if the macro TPACKET_HDRLEN is defined, because it
* uses many ring related structs and macros */
# ifdef PCAP_SUPPORT_PACKET_RING
#include <linux/net_tstamp.h>
#endif
+#ifdef HAVE_LINUX_SOCKIOS_H
+#include <linux/sockios.h>
+#endif
+
+#ifdef HAVE_LINUX_IF_BONDING_H
+#include <linux/if_bonding.h>
+
+/*
+ * The ioctl code to use to check whether a device is a bonding device.
+ */
+#if defined(SIOCBONDINFOQUERY)
+ #define BOND_INFO_QUERY_IOCTL SIOCBONDINFOQUERY
+#elif defined(BOND_INFO_QUERY_OLD)
+ #define BOND_INFO_QUERY_IOCTL BOND_INFO_QUERY_OLD
+#endif
+#endif /* HAVE_LINUX_IF_BONDING_H */
+
/*
* Got Wireless Extensions?
*/
u_int tp_version; /* version of tpacket_hdr for mmaped ring */
u_int tp_hdrlen; /* hdrlen of tpacket_hdr for mmaped ring */
u_char *oneshot_buffer; /* buffer for copy of packet */
+ int poll_timeout; /* timeout to use in poll() */
#ifdef HAVE_TPACKET3
unsigned char *current_packet; /* Current packet within the TPACKET_V3 block. Move to next block if NULL. */
int packets_left; /* Unhandled packets left within the block from previous call to pcap_read_linux_mmap_v3 in case of TPACKET_V3. */
};
#ifdef HAVE_PACKET_RING
-#define RING_GET_FRAME(h) (((union thdr **)h->buffer)[h->offset])
+#define RING_GET_FRAME_AT(h, offset) (((union thdr **)h->buffer)[(offset)])
+#define RING_GET_CURRENT_FRAME(h) RING_GET_FRAME_AT(h, h->offset)
static void destroy_ring(pcap_t *handle);
static int create_ring(pcap_t *handle, int *status);
static int pcap_read_linux_mmap_v3(pcap_t *, int, pcap_handler , u_char *);
#endif
static int pcap_setfilter_linux_mmap(pcap_t *, struct bpf_program *);
-static int pcap_setnonblock_mmap(pcap_t *p, int nonblock, char *errbuf);
-static int pcap_getnonblock_mmap(pcap_t *p, char *errbuf);
+static int pcap_setnonblock_mmap(pcap_t *p, int nonblock);
+static int pcap_getnonblock_mmap(pcap_t *p);
static void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h,
const u_char *bytes);
#endif
static int iface_get_mtu(int fd, const char *device, char *ebuf);
static int iface_get_arptype(int fd, const char *device, char *ebuf);
#ifdef HAVE_PF_PACKET_SOCKETS
-static int iface_bind(int fd, int ifindex, char *ebuf);
+static int iface_bind(int fd, int ifindex, char *ebuf, int protocol);
#ifdef IW_MODE_MONITOR
static int has_wext(int sock_fd, const char *device, char *ebuf);
#endif /* IW_MODE_MONITOR */
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);
+static int iface_ethtool_get_ts_info(const char *device, pcap_t *handle,
+ char *ebuf);
#endif
#ifdef HAVE_PACKET_RING
static int iface_get_offload(pcap_t *handle);
{
pcap_t *handle;
- handle = pcap_create_common(device, ebuf, sizeof (struct pcap_linux));
+ handle = pcap_create_common(ebuf, sizeof (struct pcap_linux));
if (handle == NULL)
return NULL;
/*
* See what time stamp types we support.
*/
- if (iface_ethtool_get_ts_info(handle, ebuf) == -1) {
- free(handle);
+ if (iface_ethtool_get_ts_info(device, handle, ebuf) == -1) {
+ pcap_close(handle);
return NULL;
}
#endif
handle->tstamp_precision_count = 2;
handle->tstamp_precision_list = malloc(2 * sizeof(u_int));
if (handle->tstamp_precision_list == NULL) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
pcap_strerror(errno));
- if (handle->tstamp_type_list != NULL)
- free(handle->tstamp_type_list);
- free(handle);
+ pcap_close(handle);
return NULL;
}
handle->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO;
*
* Yes, you can have multiple monitor devices for a given
* physical device.
-*/
+ */
/*
* Is this a mac80211 device? If so, fill in the physical device path and
* Generate the path string for the symlink to the physical device.
*/
if (asprintf(&pathstr, "/sys/class/net/%s/phy80211", device) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: Can't generate path name string for /sys/class/net device",
device);
return PCAP_ERROR;
free(pathstr);
return 0;
}
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: Can't readlink %s: %s", device, pathstr,
strerror(errno));
free(pathstr);
state->nl_sock = nl_socket_alloc();
if (!state->nl_sock) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: failed to allocate netlink handle", device);
return PCAP_ERROR;
}
if (genl_connect(state->nl_sock)) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: failed to connect to generic netlink", device);
goto out_handle_destroy;
}
err = genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache);
if (err < 0) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: failed to allocate generic netlink cache: %s",
device, get_nl_errmsg(-err));
goto out_handle_destroy;
state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211");
if (!state->nl80211) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: nl80211 not found", device);
goto out_cache_free;
}
nl_socket_free(state->nl_sock);
}
+static int
+del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
+ const char *device, const char *mondevice);
+
static int
add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
const char *device, const char *mondevice)
{
+ struct pcap_linux *handlep = handle->priv;
int ifindex;
struct nl_msg *msg;
int err;
msg = nlmsg_alloc();
if (!msg) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: failed to allocate netlink msg", device);
return PCAP_ERROR;
}
* Real failure, not just "that device is not
* available.
*/
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: nl_send_auto_complete failed adding %s interface: %s",
device, mondevice, get_nl_errmsg(-err));
nlmsg_free(msg);
* Real failure, not just "that device is not
* available.
*/
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: nl_wait_for_ack failed adding %s interface: %s",
device, mondevice, get_nl_errmsg(-err));
nlmsg_free(msg);
* Success.
*/
nlmsg_free(msg);
+
+ /*
+ * Try to remember the monitor device.
+ */
+ handlep->mondevice = strdup(mondevice);
+ if (handlep->mondevice == NULL) {
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s",
+ pcap_strerror(errno));
+ /*
+ * Get rid of the monitor device.
+ */
+ del_mon_if(handle, sock_fd, state, device, mondevice);
+ return PCAP_ERROR;
+ }
return 1;
nla_put_failure:
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: nl_put failed adding %s interface",
device, mondevice);
nlmsg_free(msg);
msg = nlmsg_alloc();
if (!msg) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: failed to allocate netlink msg", device);
return PCAP_ERROR;
}
err = nl_send_auto_complete(state->nl_sock, msg);
if (err < 0) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: nl_send_auto_complete failed deleting %s interface: %s",
device, mondevice, get_nl_errmsg(-err));
nlmsg_free(msg);
}
err = nl_wait_for_ack(state->nl_sock);
if (err < 0) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: nl_wait_for_ack failed adding %s interface: %s",
device, mondevice, get_nl_errmsg(-err));
nlmsg_free(msg);
return 1;
nla_put_failure:
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: nl_put failed deleting %s interface",
device, mondevice);
nlmsg_free(msg);
*/
char mondevice[3+10+1]; /* mon{UINT_MAX}\0 */
- snprintf(mondevice, sizeof mondevice, "mon%u", n);
+ pcap_snprintf(mondevice, sizeof mondevice, "mon%u", n);
ret = add_mon_if(handle, sock_fd, &nlstate, device, mondevice);
if (ret == 1) {
- handlep->mondevice = strdup(mondevice);
+ /*
+ * Success. We don't clean up the libnl state
+ * yet, as we'll be using it later.
+ */
goto added;
}
if (ret < 0) {
}
}
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: No free monN interfaces", device);
nl80211_cleanup(&nlstate);
return PCAP_ERROR;
* "atexit()" failed; don't put the interface
* in rfmon mode, just give up.
*/
- return PCAP_ERROR_RFMON_NOTSUP;
+ del_mon_if(handle, sock_fd, &nlstate, device,
+ handlep->mondevice);
+ nl80211_cleanup(&nlstate);
+ return PCAP_ERROR;
}
/*
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, handlep->mondevice, sizeof(ifr.ifr_name));
if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: Can't get flags for %s: %s", device,
handlep->mondevice, strerror(errno));
del_mon_if(handle, sock_fd, &nlstate, device,
}
ifr.ifr_flags |= IFF_UP|IFF_RUNNING;
if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: Can't set flags for %s: %s", device,
handlep->mondevice, strerror(errno));
del_mon_if(handle, sock_fd, &nlstate, device,
}
#endif /* HAVE_LIBNL */
+#ifdef IW_MODE_MONITOR
+/*
+ * Bonding devices mishandle unknown ioctls; they fail with ENODEV
+ * rather than ENOTSUP, EOPNOTSUPP, or ENOTTY, so Wireless Extensions
+ * will fail with ENODEV if we try to do them on a bonding device,
+ * making us return a "no such device" indication rather than just
+ * saying "no Wireless Extensions".
+ *
+ * So we check for bonding devices, if we can, before trying those
+ * ioctls, by trying a bonding device information query ioctl to see
+ * whether it succeeds.
+ */
+static int
+is_bonding_device(int fd, const char *device)
+{
+#ifdef BOND_INFO_QUERY_IOCTL
+ struct ifreq ifr;
+ ifbond ifb;
+
+ memset(&ifr, 0, sizeof ifr);
+ strlcpy(ifr.ifr_name, device, sizeof ifr.ifr_name);
+ memset(&ifb, 0, sizeof ifb);
+ ifr.ifr_data = (caddr_t)&ifb;
+ if (ioctl(fd, BOND_INFO_QUERY_IOCTL, &ifr) == 0)
+ return 1; /* success, so it's a bonding device */
+#endif /* BOND_INFO_QUERY_IOCTL */
+
+ return 0; /* no, it's not a bonding device */
+}
+#endif /* IW_MODE_MONITOR */
+
+static int pcap_protocol(pcap_t *handle)
+{
+ int protocol;
+
+ protocol = handle->opt.protocol;
+ if (protocol == 0)
+ protocol = ETH_P_ALL;
+
+ return htons(protocol);
+}
+
static int
pcap_can_set_rfmon_linux(pcap_t *handle)
{
struct iwreq ireq;
#endif
- if (strcmp(handle->opt.source, "any") == 0) {
+ if (strcmp(handle->opt.device, "any") == 0) {
/*
* Monitor mode makes no sense on the "any" device.
*/
* wmaster device, so we don't bother checking whether
* a mac80211 device supports the Wireless Extensions.
*/
- ret = get_mac80211_phydev(handle, handle->opt.source, phydev_path,
+ ret = get_mac80211_phydev(handle, handle->opt.device, phydev_path,
PATH_MAX);
if (ret < 0)
return ret; /* error */
* (We assume that if we have Wireless Extensions support
* we also have PF_PACKET support.)
*/
- sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ sock_fd = socket(PF_PACKET, SOCK_RAW, pcap_protocol(handle));
if (sock_fd == -1) {
- (void)snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ (void)pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"socket: %s", pcap_strerror(errno));
return PCAP_ERROR;
}
+ if (is_bonding_device(sock_fd, handle->opt.device)) {
+ /* It's a bonding device, so don't even try. */
+ close(sock_fd);
+ return 0;
+ }
+
/*
* Attempt to get the current mode.
*/
- strlcpy(ireq.ifr_ifrn.ifrn_name, handle->opt.source,
+ strlcpy(ireq.ifr_ifrn.ifrn_name, handle->opt.device,
sizeof ireq.ifr_ifrn.ifrn_name);
if (ioctl(sock_fd, SIOCGIWMODE, &ireq) != -1) {
/*
}
if (errno == ENODEV) {
/* The device doesn't even exist. */
- (void)snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ (void)pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"SIOCGIWMODE failed: %s", pcap_strerror(errno));
close(sock_fd);
return PCAP_ERROR_NO_SUCH_DEVICE;
FILE * file;
int field_to_convert = 3, if_name_sz = strlen(if_name);
long int dropped_pkts = 0;
-
+
file = fopen("/proc/net/dev", "r");
if (!file)
return 0;
field_to_convert = 4;
continue;
}
-
+
/* find iface and make sure it actually matches -- space before the name and : after it */
if ((bufptr = strstr(buffer, if_name)) &&
(bufptr == buffer || *(bufptr-1) == ' ') &&
while (*bufptr != '\0' && *(bufptr++) == ' ');
while (*bufptr != '\0' && *(bufptr++) != ' ');
}
-
+
/* get rid of any final spaces */
while (*bufptr != '\0' && *bufptr == ' ') bufptr++;
-
+
if (*bufptr != '\0')
dropped_pkts = strtol(bufptr, NULL, 10);
break;
}
}
-
+
fclose(file);
return dropped_pkts;
-}
+}
/*
pcap_cleanup_live_common(handle);
}
+/*
+ * Set the timeout to be used in poll() with memory-mapped packet capture.
+ */
+static void
+set_poll_timeout(struct pcap_linux *handlep)
+{
+#ifdef HAVE_TPACKET3
+ struct utsname utsname;
+ char *version_component, *endp;
+ int major, minor;
+ int broken_tpacket_v3 = 1;
+
+ /*
+ * Some versions of TPACKET_V3 have annoying bugs/misfeatures
+ * around which we have to work. Determine if we have those
+ * problems or not.
+ */
+ if (uname(&utsname) == 0) {
+ /*
+ * 3.19 is the first release with a fixed version of
+ * TPACKET_V3. We treat anything before that as
+ * not haveing a fixed version; that may really mean
+ * it has *no* version.
+ */
+ version_component = utsname.release;
+ major = strtol(version_component, &endp, 10);
+ if (endp != version_component && *endp == '.') {
+ /*
+ * OK, that was a valid major version.
+ * Get the minor version.
+ */
+ version_component = endp + 1;
+ minor = strtol(version_component, &endp, 10);
+ if (endp != version_component &&
+ (*endp == '.' || *endp == '\0')) {
+ /*
+ * OK, that was a valid minor version.
+ * Is this 3.19 or newer?
+ */
+ if (major >= 4 || (major == 3 && minor >= 19)) {
+ /* Yes. TPACKET_V3 works correctly. */
+ broken_tpacket_v3 = 0;
+ }
+ }
+ }
+ }
+#endif
+ if (handlep->timeout == 0) {
+#ifdef HAVE_TPACKET3
+ /*
+ * XXX - due to a set of (mis)features in the TPACKET_V3
+ * kernel code prior to the 3.19 kernel, blocking forever
+ * with a TPACKET_V3 socket can, if few packets are
+ * arriving and passing the socket filter, cause most
+ * packets to be dropped. See libpcap issue #335 for the
+ * full painful story.
+ *
+ * The workaround is to have poll() time out very quickly,
+ * so we grab the frames handed to us, and return them to
+ * the kernel, ASAP.
+ */
+ if (handlep->tp_version == TPACKET_V3 && broken_tpacket_v3)
+ handlep->poll_timeout = 1; /* don't block for very long */
+ else
+#endif
+ handlep->poll_timeout = -1; /* block forever */
+ } else if (handlep->timeout > 0) {
+#ifdef HAVE_TPACKET3
+ /*
+ * For TPACKET_V3, the timeout is handled by the kernel,
+ * so block forever; that way, we don't get extra timeouts.
+ * Don't do that if we have a broken TPACKET_V3, though.
+ */
+ if (handlep->tp_version == TPACKET_V3 && !broken_tpacket_v3)
+ handlep->poll_timeout = -1; /* block forever, let TPACKET_V3 wake us up */
+ else
+#endif
+ handlep->poll_timeout = handlep->timeout; /* block for that amount of time */
+ } else {
+ /*
+ * Non-blocking mode; we call poll() to pick up error
+ * indications, but we don't want it to wait for
+ * anything.
+ */
+ handlep->poll_timeout = 0;
+ }
+}
+
/*
* Get a handle for a live capture from the given device. You can
* pass NULL as device to get all packages (without link level
int status = 0;
int ret;
- device = handle->opt.source;
+ device = handle->opt.device;
/*
* Make sure the name we were handed will fit into the ioctls we
goto fail;
}
+ /*
+ * Turn a negative snapshot value (invalid), a snapshot value of
+ * 0 (unspecified), or a value bigger than the normal maximum
+ * value, into the maximum allowed value.
+ *
+ * If some application really *needs* a bigger snapshot
+ * length, we should just increase MAXIMUM_SNAPLEN.
+ */
+ if (handle->snapshot <= 0 || handle->snapshot > MAXIMUM_SNAPLEN)
+ handle->snapshot = MAXIMUM_SNAPLEN;
+
handle->inject_op = pcap_inject_linux;
handle->setfilter_op = pcap_setfilter_linux;
handle->setdirection_op = pcap_setdirection_linux;
if (handle->opt.promisc) {
handle->opt.promisc = 0;
/* Just a warning. */
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Promiscuous mode not supported on the \"any\" device");
status = PCAP_WARNING_PROMISC_NOTSUP;
}
handlep->device = strdup(device);
if (handlep->device == NULL) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s",
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s",
pcap_strerror(errno) );
return PCAP_ERROR;
}
-
+
/* copy timeout value */
handlep->timeout = handle->opt.timeout;
/*
- * If we're in promiscuous mode, then we probably want
+ * If we're in promiscuous mode, then we probably want
* to see when the interface drops packets too, so get an
* initial count from /proc/net/dev
*/
* set to the status to return,
* which might be 0, or might be
* a PCAP_WARNING_ value.
+ *
+ * Set the timeout to use in poll() before
+ * returning.
*/
+ set_poll_timeout(handlep);
return status;
case 0:
if (setsockopt(handle->fd, SOL_SOCKET, SO_RCVBUF,
&handle->opt.buffer_size,
sizeof(handle->opt.buffer_size)) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"SO_RCVBUF: %s", pcap_strerror(errno));
status = PCAP_ERROR;
goto fail;
handle->buffer = malloc(handle->bufsize + handle->offset);
if (!handle->buffer) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"malloc: %s", pcap_strerror(errno));
status = PCAP_ERROR;
goto fail;
if (sll->sll_ifindex == handlep->lo_ifindex)
return 0;
+ /*
+ * If this is an outgoing CAN or CAN FD frame, and
+ * the user doesn't only want outgoing packets,
+ * reject it; CAN devices and drivers, and the CAN
+ * stack, always arrange to loop back transmitted
+ * packets, so they also appear as incoming packets.
+ * We don't want duplicate packets, and we can't
+ * easily distinguish packets looped back by the CAN
+ * layer than those received by the CAN layer, so we
+ * eliminate this packet instead.
+ */
+ if ((sll->sll_protocol == LINUX_SLL_P_CAN ||
+ sll->sll_protocol == LINUX_SLL_P_CANFD) &&
+ handle->direction != PCAP_D_OUT)
+ return 0;
+
/*
* If the user only wants incoming packets, reject it.
*/
* if we're using a memory-mapped buffer, we won't even
* get notified of "network down" events.
*/
- bp = handle->buffer + handle->offset;
+ bp = (u_char *)handle->buffer + handle->offset;
#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI)
msg.msg_name = &from;
* PCAP_ERROR_IFACE_NOT_UP, but pcap_dispatch()
* etc. aren't defined to return that.
*/
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"The interface went down");
return PCAP_ERROR;
default:
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"recvfrom: %s", pcap_strerror(errno));
return PCAP_ERROR;
}
#endif
continue;
- len = packet_len > iov.iov_len ? iov.iov_len : packet_len;
- if (len < (unsigned int) handlep->vlan_offset)
+ len = (u_int)packet_len > iov.iov_len ? iov.iov_len : (u_int)packet_len;
+ if (len < (u_int)handlep->vlan_offset)
break;
+ /*
+ * Move everything in the header, except the
+ * type field, down VLAN_TAG_LEN bytes, to
+ * allow us to insert the VLAN tag between
+ * that stuff and the type field.
+ */
bp -= VLAN_TAG_LEN;
memmove(bp, bp + VLAN_TAG_LEN, handlep->vlan_offset);
+ /*
+ * Now insert the tag.
+ */
tag = (struct vlan_tag *)(bp + handlep->vlan_offset);
tag->vlan_tpid = htons(VLAN_TPID(aux, aux));
tag->vlan_tci = htons(aux->tp_vlan_tci);
aux_data.vlan_tag = htons(aux->tp_vlan_tci) & 0x0fff;
aux_data.vlan_tag_present = (aux->tp_status & TP_STATUS_VLAN_VALID);
#endif
+
+ /*
+ * Add the tag to the packet lengths.
+ */
packet_len += VLAN_TAG_LEN;
}
}
#if defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS)
if (handle->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO) {
if (ioctl(handle->fd, SIOCGSTAMPNS, &pcap_header.ts) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"SIOCGSTAMPNS: %s", pcap_strerror(errno));
return PCAP_ERROR;
}
#endif
{
if (ioctl(handle->fd, SIOCGSTAMP, &pcap_header.ts) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"SIOCGSTAMP: %s", pcap_strerror(errno));
return PCAP_ERROR;
}
ret = send(handle->fd, buf, size, 0);
if (ret == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "send: %s",
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "send: %s",
pcap_strerror(errno));
return (-1);
}
return (ret);
-}
+}
/*
* Get the statistics for the given packet capture handle.
#endif /* HAVE_TPACKET_STATS */
long if_dropped = 0;
-
- /*
+
+ /*
* To fill in ps_ifdrop, we parse /proc/net/dev for the number
*/
if (handle->opt.promisc)
* dropped by the interface driver. It counts only
* packets that passed the filter.
*
- * See above for ps_ifdrop.
+ * See above for ps_ifdrop.
*
* Both statistics include packets not yet read from
* the kernel by libpcap, and thus not yet seen by
* "tp_packets" as the count of packets and "tp_drops"
* as the count of drops.
*
- * Keep a running total because each call to
+ * Keep a running total because each call to
* getsockopt(handle->fd, SOL_PACKET, PACKET_STATISTICS, ....
* resets the counters to zero.
*/
* is built on a system without "struct tpacket_stats".
*/
if (errno != EOPNOTSUPP) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"pcap_stats: %s", pcap_strerror(errno));
return -1;
}
* We maintain the count of packets processed by libpcap in
* "handlep->packets_read", for reasons described in the comment
* at the end of pcap_read_packet(). We have no idea how many
- * packets were dropped by the kernel buffers -- but we know
+ * packets were dropped by the kernel buffers -- but we know
* how many the interface dropped, so we can return that.
*/
-
+
stats->ps_recv = handlep->packets_read;
stats->ps_drop = 0;
stats->ps_ifdrop = handlep->stat.ps_ifdrop;
}
static int
-add_linux_if(pcap_if_t **devlistp, const char *ifname, int fd, char *errbuf)
+add_linux_if(pcap_if_list_t *devlistp, const char *ifname, int fd, char *errbuf)
{
const char *p;
char name[512]; /* XXX - pick a size */
if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) {
if (errno == ENXIO || errno == ENODEV)
return (0); /* device doesn't actually exist - ignore it */
- (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
"SIOCGIFFLAGS: %.*s: %s",
(int)sizeof(ifrflags.ifr_name),
ifrflags.ifr_name,
}
/*
- * Add an entry for this interface, with no addresses.
+ * Add an entry for this interface, with no addresses, if it's
+ * not already in the list.
*/
- if (pcap_add_if(devlistp, name, ifrflags.ifr_flags, NULL,
- errbuf) == -1) {
+ if (find_or_add_if(devlistp, name, ifrflags.ifr_flags,
+ errbuf) == NULL) {
/*
* Failure.
*/
* Otherwise, we return 1 if we don't get an error and -1 if we do.
*/
static int
-scan_sys_class_net(pcap_if_t **devlistp, char *errbuf)
+scan_sys_class_net(pcap_if_list_t *devlistp, char *errbuf)
{
DIR *sys_class_net_d;
int fd;
/*
* Fail if we got some other error.
*/
- (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
"Can't open /sys/class/net: %s", pcap_strerror(errno));
return (-1);
}
/*
* Create a socket from which to fetch interface information.
*/
- fd = socket(AF_INET, SOCK_DGRAM, 0);
+ fd = socket(PF_UNIX, SOCK_RAW, 0);
if (fd < 0) {
- (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
"socket: %s", pcap_strerror(errno));
(void)closedir(sys_class_net_d);
return (-1);
* for devices, newer kernels have symlinks to
* directories.)
*/
- snprintf(subsystem_path, sizeof subsystem_path,
+ pcap_snprintf(subsystem_path, sizeof subsystem_path,
"/sys/class/net/%s/ifindex", ent->d_name);
if (lstat(subsystem_path, &statb) != 0) {
/*
* fail due to an error reading the directory?
*/
if (errno != 0) {
- (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
"Error reading /sys/class/net: %s",
pcap_strerror(errno));
ret = -1;
* See comments from scan_sys_class_net().
*/
static int
-scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf)
+scan_proc_net_dev(pcap_if_list_t *devlistp, char *errbuf)
{
FILE *proc_net_f;
int fd;
/*
* Fail if we got some other error.
*/
- (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
"Can't open /proc/net/dev: %s", pcap_strerror(errno));
return (-1);
}
/*
* Create a socket from which to fetch interface information.
*/
- fd = socket(AF_INET, SOCK_DGRAM, 0);
+ fd = socket(PF_UNIX, SOCK_RAW, 0);
if (fd < 0) {
- (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
"socket: %s", pcap_strerror(errno));
(void)fclose(proc_net_f);
return (-1);
* fail due to an error reading the file?
*/
if (ferror(proc_net_f)) {
- (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
"Error reading /proc/net/dev: %s",
pcap_strerror(errno));
ret = -1;
*/
static const char any_descr[] = "Pseudo-device that captures on all interfaces";
+/*
+ * A SOCK_PACKET or PF_PACKET socket can be bound to any network interface.
+ */
+static int
+can_be_bound(const char *name _U_)
+{
+ return (1);
+}
+
int
-pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf)
+pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
{
int ret;
+ /*
+ * Get the list of regular interfaces first.
+ */
+ if (pcap_findalldevs_interfaces(devlistp, errbuf, can_be_bound) == -1)
+ return (-1); /* failure */
+
/*
* Read "/sys/class/net", and add to the list of interfaces all
* interfaces listed there that we don't already have, because,
* interfaces with no addresses, so you need to read "/sys/class/net"
* to get the names of the rest of the interfaces.
*/
- ret = scan_sys_class_net(alldevsp, errbuf);
+ ret = scan_sys_class_net(devlistp, errbuf);
if (ret == -1)
return (-1); /* failed */
if (ret == 0) {
/*
* No /sys/class/net; try reading /proc/net/dev instead.
*/
- if (scan_proc_net_dev(alldevsp, errbuf) == -1)
+ if (scan_proc_net_dev(devlistp, errbuf) == -1)
return (-1);
}
/*
* Add the "any" device.
*/
- if (pcap_add_if(alldevsp, "any", IFF_UP|IFF_RUNNING,
- any_descr, errbuf) < 0)
+ if (add_dev(devlistp, "any", PCAP_IF_UP|PCAP_IF_RUNNING,
+ any_descr, errbuf) == NULL)
return (-1);
return (0);
*/
if (handlep->filter_in_userland) {
if (reset_kernel_filter(handle) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't remove kernel filter: %s",
pcap_strerror(errno));
err = -2; /* fatal error */
* We're not using PF_PACKET sockets, so we can't determine
* the direction of the packet.
*/
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Setting direction is not supported on SOCK_PACKET sockets");
return -1;
}
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
#define ARPHRD_CAN 280
#endif
case ARPHRD_CAN:
- handle->linktype = DLT_CAN_SOCKETCAN;
+ /*
+ * Map this to DLT_LINUX_SLL; that way, CAN frames will
+ * have ETH_P_CAN/LINUX_SLL_P_CAN as the protocol and
+ * CAN FD frames will have ETH_P_CANFD/LINUX_SLL_P_CANFD
+ * as the protocol, so they can be distinguished by the
+ * protocol in the SLL header.
+ */
+ handle->linktype = DLT_LINUX_SLL;
break;
#ifndef ARPHRD_IEEE802_TR
* IP-over-FC on which somebody wants to capture
* packets.
*/
- handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
+ handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 3);
/*
* If that fails, just leave the list empty.
*/
* so let's use "Linux-cooked" mode. Jean II
*
* XXX - this is handled in activate_new(). */
- //handlep->cooked = 1;
+ /* handlep->cooked = 1; */
break;
/* ARPHRD_LAPD is unofficial and randomly allocated, if reallocation
*
* XXX - this is handled in activate_new().
*/
- //handlep->cooked = 1;
+ /* handlep->cooked = 1; */
+ break;
+
+#ifndef ARPHRD_VSOCKMON
+#define ARPHRD_VSOCKMON 826
+#endif
+ case ARPHRD_VSOCKMON:
+ handle->linktype = DLT_VSOCK;
break;
default:
{
#ifdef HAVE_PF_PACKET_SOCKETS
struct pcap_linux *handlep = handle->priv;
- const char *device = handle->opt.source;
+ const char *device = handle->opt.device;
int is_any_device = (strcmp(device, "any") == 0);
+ int protocol = pcap_protocol(handle);
int sock_fd = -1, arptype;
#ifdef HAVE_PACKET_AUXDATA
int val;
#endif
int err = 0;
struct packet_mreq mr;
-#ifdef SO_BPF_EXTENSIONS
+#if defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT)
int bpf_extensions;
socklen_t len = sizeof(bpf_extensions);
#endif
* try a SOCK_RAW socket for the raw interface.
*/
sock_fd = is_any_device ?
- socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)) :
- socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ socket(PF_PACKET, SOCK_DGRAM, protocol) :
+ socket(PF_PACKET, SOCK_RAW, protocol);
if (sock_fd == -1) {
if (errno == EINVAL || errno == EAFNOSUPPORT) {
return 0;
}
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s",
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s",
pcap_strerror(errno) );
if (errno == EPERM || errno == EACCES) {
/*
* kernels) - reopen in cooked mode.
*/
if (close(sock_fd) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"close: %s", pcap_strerror(errno));
return PCAP_ERROR;
}
- sock_fd = socket(PF_PACKET, SOCK_DGRAM,
- htons(ETH_P_ALL));
+ sock_fd = socket(PF_PACKET, SOCK_DGRAM, protocol);
if (sock_fd == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"socket: %s", pcap_strerror(errno));
if (errno == EPERM || errno == EACCES) {
/*
* update "map_arphrd_to_dlt()"
* to handle the new type.
*/
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"arptype %d not "
"supported by libpcap - "
"falling back to cooked "
}
if ((err = iface_bind(sock_fd, handlep->ifindex,
- handle->errbuf)) != 1) {
+ handle->errbuf, protocol)) != 1) {
close(sock_fd);
if (err < 0)
return err;
mr.mr_type = PACKET_MR_PROMISC;
if (setsockopt(sock_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
&mr, sizeof(mr)) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"setsockopt: %s", pcap_strerror(errno));
close(sock_fd);
return PCAP_ERROR;
val = 1;
if (setsockopt(sock_fd, SOL_PACKET, PACKET_AUXDATA, &val,
sizeof(val)) == -1 && errno != ENOPROTOOPT) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"setsockopt: %s", pcap_strerror(errno));
close(sock_fd);
return PCAP_ERROR;
/*
* Set the offset at which to insert VLAN tags.
+ * That should be the offset of the type field.
*/
switch (handle->linktype) {
case DLT_EN10MB:
+ /*
+ * The type field is after the destination and source
+ * MAC address.
+ */
handlep->vlan_offset = 2 * ETH_ALEN;
break;
case DLT_LINUX_SLL:
- handlep->vlan_offset = 14;
+ /*
+ * The type field is in the last 2 bytes of the
+ * DLT_LINUX_SLL header.
+ */
+ handlep->vlan_offset = SLL_HDR_LEN - 2;
break;
default:
int nsec_tstamps = 1;
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, &nsec_tstamps, sizeof(nsec_tstamps)) < 0) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "setsockopt: unable to set SO_TIMESTAMPNS");
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "setsockopt: unable to set SO_TIMESTAMPNS");
close(sock_fd);
return PCAP_ERROR;
}
*/
handle->fd = sock_fd;
-#ifdef SO_BPF_EXTENSIONS
+#if defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT)
/*
* Can we generate special code for VLAN checks?
* (XXX - what if we need the special code but it's not supported
handle->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING;
}
}
-#endif /* SO_BPF_EXTENSIONS */
+#endif /* defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT) */
return 1;
#else /* HAVE_PF_PACKET_SOCKETS */
* On error, returns -1, and sets *status to the appropriate error code;
* if that is PCAP_ERROR, sets handle->errbuf to the appropriate message.
*/
-static int
+static int
activate_mmap(pcap_t *handle, int *status)
{
struct pcap_linux *handlep = handle->priv;
*/
handlep->oneshot_buffer = malloc(handle->snapshot);
if (handlep->oneshot_buffer == NULL) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't allocate oneshot buffer: %s",
pcap_strerror(errno));
*status = PCAP_ERROR;
return 1;
}
#else /* HAVE_PACKET_RING */
-static int
+static int
activate_mmap(pcap_t *handle _U_, int *status _U_)
{
return 0;
return 1; /* no */
/* Failed to even find out; this is a fatal error. */
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't get %s header len on packet socket: %s",
version_str,
pcap_strerror(errno));
val = version;
if (setsockopt(handle->fd, SOL_PACKET, PACKET_VERSION, &val,
sizeof(val)) < 0) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't activate %s on packet socket: %s",
version_str,
pcap_strerror(errno));
val = VLAN_TAG_LEN;
if (setsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE, &val,
sizeof(val)) < 0) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't set up reserve on packet socket: %s",
pcap_strerror(errno));
return -1;
/*
* Failed.
*/
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"uname failed: %s", pcap_strerror(errno));
return -1;
}
return 1;
}
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
/*
* Attempt to set up memory-mapped access.
*
#ifdef HAVE_TPACKET2
case TPACKET_V2:
#endif
- /* Note that with large snapshot length (say 64K, which is
- * the default for recent versions of tcpdump, the value that
- * "-s 0" has given for a long time with tcpdump, and the
- * default in Wireshark/TShark/dumpcap), if we use the snapshot
+ /* Note that with large snapshot length (say 256K, which is
+ * the default for recent versions of tcpdump, Wireshark,
+ * TShark, dumpcap or 64K, the value that "-s 0" has given for
+ * a long time with tcpdump), if we use the snapshot
* length to calculate the frame length, only a few frames
* will be available in the ring even with pretty
* large ring size (and a lot of memory will be unused).
* based on the MTU, so that the MTU limits the maximum size
* of packets that we can receive.)
*
- * We don't do that if segmentation/fragmentation or receive
- * offload are enabled, so we don't get rudely surprised by
- * "packets" bigger than the MTU. */
+ * If segmentation/fragmentation or receive offload are
+ * enabled, we can get reassembled/aggregated packets larger
+ * than MTU, but bounded to 65535 plus the Ethernet overhead,
+ * due to kernel and protocol constraints */
frame_size = handle->snapshot;
if (handle->linktype == DLT_EN10MB) {
+ unsigned int max_frame_len;
int mtu;
int offload;
+ mtu = iface_get_mtu(handle->fd, handle->opt.device,
+ handle->errbuf);
+ if (mtu == -1) {
+ *status = PCAP_ERROR;
+ return -1;
+ }
offload = iface_get_offload(handle);
if (offload == -1) {
*status = PCAP_ERROR;
return -1;
}
- if (!offload) {
- mtu = iface_get_mtu(handle->fd, handle->opt.source,
- handle->errbuf);
- if (mtu == -1) {
- *status = PCAP_ERROR;
- return -1;
- }
- if (frame_size > mtu + 18)
- frame_size = mtu + 18;
- }
+ if (offload)
+ max_frame_len = MAX(mtu, 65535);
+ else
+ max_frame_len = mtu;
+ max_frame_len += 18;
+
+ if (frame_size > max_frame_len)
+ frame_size = max_frame_len;
}
/* NOTE: calculus matching those in tpacket_rcv()
len = sizeof(sk_type);
if (getsockopt(handle->fd, SOL_SOCKET, SO_TYPE, &sk_type,
&len) < 0) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"getsockopt: %s", pcap_strerror(errno));
*status = PCAP_ERROR;
return -1;
* PACKET_RESERVE", in which case we fall back
* as best we can.
*/
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"getsockopt: %s", pcap_strerror(errno));
*status = PCAP_ERROR;
return -1;
*/
macoff = netoff - maclen;
req.tp_frame_size = TPACKET_ALIGN(macoff + frame_size);
- req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size;
+ /*
+ * Round the buffer size up to a multiple of the
+ * frame size (rather than rounding down, which
+ * would give a buffer smaller than our caller asked
+ * for, and possibly give zero frames if the requested
+ * buffer size is too small for one frame).
+ */
+ req.tp_frame_nr = (handle->opt.buffer_size + req.tp_frame_size - 1)/req.tp_frame_size;
break;
#ifdef HAVE_TPACKET3
/* The "frames" for this are actually buffers that
* contain multiple variable-sized frames.
*
- * We pick a "frame" size of 128K to leave enough
- * room for at least one reasonably-sized packet
+ * We pick a "frame" size of MAXIMUM_SNAPLEN to leave
+ * enough room for at least one reasonably-sized packet
* in the "frame". */
req.tp_frame_size = MAXIMUM_SNAPLEN;
- req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size;
+ /*
+ * Round the buffer size up to a multiple of the
+ * "frame" size (rather than rounding down, which
+ * would give a buffer smaller than our caller asked
+ * for, and possibly give zero "frames" if the requested
+ * buffer size is too small for one "frame").
+ */
+ req.tp_frame_nr = (handle->opt.buffer_size + req.tp_frame_size - 1)/req.tp_frame_size;
break;
#endif
default:
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_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.
- * The block has to be page size aligned.
- * The max block size allowed by the kernel is arch-dependent and
+ /* compute the minumum block size that will handle this frame.
+ * The block has to be page size aligned.
+ * The max block size allowed by the kernel is arch-dependent and
* it's not explicitly checked here. */
req.tp_block_size = getpagesize();
- while (req.tp_block_size < req.tp_frame_size)
+ while (req.tp_block_size < req.tp_frame_size)
req.tp_block_size <<= 1;
frames_per_block = req.tp_block_size/req.tp_frame_size;
hwconfig.rx_filter = HWTSTAMP_FILTER_ALL;
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name));
+ strlcpy(ifr.ifr_name, handle->opt.device, sizeof(ifr.ifr_name));
ifr.ifr_data = (void *)&hwconfig;
if (ioctl(handle->fd, SIOCSHWTSTAMP, &ifr) < 0) {
return -1;
case EOPNOTSUPP:
+ case ERANGE:
/*
* Treat this as a warning, as the
* only way to fix the warning is to
* get an adapter that supports hardware
- * time stamps. We'll just fall back
- * on the standard host time stamps.
+ * time stamps for *all* packets.
+ * (ERANGE means "we support hardware
+ * time stamps, but for packets matching
+ * that particular filter", so it means
+ * "we don't support hardware time stamps
+ * for all incoming packets" here.)
+ *
+ * We'll just fall back on the standard
+ * host time stamps.
*/
*status = PCAP_WARNING_TSTAMP_TYPE_NOTSUP;
break;
default:
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"SIOCSHWTSTAMP failed: %s",
pcap_strerror(errno));
*status = PCAP_ERROR;
}
if (setsockopt(handle->fd, SOL_PACKET, PACKET_TIMESTAMP,
(void *)×ource, sizeof(timesource))) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "can't set PACKET_TIMESTAMP: %s",
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't set PACKET_TIMESTAMP: %s",
pcap_strerror(errno));
*status = PCAP_ERROR;
return -1;
/* req.tp_frame_nr is requested to match frames_per_block*req.tp_block_nr */
req.tp_frame_nr = req.tp_block_nr * frames_per_block;
-
+
#ifdef HAVE_TPACKET3
/* timeout value to retire block - use the configured buffering timeout, or default if <0. */
req.tp_retire_blk_tov = (handlep->timeout>=0)?handlep->timeout:0;
*/
return 0;
}
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't create rx ring on packet socket: %s",
pcap_strerror(errno));
*status = PCAP_ERROR;
handlep->mmapbuf = mmap(0, handlep->mmapbuflen,
PROT_READ|PROT_WRITE, MAP_SHARED, handle->fd, 0);
if (handlep->mmapbuf == MAP_FAILED) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't mmap rx ring: %s", pcap_strerror(errno));
/* clear the allocated ring on error*/
handle->cc = req.tp_frame_nr;
handle->buffer = malloc(handle->cc * sizeof(union thdr *));
if (!handle->buffer) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't allocate ring of frame headers: %s",
pcap_strerror(errno));
for (i=0; i<req.tp_block_nr; ++i) {
void *base = &handlep->mmapbuf[i*req.tp_block_size];
for (j=0; j<frames_per_block; ++j, ++handle->offset) {
- RING_GET_FRAME(handle) = base;
+ RING_GET_CURRENT_FRAME(handle) = base;
base += req.tp_frame_size;
}
}
memcpy(handlep->oneshot_buffer, bytes, h->caplen);
*sp->pkt = handlep->oneshot_buffer;
}
-
+
static void
pcap_cleanup_linux_mmap( pcap_t *handle )
{
static int
-pcap_getnonblock_mmap(pcap_t *p, char *errbuf)
+pcap_getnonblock_mmap(pcap_t *handle)
{
- struct pcap_linux *handlep = p->priv;
+ struct pcap_linux *handlep = handle->priv;
/* use negative value of timeout to indicate non blocking ops */
return (handlep->timeout<0);
}
static int
-pcap_setnonblock_mmap(pcap_t *p, int nonblock, char *errbuf)
+pcap_setnonblock_mmap(pcap_t *handle, int nonblock)
{
- struct pcap_linux *handlep = p->priv;
+ struct pcap_linux *handlep = handle->priv;
/*
* Set the file descriptor to non-blocking mode, as we use
* it for sending packets.
*/
- if (pcap_setnonblock_fd(p, nonblock, errbuf) == -1)
+ if (pcap_setnonblock_fd(handle, nonblock) == -1)
return -1;
/*
handlep->timeout = ~handlep->timeout;
}
}
+ /* Update the timeout to use in poll(). */
+ set_poll_timeout(handlep);
return 0;
}
-static inline union thdr *
-pcap_get_ring_frame(pcap_t *handle, int status)
+/*
+ * Get the status field of the ring buffer frame at a specified offset.
+ */
+static inline int
+pcap_get_ring_frame_status(pcap_t *handle, int offset)
{
struct pcap_linux *handlep = handle->priv;
union thdr h;
- h.raw = RING_GET_FRAME(handle);
+ h.raw = RING_GET_FRAME_AT(handle, offset);
switch (handlep->tp_version) {
case TPACKET_V1:
- if (status != (h.h1->tp_status ? TP_STATUS_USER :
- TP_STATUS_KERNEL))
- return NULL;
+ return (h.h1->tp_status);
break;
case TPACKET_V1_64:
- if (status != (h.h1_64->tp_status ? TP_STATUS_USER :
- TP_STATUS_KERNEL))
- return NULL;
+ return (h.h1_64->tp_status);
break;
#ifdef HAVE_TPACKET2
case TPACKET_V2:
- if (status != (h.h2->tp_status ? TP_STATUS_USER :
- TP_STATUS_KERNEL))
- return NULL;
+ return (h.h2->tp_status);
break;
#endif
#ifdef HAVE_TPACKET3
case TPACKET_V3:
- if (status != (h.h3->hdr.bh1.block_status ? TP_STATUS_USER :
- TP_STATUS_KERNEL))
- return NULL;
+ return (h.h3->hdr.bh1.block_status);
break;
#endif
}
- return h.raw;
+ /* This should not happen. */
+ return 0;
}
#ifndef POLLRDHUP
#define POLLRDHUP 0
#endif
-/* wait for frames availability.*/
+/*
+ * Block waiting for frames to be available.
+ */
static int pcap_wait_for_frames_mmap(pcap_t *handle)
{
- if (!pcap_get_ring_frame(handle, TP_STATUS_USER)) {
- struct pcap_linux *handlep = handle->priv;
- int timeout;
- char c;
- struct pollfd pollinfo;
- int ret;
+ struct pcap_linux *handlep = handle->priv;
+ char c;
+ struct pollfd pollinfo;
+ int ret;
- pollinfo.fd = handle->fd;
- pollinfo.events = POLLIN;
+ pollinfo.fd = handle->fd;
+ pollinfo.events = POLLIN;
- if (handlep->timeout == 0) {
-#ifdef HAVE_TPACKET3
+ do {
+ /*
+ * Yes, we do this even in non-blocking mode, as it's
+ * the only way to get error indications from a
+ * tpacket socket.
+ *
+ * The timeout is 0 in non-blocking mode, so poll()
+ * returns immediately.
+ */
+ ret = poll(&pollinfo, 1, handlep->poll_timeout);
+ if (ret < 0 && errno != EINTR) {
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't poll on packet socket: %s",
+ pcap_strerror(errno));
+ return PCAP_ERROR;
+ } else if (ret > 0 &&
+ (pollinfo.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) {
/*
- * XXX - due to a set of (mis)features in the
- * TPACKET_V3 kernel code, blocking forever with
- * a TPACKET_V3 socket can, if few packets
- * are arriving and passing the socket filter,
- * cause most packets to be dropped. See
- * libpcap issue #335 for the full painful
- * story. The workaround is to have poll()
- * time out very quickly, so we grab the
- * frames handed to us, and return them to
- * the kernel, ASAP.
- *
- * If those issues are ever fixed, we might
- * want to check the kernel version and block
- * forever with TPACKET_V3 if we're running
- * with a kernel that has the fix.
+ * There's some indication other than
+ * "you can read on this descriptor" on
+ * the descriptor.
*/
- if (handlep->tp_version == TPACKET_V3)
- timeout = 1; /* don't block for very long */
- else
-#endif
- timeout = -1; /* block forever */
- } else if (handlep->timeout > 0)
- timeout = handlep->timeout; /* block for that amount of time */
- else
- timeout = 0; /* non-blocking mode - poll to pick up errors */
- do {
- ret = poll(&pollinfo, 1, timeout);
- if (ret < 0 && errno != EINTR) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "can't poll on packet socket: %s",
- pcap_strerror(errno));
+ if (pollinfo.revents & (POLLHUP | POLLRDHUP)) {
+ pcap_snprintf(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ "Hangup on packet socket");
return PCAP_ERROR;
- } else if (ret > 0 &&
- (pollinfo.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) {
+ }
+ if (pollinfo.revents & POLLERR) {
/*
- * There's some indication other than
- * "you can read on this descriptor" on
- * the descriptor.
+ * A recv() will give us the actual error code.
+ *
+ * XXX - make the socket non-blocking?
*/
- if (pollinfo.revents & (POLLHUP | POLLRDHUP)) {
- snprintf(handle->errbuf,
- PCAP_ERRBUF_SIZE,
- "Hangup on packet socket");
- return PCAP_ERROR;
- }
- if (pollinfo.revents & POLLERR) {
+ if (recv(handle->fd, &c, sizeof c,
+ MSG_PEEK) != -1)
+ continue; /* what, no error? */
+ if (errno == ENETDOWN) {
/*
- * A recv() will give us the
- * actual error code.
+ * The device on which we're
+ * capturing went away.
*
- * XXX - make the socket non-blocking?
+ * XXX - we should really return
+ * PCAP_ERROR_IFACE_NOT_UP, but
+ * pcap_dispatch() etc. aren't
+ * defined to return that.
*/
- if (recv(handle->fd, &c, sizeof c,
- MSG_PEEK) != -1)
- continue; /* what, no error? */
- if (errno == ENETDOWN) {
- /*
- * The device on which we're
- * capturing went away.
- *
- * XXX - we should really return
- * PCAP_ERROR_IFACE_NOT_UP,
- * but pcap_dispatch() etc.
- * aren't defined to return
- * that.
- */
- snprintf(handle->errbuf,
- PCAP_ERRBUF_SIZE,
- "The interface went down");
- } else {
- snprintf(handle->errbuf,
- PCAP_ERRBUF_SIZE,
- "Error condition on packet socket: %s",
- strerror(errno));
- }
- return PCAP_ERROR;
- }
- if (pollinfo.revents & POLLNVAL) {
- snprintf(handle->errbuf,
+ pcap_snprintf(handle->errbuf,
PCAP_ERRBUF_SIZE,
- "Invalid polling request on packet socket");
- return PCAP_ERROR;
+ "The interface went down");
+ } else {
+ pcap_snprintf(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ "Error condition on packet socket: %s",
+ strerror(errno));
}
+ return PCAP_ERROR;
}
- /* check for break loop condition on interrupted syscall*/
- if (handle->break_loop) {
- handle->break_loop = 0;
- return PCAP_ERROR_BREAK;
+ if (pollinfo.revents & POLLNVAL) {
+ pcap_snprintf(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ "Invalid polling request on packet socket");
+ return PCAP_ERROR;
}
- } while (ret < 0);
- }
+ }
+ /* check for break loop condition on interrupted syscall*/
+ if (handle->break_loop) {
+ handle->break_loop = 0;
+ return PCAP_ERROR_BREAK;
+ }
+ } while (ret < 0);
return 0;
}
unsigned char *bp;
struct sockaddr_ll *sll;
struct pcap_pkthdr pcaphdr;
+ unsigned int snaplen = tp_snaplen;
/* perform sanity check on internal offset. */
if (tp_mac + tp_snaplen > handle->bufsize) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"corrupted frame on kernel ring mac "
"offset %u + caplen %u > frame len %d",
tp_mac, tp_snaplen, handle->bufsize);
if (bp < (u_char *)frame +
TPACKET_ALIGN(handlep->tp_hdrlen) +
sizeof(struct sockaddr_ll)) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"cooked-mode frame doesn't have room for sll header");
return -1;
}
hdrp->sll_halen = htons(sll->sll_halen);
memcpy(hdrp->sll_addr, sll->sll_addr, SLL_ADDRLEN);
hdrp->sll_protocol = sll->sll_protocol;
+
+ snaplen += sizeof(struct sll_header);
}
if (handlep->filter_in_userland && handle->fcode.bf_insns) {
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)
+ if (bpf_filter_with_aux_data(handle->fcode.bf_insns,
+ bp,
+ tp_len,
+ snaplen,
+ &aux_data) == 0)
return 0;
}
{
struct vlan_tag *tag;
+ /*
+ * Move everything in the header, except the type field,
+ * down VLAN_TAG_LEN bytes, to allow us to insert the
+ * VLAN tag between that stuff and the type field.
+ */
bp -= VLAN_TAG_LEN;
memmove(bp, bp + VLAN_TAG_LEN, handlep->vlan_offset);
+ /*
+ * Now insert the tag.
+ */
tag = (struct vlan_tag *)(bp + handlep->vlan_offset);
tag->vlan_tpid = htons(tp_vlan_tpid);
tag->vlan_tci = htons(tp_vlan_tci);
+ /*
+ * Add the tag to the packet lengths.
+ */
pcaphdr.caplen += VLAN_TAG_LEN;
pcaphdr.len += VLAN_TAG_LEN;
}
* Trim the snapshot length to be no longer than the
* specified snapshot length.
*/
- if (pcaphdr.caplen > handle->snapshot)
+ if (pcaphdr.caplen > (bpf_u_int32)handle->snapshot)
pcaphdr.caplen = handle->snapshot;
/* pass the packet to the user */
u_char *user)
{
struct pcap_linux *handlep = handle->priv;
+ union thdr h;
int pkts = 0;
int ret;
/* wait for frames availability.*/
- ret = pcap_wait_for_frames_mmap(handle);
- if (ret) {
- return ret;
+ h.raw = RING_GET_CURRENT_FRAME(handle);
+ if (h.h1->tp_status == TP_STATUS_KERNEL) {
+ /*
+ * The current frame is owned by the kernel; wait for
+ * a frame to be handed to us.
+ */
+ ret = pcap_wait_for_frames_mmap(handle);
+ if (ret) {
+ return ret;
+ }
}
/* non-positive values of max_packets are used to require all
* packets currently available in the ring */
while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) {
- union thdr h;
-
- h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER);
- if (!h.raw)
+ /*
+ * Get the current ring buffer frame, and break if
+ * it's still owned by the kernel.
+ */
+ h.raw = RING_GET_CURRENT_FRAME(handle);
+ if (h.h1->tp_status == TP_STATUS_KERNEL)
break;
ret = pcap_handle_packet_mmap(
u_char *user)
{
struct pcap_linux *handlep = handle->priv;
+ union thdr h;
int pkts = 0;
int ret;
/* wait for frames availability.*/
- ret = pcap_wait_for_frames_mmap(handle);
- if (ret) {
- return ret;
+ h.raw = RING_GET_CURRENT_FRAME(handle);
+ if (h.h1_64->tp_status == TP_STATUS_KERNEL) {
+ /*
+ * The current frame is owned by the kernel; wait for
+ * a frame to be handed to us.
+ */
+ ret = pcap_wait_for_frames_mmap(handle);
+ if (ret) {
+ return ret;
+ }
}
/* non-positive values of max_packets are used to require all
* packets currently available in the ring */
while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) {
- union thdr h;
-
- h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER);
- if (!h.raw)
+ /*
+ * Get the current ring buffer frame, and break if
+ * it's still owned by the kernel.
+ */
+ h.raw = RING_GET_CURRENT_FRAME(handle);
+ if (h.h1_64->tp_status == TP_STATUS_KERNEL)
break;
ret = pcap_handle_packet_mmap(
u_char *user)
{
struct pcap_linux *handlep = handle->priv;
+ union thdr h;
int pkts = 0;
int ret;
/* wait for frames availability.*/
- ret = pcap_wait_for_frames_mmap(handle);
- if (ret) {
- return ret;
+ h.raw = RING_GET_CURRENT_FRAME(handle);
+ if (h.h2->tp_status == TP_STATUS_KERNEL) {
+ /*
+ * The current frame is owned by the kernel; wait for
+ * a frame to be handed to us.
+ */
+ ret = pcap_wait_for_frames_mmap(handle);
+ if (ret) {
+ return ret;
+ }
}
/* non-positive values of max_packets are used to require all
* packets currently available in the ring */
while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) {
- union thdr h;
-
- h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER);
- if (!h.raw)
+ /*
+ * Get the current ring buffer frame, and break if
+ * it's still owned by the kernel.
+ */
+ h.raw = RING_GET_CURRENT_FRAME(handle);
+ if (h.h2->tp_status == TP_STATUS_KERNEL)
break;
ret = pcap_handle_packet_mmap(
again:
if (handlep->current_packet == NULL) {
/* wait for frames availability.*/
- ret = pcap_wait_for_frames_mmap(handle);
- if (ret) {
- return ret;
+ h.raw = RING_GET_CURRENT_FRAME(handle);
+ if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) {
+ /*
+ * The current frame is owned by the kernel; wait
+ * for a frame to be handed to us.
+ */
+ ret = pcap_wait_for_frames_mmap(handle);
+ if (ret) {
+ return ret;
+ }
}
}
- h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER);
- if (!h.raw) {
+ h.raw = RING_GET_CURRENT_FRAME(handle);
+ if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) {
if (pkts == 0 && handlep->timeout == 0) {
/* Block until we see a packet. */
goto again;
/* non-positive values of max_packets are used to require all
* packets currently available in the ring */
while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) {
+ int packets_to_read;
+
if (handlep->current_packet == NULL) {
- h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER);
- if (!h.raw)
+ h.raw = RING_GET_CURRENT_FRAME(handle);
+ if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL)
break;
handlep->current_packet = h.raw + h.h3->hdr.bh1.offset_to_first_pkt;
handlep->packets_left = h.h3->hdr.bh1.num_pkts;
}
- int packets_to_read = handlep->packets_left;
+ packets_to_read = handlep->packets_left;
- if (!PACKET_COUNT_IS_UNLIMITED(max_packets) && packets_to_read > max_packets) {
- packets_to_read = max_packets;
+ if (!PACKET_COUNT_IS_UNLIMITED(max_packets) &&
+ packets_to_read > (max_packets - pkts)) {
+ /*
+ * We've been given a maximum number of packets
+ * to process, and there are more packets in
+ * this buffer than that. Only process enough
+ * of them to get us up to that maximum.
+ */
+ packets_to_read = max_packets - pkts;
}
- while(packets_to_read--) {
+ while (packets_to_read-- && !handle->break_loop) {
struct tpacket3_hdr* tp3_hdr = (struct tpacket3_hdr*) handlep->current_packet;
ret = pcap_handle_packet_mmap(
handle,
}
#endif /* HAVE_TPACKET3 */
-static int
+static int
pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter)
{
struct pcap_linux *handlep = handle->priv;
* walk the ring backward and count the free blocks.
*/
offset = handle->offset;
- if (--handle->offset < 0)
- handle->offset = handle->cc - 1;
+ if (--offset < 0)
+ offset = handle->cc - 1;
for (n=0; n < handle->cc; ++n) {
- if (--handle->offset < 0)
- handle->offset = handle->cc - 1;
- if (!pcap_get_ring_frame(handle, TP_STATUS_KERNEL))
+ if (--offset < 0)
+ offset = handle->cc - 1;
+ if (pcap_get_ring_frame_status(handle, offset) != TP_STATUS_KERNEL)
break;
}
if (n != 0)
n--;
- /* be careful to not change current ring position */
- handle->offset = offset;
-
/*
* Set the count of blocks worth of packets to filter
* in userland to the total number of blocks in the
strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
"SIOCGIFINDEX: %s", pcap_strerror(errno));
return -1;
}
* or a PCAP_ERROR_ value on a hard error.
*/
static int
-iface_bind(int fd, int ifindex, char *ebuf)
+iface_bind(int fd, int ifindex, char *ebuf, int protocol)
{
struct sockaddr_ll sll;
int err;
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
- sll.sll_protocol = htons(ETH_P_ALL);
+ sll.sll_protocol = protocol;
if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) == -1) {
if (errno == ENETDOWN) {
*/
return PCAP_ERROR_IFACE_NOT_UP;
} else {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
"bind: %s", pcap_strerror(errno));
return PCAP_ERROR;
}
/* Any pending errors, e.g., network is down? */
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
"getsockopt: %s", pcap_strerror(errno));
return 0;
}
*/
return PCAP_ERROR_IFACE_NOT_UP;
} else if (err > 0) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
"bind: %s", pcap_strerror(err));
return 0;
}
{
struct iwreq ireq;
+ if (is_bonding_device(sock_fd, device))
+ return 0; /* bonding device, so don't even try */
+
strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
if (ioctl(sock_fd, SIOCGIWNAME, &ireq) >= 0)
return 1; /* yes */
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
- "%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno));
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "%s: SIOCGIWNAME: %s", device, pcap_strerror(errno));
if (errno == ENODEV)
return PCAP_ERROR_NO_SUCH_DEVICE;
return 0;
ireq.u.data.length = 0;
ireq.u.data.flags = 0;
if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) != -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: SIOCGIWPRIV with a zero-length buffer didn't fail!",
device);
return PCAP_ERROR;
/*
* Failed.
*/
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_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,
+ pcap_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,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: SIOCGIWPRIV: %s", device,
pcap_strerror(errno));
free(priv);
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: Can't get flags: %s", device, strerror(errno));
return PCAP_ERROR;
}
oldflags = ifr.ifr_flags;
ifr.ifr_flags &= ~IFF_UP;
if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: Can't set flags: %s", device, strerror(errno));
return PCAP_ERROR;
}
*/
ifr.ifr_flags = oldflags;
if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: Can't set flags: %s", device, strerror(errno));
return PCAP_ERROR;
}
strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
if (ioctl(sock_fd, SIOCGIWFREQ, &ireq) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: SIOCGIWFREQ: %s", device,
pcap_strerror(errno));
return PCAP_ERROR;
if (oldflags != 0) {
ifr.ifr_flags = oldflags;
if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: Can't set flags: %s", device, strerror(errno));
/*
};
#define NUM_SOF_TIMESTAMPING_TYPES (sizeof sof_ts_type_map / sizeof sof_ts_type_map[0])
+/*
+ * Set the list of time stamping types to include all types.
+ */
static void
-iface_set_default_ts_types(pcap_t *handle)
+iface_set_all_ts_types(pcap_t *handle)
{
- int i;
+ u_int i;
handle->tstamp_type_count = NUM_SOF_TIMESTAMPING_TYPES;
handle->tstamp_type_list = malloc(NUM_SOF_TIMESTAMPING_TYPES * sizeof(u_int));
* Get a list of time stamping capabilities.
*/
static int
-iface_ethtool_get_ts_info(pcap_t *handle, char *ebuf)
+iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf)
{
int fd;
struct ifreq ifr;
struct ethtool_ts_info info;
int num_ts_types;
- int i, j;
+ u_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.
+ * This doesn't apply to the "any" device; you can't say "turn on
+ * hardware time stamping for all devices that exist now and arrange
+ * that it be turned on for any device that appears in the future",
+ * and not all devices even necessarily *support* hardware time
+ * stamping, so don't report any time stamp types.
*/
- if (strcmp(handle->opt.source, "any") == 0) {
- iface_set_default_ts_types(handle);
+ if (strcmp(device, "any") == 0) {
+ handle->tstamp_type_list = NULL;
return 0;
}
/*
* Create a socket from which to fetch time stamping capabilities.
*/
- fd = socket(AF_INET, SOCK_DGRAM, 0);
+ fd = socket(PF_UNIX, SOCK_RAW, 0);
if (fd < 0) {
- (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ (void)pcap_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));
+ strlcpy(ifr.ifr_name, device, 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) {
+ int save_errno = errno;
+
close(fd);
- if (errno == EOPNOTSUPP || errno == EINVAL) {
+ switch (save_errno) {
+
+ case EOPNOTSUPP:
+ case EINVAL:
/*
- * OK, let's just return all the possible time
- * stamping types.
+ * OK, this OS version or driver doesn't support
+ * asking for the time stamping types, so let's
+ * just return all the possible types.
*/
- iface_set_default_ts_types(handle);
+ iface_set_all_ts_types(handle);
return 0;
+
+ case ENODEV:
+ /*
+ * OK, no such device.
+ * The user will find that out when they try to
+ * activate the device; just return an empty
+ * list of time stamp types.
+ */
+ handle->tstamp_type_list = NULL;
+ return 0;
+
+ default:
+ /*
+ * Other error.
+ */
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "%s: SIOCETHTOOL(ETHTOOL_GET_TS_INFO) ioctl failed: %s", device,
+ strerror(save_errno));
+ return -1;
}
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
- "%s: SIOCETHTOOL(ETHTOOL_GET_TS_INFO) ioctl failed: %s", handle->opt.source,
- strerror(errno));
- return -1;
}
close(fd);
+ /*
+ * Do we support hardware time stamping of *all* packets?
+ */
+ if (!(info.rx_filters & (1 << HWTSTAMP_FILTER_ALL))) {
+ /*
+ * No, so don't report any time stamp types.
+ *
+ * XXX - some devices either don't report
+ * HWTSTAMP_FILTER_ALL when they do support it, or
+ * report HWTSTAMP_FILTER_ALL but map it to only
+ * time stamping a few PTP packets. See
+ * http://marc.info/?l=linux-netdev&m=146318183529571&w=2
+ */
+ handle->tstamp_type_list = NULL;
+ return 0;
+ }
+
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)
}
#else /* ETHTOOL_GET_TS_INFO */
static int
-iface_ethtool_get_ts_info(pcap_t *handle, char *ebuf _U_)
+iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf _U_)
{
+ /*
+ * This doesn't apply to the "any" device; you can't say "turn on
+ * hardware time stamping for all devices that exist now and arrange
+ * that it be turned on for any device that appears in the future",
+ * and not all devices even necessarily *support* hardware time
+ * stamping, so don't report any time stamp types.
+ */
+ if (strcmp(device, "any") == 0) {
+ handle->tstamp_type_list = NULL;
+ return 0;
+ }
+
/*
* We don't have an ioctl to use to ask what's supported,
* so say we support everything.
*/
- iface_set_default_ts_types(handle);
+ iface_set_all_ts_types(handle);
return 0;
}
#endif /* ETHTOOL_GET_TS_INFO */
struct ethtool_value eval;
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name));
+ strlcpy(ifr.ifr_name, handle->opt.device, sizeof(ifr.ifr_name));
eval.cmd = cmd;
eval.data = 0;
ifr.ifr_data = (caddr_t)&eval;
*/
return 0;
}
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: SIOCETHTOOL(%s) ioctl failed: %s", handle->opt.source,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "%s: SIOCETHTOOL(%s) ioctl failed: %s", handle->opt.device,
cmdname, strerror(errno));
return -1;
}
- return eval.data;
+ return eval.data;
}
static int
struct pcap_linux *handlep = handle->priv;
int arptype;
struct ifreq ifr;
- const char *device = handle->opt.source;
+ const char *device = handle->opt.device;
struct utsname utsname;
int mtu;
handle->fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL));
if (handle->fd == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"socket: %s", pcap_strerror(errno));
if (errno == EPERM || errno == EACCES) {
/*
*/
map_arphrd_to_dlt(handle, handle->fd, arptype, device, 0);
if (handle->linktype == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"unknown arptype %d", arptype);
return PCAP_ERROR;
}
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"SIOCGIFFLAGS: %s", pcap_strerror(errno));
return PCAP_ERROR;
}
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"SIOCSIFFLAGS: %s",
pcap_strerror(errno));
return PCAP_ERROR;
if (mtu == -1)
return PCAP_ERROR;
handle->bufsize = MAX_LINKHEADER_SIZE + mtu;
- if (handle->bufsize < handle->snapshot)
- handle->bufsize = handle->snapshot;
+ if (handle->bufsize < (u_int)handle->snapshot)
+ handle->bufsize = (u_int)handle->snapshot;
} else {
/*
* This is a 2.2[.x] or later kernel.
* We can safely pass "recvfrom()" a byte count
* based on the snapshot length.
*/
- handle->bufsize = handle->snapshot;
+ handle->bufsize = (u_int)handle->snapshot;
}
/*
memset(&saddr, 0, sizeof(saddr));
strlcpy(saddr.sa_data, device, sizeof(saddr.sa_data));
if (bind(fd, &saddr, sizeof(saddr)) == -1) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
"bind: %s", pcap_strerror(errno));
return -1;
}
/* Any pending errors, e.g., network is down? */
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
"getsockopt: %s", pcap_strerror(errno));
return -1;
}
if (err > 0) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
"bind: %s", pcap_strerror(err));
return -1;
}
strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(fd, SIOCGIFMTU, &ifr) == -1) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
"SIOCGIFMTU: %s", pcap_strerror(errno));
return -1;
}
strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
"SIOCGIFHWADDR: %s", pcap_strerror(errno));
if (errno == ENODEV) {
/*
len = handle->fcode.bf_len;
f = (struct bpf_insn *)malloc(prog_size);
if (f == NULL) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"malloc: %s", pcap_strerror(errno));
return -1;
}
*/
save_mode = fcntl(handle->fd, F_GETFL, 0);
if (save_mode == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_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,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't set nonblocking mode when changing filter: %s",
pcap_strerror(errno));
return -2;
*/
(void)fcntl(handle->fd, F_SETFL, save_mode);
(void)reset_kernel_filter(handle);
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_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,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't restore FD flags when changing filter: %s",
pcap_strerror(save_errno));
return -2;
* Report it as a fatal error.
*/
if (reset_kernel_filter(handle) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't remove kernel total filter: %s",
pcap_strerror(errno));
return -2; /* fatal error */
static int
reset_kernel_filter(pcap_t *handle)
{
+ int ret;
/*
* setsockopt() barfs unless it get a dummy parameter.
* valgrind whines unless the value is initialized,
*/
int dummy = 0;
- return setsockopt(handle->fd, SOL_SOCKET, SO_DETACH_FILTER,
+ ret = setsockopt(handle->fd, SOL_SOCKET, SO_DETACH_FILTER,
&dummy, sizeof(dummy));
+ /*
+ * Ignore ENOENT - it means "we don't have a filter", so there
+ * was no filter to remove, and there's still no filter.
+ *
+ * Also ignore ENONET, as a lot of kernel versions had a
+ * typo where ENONET, rather than ENOENT, was returned.
+ */
+ if (ret == -1 && errno != ENOENT && errno != ENONET)
+ return -1;
+ return 0;
+}
+#endif
+
+int
+pcap_set_protocol(pcap_t *p, int protocol)
+{
+ if (pcap_check_activated(p))
+ return (PCAP_ERROR_ACTIVATED);
+ p->opt.protocol = protocol;
+ return (0);
}
+
+#include "pcap_version.h"
+
+/*
+ * Libpcap version string.
+ */
+const char *
+pcap_lib_version(void)
+{
+#ifdef HAVE_PACKET_RING
+ #if defined(HAVE_TPACKET3)
+ return (PCAP_VERSION_STRING " (with TPACKET_V3)");
+ #elif defined(HAVE_TPACKET2)
+ return (PCAP_VERSION_STRING " (with TPACKET_V2)");
+ #else
+ return (PCAP_VERSION_STRING " (with TPACKET_V1)");
+ #endif
+#else
+ return (PCAP_VERSION_STRING " (without TPACKET)");
#endif
+}