aboutsummaryrefslogtreecommitdiff
path: root/testprogs/selpolltest.c
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2020-01-29 14:25:15 -0800
committerGuy Harris <guy@alum.mit.edu>2020-01-29 14:25:34 -0800
commita973128a85d7dd75c7ea6fdcf746fc143a987d03 (patch)
tree244eab6a441630b5ff7832c9a8157ce29b228dde /testprogs/selpolltest.c
parente97a651aff28c4bae6696958afbb94f88d80645b (diff)
On Linux, return error on interface going away, not just going down.
This is a pain to detect, because the PF_PACKET socket code appears to get separate "interface went down" and "interface went away" notifications in my "unplug a USB Wi-Fi adapter" tests on my VMware Fusion Ubuntu 18.04 virtual machine (5.3.0 kernel), and the first notification delivers a wakeup and returns ENETDOWN while the second notificaiton delivers *no* wakeup and sets the ifindex member of the struct packet_sock for the socket, so there's nothing we can test after the wakeup that's guaranteed to indicate that the interface has disappeared. So what we have to do is remember the ENETDOWN but not return it as an error, and then arrange to periodically check whether the interface is still there; if it isn't, we *then* return the "interface went away" error, and, if we see traffic or see that the interface is up, we clear the remembered ENETDOWN and stop doing the periodic checks. This is tricky, because it needs to work not only for blocking pcap_t's, where we're in a loop doing poll() calls, so we can keep checking within the loop, but also for non-blocking pcap_t's on which the caller is doing select()/poll()/epoll_wait(). In order to make *that* work, we need to tweak the semantics of pcap_get_required_select_timeout() so that it's not guaranteed that it will always return the same value, so that it should be called within event loops rather than called once outside the event loop. Normally, there is no timeout required for Linux PF_PACKET sockets, but when we're doing the periodic tests, the timeout is required. While we're doing that, we make the return value of pcap_get_required_select_timeout() a const pointer - there was no good reason for the caller to modify it (it doesn't belong to the caller). If poll() returns POLLERR, use getsockopt(SO_ERROR) to get the socket error, rather than a read(). Update the documentation to reflect this, and make various other cleanups (including documenting the error return value for pcap_get_selectable_fd() to -1 rather than PCAP_ERROR - it's not an error code, it's just a specific error value). Also note that, for kqueues on *BSD/macOS and for select/poll on Linux, the timeout needn't be used as a timeout for the call - you can have a timer, so that when that *particular* timer fires, you try calling pcap_dispatch() on the pcap_t to which it corresponds. Update selpolltest to add more capabilities needed when testing this on Linux. This should address GitHub issue #859 and pull request #858.
Diffstat (limited to 'testprogs/selpolltest.c')
-rw-r--r--testprogs/selpolltest.c58
1 files changed, 30 insertions, 28 deletions
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);
}