#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
+#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <linux/if_ether.h>
#include <net/if_arp.h>
#include <poll.h>
+#include <dirent.h>
+
+#ifdef HAVE_LINUX_NET_TSTAMP_H
+#include <linux/net_tstamp.h>
+#include <linux/sockios.h>
+#endif
/*
* Got Wireless Extensions?
#include "pcap-septel.h"
#endif /* HAVE_SEPTEL_API */
+#ifdef HAVE_SNF_API
+#include "pcap-snf.h"
+#endif /* HAVE_SNF_API */
+
#ifdef PCAP_SUPPORT_USB
#include "pcap-usb-linux.h"
#endif
#include "pcap-bt-linux.h"
#endif
+#ifdef PCAP_SUPPORT_CAN
+#include "pcap-can-linux.h"
+#endif
+
/*
* If PF_PACKET is defined, we can use {SOCK_RAW,SOCK_DGRAM}/PF_PACKET
* sockets rather than SOCK_PACKET sockets.
static int pcap_activate_linux(pcap_t *);
static int activate_old(pcap_t *);
static int activate_new(pcap_t *);
-static int activate_mmap(pcap_t *);
+static int activate_mmap(pcap_t *, int *);
static int pcap_can_set_rfmon_linux(pcap_t *);
static int pcap_read_linux(pcap_t *, int, pcap_handler, u_char *);
static int pcap_read_packet(pcap_t *, pcap_handler, u_char *);
#define RING_GET_FRAME(h) (((union thdr **)h->buffer)[h->offset])
static void destroy_ring(pcap_t *handle);
-static int create_ring(pcap_t *handle);
+static int create_ring(pcap_t *handle, int *status);
static int prepare_tpacket_socket(pcap_t *handle);
static void pcap_cleanup_linux_mmap(pcap_t *);
static int pcap_read_linux_mmap(pcap_t *, int, pcap_handler , u_char *);
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 void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h,
+ const u_char *bytes);
#endif
/*
static int iface_bind_old(int fd, const char *device, char *ebuf);
#ifdef SO_ATTACH_FILTER
-static int fix_program(pcap_t *handle, struct sock_fprog *fcode);
+static int fix_program(pcap_t *handle, struct sock_fprog *fcode,
+ int is_mapped);
static int fix_offset(struct bpf_insn *p);
static int set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode);
static int reset_kernel_filter(pcap_t *handle);
}
#endif /* HAVE_SEPTEL_API */
+#ifdef HAVE_SNF_API
+ handle = snf_create(device, ebuf);
+ if (strstr(device, "snf") || handle != NULL)
+ return handle;
+
+#endif /* HAVE_SNF_API */
+
#ifdef PCAP_SUPPORT_BT
if (strstr(device, "bluetooth")) {
return bt_create(device, ebuf);
}
#endif
+#ifdef PCAP_SUPPORT_CAN
+ if (strstr(device, "can") || strstr(device, "vcan")) {
+ return can_create(device, ebuf);
+ }
+#endif
+
#ifdef PCAP_SUPPORT_USB
if (strstr(device, "usbmon")) {
return usb_create(device, 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.
+ */
+ handle->tstamp_type_count = 3;
+ handle->tstamp_type_list = malloc(3 * sizeof(u_int));
+ if (handle->tstamp_type_list == NULL) {
+ free(handle);
+ return NULL;
+ }
+ handle->tstamp_type_list[0] = PCAP_TSTAMP_HOST;
+ handle->tstamp_type_list[1] = PCAP_TSTAMP_ADAPTER;
+ handle->tstamp_type_list[2] = PCAP_TSTAMP_ADAPTER_UNSYNCED;
+#endif
+
return handle;
}
#ifdef HAVE_LIBNL
/*
- *
- * If interface {if} is a mac80211 driver, the file
- * /sys/class/net/{if}/phy80211 is a symlink to
- * /sys/class/ieee80211/{phydev}, for some {phydev}.
- *
- * On Fedora 9, with a 2.6.26.3-29 kernel, my Zydas stick, at
- * least, has a "wmaster0" device and a "wlan0" device; the
- * latter is the one with the IP address. Both show up in
- * "tcpdump -D" output. Capturing on the wmaster0 device
- * captures with 802.11 headers.
- *
- * airmon-ng searches through /sys/class/net for devices named
- * monN, starting with mon0; as soon as one *doesn't* exist,
- * it chooses that as the monitor device name. If the "iw"
- * command exists, it does "iw dev {if} interface add {monif}
- * type monitor", where {monif} is the monitor device. It
- * then (sigh) sleeps .1 second, and then configures the
- * device up. Otherwise, if /sys/class/ieee80211/{phydev}/add_iface
- * is a file, it writes {mondev}, without a newline, to that file,
- * and again (sigh) sleeps .1 second, and then iwconfig's that
- * device into monitor mode and configures it up. Otherwise,
- * you can't do monitor mode.
- *
- * All these devices are "glued" together by having the
- * /sys/class/net/{device}/phy80211 links pointing to the same
- * place, so, given a wmaster, wlan, or mon device, you can
- * find the other devices by looking for devices with
- * the same phy80211 link.
- *
- * To turn monitor mode off, delete the monitor interface,
- * either with "iw dev {monif} interface del" or by sending
- * {monif}, with no NL, down /sys/class/ieee80211/{phydev}/remove_iface
- *
- * Note: if you try to create a monitor device named "monN", and
- * there's already a "monN" device, it fails, as least with
- * the netlink interface (which is what iw uses), with a return
- * value of -ENFILE. (Return values are negative errnos.) We
- * could probably use that to find an unused device.
- *
- * Yes, you can have multiple monitor devices for a given
- * physical device.
+ * If interface {if} is a mac80211 driver, the file
+ * /sys/class/net/{if}/phy80211 is a symlink to
+ * /sys/class/ieee80211/{phydev}, for some {phydev}.
+ *
+ * On Fedora 9, with a 2.6.26.3-29 kernel, my Zydas stick, at
+ * least, has a "wmaster0" device and a "wlan0" device; the
+ * latter is the one with the IP address. Both show up in
+ * "tcpdump -D" output. Capturing on the wmaster0 device
+ * captures with 802.11 headers.
+ *
+ * airmon-ng searches through /sys/class/net for devices named
+ * monN, starting with mon0; as soon as one *doesn't* exist,
+ * it chooses that as the monitor device name. If the "iw"
+ * command exists, it does "iw dev {if} interface add {monif}
+ * type monitor", where {monif} is the monitor device. It
+ * then (sigh) sleeps .1 second, and then configures the
+ * device up. Otherwise, if /sys/class/ieee80211/{phydev}/add_iface
+ * is a file, it writes {mondev}, without a newline, to that file,
+ * and again (sigh) sleeps .1 second, and then iwconfig's that
+ * device into monitor mode and configures it up. Otherwise,
+ * you can't do monitor mode.
+ *
+ * All these devices are "glued" together by having the
+ * /sys/class/net/{device}/phy80211 links pointing to the same
+ * place, so, given a wmaster, wlan, or mon device, you can
+ * find the other devices by looking for devices with
+ * the same phy80211 link.
+ *
+ * To turn monitor mode off, delete the monitor interface,
+ * either with "iw dev {monif} interface del" or by sending
+ * {monif}, with no NL, down /sys/class/ieee80211/{phydev}/remove_iface
+ *
+ * Note: if you try to create a monitor device named "monN", and
+ * there's already a "monN" device, it fails, as least with
+ * the netlink interface (which is what iw uses), with a return
+ * value of -ENFILE. (Return values are negative errnos.) We
+ * could probably use that to find an unused device.
+ *
+ * Yes, you can have multiple monitor devices for a given
+ * physical device.
*/
/*
return 1;
}
+#ifndef HAVE_LIBNL_2_x
+/* libnl 2.x compatibility code */
+
+#define nl_sock nl_handle
+
+static inline struct nl_handle *
+nl_socket_alloc(void)
+{
+ return nl_handle_alloc();
+}
+
+static inline void
+nl_socket_free(struct nl_handle *h)
+{
+ nl_handle_destroy(h);
+}
+
+static inline int
+__genl_ctrl_alloc_cache(struct nl_handle *h, struct nl_cache **cache)
+{
+ struct nl_cache *tmp = genl_ctrl_alloc_cache(h);
+ if (!tmp)
+ return -ENOMEM;
+ *cache = tmp;
+ return 0;
+}
+#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache
+#endif /* !HAVE_LIBNL_2_x */
+
struct nl80211_state {
- struct nl_handle *nl_handle;
+ struct nl_sock *nl_sock;
struct nl_cache *nl_cache;
struct genl_family *nl80211;
};
static int
nl80211_init(pcap_t *handle, struct nl80211_state *state, const char *device)
{
- state->nl_handle = nl_handle_alloc();
- if (!state->nl_handle) {
+ int err;
+
+ state->nl_sock = nl_socket_alloc();
+ if (!state->nl_sock) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: failed to allocate netlink handle", device);
return PCAP_ERROR;
}
- if (genl_connect(state->nl_handle)) {
+ if (genl_connect(state->nl_sock)) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: failed to connect to generic netlink", device);
goto out_handle_destroy;
}
- state->nl_cache = genl_ctrl_alloc_cache(state->nl_handle);
- if (!state->nl_cache) {
+ err = genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache);
+ if (err < 0) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: failed to allocate generic netlink cache", device);
+ "%s: failed to allocate generic netlink cache: %s",
+ device, strerror(-err));
goto out_handle_destroy;
}
out_cache_free:
nl_cache_free(state->nl_cache);
out_handle_destroy:
- nl_handle_destroy(state->nl_handle);
+ nl_socket_free(state->nl_sock);
return PCAP_ERROR;
}
{
genl_family_put(state->nl80211);
nl_cache_free(state->nl_cache);
- nl_handle_destroy(state->nl_handle);
+ nl_socket_free(state->nl_sock);
}
static int
NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, mondevice);
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR);
- err = nl_send_auto_complete(state->nl_handle, msg);
+ err = nl_send_auto_complete(state->nl_sock, msg);
if (err < 0) {
if (err == -ENFILE) {
/*
return PCAP_ERROR;
}
}
- err = nl_wait_for_ack(state->nl_handle);
+ err = nl_wait_for_ack(state->nl_sock);
if (err < 0) {
if (err == -ENFILE) {
/*
0, NL80211_CMD_DEL_INTERFACE, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
- err = nl_send_auto_complete(state->nl_handle, msg);
+ err = nl_send_auto_complete(state->nl_sock, msg);
if (err < 0) {
if (err == -ENFILE) {
/*
return PCAP_ERROR;
}
}
- err = nl_wait_for_ack(state->nl_handle);
+ err = nl_wait_for_ack(state->nl_sock);
if (err < 0) {
if (err == -ENFILE) {
/*
}
if (errno == ENODEV) {
/* The device doesn't even exist. */
+ (void)snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "SIOCGIWMODE failed: %s", pcap_strerror(errno));
close(sock_fd);
return PCAP_ERROR_NO_SUCH_DEVICE;
}
return 0;
}
+/*
+ * Grabs the number of dropped packets by the interface from /proc/net/dev.
+ *
+ * XXX - what about /sys/class/net/{interface name}/rx_*? There are
+ * individual devices giving, in ASCII, various rx_ and tx_ statistics.
+ *
+ * Or can we get them in binary form from netlink?
+ */
+static long int
+linux_if_drops(const char * if_name)
+{
+ char buffer[512];
+ char * bufptr;
+ 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;
+
+ while (!dropped_pkts && fgets( buffer, sizeof(buffer), file ))
+ {
+ /* search for 'bytes' -- if its in there, then
+ that means we need to grab the fourth field. otherwise
+ grab the third field. */
+ if (field_to_convert != 4 && strstr(buffer, "bytes"))
+ {
+ 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) == ' ') &&
+ *(bufptr + if_name_sz) == ':')
+ {
+ bufptr = bufptr + if_name_sz + 1;
+
+ /* grab the nth field from it */
+ while( --field_to_convert && *bufptr != '\0')
+ {
+ 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;
+}
+
+
/*
* With older kernels promiscuous mode is kind of interesting because we
* have to reset the interface before exiting. The problem can't really
pcap_strerror(errno) );
return PCAP_ERROR;
}
+
+ /*
+ * 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
+ */
+ if (handle->opt.promisc)
+ handle->md.proc_dropped = linux_if_drops(handle->md.device);
/*
* Current Linux kernels use the protocol family PF_PACKET to
* to be compatible with older kernels for a while so we are
* trying both methods with the newer method preferred.
*/
-
- if ((status = activate_new(handle)) == 1) {
+ status = activate_new(handle);
+ if (status < 0) {
+ /*
+ * Fatal error with the new way; just fail.
+ * status has the error return; if it's PCAP_ERROR,
+ * handle->errbuf has been set appropriately.
+ */
+ goto fail;
+ }
+ if (status == 1) {
/*
* Success.
* Try to use memory-mapped access.
*/
- switch (activate_mmap(handle)) {
+ switch (activate_mmap(handle, &status)) {
case 1:
- /* we succeeded; nothing more to do */
- return 0;
+ /*
+ * We succeeded. status has been
+ * set to the status to return,
+ * which might be 0, or might be
+ * a PCAP_WARNING_ value.
+ */
+ return status;
case 0:
/*
* Kernel doesn't support it - just continue
* with non-memory-mapped access.
*/
- status = 0;
break;
case -1:
/*
- * We failed to set up to use it, or kernel
- * supports it, but we failed to enable it;
- * return an error. handle->errbuf contains
- * an error message.
+ * We failed to set up to use it, or the kernel
+ * supports it, but we failed to enable it.
+ * status has been set to the error status to
+ * return and, if it's PCAP_ERROR, handle->errbuf
+ * contains the error message.
*/
- status = PCAP_ERROR;
goto fail;
}
}
*/
goto fail;
}
- } else {
- /*
- * Fatal error with the new way; just fail.
- * status has the error return; if it's PCAP_ERROR,
- * handle->errbuf has been set appropriately.
- */
- goto fail;
}
/*
* We set up the socket, but not with memory-mapped access.
*/
+ status = 0;
if (handle->opt.buffer_size != 0) {
/*
* Set the socket buffer size to the specified value.
*/
if (handle->break_loop) {
/*
- * Yes - clear the flag that indicates that it
- * has, and return -2 as an indication that we
- * were told to break out of the loop.
+ * Yes - clear the flag that indicates that it has,
+ * and return PCAP_ERROR_BREAK as an indication that
+ * we were told to break out of the loop.
*/
handle->break_loop = 0;
- return -2;
+ return PCAP_ERROR_BREAK;
}
#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI)
handle->bufsize - offset, MSG_TRUNC,
(struct sockaddr *) &from, &fromlen);
#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
- } while (packet_len == -1 && (errno == EINTR || errno == ENETDOWN));
+ } while (packet_len == -1 && errno == EINTR);
/* Check if an error occured */
if (packet_len == -1) {
- if (errno == EAGAIN)
+ switch (errno) {
+
+ case EAGAIN:
return 0; /* no packet there */
- else {
+
+ case ENETDOWN:
+ /*
+ * The device on which we're capturing went away.
+ *
+ * XXX - we should really return
+ * PCAP_ERROR_IFACE_NOT_UP, but pcap_dispatch()
+ * etc. aren't defined to return that.
+ */
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "The interface went down");
+ return PCAP_ERROR;
+
+ default:
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"recvfrom: %s", pcap_strerror(errno));
- return -1;
+ return PCAP_ERROR;
}
}
if (ioctl(handle->fd, SIOCGSTAMP, &pcap_header.ts) == -1) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"SIOCGSTAMP: %s", pcap_strerror(errno));
- return -1;
+ return PCAP_ERROR;
}
pcap_header.caplen = caplen;
pcap_header.len = packet_len;
socklen_t len = sizeof (struct tpacket_stats);
#endif
+ long if_dropped = 0;
+
+ /*
+ * To fill in ps_ifdrop, we parse /proc/net/dev for the number
+ */
+ if (handle->opt.promisc)
+ {
+ if_dropped = handle->md.proc_dropped;
+ handle->md.proc_dropped = linux_if_drops(handle->md.device);
+ handle->md.stat.ps_ifdrop += (handle->md.proc_dropped - if_dropped);
+ }
+
#ifdef HAVE_TPACKET_STATS
/*
* Try to get the packet counts from the kernel.
* dropped by the interface driver. It counts only
* packets that passed the filter.
*
+ * See above for ps_ifdrop.
+ *
* Both statistics include packets not yet read from
* the kernel by libpcap, and thus not yet seen by
* the application.
*
* "ps_drop" is not supported.
*
+ * "ps_ifdrop" is supported. It will return the number
+ * of drops the interface reports in /proc/net/dev,
+ * if that is available.
+ *
* "ps_recv" doesn't include packets not yet read from
* the kernel by libpcap.
*
* We maintain the count of packets processed by libpcap in
* "md.packets_read", for reasons described in the comment
* at the end of pcap_read_packet(). We have no idea how many
- * packets were dropped.
+ * packets were dropped by the kernel buffers -- but we know
+ * how many the interface dropped, so we can return that.
*/
+
stats->ps_recv = handle->md.packets_read;
stats->ps_drop = 0;
+ stats->ps_ifdrop = handle->md.stat.ps_ifdrop;
return 0;
}
+/*
+ * Get from "/sys/class/net" all interfaces listed there; if they're
+ * already in the list of interfaces we have, that won't add another
+ * instance, but if they're not, that'll add them.
+ *
+ * We don't bother getting any addresses for them; it appears you can't
+ * use SIOCGIFADDR on Linux to get IPv6 addresses for interfaces, and,
+ * although some other types of addresses can be fetched with SIOCGIFADDR,
+ * we don't bother with them for now.
+ *
+ * We also don't fail if we couldn't open "/sys/class/net"; we just leave
+ * the list of interfaces as is, and return 0, so that we can try
+ * scanning /proc/net/dev.
+ */
+static int
+scan_sys_class_net(pcap_if_t **devlistp, char *errbuf)
+{
+ DIR *sys_class_net_d;
+ int fd;
+ struct dirent *ent;
+ char *p;
+ char name[512]; /* XXX - pick a size */
+ char *q, *saveq;
+ struct ifreq ifrflags;
+ int ret = 1;
+
+ sys_class_net_d = opendir("/sys/class/net");
+ if (sys_class_net_d == NULL && errno == ENOENT)
+ return (0);
+
+ /*
+ * Create a socket from which to fetch interface information.
+ */
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "socket: %s", pcap_strerror(errno));
+ return (-1);
+ }
+
+ for (;;) {
+ errno = 0;
+ ent = readdir(sys_class_net_d);
+ if (ent == NULL) {
+ /*
+ * Error or EOF; if errno != 0, it's an error.
+ */
+ break;
+ }
+
+ /*
+ * Ignore directories (".", "..", and any subdirectories).
+ */
+ if (ent->d_type == DT_DIR)
+ continue;
+
+ /*
+ * Get the interface name.
+ */
+ p = &ent->d_name[0];
+ q = &name[0];
+ while (*p != '\0' && isascii(*p) && !isspace(*p)) {
+ if (*p == ':') {
+ /*
+ * This could be the separator between a
+ * name and an alias number, or it could be
+ * the separator between a name with no
+ * alias number and the next field.
+ *
+ * If there's a colon after digits, it
+ * separates the name and the alias number,
+ * otherwise it separates the name and the
+ * next field.
+ */
+ saveq = q;
+ while (isascii(*p) && isdigit(*p))
+ *q++ = *p++;
+ if (*p != ':') {
+ /*
+ * That was the next field,
+ * not the alias number.
+ */
+ q = saveq;
+ }
+ break;
+ } else
+ *q++ = *p++;
+ }
+ *q = '\0';
+
+ /*
+ * Get the flags for this interface, and skip it if
+ * it's not up.
+ */
+ strncpy(ifrflags.ifr_name, name, sizeof(ifrflags.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) {
+ if (errno == ENXIO || errno == ENODEV)
+ continue;
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "SIOCGIFFLAGS: %.*s: %s",
+ (int)sizeof(ifrflags.ifr_name),
+ ifrflags.ifr_name,
+ pcap_strerror(errno));
+ ret = -1;
+ break;
+ }
+ if (!(ifrflags.ifr_flags & IFF_UP))
+ continue;
+
+ /*
+ * Add an entry for this interface, with no addresses.
+ */
+ if (pcap_add_if(devlistp, name, ifrflags.ifr_flags, NULL,
+ errbuf) == -1) {
+ /*
+ * Failure.
+ */
+ ret = -1;
+ break;
+ }
+ }
+ if (ret != -1) {
+ /*
+ * Well, we didn't fail for any other reason; did we
+ * fail due to an error reading the directory?
+ */
+ if (errno != 0) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "Error reading /sys/class/net: %s",
+ pcap_strerror(errno));
+ ret = -1;
+ }
+ }
+
+ (void)close(fd);
+ (void)closedir(sys_class_net_d);
+ return (ret);
+}
+
+/*
+ * Get from "/proc/net/dev" all interfaces listed there; if they're
+ * already in the list of interfaces we have, that won't add another
+ * instance, but if they're not, that'll add them.
+ *
+ * See comments from scan_sys_class_net().
+ */
+static int
+scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf)
+{
+ FILE *proc_net_f;
+ int fd;
+ char linebuf[512];
+ int linenum;
+ char *p;
+ char name[512]; /* XXX - pick a size */
+ char *q, *saveq;
+ struct ifreq ifrflags;
+ int ret = 0;
+
+ proc_net_f = fopen("/proc/net/dev", "r");
+ if (proc_net_f == NULL && errno == ENOENT)
+ return (0);
+
+ /*
+ * Create a socket from which to fetch interface information.
+ */
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "socket: %s", pcap_strerror(errno));
+ return (-1);
+ }
+
+ for (linenum = 1;
+ fgets(linebuf, sizeof linebuf, proc_net_f) != NULL; linenum++) {
+ /*
+ * Skip the first two lines - they're headers.
+ */
+ if (linenum <= 2)
+ continue;
+
+ p = &linebuf[0];
+
+ /*
+ * Skip leading white space.
+ */
+ while (*p != '\0' && isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0' || *p == '\n')
+ continue; /* blank line */
+
+ /*
+ * Get the interface name.
+ */
+ q = &name[0];
+ while (*p != '\0' && isascii(*p) && !isspace(*p)) {
+ if (*p == ':') {
+ /*
+ * This could be the separator between a
+ * name and an alias number, or it could be
+ * the separator between a name with no
+ * alias number and the next field.
+ *
+ * If there's a colon after digits, it
+ * separates the name and the alias number,
+ * otherwise it separates the name and the
+ * next field.
+ */
+ saveq = q;
+ while (isascii(*p) && isdigit(*p))
+ *q++ = *p++;
+ if (*p != ':') {
+ /*
+ * That was the next field,
+ * not the alias number.
+ */
+ q = saveq;
+ }
+ break;
+ } else
+ *q++ = *p++;
+ }
+ *q = '\0';
+
+ /*
+ * Get the flags for this interface, and skip it if
+ * it's not up.
+ */
+ strncpy(ifrflags.ifr_name, name, sizeof(ifrflags.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) {
+ if (errno == ENXIO)
+ continue;
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "SIOCGIFFLAGS: %.*s: %s",
+ (int)sizeof(ifrflags.ifr_name),
+ ifrflags.ifr_name,
+ pcap_strerror(errno));
+ ret = -1;
+ break;
+ }
+ if (!(ifrflags.ifr_flags & IFF_UP))
+ continue;
+
+ /*
+ * Add an entry for this interface, with no addresses.
+ */
+ if (pcap_add_if(devlistp, name, ifrflags.ifr_flags, NULL,
+ errbuf) == -1) {
+ /*
+ * Failure.
+ */
+ ret = -1;
+ break;
+ }
+ }
+ if (ret != -1) {
+ /*
+ * Well, we didn't fail for any other reason; did we
+ * fail due to an error reading the file?
+ */
+ if (ferror(proc_net_f)) {
+ (void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
+ "Error reading /proc/net/dev: %s",
+ pcap_strerror(errno));
+ ret = -1;
+ }
+ }
+
+ (void)close(fd);
+ (void)fclose(proc_net_f);
+ return (ret);
+}
+
/*
* Description string for the "any" device.
*/
int
pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf)
{
+ int ret;
+
+ /*
+ * Read "/sys/class/net", and add to the list of interfaces all
+ * interfaces listed there that we don't already have, because,
+ * on Linux, SIOCGIFCONF reports only interfaces with IPv4 addresses,
+ * and even getifaddrs() won't return information about
+ * interfaces with no addresses, so you need to read "/sys/class/net"
+ * to get the names of the rest of the interfaces.
+ */
+ ret = scan_sys_class_net(alldevsp, errbuf);
+ if (ret == -1)
+ return (-1); /* failed */
+ if (ret == 0) {
+ /*
+ * No /sys/class/net; try reading /proc/net/dev instead.
+ */
+ if (scan_proc_net_dev(alldevsp, errbuf) == -1)
+ return (-1);
+ }
+
+ /*
+ * Add the "any" device.
+ */
if (pcap_add_if(alldevsp, "any", 0, any_descr, errbuf) < 0)
return (-1);
#ifdef HAVE_DAG_API
+ /*
+ * Add DAG devices.
+ */
if (dag_platform_finddevs(alldevsp, errbuf) < 0)
return (-1);
#endif /* HAVE_DAG_API */
#ifdef HAVE_SEPTEL_API
+ /*
+ * Add Septel devices.
+ */
if (septel_platform_finddevs(alldevsp, errbuf) < 0)
return (-1);
#endif /* HAVE_SEPTEL_API */
+#ifdef HAVE_SNF_API
+ if (snf_platform_finddevs(alldevsp, errbuf) < 0)
+ return (-1);
+#endif /* HAVE_SNF_API */
+
#ifdef PCAP_SUPPORT_BT
+ /*
+ * Add Bluetooth devices.
+ */
if (bt_platform_finddevs(alldevsp, errbuf) < 0)
return (-1);
#endif
#ifdef PCAP_SUPPORT_USB
+ /*
+ * Add USB devices.
+ */
if (usb_platform_finddevs(alldevsp, errbuf) < 0)
return (-1);
#endif
* Attach the given BPF code to the packet capture device.
*/
static int
-pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter)
+pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter,
+ int is_mmapped)
{
#ifdef SO_ATTACH_FILTER
struct sock_fprog fcode;
*
* Oh, and we also need to fix it up so that all "ret"
* instructions with non-zero operands have 65535 as the
- * operand, 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.
+ * 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.
*/
- switch (fix_program(handle, &fcode)) {
+ switch (fix_program(handle, &fcode, is_mmapped)) {
case -1:
default:
return 0;
}
+static int
+pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter)
+{
+ return pcap_setfilter_linux_common(handle, filter, 0);
+}
+
+
/*
* Set direction flag: Which packets do we accept on a forwarding
* single device? IN, OUT or both?
return -1;
}
-
#ifdef HAVE_PF_PACKET_SOCKETS
/*
* Map the PACKET_ value to a LINUX_SLL_ value; we
case ARPHRD_CHAOS:
handle->linktype = DLT_CHAOS;
break;
+#ifndef ARPHRD_CAN
+#define ARPHRD_CAN 280
+#endif
+ case ARPHRD_CAN:
+ handle->linktype = DLT_CAN_SOCKETCAN;
+ break;
#ifndef ARPHRD_IEEE802_TR
#define ARPHRD_IEEE802_TR 800 /* From Linux 2.4 */
handle->linktype = DLT_RAW;
break;
+#ifndef ARPHRD_IEEE802154
+#define ARPHRD_IEEE802154 804
+#endif
+ case ARPHRD_IEEE802154:
+ handle->linktype = DLT_IEEE802_15_4_NOFCS;
+ break;
+
default:
handle->linktype = -1;
break;
#endif
}
+#ifdef HAVE_PACKET_RING
+/*
+ * Attempt to activate with memory-mapped access.
+ *
+ * On success, returns 1, and sets *status to 0 if there are no warnings
+ * or to a PCAP_WARNING_ code if there is a warning.
+ *
+ * On failure due to lack of support for memory-mapped capture, returns
+ * 0.
+ *
+ * On error, returns -1, and sets *status to the appropriate error code;
+ * if that is PCAP_ERROR, sets handle->errbuf to the appropriate message.
+ */
static int
-activate_mmap(pcap_t *handle)
+activate_mmap(pcap_t *handle, int *status)
{
-#ifdef HAVE_PACKET_RING
int ret;
+ /*
+ * Attempt to allocate a buffer to hold the contents of one
+ * packet, for use by the oneshot callback.
+ */
+ handle->md.oneshot_buffer = malloc(handle->snapshot);
+ if (handle->md.oneshot_buffer == NULL) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't allocate oneshot buffer: %s",
+ pcap_strerror(errno));
+ *status = PCAP_ERROR;
+ return -1;
+ }
+
if (handle->opt.buffer_size == 0) {
/* by default request 2M for the ring buffer */
handle->opt.buffer_size = 2*1024*1024;
}
ret = prepare_tpacket_socket(handle);
- if (ret != 1)
- return ret;
- ret = create_ring(handle);
- if (ret != 1)
+ if (ret == -1) {
+ free(handle->md.oneshot_buffer);
+ *status = PCAP_ERROR;
return ret;
+ }
+ ret = create_ring(handle, status);
+ if (ret == 0) {
+ /*
+ * We don't support memory-mapped capture; our caller
+ * will fall back on reading from the socket.
+ */
+ free(handle->md.oneshot_buffer);
+ return 0;
+ }
+ if (ret == -1) {
+ /*
+ * Error attempting to enable memory-mapped capture;
+ * fail. create_ring() has set *status.
+ */
+ free(handle->md.oneshot_buffer);
+ return -1;
+ }
- /* override some defaults and inherit the other fields from
- * activate_new
- * handle->offset is used to get the current position into the rx ring
- * handle->cc is used to store the ring size */
+ /*
+ * Success. *status has been set either to 0 if there are no
+ * warnings or to a PCAP_WARNING_ value if there is a warning.
+ *
+ * Override some defaults and inherit the other fields from
+ * activate_new.
+ * handle->offset is used to get the current position into the rx ring.
+ * handle->cc is used to store the ring size.
+ */
handle->read_op = pcap_read_linux_mmap;
handle->cleanup_op = pcap_cleanup_linux_mmap;
handle->setfilter_op = pcap_setfilter_linux_mmap;
handle->setnonblock_op = pcap_setnonblock_mmap;
handle->getnonblock_op = pcap_getnonblock_mmap;
+ handle->oneshot_callback = pcap_oneshot_mmap;
handle->selectable_fd = handle->fd;
return 1;
+}
#else /* HAVE_PACKET_RING */
+static int
+activate_mmap(pcap_t *handle _U_, int *status _U_)
+{
return 0;
-#endif /* HAVE_PACKET_RING */
}
+#endif /* HAVE_PACKET_RING */
#ifdef HAVE_PACKET_RING
+/*
+ * Attempt to set the socket to version 2 of the memory-mapped header.
+ * Return 1 if we succeed or if we fail because version 2 isn't
+ * supported; return -1 on any other error, and set handle->errbuf.
+ */
static int
prepare_tpacket_socket(pcap_t *handle)
{
return 1;
}
-static void
-compute_ring_block(int frame_size, unsigned *block_size, unsigned *frames_per_block)
-{
- /* 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. */
- *block_size = getpagesize();
- while (*block_size < frame_size)
- *block_size <<= 1;
-
- *frames_per_block = *block_size/frame_size;
-}
-
+/*
+ * Attempt to set up memory-mapped access.
+ *
+ * On success, returns 1, and sets *status to 0 if there are no warnings
+ * or to a PCAP_WARNING_ code if there is a warning.
+ *
+ * On failure due to lack of support for memory-mapped capture, returns
+ * 0.
+ *
+ * On error, returns -1, and sets *status to the appropriate error code;
+ * if that is PCAP_ERROR, sets handle->errbuf to the appropriate message.
+ */
static int
-create_ring(pcap_t *handle)
+create_ring(pcap_t *handle, int *status)
{
- unsigned i, j, ringsize, frames_per_block;
+ unsigned i, j, frames_per_block;
struct tpacket_req req;
+ /*
+ * Start out assuming no warnings or errors.
+ */
+ *status = 0;
+
/* Note that with large snapshot (say 64K) only a few frames
* will be available in the ring even with pretty large ring size
* (and a lot of memory will be unused).
TPACKET_ALIGN(handle->md.tp_hdrlen) +
sizeof(struct sockaddr_ll));
req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size;
- compute_ring_block(req.tp_frame_size, &req.tp_block_size, &frames_per_block);
- req.tp_block_nr = req.tp_frame_nr / frames_per_block;
- /* 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;
+ /* 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)
+ req.tp_block_size <<= 1;
+
+ frames_per_block = req.tp_block_size/req.tp_frame_size;
+
+ /*
+ * PACKET_TIMESTAMP was added after linux/net_tstamp.h was,
+ * so we check for PACKET_TIMESTAMP. We check for
+ * linux/net_tstamp.h just in case a system somehow has
+ * PACKET_TIMESTAMP but not linux/net_tstamp.h; that might
+ * be unnecessary.
+ *
+ * SIOCSHWTSTAMP was introduced in the patch that introduced
+ * linux/net_tstamp.h, so we don't bother checking whether
+ * SIOCSHWTSTAMP is defined (if your Linux system has
+ * linux/net_tstamp.h but doesn't define SIOCSHWTSTAMP, your
+ * Linux system is badly broken).
+ */
+#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
+ /*
+ * If we were told to do so, ask the kernel and the driver
+ * to use hardware timestamps.
+ *
+ * Hardware timestamps are only supported with mmapped
+ * captures.
+ */
+ if (handle->opt.tstamp_type == PCAP_TSTAMP_ADAPTER ||
+ handle->opt.tstamp_type == PCAP_TSTAMP_ADAPTER_UNSYNCED) {
+ struct hwtstamp_config hwconfig;
+ struct ifreq ifr;
+ int timesource;
+
+ /*
+ * Ask for hardware time stamps on all packets,
+ * including transmitted packets.
+ */
+ memset(&hwconfig, 0, sizeof(hwconfig));
+ hwconfig.tx_type = HWTSTAMP_TX_ON;
+ hwconfig.rx_filter = HWTSTAMP_FILTER_ALL;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, handle->opt.source);
+ ifr.ifr_data = (void *)&hwconfig;
+
+ if (ioctl(handle->fd, SIOCSHWTSTAMP, &ifr) < 0) {
+ switch (errno) {
+
+ case EPERM:
+ /*
+ * Treat this as an error, as the
+ * user should try to run this
+ * with the appropriate privileges -
+ * and, if they can't, shouldn't
+ * try requesting hardware time stamps.
+ */
+ *status = PCAP_ERROR_PERM_DENIED;
+ return -1;
+
+ case EOPNOTSUPP:
+ /*
+ * Treat this as a warning, as the
+ * only way to fix the warning is to
+ * get an adapter that supports hardware
+ * time stamps. We'll just fall back
+ * on the standard host time stamps.
+ */
+ *status = PCAP_WARNING_TSTAMP_TYPE_NOTSUP;
+ break;
+
+ default:
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "SIOCSHWTSTAMP failed: %s",
+ pcap_strerror(errno));
+ *status = PCAP_ERROR;
+ return -1;
+ }
+ } else {
+ /*
+ * Well, that worked. Now specify the type of
+ * hardware time stamp we want for this
+ * socket.
+ */
+ if (handle->opt.tstamp_type == PCAP_TSTAMP_ADAPTER) {
+ /*
+ * Hardware timestamp, synchronized
+ * with the system clock.
+ */
+ timesource = SOF_TIMESTAMPING_SYS_HARDWARE;
+ } else {
+ /*
+ * PCAP_TSTAMP_ADAPTER_UNSYNCED - hardware
+ * timestamp, not synchronized with the
+ * system clock.
+ */
+ timesource = SOF_TIMESTAMPING_RAW_HARDWARE;
+ }
+ if (setsockopt(handle->fd, SOL_PACKET, PACKET_TIMESTAMP,
+ (void *)×ource, sizeof(timesource))) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't set PACKET_TIMESTAMP: %s",
+ pcap_strerror(errno));
+ *status = PCAP_ERROR;
+ return -1;
+ }
+ }
+ }
+#endif /* HAVE_LINUX_NET_TSTAMP_H && PACKET_TIMESTAMP */
/* ask the kernel to create the ring */
retry:
+ req.tp_block_nr = req.tp_frame_nr / frames_per_block;
+
+ /* 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;
+
if (setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING,
(void *) &req, sizeof(req))) {
- /* try to reduce requested ring size to prevent memory failure */
if ((errno == ENOMEM) && (req.tp_block_nr > 1)) {
- req.tp_frame_nr >>= 1;
- req.tp_block_nr = req.tp_frame_nr/frames_per_block;
+ /*
+ * Memory failure; try to reduce the requested ring
+ * size.
+ *
+ * We used to reduce this by half -- do 5% instead.
+ * That may result in more iterations and a longer
+ * startup, but the user will be much happier with
+ * the resulting buffer size.
+ */
+ if (req.tp_frame_nr < 20)
+ req.tp_frame_nr -= 1;
+ else
+ req.tp_frame_nr -= req.tp_frame_nr/20;
goto retry;
}
if (errno == ENOPROTOOPT) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't create rx ring on packet socket: %s",
pcap_strerror(errno));
+ *status = PCAP_ERROR;
return -1;
}
/* memory map the rx ring */
- ringsize = req.tp_block_nr * req.tp_block_size;
- handle->bp = mmap(0, ringsize, PROT_READ| PROT_WRITE, MAP_SHARED,
- handle->fd, 0);
- if (handle->bp == MAP_FAILED) {
+ handle->md.mmapbuflen = req.tp_block_nr * req.tp_block_size;
+ handle->md.mmapbuf = mmap(0, handle->md.mmapbuflen,
+ PROT_READ|PROT_WRITE, MAP_SHARED, handle->fd, 0);
+ if (handle->md.mmapbuf == MAP_FAILED) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"can't mmap rx ring: %s", pcap_strerror(errno));
/* clear the allocated ring on error*/
destroy_ring(handle);
+ *status = PCAP_ERROR;
return -1;
}
pcap_strerror(errno));
destroy_ring(handle);
+ *status = PCAP_ERROR;
return -1;
}
/* fill the header ring with proper frame ptr*/
handle->offset = 0;
for (i=0; i<req.tp_block_nr; ++i) {
- void *base = &handle->bp[i*req.tp_block_size];
+ void *base = &handle->md.mmapbuf[i*req.tp_block_size];
for (j=0; j<frames_per_block; ++j, ++handle->offset) {
RING_GET_FRAME(handle) = base;
base += req.tp_frame_size;
(void *) &req, sizeof(req));
/* if ring is mapped, unmap it*/
- if (handle->bp) {
- /* need to re-compute the ring size */
- unsigned frames_per_block, block_size;
- compute_ring_block(handle->bufsize, &block_size, &frames_per_block);
-
- /* do not perform sanity check here: we can't recover any error */
- munmap(handle->bp, block_size * handle->cc / frames_per_block);
- handle->bp = 0;
+ if (handle->md.mmapbuf) {
+ /* do not test for mmap failure, as we can't recover from any error */
+ munmap(handle->md.mmapbuf, handle->md.mmapbuflen);
+ handle->md.mmapbuf = NULL;
}
}
+/*
+ * Special one-shot callback, used for pcap_next() and pcap_next_ex(),
+ * for Linux mmapped capture.
+ *
+ * The problem is that pcap_next() and pcap_next_ex() expect the packet
+ * data handed to the callback to be valid after the callback returns,
+ * but pcap_read_linux_mmap() has to release that packet as soon as
+ * the callback returns (otherwise, the kernel thinks there's still
+ * at least one unprocessed packet available in the ring, so a select()
+ * will immediately return indicating that there's data to process), so,
+ * in the callback, we have to make a copy of the packet.
+ *
+ * Yes, this means that, if the capture is using the ring buffer, using
+ * pcap_next() or pcap_next_ex() requires more copies than using
+ * pcap_loop() or pcap_dispatch(). If that bothers you, don't use
+ * pcap_next() or pcap_next_ex().
+ */
+static void
+pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h,
+ const u_char *bytes)
+{
+ struct oneshot_userdata *sp = (struct oneshot_userdata *)user;
+
+ *sp->hdr = *h;
+ memcpy(sp->pd->md.oneshot_buffer, bytes, h->caplen);
+ *sp->pkt = sp->pd->md.oneshot_buffer;
+}
+
static void
pcap_cleanup_linux_mmap( pcap_t *handle )
{
destroy_ring(handle);
+ if (handle->md.oneshot_buffer != NULL) {
+ free(handle->md.oneshot_buffer);
+ handle->md.oneshot_buffer = NULL;
+ }
pcap_cleanup_linux(handle);
}
/* map each value to the corresponding 2's complement, to
* preserve the timeout value provided with pcap_set_timeout */
if (nonblock) {
- if (p->md.timeout > 0)
+ if (p->md.timeout >= 0) {
+ /*
+ * Timeout is non-negative, so we're not already
+ * in non-blocking mode; set it to the 2's
+ * complement, to make it negative, as an
+ * indication that we're in non-blocking mode.
+ */
p->md.timeout = p->md.timeout*-1 - 1;
- } else
- if (p->md.timeout < 0)
+ }
+ } else {
+ if (p->md.timeout < 0) {
+ /*
+ * Timeout is negative, so we're not already
+ * in blocking mode; reverse the previous
+ * operation, to make the timeout non-negative
+ * again.
+ */
p->md.timeout = (p->md.timeout+1)*-1;
+ }
+ }
return 0;
}
return h.raw;
}
+#ifndef POLLRDHUP
+#define POLLRDHUP 0
+#endif
+
static int
pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback,
u_char *user)
{
+ int timeout;
int pkts = 0;
+ char c;
/* wait for frames availability.*/
- if ((handle->md.timeout >= 0) &&
- !pcap_get_ring_frame(handle, TP_STATUS_USER)) {
+ if (!pcap_get_ring_frame(handle, TP_STATUS_USER)) {
struct pollfd pollinfo;
int ret;
pollinfo.fd = handle->fd;
pollinfo.events = POLLIN;
+ if (handle->md.timeout == 0)
+ timeout = -1; /* block forever */
+ else if (handle->md.timeout > 0)
+ timeout = handle->md.timeout; /* block for that amount of time */
+ else
+ timeout = 0; /* non-blocking mode - poll to pick up errors */
do {
- /* poll() requires a negative timeout to wait forever */
- ret = poll(&pollinfo, 1, (handle->md.timeout > 0)?
- handle->md.timeout: -1);
- if ((ret < 0) && (errno != EINTR)) {
+ ret = poll(&pollinfo, 1, timeout);
+ if (ret < 0 && errno != EINTR) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "can't poll on packet socket fd %d: %d-%s",
- handle->fd, errno, pcap_strerror(errno));
- return -1;
- }
+ "can't poll on packet socket: %s",
+ pcap_strerror(errno));
+ return PCAP_ERROR;
+ } else if (ret > 0 &&
+ (pollinfo.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) {
+ /*
+ * There's some indication other than
+ * "you can read on this descriptor" on
+ * the descriptor.
+ */
+ if (pollinfo.revents & (POLLHUP | POLLRDHUP)) {
+ snprintf(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ "Hangup on packet socket");
+ return PCAP_ERROR;
+ }
+ if (pollinfo.revents & POLLERR) {
+ /*
+ * A recv() will give us the
+ * actual error code.
+ *
+ * XXX - make the socket non-blocking?
+ */
+ if (recv(handle->fd, &c, sizeof c,
+ MSG_PEEK) != -1)
+ continue; /* what, no error? */
+ if (errno == ENETDOWN) {
+ /*
+ * The device on which we're
+ * capturing went away.
+ *
+ * XXX - we should really return
+ * PCAP_ERROR_IFACE_NOT_UP,
+ * but pcap_dispatch() etc.
+ * aren't defined to return
+ * that.
+ */
+ snprintf(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ "The interface went down");
+ } else {
+ snprintf(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ "Error condition on packet socket: %s",
+ strerror(errno));
+ }
+ return PCAP_ERROR;
+ }
+ if (pollinfo.revents & POLLNVAL) {
+ snprintf(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ "Invalid polling request on packet socket");
+ return PCAP_ERROR;
+ }
+ }
/* check for break loop condition on interrupted syscall*/
if (handle->break_loop) {
handle->break_loop = 0;
- return -2;
+ return PCAP_ERROR_BREAK;
}
} while (ret < 0);
}
}
#endif
+ /*
+ * The only way to tell the kernel to cut off the
+ * packet at a snapshot length is with a filter program;
+ * if there's no filter program, the kernel won't cut
+ * the packet off.
+ *
+ * Trim the snapshot length to be no longer than the
+ * specified snapshot length.
+ */
+ if (pcaphdr.caplen > handle->snapshot)
+ pcaphdr.caplen = handle->snapshot;
+
/* pass the packet to the user */
pkts++;
callback(user, &pcaphdr, bp);
/* check for break loop condition*/
if (handle->break_loop) {
handle->break_loop = 0;
- return -2;
+ return PCAP_ERROR_BREAK;
}
}
return pkts;
pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter)
{
int n, offset;
- int ret = pcap_setfilter_linux(handle, filter);
+ int ret;
+
+ /*
+ * Don't rewrite "ret" instructions; we don't need to, as
+ * we're not reading packets with recvmsg(), and we don't
+ * want to, as, by not rewriting them, the kernel can avoid
+ * copying extra data.
+ */
+ ret = pcap_setfilter_linux_common(handle, filter, 1);
if (ret < 0)
return ret;
#ifdef SO_ATTACH_FILTER
static int
-fix_program(pcap_t *handle, struct sock_fprog *fcode)
+fix_program(pcap_t *handle, struct sock_fprog *fcode, int is_mmapped)
{
size_t prog_size;
register int i;
case BPF_RET:
/*
- * It's a return instruction; is the snapshot
- * length a constant, rather than the contents
- * of the accumulator?
+ * It's a return instruction; are we capturing
+ * in memory-mapped mode?
*/
- if (BPF_MODE(p->code) == BPF_K) {
+ if (!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()",
- * 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.
+ * No; is the snapshot length a constant,
+ * rather than the contents of the
+ * accumulator?
*/
- if (p->k != 0)
- p->k = 65535;
+ if (BPF_MODE(p->code) == BPF_K) {
+ /*
+ * 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()",
+ * 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.
+ */
+ if (p->k != 0)
+ p->k = 65535;
+ }
}
break;