summaryrefslogtreecommitdiff
path: root/pcap-linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'pcap-linux.c')
-rw-r--r--pcap-linux.c1571
1 files changed, 1171 insertions, 400 deletions
diff --git a/pcap-linux.c b/pcap-linux.c
index 900ebbc0dd56..924df42af061 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -25,10 +25,10 @@
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Modifications: Added PACKET_MMAP support
- * Paolo Abeni <paolo.abeni@email.it>
+ * Paolo Abeni <paolo.abeni@email.it>
* Added TPACKET_V3 support
* Gabor Tatarka <gabor.tatarka@ericsson.com>
- *
+ *
* based on previous works of:
* Simon Patarin <patarin@cs.unibo.it>
* Phil Wood <cpw@lanl.gov>
@@ -47,7 +47,7 @@
* 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.
@@ -56,12 +56,12 @@
*
* 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.
@@ -188,9 +188,10 @@
# 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
# ifdef TPACKET_HDRLEN
# define HAVE_PACKET_RING
# ifdef TPACKET3_HDRLEN
@@ -202,6 +203,7 @@
# define TPACKET_V1 0 /* Old kernel with only V1, so no TPACKET_Vn defined */
# endif /* TPACKET2_HDRLEN */
# endif /* TPACKET_HDRLEN */
+# endif /* PCAP_SUPPORT_PACKET_RING */
#endif /* PF_PACKET */
#ifdef SO_ATTACH_FILTER
@@ -213,6 +215,23 @@
#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?
*/
@@ -300,6 +319,7 @@ struct pcap_linux {
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. */
@@ -316,7 +336,7 @@ struct pcap_linux {
/*
* Prototypes for internal functions and methods.
*/
-static void map_arphrd_to_dlt(pcap_t *, int, const char *, int);
+static void map_arphrd_to_dlt(pcap_t *, int, int, const char *, int);
#ifdef HAVE_PF_PACKET_SOCKETS
static short int map_packet_type_to_sll_type(short int);
#endif
@@ -334,8 +354,30 @@ static int pcap_setdirection_linux(pcap_t *, pcap_direction_t);
static int pcap_set_datalink_linux(pcap_t *, int);
static void pcap_cleanup_linux(pcap_t *);
+/*
+ * This is what the header structure looks like in a 64-bit kernel;
+ * we use this, rather than struct tpacket_hdr, if we're using
+ * TPACKET_V1 in 32-bit code running on a 64-bit kernel.
+ */
+struct tpacket_hdr_64 {
+ uint64_t tp_status;
+ unsigned int tp_len;
+ unsigned int tp_snaplen;
+ unsigned short tp_mac;
+ unsigned short tp_net;
+ unsigned int tp_sec;
+ unsigned int tp_usec;
+};
+
+/*
+ * We use this internally as the tpacket version for TPACKET_V1 in
+ * 32-bit code on a 64-bit kernel.
+ */
+#define TPACKET_V1_64 99
+
union thdr {
struct tpacket_hdr *h1;
+ struct tpacket_hdr_64 *h1_64;
#ifdef HAVE_TPACKET2
struct tpacket2_hdr *h2;
#endif
@@ -346,13 +388,15 @@ union thdr {
};
#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 prepare_tpacket_socket(pcap_t *handle);
static void pcap_cleanup_linux_mmap(pcap_t *);
static int pcap_read_linux_mmap_v1(pcap_t *, int, pcap_handler , u_char *);
+static int pcap_read_linux_mmap_v1_64(pcap_t *, int, pcap_handler , u_char *);
#ifdef HAVE_TPACKET2
static int pcap_read_linux_mmap_v2(pcap_t *, int, pcap_handler , u_char *);
#endif
@@ -366,6 +410,12 @@ static void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h,
const u_char *bytes);
#endif
+#ifdef TP_STATUS_VLAN_TPID_VALID
+# define VLAN_TPID(hdr, hv) (((hv)->tp_vlan_tpid || ((hdr)->tp_status & TP_STATUS_VLAN_TPID_VALID)) ? (hv)->tp_vlan_tpid : ETH_P_8021Q)
+#else
+# define VLAN_TPID(hdr, hv) ETH_P_8021Q
+#endif
+
/*
* Wrap some ioctl calls
*/
@@ -382,7 +432,13 @@ static int has_wext(int sock_fd, const char *device, char *ebuf);
static int enter_rfmon_mode(pcap_t *handle, int sock_fd,
const char *device);
#endif /* HAVE_PF_PACKET_SOCKETS */
+#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
+static int iface_ethtool_get_ts_info(const char *device, pcap_t *handle,
+ char *ebuf);
+#endif
+#ifdef HAVE_PACKET_RING
static int iface_get_offload(pcap_t *handle);
+#endif
static int iface_bind_old(int fd, const char *device, char *ebuf);
#ifdef SO_ATTACH_FILTER
@@ -403,34 +459,21 @@ pcap_create_interface(const char *device, char *ebuf)
{
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;
handle->activate_op = pcap_activate_linux;
handle->can_set_rfmon_op = pcap_can_set_rfmon_linux;
+
#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
/*
- * We claim that we support:
- *
- * software time stamps, with no details about their precision;
- * hardware time stamps, synced to the host time;
- * hardware time stamps, not synced to the host time.
- *
- * XXX - we can't ask a device whether it supports
- * hardware time stamps, so we just claim all devices do.
+ * See what time stamp types we support.
*/
- handle->tstamp_type_count = 3;
- handle->tstamp_type_list = malloc(3 * sizeof(u_int));
- if (handle->tstamp_type_list == NULL) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
- pcap_strerror(errno));
- free(handle);
+ if (iface_ethtool_get_ts_info(device, handle, ebuf) == -1) {
+ pcap_close(handle);
return NULL;
}
- handle->tstamp_type_list[0] = PCAP_TSTAMP_HOST;
- handle->tstamp_type_list[1] = PCAP_TSTAMP_ADAPTER;
- handle->tstamp_type_list[2] = PCAP_TSTAMP_ADAPTER_UNSYNCED;
#endif
#if defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS)
@@ -445,11 +488,9 @@ pcap_create_interface(const char *device, char *ebuf)
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;
@@ -501,7 +542,7 @@ pcap_create_interface(const char *device, char *ebuf)
*
* 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
@@ -519,7 +560,7 @@ get_mac80211_phydev(pcap_t *handle, const char *device, char *phydev_path,
* 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;
@@ -534,7 +575,7 @@ get_mac80211_phydev(pcap_t *handle, const char *device, char *phydev_path,
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);
@@ -591,20 +632,20 @@ nl80211_init(pcap_t *handle, struct nl80211_state *state, const char *device)
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;
@@ -612,7 +653,7 @@ nl80211_init(pcap_t *handle, struct nl80211_state *state, const char *device)
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;
}
@@ -635,9 +676,14 @@ nl80211_cleanup(struct nl80211_state *state)
}
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;
@@ -648,7 +694,7 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
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;
}
@@ -680,7 +726,7 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
* 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);
@@ -708,7 +754,7 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
* 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);
@@ -720,10 +766,24 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
* 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);
@@ -744,7 +804,7 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
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;
}
@@ -755,7 +815,7 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
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);
@@ -763,7 +823,7 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
}
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);
@@ -777,7 +837,7 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
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);
@@ -822,10 +882,13 @@ enter_rfmon_mode_mac80211(pcap_t *handle, int sock_fd, const char *device)
*/
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) {
@@ -838,7 +901,7 @@ enter_rfmon_mode_mac80211(pcap_t *handle, int sock_fd, const char *device)
}
}
- 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;
@@ -863,7 +926,10 @@ added:
* "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;
}
/*
@@ -872,7 +938,7 @@ added:
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,
@@ -882,7 +948,7 @@ added:
}
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,
@@ -911,6 +977,37 @@ added:
}
#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_can_set_rfmon_linux(pcap_t *handle)
{
@@ -923,7 +1020,7 @@ 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.
*/
@@ -943,7 +1040,7 @@ pcap_can_set_rfmon_linux(pcap_t *handle)
* 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 */
@@ -964,15 +1061,21 @@ pcap_can_set_rfmon_linux(pcap_t *handle)
*/
sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
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) {
/*
@@ -983,7 +1086,7 @@ pcap_can_set_rfmon_linux(pcap_t *handle)
}
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;
@@ -1009,7 +1112,7 @@ linux_if_drops(const char * if_name)
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;
@@ -1024,7 +1127,7 @@ linux_if_drops(const char * if_name)
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) == ' ') &&
@@ -1038,20 +1141,20 @@ linux_if_drops(const char * if_name)
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;
-}
+}
/*
@@ -1221,6 +1324,94 @@ static void pcap_cleanup_linux( pcap_t *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
* information of course). If you pass 1 as promisc the interface
@@ -1237,7 +1428,7 @@ pcap_activate_linux(pcap_t *handle)
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
@@ -1274,7 +1465,7 @@ pcap_activate_linux(pcap_t *handle)
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;
}
@@ -1282,16 +1473,16 @@ pcap_activate_linux(pcap_t *handle)
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
*/
@@ -1330,7 +1521,11 @@ pcap_activate_linux(pcap_t *handle)
* 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:
@@ -1375,7 +1570,7 @@ pcap_activate_linux(pcap_t *handle)
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;
@@ -1386,7 +1581,7 @@ pcap_activate_linux(pcap_t *handle)
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;
@@ -1448,6 +1643,22 @@ linux_check_direction(const pcap_t *handle, const struct sockaddr_ll *sll)
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 (handle->direction == PCAP_D_IN)
@@ -1494,6 +1705,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
int packet_len, caplen;
struct pcap_pkthdr pcap_header;
+ struct bpf_aux_data aux_data;
#ifdef HAVE_PF_PACKET_SOCKETS
/*
* If this is a cooked device, leave extra room for a
@@ -1526,7 +1738,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
* 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;
@@ -1582,12 +1794,12 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
* 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;
}
@@ -1666,17 +1878,35 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
#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(ETH_P_8021Q);
+ tag->vlan_tpid = htons(VLAN_TPID(aux, aux));
tag->vlan_tci = htons(aux->tp_vlan_tci);
+ /* store vlan tci to bpf_aux_data struct for userland bpf filter */
+#if defined(TP_STATUS_VLAN_VALID)
+ aux_data.vlan_tag = htons(aux->tp_vlan_tci) & 0x0fff;
+ aux_data.vlan_tag_present = (aux->tp_status & TP_STATUS_VLAN_VALID);
+#endif
+
+ /*
+ * Add the tag to the packet lengths.
+ */
packet_len += VLAN_TAG_LEN;
}
}
@@ -1721,9 +1951,8 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
/* Run the packet filter if not using kernel filter */
if (handlep->filter_in_userland && handle->fcode.bf_insns) {
- if (bpf_filter(handle->fcode.bf_insns, bp,
- packet_len, caplen) == 0)
- {
+ if (bpf_filter_with_aux_data(handle->fcode.bf_insns, bp,
+ packet_len, caplen, &aux_data) == 0) {
/* rejected by filter */
return 0;
}
@@ -1735,7 +1964,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
#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;
}
@@ -1743,7 +1972,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
#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;
}
@@ -1841,12 +2070,12 @@ pcap_inject_linux(pcap_t *handle, const void *buf, size_t size)
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.
@@ -1884,8 +2113,8 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
#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)
@@ -1915,7 +2144,7 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
* 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
@@ -1944,7 +2173,7 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
* "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.
*/
@@ -1963,7 +2192,7 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
* 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;
}
@@ -1990,10 +2219,10 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
* 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;
@@ -2049,7 +2278,7 @@ add_linux_if(pcap_if_t **devlistp, const char *ifname, int fd, char *errbuf)
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,
@@ -2060,7 +2289,8 @@ add_linux_if(pcap_if_t **devlistp, const char *ifname, int fd, char *errbuf)
/*
* Add an entry for this interface, with no addresses.
*/
- if (pcap_add_if(devlistp, name, ifrflags.ifr_flags, NULL,
+ if (pcap_add_if(devlistp, name,
+ if_flags_to_pcap_flags(name, ifrflags.ifr_flags), NULL,
errbuf) == -1) {
/*
* Failure.
@@ -2108,7 +2338,7 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf)
/*
* 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);
}
@@ -2116,9 +2346,9 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf)
/*
* 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);
@@ -2155,7 +2385,7 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf)
* 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) {
/*
@@ -2186,7 +2416,7 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf)
* 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;
@@ -2226,7 +2456,7 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf)
/*
* 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);
}
@@ -2234,9 +2464,9 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf)
/*
* 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);
@@ -2275,7 +2505,7 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf)
* 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;
@@ -2292,12 +2522,27 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf)
*/
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)
{
int ret;
/*
+ * Get the list of regular interfaces first.
+ */
+ if (pcap_findalldevs_interfaces(alldevsp, 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,
* on Linux, SIOCGIFCONF reports only interfaces with IPv4 addresses,
@@ -2319,7 +2564,7 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf)
/*
* Add the "any" device.
*/
- if (pcap_add_if(alldevsp, "any", IFF_UP|IFF_RUNNING,
+ if (pcap_add_if(alldevsp, "any", PCAP_IF_UP|PCAP_IF_RUNNING,
any_descr, errbuf) < 0)
return (-1);
@@ -2478,8 +2723,14 @@ pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter,
* calling "pcap_setfilter()". Otherwise, the kernel filter may
* filter out packets that would pass the new userland filter.
*/
- if (handlep->filter_in_userland)
- reset_kernel_filter(handle);
+ if (handlep->filter_in_userland) {
+ if (reset_kernel_filter(handle) == -1) {
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't remove kernel filter: %s",
+ pcap_strerror(errno));
+ err = -2; /* fatal error */
+ }
+ }
/*
* Free up the copy of the filter that was made by "fix_program()".
@@ -2521,7 +2772,7 @@ pcap_setdirection_linux(pcap_t *handle, pcap_direction_t d)
* 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;
}
@@ -2561,6 +2812,51 @@ map_packet_type_to_sll_type(short int sll_pkttype)
}
#endif
+static int
+is_wifi(int sock_fd
+#ifndef IW_MODE_MONITOR
+_U_
+#endif
+, const char *device)
+{
+ char *pathstr;
+ struct stat statb;
+#ifdef IW_MODE_MONITOR
+ char errbuf[PCAP_ERRBUF_SIZE];
+#endif
+
+ /*
+ * See if there's a sysfs wireless directory for it.
+ * If so, it's a wireless interface.
+ */
+ if (asprintf(&pathstr, "/sys/class/net/%s/wireless", device) == -1) {
+ /*
+ * Just give up here.
+ */
+ return 0;
+ }
+ if (stat(pathstr, &statb) == 0) {
+ free(pathstr);
+ return 1;
+ }
+ free(pathstr);
+
+#ifdef IW_MODE_MONITOR
+ /*
+ * OK, maybe it's not wireless, or maybe this kernel doesn't
+ * support sysfs. Try the wireless extensions.
+ */
+ if (has_wext(sock_fd, device, errbuf) == 1) {
+ /*
+ * It supports the wireless extensions, so it's a Wi-Fi
+ * device.
+ */
+ return 1;
+ }
+#endif
+ return 0;
+}
+
/*
* Linux uses the ARP hardware type to identify the type of an
* interface. pcap uses the DLT_xxx constants for this. This
@@ -2579,8 +2875,8 @@ map_packet_type_to_sll_type(short int sll_pkttype)
*
* Sets the link type to -1 if unable to map the type.
*/
-static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device,
- int cooked_ok)
+static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype,
+ const char *device, int cooked_ok)
{
static const char cdma_rmnet[] = "cdma_rmnet";
@@ -2591,7 +2887,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device,
* For various annoying reasons having to do with DHCP
* software, some versions of Android give the mobile-
* phone-network interface an ARPHRD_ value of
- * ARPHRD_ETHER, even though the packet supplied by
+ * ARPHRD_ETHER, even though the packets supplied by
* that interface have no link-layer header, and begin
* with an IP header, so that the ARPHRD_ value should
* be ARPHRD_NONE.
@@ -2603,9 +2899,9 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device,
handle->linktype = DLT_RAW;
return;
}
-
+
/*
- * This is (presumably) a real Ethernet capture; give it a
+ * Is this a real Ethernet device? If so, give it a
* link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so
* that an application can let you choose it, in case you're
* capturing DOCSIS traffic that a Cisco Cable Modem
@@ -2614,21 +2910,27 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device,
* DOCSIS frames out on the wire inside the low-level
* Ethernet framing).
*
- * XXX - are there any sorts of "fake Ethernet" that have
- * ARPHRD_ETHER but that *shouldn't offer DLT_DOCSIS as
+ * XXX - are there any other sorts of "fake Ethernet" that
+ * have ARPHRD_ETHER but that shouldn't offer DLT_DOCSIS as
* a Cisco CMTS won't put traffic onto it or get traffic
* bridged onto it? ISDN is handled in "activate_new()",
- * as we fall back on cooked mode there; are there any
+ * as we fall back on cooked mode there, and we use
+ * is_wifi() to check for 802.11 devices; are there any
* others?
*/
- handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
- /*
- * If that fails, just leave the list empty.
- */
- if (handle->dlt_list != NULL) {
- handle->dlt_list[0] = DLT_EN10MB;
- handle->dlt_list[1] = DLT_DOCSIS;
- handle->dlt_count = 2;
+ if (!is_wifi(sock_fd, device)) {
+ /*
+ * It's not a Wi-Fi device; offer DOCSIS.
+ */
+ handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
+ /*
+ * If that fails, just leave the list empty.
+ */
+ if (handle->dlt_list != NULL) {
+ handle->dlt_list[0] = DLT_EN10MB;
+ handle->dlt_list[1] = DLT_DOCSIS;
+ handle->dlt_count = 2;
+ }
}
/* FALLTHROUGH */
@@ -2657,7 +2959,14 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device,
#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
@@ -2940,7 +3249,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device,
* 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
@@ -2983,7 +3292,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device,
*
* XXX - this is handled in activate_new().
*/
- //handlep->cooked = 1;
+ /* handlep->cooked = 1; */
break;
default:
@@ -3006,7 +3315,7 @@ activate_new(pcap_t *handle)
{
#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 sock_fd = -1, arptype;
#ifdef HAVE_PACKET_AUXDATA
@@ -3014,6 +3323,10 @@ activate_new(pcap_t *handle)
#endif
int err = 0;
struct packet_mreq mr;
+#if defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT)
+ int bpf_extensions;
+ socklen_t len = sizeof(bpf_extensions);
+#endif
/*
* Open a socket with protocol family packet. If the
@@ -3034,7 +3347,7 @@ activate_new(pcap_t *handle)
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) {
/*
@@ -3117,7 +3430,7 @@ activate_new(pcap_t *handle)
close(sock_fd);
return arptype;
}
- map_arphrd_to_dlt(handle, arptype, device, 1);
+ map_arphrd_to_dlt(handle, sock_fd, arptype, device, 1);
if (handle->linktype == -1 ||
handle->linktype == DLT_LINUX_SLL ||
handle->linktype == DLT_LINUX_IRDA ||
@@ -3136,14 +3449,14 @@ activate_new(pcap_t *handle)
* 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));
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) {
/*
@@ -3178,7 +3491,7 @@ activate_new(pcap_t *handle)
* 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 "
@@ -3268,7 +3581,7 @@ activate_new(pcap_t *handle)
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;
@@ -3281,7 +3594,7 @@ activate_new(pcap_t *handle)
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;
@@ -3311,15 +3624,24 @@ activate_new(pcap_t *handle)
/*
* 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:
@@ -3332,7 +3654,7 @@ activate_new(pcap_t *handle)
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;
}
@@ -3344,6 +3666,23 @@ activate_new(pcap_t *handle)
*/
handle->fd = sock_fd;
+#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
+ * by the OS? Is that possible?)
+ */
+ if (getsockopt(sock_fd, SOL_SOCKET, SO_BPF_EXTENSIONS,
+ &bpf_extensions, &len) == 0) {
+ if (bpf_extensions >= SKF_AD_VLAN_TAG_PRESENT) {
+ /*
+ * Yes, we can. Request that we do so.
+ */
+ handle->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING;
+ }
+ }
+#endif /* defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT) */
+
return 1;
#else /* HAVE_PF_PACKET_SOCKETS */
strlcpy(ebuf,
@@ -3366,7 +3705,7 @@ activate_new(pcap_t *handle)
* 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;
@@ -3378,7 +3717,7 @@ activate_mmap(pcap_t *handle, int *status)
*/
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;
@@ -3427,6 +3766,9 @@ activate_mmap(pcap_t *handle, int *status)
case TPACKET_V1:
handle->read_op = pcap_read_linux_mmap_v1;
break;
+ case TPACKET_V1_64:
+ handle->read_op = pcap_read_linux_mmap_v1_64;
+ break;
#ifdef HAVE_TPACKET2
case TPACKET_V2:
handle->read_op = pcap_read_linux_mmap_v2;
@@ -3447,7 +3789,7 @@ activate_mmap(pcap_t *handle, int *status)
return 1;
}
#else /* HAVE_PACKET_RING */
-static int
+static int
activate_mmap(pcap_t *handle _U_, int *status _U_)
{
return 0;
@@ -3471,13 +3813,16 @@ init_tpacket(pcap_t *handle, int version, const char *version_str)
int val = version;
socklen_t len = sizeof(val);
- /* Probe whether kernel supports the specified TPACKET version */
+ /*
+ * Probe whether kernel supports the specified TPACKET version;
+ * this also gets the length of the header for that version.
+ */
if (getsockopt(handle->fd, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) {
if (errno == ENOPROTOOPT || errno == EINVAL)
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));
@@ -3488,7 +3833,7 @@ init_tpacket(pcap_t *handle, int version, const char *version_str)
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));
@@ -3500,7 +3845,7 @@ init_tpacket(pcap_t *handle, int version, const char *version_str)
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;
@@ -3511,6 +3856,36 @@ init_tpacket(pcap_t *handle, int version, const char *version_str)
#endif /* defined HAVE_TPACKET2 || defined HAVE_TPACKET3 */
/*
+ * If the instruction set for which we're compiling has both 32-bit
+ * and 64-bit versions, and Linux support for the 64-bit version
+ * predates TPACKET_V2, define ISA_64_BIT as the .machine value
+ * you get from uname() for the 64-bit version. Otherwise, leave
+ * it undefined. (This includes ARM, which has a 64-bit version,
+ * but Linux support for it appeared well after TPACKET_V2 support
+ * did, so there should never be a case where 32-bit ARM code is
+ * running o a 64-bit kernel that only supports TPACKET_V1.)
+ *
+ * If we've omitted your favorite such architecture, please contribute
+ * a patch. (No patch is needed for architectures that are 32-bit-only
+ * or for which Linux has no support for 32-bit userland - or for which,
+ * as noted, 64-bit support appeared in Linux after TPACKET_V2 support
+ * did.)
+ */
+#if defined(__i386__)
+#define ISA_64_BIT "x86_64"
+#elif defined(__ppc__)
+#define ISA_64_BIT "ppc64"
+#elif defined(__sparc__)
+#define ISA_64_BIT "sparc64"
+#elif defined(__s390__)
+#define ISA_64_BIT "s390x"
+#elif defined(__mips__)
+#define ISA_64_BIT "mips64"
+#elif defined(__hppa__)
+#define ISA_64_BIT "parisc64"
+#endif
+
+/*
* Attempt to set the socket to version 3 of the memory-mapped header and,
* if that fails because version 3 isn't supported, attempt to fall
* back to version 2. If version 2 isn't supported, just leave it at
@@ -3527,11 +3902,10 @@ prepare_tpacket_socket(pcap_t *handle)
int ret;
#endif
- handlep->tp_version = TPACKET_V1;
- handlep->tp_hdrlen = sizeof(struct tpacket_hdr);
-
#ifdef HAVE_TPACKET3
/*
+ * Try setting the version to TPACKET_V3.
+ *
* The only mode in which buffering is done on PF_PACKET
* sockets, so that packets might not be delivered
* immediately, is TPACKET_V3 mode.
@@ -3540,28 +3914,87 @@ prepare_tpacket_socket(pcap_t *handle)
* if the user has requested immediate mode, we don't
* use TPACKET_V3.
*/
- if (handle->opt.immediate)
- ret = 1; /* pretend TPACKET_V3 couldn't be set */
- else
+ if (!handle->opt.immediate) {
ret = init_tpacket(handle, TPACKET_V3, "TPACKET_V3");
- if (-1 == ret) {
- /* Error during setting up TPACKET_V3. */
- return -1;
- } else if (1 == ret) {
- /* TPACKET_V3 not supported - fall back to TPACKET_V2. */
+ if (ret == 0) {
+ /*
+ * Success.
+ */
+ return 1;
+ }
+ if (ret == -1) {
+ /*
+ * We failed for some reason other than "the
+ * kernel doesn't support TPACKET_V3".
+ */
+ return -1;
+ }
+ }
#endif /* HAVE_TPACKET3 */
#ifdef HAVE_TPACKET2
- ret = init_tpacket(handle, TPACKET_V2, "TPACKET_V2");
- if (-1 == ret) {
- /* Error during setting up TPACKET_V2. */
- return -1;
- }
+ /*
+ * Try setting the version to TPACKET_V2.
+ */
+ ret = init_tpacket(handle, TPACKET_V2, "TPACKET_V2");
+ if (ret == 0) {
+ /*
+ * Success.
+ */
+ return 1;
+ }
+ if (ret == -1) {
+ /*
+ * We failed for some reason other than "the
+ * kernel doesn't support TPACKET_V2".
+ */
+ return -1;
+ }
#endif /* HAVE_TPACKET2 */
-#ifdef HAVE_TPACKET3
+ /*
+ * OK, we're using TPACKET_V1, as that's all the kernel supports.
+ */
+ handlep->tp_version = TPACKET_V1;
+ handlep->tp_hdrlen = sizeof(struct tpacket_hdr);
+
+#ifdef ISA_64_BIT
+ /*
+ * 32-bit userspace + 64-bit kernel + TPACKET_V1 are not compatible with
+ * each other due to platform-dependent data type size differences.
+ *
+ * If we have a 32-bit userland and a 64-bit kernel, use an
+ * internally-defined TPACKET_V1_64, with which we use a 64-bit
+ * version of the data structures.
+ */
+ if (sizeof(long) == 4) {
+ /*
+ * This is 32-bit code.
+ */
+ struct utsname utsname;
+
+ if (uname(&utsname) == -1) {
+ /*
+ * Failed.
+ */
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "uname failed: %s", pcap_strerror(errno));
+ return -1;
+ }
+ if (strcmp(utsname.machine, ISA_64_BIT) == 0) {
+ /*
+ * uname() tells us the machine is 64-bit,
+ * so we presumably have a 64-bit kernel.
+ *
+ * XXX - this presumes that uname() won't lie
+ * in 32-bit code and claim that the machine
+ * has the 32-bit version of the ISA.
+ */
+ handlep->tp_version = TPACKET_V1_64;
+ handlep->tp_hdrlen = sizeof(struct tpacket_hdr_64);
+ }
}
-#endif /* HAVE_TPACKET3 */
+#endif
return 1;
}
@@ -3605,6 +4038,7 @@ create_ring(pcap_t *handle, int *status)
switch (handlep->tp_version) {
case TPACKET_V1:
+ case TPACKET_V1_64:
#ifdef HAVE_TPACKET2
case TPACKET_V2:
#endif
@@ -3649,14 +4083,14 @@ create_ring(pcap_t *handle, int *status)
return -1;
}
if (!offload) {
- mtu = iface_get_mtu(handle->fd, handle->opt.source,
+ mtu = iface_get_mtu(handle->fd, handle->opt.device,
handle->errbuf);
if (mtu == -1) {
*status = PCAP_ERROR;
return -1;
}
- if (frame_size > mtu + 18)
- frame_size = mtu + 18;
+ if (frame_size > (unsigned int)mtu + 18)
+ frame_size = (unsigned int)mtu + 18;
}
}
@@ -3666,7 +4100,7 @@ create_ring(pcap_t *handle, int *status)
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;
@@ -3681,7 +4115,7 @@ create_ring(pcap_t *handle, int *status)
* 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;
@@ -3736,14 +4170,20 @@ create_ring(pcap_t *handle, int *status)
req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size;
break;
#endif
+ default:
+ 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;
@@ -3784,7 +4224,7 @@ create_ring(pcap_t *handle, int *status)
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) {
@@ -3802,18 +4242,26 @@ create_ring(pcap_t *handle, int *status)
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;
@@ -3841,8 +4289,8 @@ create_ring(pcap_t *handle, int *status)
}
if (setsockopt(handle->fd, SOL_PACKET, PACKET_TIMESTAMP,
(void *)&timesource, 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;
@@ -3857,7 +4305,7 @@ retry:
/* 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;
@@ -3891,7 +4339,7 @@ retry:
*/
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;
@@ -3903,7 +4351,7 @@ retry:
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*/
@@ -3916,7 +4364,7 @@ retry:
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));
@@ -3930,7 +4378,7 @@ retry:
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;
}
}
@@ -3949,13 +4397,14 @@ destroy_ring(pcap_t *handle)
/* tell the kernel to destroy the ring*/
struct tpacket_req req;
memset(&req, 0, sizeof(req));
- setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING,
+ /* do not test for setsockopt failure, as we can't recover from any error */
+ (void)setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING,
(void *) &req, sizeof(req));
/* if ring is mapped, unmap it*/
if (handlep->mmapbuf) {
/* do not test for mmap failure, as we can't recover from any error */
- munmap(handlep->mmapbuf, handlep->mmapbuflen);
+ (void)munmap(handlep->mmapbuf, handlep->mmapbuflen);
handlep->mmapbuf = NULL;
}
}
@@ -3989,7 +4438,7 @@ pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h,
memcpy(handlep->oneshot_buffer, bytes, h->caplen);
*sp->pkt = handlep->oneshot_buffer;
}
-
+
static void
pcap_cleanup_linux_mmap( pcap_t *handle )
{
@@ -4042,151 +4491,131 @@ pcap_setnonblock_mmap(pcap_t *p, int nonblock, char *errbuf)
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:
+ 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;
}
@@ -4202,7 +4631,8 @@ static int pcap_handle_packet_mmap(
unsigned int tp_sec,
unsigned int tp_usec,
int tp_vlan_tci_valid,
- __u16 tp_vlan_tci)
+ __u16 tp_vlan_tci,
+ __u16 tp_vlan_tpid)
{
struct pcap_linux *handlep = handle->priv;
unsigned char *bp;
@@ -4211,9 +4641,9 @@ static int pcap_handle_packet_mmap(
/* 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 %d + caplen %d > frame len %d",
+ "offset %u + caplen %u > frame len %d",
tp_mac, tp_snaplen, handle->bufsize);
return -1;
}
@@ -4228,22 +4658,9 @@ static int pcap_handle_packet_mmap(
* the filter when the ring became empty, but it can possibly
* happen a lot later... */
bp = frame + tp_mac;
- if (handlep->filter_in_userland && handle->fcode.bf_insns &&
- (bpf_filter(handle->fcode.bf_insns, bp,
- tp_len, tp_snaplen) == 0))
- return 0;
-
- sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen);
- if (!linux_check_direction(handle, sll))
- return 0;
-
- /* get required packet info from ring header */
- pcaphdr.ts.tv_sec = tp_sec;
- pcaphdr.ts.tv_usec = tp_usec;
- pcaphdr.caplen = tp_snaplen;
- pcaphdr.len = tp_len;
/* if required build in place the sll header*/
+ sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen);
if (handlep->cooked) {
struct sll_header *hdrp;
@@ -4256,7 +4673,7 @@ static int pcap_handle_packet_mmap(
*/
bp -= SLL_HDR_LEN;
- /*/*
+ /*
* Let's make sure that's past the end of
* the tpacket header, i.e. >=
* ((u_char *)thdr + TPACKET_HDRLEN), so we
@@ -4266,7 +4683,7 @@ static int pcap_handle_packet_mmap(
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;
}
@@ -4281,7 +4698,30 @@ static int pcap_handle_packet_mmap(
hdrp->sll_halen = htons(sll->sll_halen);
memcpy(hdrp->sll_addr, sll->sll_addr, SLL_ADDRLEN);
hdrp->sll_protocol = sll->sll_protocol;
+ }
+
+ if (handlep->filter_in_userland && handle->fcode.bf_insns) {
+ struct bpf_aux_data aux_data;
+
+ aux_data.vlan_tag = tp_vlan_tci & 0x0fff;
+ aux_data.vlan_tag_present = tp_vlan_tci_valid;
+ if (bpf_filter_with_aux_data(handle->fcode.bf_insns, bp,
+ tp_len, tp_snaplen, &aux_data) == 0)
+ return 0;
+ }
+
+ if (!linux_check_direction(handle, sll))
+ return 0;
+
+ /* get required packet info from ring header */
+ pcaphdr.ts.tv_sec = tp_sec;
+ pcaphdr.ts.tv_usec = tp_usec;
+ pcaphdr.caplen = tp_snaplen;
+ pcaphdr.len = tp_len;
+
+ /* if required build in place the sll header*/
+ if (handlep->cooked) {
/* update packet len */
pcaphdr.caplen += SLL_HDR_LEN;
pcaphdr.len += SLL_HDR_LEN;
@@ -4294,13 +4734,24 @@ static int pcap_handle_packet_mmap(
{
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(ETH_P_8021Q);
+ 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;
}
@@ -4315,7 +4766,7 @@ static int pcap_handle_packet_mmap(
* 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 */
@@ -4329,22 +4780,32 @@ pcap_read_linux_mmap_v1(pcap_t *handle, int max_packets, pcap_handler callback,
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(
@@ -4358,6 +4819,7 @@ pcap_read_linux_mmap_v1(pcap_t *handle, int max_packets, pcap_handler callback,
h.h1->tp_sec,
h.h1->tp_usec,
0,
+ 0,
0);
if (ret == 1) {
pkts++;
@@ -4397,28 +4859,122 @@ pcap_read_linux_mmap_v1(pcap_t *handle, int max_packets, pcap_handler callback,
return pkts;
}
+static int
+pcap_read_linux_mmap_v1_64(pcap_t *handle, int max_packets, pcap_handler callback,
+ u_char *user)
+{
+ struct pcap_linux *handlep = handle->priv;
+ union thdr h;
+ int pkts = 0;
+ int ret;
+
+ /* wait for frames availability.*/
+ 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)) {
+ /*
+ * 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(
+ handle,
+ callback,
+ user,
+ h.raw,
+ h.h1_64->tp_len,
+ h.h1_64->tp_mac,
+ h.h1_64->tp_snaplen,
+ h.h1_64->tp_sec,
+ h.h1_64->tp_usec,
+ 0,
+ 0,
+ 0);
+ if (ret == 1) {
+ pkts++;
+ handlep->packets_read++;
+ } else if (ret < 0) {
+ return ret;
+ }
+
+ /*
+ * Hand this block back to the kernel, and, if we're
+ * counting blocks that need to be filtered in userland
+ * after having been filtered by the kernel, count
+ * the one we've just processed.
+ */
+ h.h1_64->tp_status = TP_STATUS_KERNEL;
+ if (handlep->blocks_to_filter_in_userland > 0) {
+ handlep->blocks_to_filter_in_userland--;
+ if (handlep->blocks_to_filter_in_userland == 0) {
+ /*
+ * No more blocks need to be filtered
+ * in userland.
+ */
+ handlep->filter_in_userland = 0;
+ }
+ }
+
+ /* next block */
+ if (++handle->offset >= handle->cc)
+ handle->offset = 0;
+
+ /* check for break loop condition*/
+ if (handle->break_loop) {
+ handle->break_loop = 0;
+ return PCAP_ERROR_BREAK;
+ }
+ }
+ return pkts;
+}
+
#ifdef HAVE_TPACKET2
static int
pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback,
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(
@@ -4436,7 +4992,8 @@ pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback,
#else
h.h2->tp_vlan_tci != 0,
#endif
- h.h2->tp_vlan_tci);
+ h.h2->tp_vlan_tci,
+ VLAN_TPID(h.h2, h.h2));
if (ret == 1) {
pkts++;
handlep->packets_read++;
@@ -4489,13 +5046,20 @@ pcap_read_linux_mmap_v3(pcap_t *handle, int max_packets, pcap_handler callback,
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;
@@ -4506,21 +5070,30 @@ 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,
@@ -4537,7 +5110,8 @@ again:
#else
tp3_hdr->hv1.tp_vlan_tci != 0,
#endif
- tp3_hdr->hv1.tp_vlan_tci);
+ tp3_hdr->hv1.tp_vlan_tci,
+ VLAN_TPID(tp3_hdr, &tp3_hdr->hv1));
if (ret == 1) {
pkts++;
handlep->packets_read++;
@@ -4590,7 +5164,7 @@ again:
}
#endif /* HAVE_TPACKET3 */
-static int
+static int
pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter)
{
struct pcap_linux *handlep = handle->priv;
@@ -4624,12 +5198,12 @@ pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter)
* 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;
}
@@ -4650,9 +5224,6 @@ pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter)
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
@@ -4686,7 +5257,7 @@ iface_get_id(int fd, const char *device, char *ebuf)
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;
}
@@ -4722,7 +5293,7 @@ iface_bind(int fd, int ifindex, char *ebuf)
*/
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;
}
@@ -4731,7 +5302,7 @@ iface_bind(int fd, int ifindex, char *ebuf)
/* 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;
}
@@ -4746,7 +5317,7 @@ iface_bind(int fd, int ifindex, char *ebuf)
*/
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;
}
@@ -4765,12 +5336,15 @@ has_wext(int sock_fd, const char *device, char *ebuf)
{
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;
@@ -4908,7 +5482,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
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;
@@ -4921,7 +5495,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
/*
* Failed.
*/
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"%s: SIOCGIWPRIV: %s", device,
pcap_strerror(errno));
return PCAP_ERROR;
@@ -4932,13 +5506,13 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
*/
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);
@@ -5194,7 +5768,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
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;
}
@@ -5203,7 +5777,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
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;
}
@@ -5222,7 +5796,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
*/
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;
}
@@ -5306,7 +5880,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
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;
@@ -5383,7 +5957,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
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));
/*
@@ -5452,6 +6026,176 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device)
return 0;
}
+#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
+/*
+ * Map SOF_TIMESTAMPING_ values to PCAP_TSTAMP_ values.
+ */
+static const struct {
+ int soft_timestamping_val;
+ int pcap_tstamp_val;
+} sof_ts_type_map[3] = {
+ { SOF_TIMESTAMPING_SOFTWARE, PCAP_TSTAMP_HOST },
+ { SOF_TIMESTAMPING_SYS_HARDWARE, PCAP_TSTAMP_ADAPTER },
+ { SOF_TIMESTAMPING_RAW_HARDWARE, PCAP_TSTAMP_ADAPTER_UNSYNCED }
+};
+#define NUM_SOF_TIMESTAMPING_TYPES (sizeof sof_ts_type_map / sizeof sof_ts_type_map[0])
+
+/*
+ * Set the list of time stamping types to include all types.
+ */
+static void
+iface_set_all_ts_types(pcap_t *handle)
+{
+ u_int i;
+
+ handle->tstamp_type_count = NUM_SOF_TIMESTAMPING_TYPES;
+ handle->tstamp_type_list = malloc(NUM_SOF_TIMESTAMPING_TYPES * sizeof(u_int));
+ for (i = 0; i < NUM_SOF_TIMESTAMPING_TYPES; i++)
+ handle->tstamp_type_list[i] = sof_ts_type_map[i].pcap_tstamp_val;
+}
+
+#ifdef ETHTOOL_GET_TS_INFO
+/*
+ * Get a list of time stamping capabilities.
+ */
+static int
+iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf)
+{
+ int fd;
+ struct ifreq ifr;
+ struct ethtool_ts_info info;
+ int num_ts_types;
+ u_int i, j;
+
+ /*
+ * 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;
+ }
+
+ /*
+ * Create a socket from which to fetch time stamping capabilities.
+ */
+ fd = socket(PF_UNIX, SOCK_RAW, 0);
+ if (fd < 0) {
+ (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, 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);
+ switch (save_errno) {
+
+ case EOPNOTSUPP:
+ case EINVAL:
+ /*
+ * 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_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;
+ }
+ }
+ 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)
+ num_ts_types++;
+ }
+ handle->tstamp_type_count = num_ts_types;
+ if (num_ts_types != 0) {
+ handle->tstamp_type_list = malloc(num_ts_types * sizeof(u_int));
+ for (i = 0, j = 0; i < NUM_SOF_TIMESTAMPING_TYPES; i++) {
+ if (info.so_timestamping & sof_ts_type_map[i].soft_timestamping_val) {
+ handle->tstamp_type_list[j] = sof_ts_type_map[i].pcap_tstamp_val;
+ j++;
+ }
+ }
+ } else
+ handle->tstamp_type_list = NULL;
+
+ return 0;
+}
+#else /* ETHTOOL_GET_TS_INFO */
+static int
+iface_ethtool_get_ts_info(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_all_ts_types(handle);
+ return 0;
+}
+#endif /* ETHTOOL_GET_TS_INFO */
+
+#endif /* defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) */
+
+#ifdef HAVE_PACKET_RING
/*
* Find out if we have any form of fragmentation/reassembly offloading.
*
@@ -5462,13 +6206,13 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device)
*/
#if defined(SIOCETHTOOL) && (defined(ETHTOOL_GTSO) || defined(ETHTOOL_GUFO) || defined(ETHTOOL_GGSO) || defined(ETHTOOL_GFLAGS) || defined(ETHTOOL_GGRO))
static int
-iface_ethtool_ioctl(pcap_t *handle, int cmd, const char *cmdname)
+iface_ethtool_flag_ioctl(pcap_t *handle, int cmd, const char *cmdname)
{
struct ifreq ifr;
struct ethtool_value eval;
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;
@@ -5482,12 +6226,12 @@ iface_ethtool_ioctl(pcap_t *handle, int cmd, const char *cmdname)
*/
return 0;
}
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: SIOETHTOOL(%s) ioctl failed: %s", handle->opt.source,
+ 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
@@ -5496,7 +6240,7 @@ iface_get_offload(pcap_t *handle)
int ret;
#ifdef ETHTOOL_GTSO
- ret = iface_ethtool_ioctl(handle, ETHTOOL_GTSO, "ETHTOOL_GTSO");
+ ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GTSO, "ETHTOOL_GTSO");
if (ret == -1)
return -1;
if (ret)
@@ -5504,7 +6248,7 @@ iface_get_offload(pcap_t *handle)
#endif
#ifdef ETHTOOL_GUFO
- ret = iface_ethtool_ioctl(handle, ETHTOOL_GUFO, "ETHTOOL_GUFO");
+ ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GUFO, "ETHTOOL_GUFO");
if (ret == -1)
return -1;
if (ret)
@@ -5517,7 +6261,7 @@ iface_get_offload(pcap_t *handle)
* handed to PF_PACKET sockets on transmission? If not,
* this need not be checked.
*/
- ret = iface_ethtool_ioctl(handle, ETHTOOL_GGSO, "ETHTOOL_GGSO");
+ ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GGSO, "ETHTOOL_GGSO");
if (ret == -1)
return -1;
if (ret)
@@ -5525,7 +6269,7 @@ iface_get_offload(pcap_t *handle)
#endif
#ifdef ETHTOOL_GFLAGS
- ret = iface_ethtool_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
+ ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
if (ret == -1)
return -1;
if (ret & ETH_FLAG_LRO)
@@ -5538,7 +6282,7 @@ iface_get_offload(pcap_t *handle)
* handed to PF_PACKET sockets on receipt? If not,
* this need not be checked.
*/
- ret = iface_ethtool_ioctl(handle, ETHTOOL_GGRO, "ETHTOOL_GGRO");
+ ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GGRO, "ETHTOOL_GGRO");
if (ret == -1)
return -1;
if (ret)
@@ -5559,6 +6303,8 @@ iface_get_offload(pcap_t *handle _U_)
}
#endif /* SIOCETHTOOL */
+#endif /* HAVE_PACKET_RING */
+
#endif /* HAVE_PF_PACKET_SOCKETS */
/* ===== Functions to interface to the older kernels ================== */
@@ -5573,7 +6319,7 @@ activate_old(pcap_t *handle)
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;
@@ -5581,7 +6327,7 @@ activate_old(pcap_t *handle)
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) {
/*
@@ -5624,9 +6370,9 @@ activate_old(pcap_t *handle)
* Try to find the DLT_ type corresponding to that
* link-layer type.
*/
- map_arphrd_to_dlt(handle, arptype, device, 0);
+ 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;
}
@@ -5637,7 +6383,7 @@ activate_old(pcap_t *handle)
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;
}
@@ -5665,7 +6411,7 @@ activate_old(pcap_t *handle)
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;
@@ -5734,8 +6480,8 @@ activate_old(pcap_t *handle)
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.
@@ -5743,7 +6489,7 @@ activate_old(pcap_t *handle)
* We can safely pass "recvfrom()" a byte count
* based on the snapshot length.
*/
- handle->bufsize = handle->snapshot;
+ handle->bufsize = (u_int)handle->snapshot;
}
/*
@@ -5775,7 +6521,7 @@ iface_bind_old(int fd, const char *device, char *ebuf)
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;
}
@@ -5783,13 +6529,13 @@ iface_bind_old(int fd, const char *device, char *ebuf)
/* 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;
}
@@ -5815,7 +6561,7 @@ iface_get_mtu(int fd, const char *device, char *ebuf)
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;
}
@@ -5835,7 +6581,7 @@ iface_get_arptype(int fd, const char *device, char *ebuf)
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) {
/*
@@ -5868,7 +6614,7 @@ fix_program(pcap_t *handle, struct sock_fprog *fcode, int is_mmapped)
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;
}
@@ -6044,20 +6790,40 @@ set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode)
* "nothing more to be read" error).
*/
save_mode = fcntl(handle->fd, F_GETFL, 0);
- if (save_mode != -1 &&
- fcntl(handle->fd, F_SETFL, save_mode | O_NONBLOCK) >= 0) {
- while (recv(handle->fd, &drain, sizeof drain,
- MSG_TRUNC) >= 0)
- ;
- save_errno = errno;
- fcntl(handle->fd, F_SETFL, save_mode);
- if (save_errno != EAGAIN) {
- /* Fatal error */
- reset_kernel_filter(handle);
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "recv: %s", pcap_strerror(save_errno));
- return -2;
- }
+ if (save_mode == -1) {
+ 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) {
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't set nonblocking mode when changing filter: %s",
+ pcap_strerror(errno));
+ return -2;
+ }
+ while (recv(handle->fd, &drain, sizeof drain, MSG_TRUNC) >= 0)
+ ;
+ save_errno = errno;
+ if (save_errno != EAGAIN) {
+ /*
+ * Fatal error.
+ *
+ * If we can't restore the mode or reset the
+ * kernel filter, there's nothing we can do.
+ */
+ (void)fcntl(handle->fd, F_SETFL, save_mode);
+ (void)reset_kernel_filter(handle);
+ 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) {
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't restore FD flags when changing filter: %s",
+ pcap_strerror(save_errno));
+ return -2;
}
}
@@ -6080,11 +6846,16 @@ set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode)
save_errno = errno;
/*
- * XXX - if this fails, we're really screwed;
- * we have the total filter on the socket,
- * and it won't come off. What do we do then?
+ * If this fails, we're really screwed; we have the
+ * total filter on the socket, and it won't come off.
+ * Report it as a fatal error.
*/
- reset_kernel_filter(handle);
+ if (reset_kernel_filter(handle) == -1) {
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't remove kernel total filter: %s",
+ pcap_strerror(errno));
+ return -2; /* fatal error */
+ }
errno = save_errno;
}