aboutsummaryrefslogtreecommitdiff
path: root/pcap-linux.c
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2015-06-04 00:09:10 -0700
committerGuy Harris <guy@alum.mit.edu>2015-06-04 00:09:10 -0700
commit85c3887483bd14c1f2748ac891bb1b19d2f0c877 (patch)
tree8eb666279612be7c98e43251f2f7afba7659066b /pcap-linux.c
parentcd03c8fd9fe7a0c42d043f9c6ad7c5dcb6515929 (diff)
Don't timeout poll() with TPACKET_V3 in 3.19 and later.
We don't need to do the workaround for not getting wakeups when buffers are handed to us, nor do we have to do a timeout in poll() for the benefit of programs that expect the timeout to expire even if no packets have arrived, so, with TPACKET_V3 in 3.19 and later, we always use a timeout of -1 for poll(). This eliminates some spurious wakeups from poll(), due to the poll() call *itself* timing out, that turn into spurious timeouts; see GitHub issue #383.
Diffstat (limited to 'pcap-linux.c')
-rw-r--r--pcap-linux.c80
1 files changed, 62 insertions, 18 deletions
diff --git a/pcap-linux.c b/pcap-linux.c
index b19e4a1a..e08f0b12 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -1300,33 +1300,77 @@ static void pcap_cleanup_linux( pcap_t *handle )
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, 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.
+ * 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.
*
- * 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.
+ * 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)
+ 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)
- handlep->poll_timeout = handlep->timeout; /* block for that amount of time */
- else
+ } else if (handlep->timeout > 0) {
+ /*
+ * 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
+ handlep->poll_timeout = handlep->timeout; /* block for that amount of time */
+ } else
handlep->poll_timeout = 0; /* non-blocking mode - poll to pick up errors */
}