aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pcap-int.h2
-rw-r--r--pcap-linux.c417
-rw-r--r--pcap.3pcap.in9
-rw-r--r--pcap.c2
-rw-r--r--pcap/pcap.h2
-rw-r--r--pcap_get_required_select_timeout.3pcap108
-rw-r--r--pcap_get_selectable_fd.3pcap8
-rw-r--r--testprogs/selpolltest.c58
8 files changed, 478 insertions, 128 deletions
diff --git a/pcap-int.h b/pcap-int.h
index d47107ec..09620436 100644
--- a/pcap-int.h
+++ b/pcap-int.h
@@ -251,7 +251,7 @@ struct pcap {
* pcap_t's with a required timeout, and the code must be
* prepared not to see any packets from the attempt.
*/
- struct timeval *required_select_timeout;
+ const struct timeval *required_select_timeout;
#endif
/*
diff --git a/pcap-linux.c b/pcap-linux.c
index 0003af1a..f9fe8ac6 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -204,6 +204,7 @@ struct pcap_linux {
int cooked; /* using SOCK_DGRAM rather than SOCK_RAW */
int ifindex; /* interface index of device we're bound to */
int lo_ifindex; /* interface index of the loopback device */
+ int netdown; /* we got an ENETDOWN and haven't resolved it */
bpf_u_int32 oldmode; /* mode to restore when turning monitor mode off */
char *mondevice; /* mac80211 monitor device we created */
u_char *mmapbuf; /* memory-mapped region pointer */
@@ -353,6 +354,14 @@ static void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h,
#endif
/*
+ * Required select timeout if we're polling for an "interface disappeared"
+ * indication - 1 millisecond.
+ */
+static const struct timeval netdown_timeout = {
+ 0, 1000 /* 1000 microseconds = 1 millisecond */
+};
+
+/*
* Wrap some ioctl calls
*/
static int iface_get_id(int fd, const char *device, char *ebuf);
@@ -1570,6 +1579,53 @@ linux_check_direction(const pcap_t *handle, const struct sockaddr_ll *sll)
}
/*
+ * Check whether the device to which the pcap_t is bound still exists.
+ * We do so by asking what address the socket is bound to, and checking
+ * whether the ifindex in the address is -1, meaning "that device is gone",
+ * or some other value, meaning "that device still exists".
+ */
+static int
+device_still_exists(pcap_t *handle)
+{
+ struct pcap_linux *handlep = handle->priv;
+ struct sockaddr_ll addr;
+ socklen_t addr_len;
+
+ /*
+ * If handlep->ifindex is -1, the socket isn't bound, meaning
+ * we're capturing on the "any" device; that device never
+ * disappears. (It should also never be configured down, so
+ * we shouldn't even get here, but let's make sure.)
+ */
+ if (handlep->ifindex == -1)
+ return (1); /* it's still here */
+
+ /*
+ * OK, now try to get the address for the socket.
+ */
+ addr_len = sizeof (addr);
+ if (getsockname(handle->fd, (struct sockaddr *) &addr, &addr_len) == -1) {
+ /*
+ * Error - report an error and return -1.
+ */
+ pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "getsockname failed");
+ return (-1);
+ }
+ if (addr.sll_ifindex == -1) {
+ /*
+ * This means the device went away.
+ */
+ return (0);
+ }
+
+ /*
+ * The device presumably just went down.
+ */
+ return (1);
+}
+
+/*
* Read a packet from the socket calling the handler provided by
* the user. Returns the number of packets received or -1 if an
* error occured.
@@ -1616,13 +1672,6 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
* loop, the signal handler should call pcap_breakloop()
* to set handle->break_loop (we ignore it on other
* platforms as well).
- * We also ignore ENETDOWN, so that we can continue to
- * capture traffic if the interface goes down and comes
- * back up again; comments in the kernel indicate that
- * we'll just block waiting for packets if we try to
- * receive from a socket that delivered ENETDOWN, and,
- * if we're using a memory-mapped buffer, we won't even
- * get notified of "network down" events.
*/
bp = (u_char *)handle->buffer + handle->offset;
@@ -1674,14 +1723,40 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
case ENETDOWN:
/*
- * The device on which we're capturing went away.
+ * The device on which we're capturing went away
+ * or the interface was taken down.
+ *
+ * Check whether the device still exists.
+ */
+ if (device_still_exists(handle)) {
+ /*
+ * It does. Just ignore this; on the
+ * *BSDs and Darwin, we don't even
+ * get told whether the interface
+ * went down, and both there and on
+ * Linux, if it comes back up again,
+ * and new packes arrive or are sent,
+ * they'll be delivered.
+ *
+ * XXX - ideally, we'd return an
+ * "interface went down" warning,
+ * so the user can be told, but
+ * pcap_dispatch() etc. aren't
+ * defined to return warnings.
+ */
+ return 0;
+ }
+
+ /*
+ * It doesn't; report that.
*
- * XXX - we should really return
- * PCAP_ERROR_IFACE_NOT_UP, but pcap_dispatch()
- * etc. aren't defined to return that.
+ * XXX - we should really return an appropriate
+ * error for that, but pcap_dispatch() etc. aren't
+ * documented as having error returns other than
+ * PCAP_ERROR or PCAP_ERROR_BREAK.
*/
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "The interface went down");
+ "The interface disappeared");
return PCAP_ERROR;
default:
@@ -4488,7 +4563,8 @@ pcap_get_ring_frame_status(pcap_t *handle, int offset)
static int pcap_wait_for_frames_mmap(pcap_t *handle)
{
struct pcap_linux *handlep = handle->priv;
- char c;
+ int timeout;
+ struct ifreq ifr;
int ret;
#ifdef HAVE_SYS_EVENTFD_H
struct pollfd pollinfo[2];
@@ -4500,8 +4576,47 @@ static int pcap_wait_for_frames_mmap(pcap_t *handle)
pollinfo[0].fd = handle->fd;
pollinfo[0].events = POLLIN;
- do {
- /*
+ /*
+ * Keep polling until we either get some packets to read, see
+ * that we got told to break out of the loop, get a fatal error,
+ * or discover that the device went away.
+ *
+ * In non-blocking mode, we must still do one poll() to catch
+ * any pending error indications, but the poll() has a timeout
+ * of 0, so that it doesn't block, and we quit after that one
+ * poll().
+ *
+ * If we've seen an ENETDOWN, it might be the first indication
+ * that the device went away, or it might just be that it was
+ * configured down. Unfortunately, there's no guarantee that
+ * the device has actually been removed as an interface, because:
+ *
+ * 1) if, as appears to be the case at least some of the time,
+ * the PF_PACKET socket code first gets a NETDEV_DOWN indication
+ * for the device and then gets a NETDEV_UNREGISTER indication
+ * for it, the first indication will cause a wakeup with ENETDOWN
+ * but won't set the packet socket's field for the interface index
+ * to -1, and the second indication won't cause a wakeup (because
+ * the first indication also caused the protocol hook to be
+ * unregistered) but will set the packet socket's field for the
+ * interface index to -1;
+ *
+ * 2) even if just a NETDEV_UNREGISTER indication is registered,
+ * the packet socket's field for the interface index only gets
+ * set to -1 after the wakeup, so there's a small but non-zero
+ * risk that a thread blocked waiting for the wakeup will get
+ * to the "fetch the socket name" code before the interface index
+ * gets set to -1, so it'll get the old interface index.
+ *
+ * Therefore, if we got an ENETDOWN and haven't seen a packet
+ * since then, we assume that we might be waiting for the interface
+ * to disappear, and poll with a timeout to try again in a short
+ * period of time. If we *do* see a packet, the interface has
+ * come back up again, and is *definitely* still there, so we
+ * don't need to poll.
+ */
+ for (;;) {
+ /*
* Yes, we do this even in non-blocking mode, as it's
* the only way to get error indications from a
* tpacket socket.
@@ -4509,80 +4624,250 @@ static int pcap_wait_for_frames_mmap(pcap_t *handle)
* The timeout is 0 in non-blocking mode, so poll()
* returns immediately.
*/
+ timeout = handlep->poll_timeout;
+ /*
+ * If we got an ENETDOWN and haven't gotten an indication
+ * that the device has gone away or that the device is up,
+ * we don't yet know for certain whether the device has
+ * gone away or not, do a poll() with a 1-millisecond timeout,
+ * as we have to poll indefinitely for "device went away"
+ * indications until we either get one or see that the
+ * device is up.
+ */
+ if (handlep->netdown) {
+ if (timeout != 0)
+ timeout = 1;
+ }
#ifdef HAVE_SYS_EVENTFD_H
- ret = poll(pollinfo, 2, handlep->poll_timeout);
+ ret = poll(pollinfo, 2, timeout);
#else
- ret = poll(pollinfo, 1, handlep->poll_timeout);
+ ret = poll(pollinfo, 1, timeout);
#endif
- if (ret < 0 && errno != EINTR) {
- pcap_fmt_errmsg_for_errno(handle->errbuf,
- PCAP_ERRBUF_SIZE, errno,
- "can't poll on packet socket");
- return PCAP_ERROR;
- } else if (ret > 0 && pollinfo[0].revents &&
- (pollinfo[0].revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) {
+ if (ret < 0) {
/*
- * There's some indication other than
- * "you can read on this descriptor" on
- * the descriptor.
+ * Error. If it's not EINTR, report it.
*/
- if (pollinfo[0].revents & (POLLHUP | POLLRDHUP)) {
- snprintf(handle->errbuf,
- PCAP_ERRBUF_SIZE,
- "Hangup on packet socket");
+ if (errno != EINTR) {
+ pcap_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE, errno,
+ "can't poll on packet socket");
return PCAP_ERROR;
}
- if (pollinfo[0].revents & POLLERR) {
+
+ /*
+ * It's EINTR; if we were told to break out of
+ * the loop, do so.
+ */
+ if (handle->break_loop) {
+ handle->break_loop = 0;
+ return PCAP_ERROR_BREAK;
+ }
+ } else if (ret > 0) {
+ /*
+ * OK, some descriptor is ready.
+ * Check the socket descriptor first.
+ *
+ * As I read the Linux man page, pollinfo[0].revents
+ * will either be POLLIN, POLLERR, POLLHUP, or POLLNVAL.
+ */
+ if (pollinfo[0].revents == POLLIN) {
+ /*
+ * OK, we may have packets to
+ * read.
+ */
+ break;
+ }
+ if (pollinfo[0].revents != 0) {
+ /*
+ * There's some indication other than
+ * "you can read on this descriptor" on
+ * the descriptor.
+ */
+ if (pollinfo[0].revents & POLLNVAL) {
+ snprintf(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ "Invalid polling request on packet socket");
+ return PCAP_ERROR;
+ }
+ if (pollinfo[0].revents & (POLLHUP | POLLRDHUP)) {
+ snprintf(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ "Hangup on packet socket");
+ return PCAP_ERROR;
+ }
+ if (pollinfo[0].revents & POLLERR) {
+ /*
+ * Get the error.
+ */
+ int err;
+ socklen_t errlen;
+
+ errlen = sizeof(err);
+ if (getsockopt(handle->fd, SOL_SOCKET,
+ SO_ERROR, &err, &errlen) == -1) {
+ /*
+ * The call *itself* returned
+ * an error; make *that*
+ * the error.
+ */
+ err = errno;
+ }
+
+ /*
+ * OK, we have the error.
+ */
+ if (err == ENETDOWN) {
+ /*
+ * The device on which we're
+ * capturing went away or the
+ * interface was taken down.
+ *
+ * We don't know for certain
+ * which happened, and the
+ * next poll() may indicate
+ * that there are packets
+ * to be read, so just set
+ * a flag to get us to do
+ * checks later, and set
+ * the required select
+ * timeout to 1 millisecond
+ * so that event loops that
+ * check our socket descriptor
+ * also time out so that
+ * they can call us and we
+ * can do the checks.
+ */
+ handlep->netdown = 1;
+ handle->required_select_timeout = &netdown_timeout;
+ } else if (err == 0) {
+ /*
+ * This shouldn't happen, so
+ * report a special indication
+ * that it did.
+ */
+ snprintf(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ "Error condition on packet socket: Reported error was 0");
+ return PCAP_ERROR;
+ } else {
+ pcap_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE,
+ err,
+ "Error condition on packet socket");
+ return PCAP_ERROR;
+ }
+ }
+ }
+#ifdef HAVE_SYS_EVENTFD_H
+ /*
+ * Now check the event device.
+ */
+ if (pollinfo[1].revents & POLLIN) {
+ uint64_t value;
+ (void)read(handlep->poll_breakloop_fd, &value,
+ sizeof(value));
+
+ /*
+ * This event gets signaled by a
+ * pcap_breakloop() call; if we were told
+ * to break out of the loop, do so.
+ */
+ if (handle->break_loop) {
+ handle->break_loop = 0;
+ return PCAP_ERROR_BREAK;
+ }
+ }
+#endif
+ }
+
+ /*
+ * Either:
+ *
+ * 1) we got neither an error from poll() nor any
+ * readable descriptors, in which case there
+ * are no packets waiting to read
+ *
+ * or
+ *
+ * 2) We got readable descriptors but the PF_PACKET
+ * socket wasn't one of them, in which case there
+ * are no packets waiting to read
+ *
+ * so, if we got an ENETDOWN, we've drained whatever
+ * packets were available to read at the point of the
+ * ENETDOWN.
+ *
+ * So, if we got an ENETDOWN and haven't gotten an indication
+ * that the device has gone away or that the device is up,
+ * we don't yet know for certain whether the device has
+ * gone away or not, check whether the device exists and is
+ * up.
+ */
+ if (handlep->netdown) {
+ if (!device_still_exists(handle)) {
/*
- * A recv() will give us the actual error code.
+ * The device doesn't exist any more;
+ * report that.
*
- * XXX - make the socket non-blocking?
+ * XXX - we should really return an
+ * appropriate error for that, but
+ * pcap_dispatch() etc. aren't documented
+ * as having error returns other than
+ * PCAP_ERROR or PCAP_ERROR_BREAK.
*/
- if (recv(handle->fd, &c, sizeof c,
- MSG_PEEK) != -1)
- continue; /* what, no error? */
- if (errno == ENETDOWN) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "The interface disappeared");
+ return PCAP_ERROR;
+ }
+
+ /*
+ * The device still exists; try to see if it's up.
+ */
+ memset(&ifr, 0, sizeof(ifr));
+ pcap_strlcpy(ifr.ifr_name, handlep->device,
+ sizeof(ifr.ifr_name));
+ if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) {
+ if (errno == ENXIO || errno == ENODEV) {
/*
- * The device on which we're
- * capturing went away.
+ * OK, *now* it's gone.
*
- * XXX - we should really return
- * PCAP_ERROR_IFACE_NOT_UP, but
- * pcap_dispatch() etc. aren't
- * defined to return that.
+ * XXX - see above comment.
*/
snprintf(handle->errbuf,
- PCAP_ERRBUF_SIZE,
- "The interface went down");
+ PCAP_ERRBUF_SIZE,
+ "The interface disappeared");
+ return PCAP_ERROR;
} else {
pcap_fmt_errmsg_for_errno(handle->errbuf,
PCAP_ERRBUF_SIZE, errno,
- "Error condition on packet socket");
+ "%s: Can't get flags",
+ handlep->device);
+ return PCAP_ERROR;
}
- return PCAP_ERROR;
}
- if (pollinfo[0].revents & POLLNVAL) {
- snprintf(handle->errbuf,
- PCAP_ERRBUF_SIZE,
- "Invalid polling request on packet socket");
- return PCAP_ERROR;
+ if (ifr.ifr_flags & IFF_UP) {
+ /*
+ * It's up, so it definitely still exists.
+ * Cancel the ENETDOWN indication - we
+ * presumably got it due to the interface
+ * going down rather than the device going
+ * away - and revert to "no required select
+ * timeout.
+ */
+ handlep->netdown = 0;
+ handle->required_select_timeout = NULL;
}
}
-#ifdef HAVE_SYS_EVENTFD_H
- if (pollinfo[1].revents & POLLIN) {
- uint64_t value;
- (void)read(handlep->poll_breakloop_fd, &value, sizeof(value));
- }
-#endif
-
- /* check for break loop condition on interrupted syscall*/
- if (handle->break_loop) {
- handle->break_loop = 0;
- return PCAP_ERROR_BREAK;
- }
- } while (ret < 0);
+ /*
+ * If we're in non-blocking mode, just quit now, rather
+ * than spinning in a loop doing poll()s that immediately
+ * time out if there's no indication on any descriptor.
+ */
+ if (handlep->poll_timeout == 0)
+ break;
+ }
return 0;
}
diff --git a/pcap.3pcap.in b/pcap.3pcap.in
index 7a69f0ac..62c65eeb 100644
--- a/pcap.3pcap.in
+++ b/pcap.3pcap.in
@@ -652,7 +652,7 @@ from the device.
Not all handles have such a descriptor available;
.BR pcap_get_selectable_fd ()
will return
-.B PCAP_ERROR
+.B \-1
if no such descriptor is available. If no such
descriptor is available, this may be because the device must be polled
periodically for packets; in that case,
@@ -727,13 +727,12 @@ and
.BR poll (2)
.TP
.BR pcap_get_required_select_timeout (3PCAP)
-if no descriptor usable with
+attempt to get a timeout required for using a
+.B pcap_t
+in calls such as
.BR select (2)
and
.BR poll (2)
-is available for the
-.BR pcap_t ,
-attempt to get a timeout usable with those routines
.RE
.SS Filters
In order to cause only certain packets to be returned when reading
diff --git a/pcap.c b/pcap.c
index bd47fabf..633b3dd3 100644
--- a/pcap.c
+++ b/pcap.c
@@ -3312,7 +3312,7 @@ pcap_get_selectable_fd(pcap_t *p)
return (p->selectable_fd);
}
-struct timeval *
+const struct timeval *
pcap_get_required_select_timeout(pcap_t *p)
{
return (p->required_select_timeout);
diff --git a/pcap/pcap.h b/pcap/pcap.h
index e738c762..60f3dbef 100644
--- a/pcap/pcap.h
+++ b/pcap/pcap.h
@@ -662,7 +662,7 @@ PCAP_API const char *pcap_lib_version(void);
*/
PCAP_API int pcap_get_selectable_fd(pcap_t *);
- PCAP_API struct timeval *pcap_get_required_select_timeout(pcap_t *);
+ PCAP_API const struct timeval *pcap_get_required_select_timeout(pcap_t *);
#endif /* _WIN32/MSDOS/UN*X */
diff --git a/pcap_get_required_select_timeout.3pcap b/pcap_get_required_select_timeout.3pcap
index b722a896..aa5d86f4 100644
--- a/pcap_get_required_select_timeout.3pcap
+++ b/pcap_get_required_select_timeout.3pcap
@@ -19,8 +19,8 @@
.\"
.TH PCAP_GET_REQUIRED_SELECT_TIMEOUT 3PCAP "25 July 2018"
.SH NAME
-pcap_get_required_select_timeout \- get a file descriptor on which a
-select() can be done for a live capture
+pcap_get_required_select_timeout \- get a timeout to be used when doing
+select() for a live capture
.SH SYNOPSIS
.nf
.ft B
@@ -28,7 +28,7 @@ select() can be done for a live capture
.ft
.LP
.ft B
-struct timeval *pcap_get_required_select_timeout(pcap_t *p);
+const struct timeval *pcap_get_required_select_timeout(pcap_t *p);
.ft
.fi
.SH DESCRIPTION
@@ -41,35 +41,78 @@ containing a value that must be used as the minimum timeout in
.BR epoll_wait (2),
and
.BR kevent (2)
-calls if
+calls, or
+.B NULL
+if there is no such timeout.
+If a
+.RB non- NULL
+value is returned, it must be used regardless of whether
.BR pcap_get_selectable_fd (3PCAP)
returns
-.BR PCAP_ERROR .
-.PP
-The timeout that should be used in those calls must be no larger than
-the smallest of all timeouts returned by
-.BR \%pcap_get_required_select_timeout ()
-for devices from which packets will be captured.
-.PP
-The device for which
-.BR pcap_get_selectable_fd ()
-returned
-.B PCAP_ERROR
-must be put in non-blocking mode with
-.BR pcap_setnonblock (3PCAP),
-and an attempt must always be made to read packets from the device
-when the
+.B \-1
+for any descriptor on which those calls are being done.
+.BR pcap_get_required_select_timeout ()
+should be called for all
+.BR pcap_t s
+before a call to
.BR select (),
.BR poll (),
.BR epoll_wait (),
or
-.BR kevent ()
-call returns.
+.BR kevent (),
+and any timeouts used for those calls should be updated as appropriate
+given the new value of the timeout.
+.PP
+For
+.BR kevent (),
+one
+.B EVFILT_TIMER
+filter per selectable descriptor can be used, rather than using the
+timeout argument to
+.BR kevent ();
+if the
+.B EVFILT_TIMER
+event for a particular selectable descriptor signals an event,
+.BR pcap_dispatch (2)
+should be called for the corresponding
+.BR pcap_t .
+.PP
+On Linux systems with
+.BR timerfd_create (2),
+one timer object created by
+.BR timerfd_create ()
+per selectable descriptor can be used, rather than using the timeout
+argument to
+.BR epoll_wait ();
+if the
+timer object for a particular selectable descriptor signals an event,
+.BR pcap_dispatch (2)
+should be called for the corresponding
+.BR pcap_t .
+.PP
+Otherwise, a timeout value no larger than
+the smallest of all timeouts returned by
+.BR \%pcap_get_required_select_timeout ()
+for devices from which packets will be captured and any other timeouts
+to be used in the call should be used as the timeout for the call, and,
+when the call returns,
+.BR pcap_dispatch (2)
+should be called for all
+.BR pcap_t s
+for which a
+.RB non- NULL
+timeout was returned, regardless of whether it's indicated as having
+anything to read from it or not.
+.PP
+All devices with a
+.RB non-NULL
+timeout must be put in non-blocking mode with
+.BR pcap_setnonblock (3PCAP).
.PP
Note that a device on which a read can be done without blocking may,
on some platforms, not have any packets to read if the packet buffer
timeout has expired. A call to
-.BR pcap_dispatch (3PCAP)
+.BR pcap_dispatch ()
or
.BR pcap_next_ex (3PCAP)
will return 0 in this case, but will not block.
@@ -93,6 +136,27 @@ and
cannot be used on any capture source for which
.BR pcap_get_selectable_fd ()
returns \-1.
+.PP
+In libpcap release 1.10.0 and later, the timeout value can change from
+call to call, so
+.BR pcap_get_required_select_timeout ()
+must be called before each call to
+.BR select (),
+.BR poll (),
+.BR epoll_wait (),
+or
+.BR kevent (),
+and the new value must be used to calculate timeouts for the call. Code
+that does that will also work with libpcap 1.9.x releases, so code
+using
+.BR pcap_get_required_select_timeout ()
+should be changed to call it for each call to
+.BR select (),
+.BR poll (),
+.BR epoll_wait (),
+or
+.BR kevent ()
+even if the code must also work with libpcap 1.9.x.
.SH SEE ALSO
.BR pcap (3PCAP),
.BR pcap_get_selectable_fd (3PCAP),
diff --git a/pcap_get_selectable_fd.3pcap b/pcap_get_selectable_fd.3pcap
index 883aae8c..5e200dd7 100644
--- a/pcap_get_selectable_fd.3pcap
+++ b/pcap_get_selectable_fd.3pcap
@@ -43,7 +43,7 @@ do a
or other such call
to wait for it to be possible to read packets without blocking, if such
a descriptor exists, or
-.BR PCAP_ERROR ,
+.BR \-1 ,
if no such descriptor exists.
.PP
Some network devices opened with
@@ -54,7 +54,7 @@ or with
.BR pcap_open_live (3PCAP),
do not support those calls (for example, regular network devices on
FreeBSD 4.3 and 4.4, and Endace DAG devices), so
-.B PCAP_ERROR
+.B \-1
is returned for
those devices. In that case, those calls must be given a timeout less
than or equal to the timeout returned by
@@ -62,7 +62,7 @@ than or equal to the timeout returned by
for the device for which
.BR pcap_get_selectable_fd ()
returned
-.BR PCAP_ERROR ,
+.BR \-1 ,
the device must be put in non-blocking mode with a call to
.BR \%pcap_setnonblock (3PCAP),
and an attempt must always be made to read packets from the device
@@ -145,7 +145,7 @@ work on that descriptor in Mac OS X 10.6 and later.
is not available on Windows.
.SH RETURN VALUE
A selectable file descriptor is returned if one exists; otherwise,
-.B PCAP_ERROR
+.B \-1
is returned.
.SH SEE ALSO
.BR pcap (3PCAP),
diff --git a/testprogs/selpolltest.c b/testprogs/selpolltest.c
index 9ee01dda..569c8294 100644
--- a/testprogs/selpolltest.c
+++ b/testprogs/selpolltest.c
@@ -69,13 +69,13 @@ main(int argc, char **argv)
register int op;
bpf_u_int32 localnet, netmask;
register char *cp, *cmdbuf, *device;
- int doselect, dopoll, dotimeout, dononblock;
+ int doselect, dopoll, dotimeout, dononblock, quiet;
const char *mechanism;
struct bpf_program fcode;
char ebuf[PCAP_ERRBUF_SIZE];
pcap_if_t *devlist;
int selectable_fd;
- struct timeval *required_timeout;
+ const struct timeval *required_timeout;
int status;
int packet_count;
@@ -85,13 +85,14 @@ main(int argc, char **argv)
mechanism = NULL;
dotimeout = 0;
dononblock = 0;
+ quiet = 0;
if ((cp = strrchr(argv[0], '/')) != NULL)
program_name = cp + 1;
else
program_name = argv[0];
opterr = 0;
- while ((op = getopt(argc, argv, "i:sptn")) != -1) {
+ while ((op = getopt(argc, argv, "i:sptnq")) != -1) {
switch (op) {
case 'i':
@@ -116,6 +117,10 @@ main(int argc, char **argv)
dononblock = 1;
break;
+ case 'q':
+ quiet = 1;
+ break;
+
default:
usage();
/* NOTREACHED */
@@ -196,6 +201,7 @@ main(int argc, char **argv)
for (;;) {
fd_set setread, setexcept;
struct timeval seltimeout;
+ struct timeval *timeoutp;
FD_ZERO(&setread);
if (selectable_fd != -1) {
@@ -203,6 +209,7 @@ main(int argc, char **argv)
FD_ZERO(&setexcept);
FD_SET(selectable_fd, &setexcept);
}
+ required_timeout = pcap_get_required_select_timeout(pd);
if (dotimeout) {
seltimeout.tv_sec = 0;
if (required_timeout != NULL &&
@@ -210,37 +217,34 @@ main(int argc, char **argv)
seltimeout.tv_usec = required_timeout->tv_usec;
else
seltimeout.tv_usec = 1000;
- status = select(selectable_fd + 1, &setread,
- NULL, &setexcept, &seltimeout);
+ timeoutp = &seltimeout;
} else if (required_timeout != NULL) {
seltimeout = *required_timeout;
- status = select(selectable_fd + 1, &setread,
- NULL, &setexcept, &seltimeout);
+ timeoutp = &seltimeout;
} else {
- status = select((selectable_fd == -1) ?
- 0 : selectable_fd + 1, &setread,
- NULL, &setexcept, NULL);
+ timeoutp = NULL;
}
+ status = select((selectable_fd == -1) ?
+ 0 : selectable_fd + 1, &setread, NULL, &setexcept,
+ timeoutp);
if (status == -1) {
printf("Select returns error (%s)\n",
strerror(errno));
} else {
- if (selectable_fd == -1) {
- if (status != 0)
- printf("Select returned a descriptor\n");
- } else {
+ if (!quiet) {
if (status == 0)
printf("Select timed out: ");
- else
+ else{
printf("Select returned a descriptor: ");
- if (FD_ISSET(selectable_fd, &setread))
- printf("readable, ");
- else
- printf("not readable, ");
- if (FD_ISSET(selectable_fd, &setexcept))
- printf("exceptional condition\n");
- else
- printf("no exceptional condition\n");
+ if (FD_ISSET(selectable_fd, &setread))
+ printf("readable, ");
+ else
+ printf("not readable, ");
+ if (FD_ISSET(selectable_fd, &setexcept))
+ printf("exceptional condition\n");
+ else
+ printf("no exceptional condition\n");
+ }
}
packet_count = 0;
status = pcap_dispatch(pd, -1, countme,
@@ -268,6 +272,7 @@ main(int argc, char **argv)
fd.fd = selectable_fd;
fd.events = POLLIN;
+ required_timeout = pcap_get_required_select_timeout(pd);
if (dotimeout)
polltimeout = 1;
else if (required_timeout != NULL &&
@@ -280,10 +285,7 @@ main(int argc, char **argv)
printf("Poll returns error (%s)\n",
strerror(errno));
} else {
- if (selectable_fd == -1) {
- if (status != 0)
- printf("Poll returned a descriptor\n");
- } else {
+ if (!quiet) {
if (status == 0)
printf("Poll timed out\n");
else {
@@ -367,7 +369,7 @@ countme(u_char *user, const struct pcap_pkthdr *h _U_, const u_char *sp _U_)
static void
usage(void)
{
- (void)fprintf(stderr, "Usage: %s [ -sptn ] [ -i interface ] [expression]\n",
+ (void)fprintf(stderr, "Usage: %s [ -sptnq ] [ -i interface ] [expression]\n",
program_name);
exit(1);
}