From 024fd9717d43835f3e08b3146714532768910d3c Mon Sep 17 00:00:00 2001 From: Bill Fenner Date: Thu, 26 May 2022 11:41:43 -0700 Subject: Close the eventfd if we are non-blocking The eventfd is used to break out of a poll() before it times out, used by pcap_breakloop(). If we are non-blocking, then the eventfd is never needed, so we close it. (And open a new eventfd if we switch to blocking). --- testprogs/Makefile.in | 5 ++ testprogs/nonblocktest.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 testprogs/nonblocktest.c (limited to 'testprogs') diff --git a/testprogs/Makefile.in b/testprogs/Makefile.in index 45e1c73e..293ed888 100644 --- a/testprogs/Makefile.in +++ b/testprogs/Makefile.in @@ -85,6 +85,7 @@ SRC = @VALGRINDTEST_SRC@ \ findalldevstest-perf.c \ findalldevstest.c \ opentest.c \ + nonblocktest.c \ reactivatetest.c \ selpolltest.c \ threadsignaltest.c \ @@ -126,6 +127,10 @@ opentest: $(srcdir)/opentest.c ../libpcap.a $(CC) $(FULL_CFLAGS) -I. -L. -o opentest $(srcdir)/opentest.c \ ../libpcap.a $(LIBS) +nonblocktest: $(srcdir)/nonblocktest.c ../libpcap.a + $(CC) $(FULL_CFLAGS) -I. -L. -o nonblocktest $(srcdir)/nonblocktest.c \ + ../libpcap.a $(LIBS) + reactivatetest: $(srcdir)/reactivatetest.c ../libpcap.a $(CC) $(FULL_CFLAGS) -I. -L. -o reactivatetest \ $(srcdir)/reactivatetest.c ../libpcap.a $(LIBS) diff --git a/testprogs/nonblocktest.c b/testprogs/nonblocktest.c new file mode 100644 index 00000000..72700a3b --- /dev/null +++ b/testprogs/nonblocktest.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "varattrs.h" + +/* + * Tests for pcap_set_nonblock / pcap_get_nonblock: + * - idempotency + * - set/get are symmetric + * - get returns the same before/after activate + * - pcap_breakloop works after setting nonblock on and then off + * + * Really this is meant to + * be run manually under strace, to check for extra + * calls to eventfd or close. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static pcap_t *pd; +static char *program_name = "nonblocktest"; +/* Forwards */ +static void PCAP_NORETURN usage(void); +static void PCAP_NORETURN error(const char *, ...) PCAP_PRINTFLIKE(1, 2); +static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2); + +/* VARARGS */ +static void +error(const char *fmt, ...) +{ + va_list ap; + + (void)fprintf(stderr, "%s: ", program_name); + va_start(ap, fmt); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (*fmt) { + fmt += strlen(fmt); + if (fmt[-1] != '\n') + (void)fputc('\n', stderr); + } + exit(1); + /* NOTREACHED */ +} + +/* VARARGS */ +static void +warning(const char *fmt, ...) +{ + va_list ap; + + (void)fprintf(stderr, "%s: WARNING: ", program_name); + va_start(ap, fmt); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (*fmt) { + fmt += strlen(fmt); + if (fmt[-1] != '\n') + (void)fputc('\n', stderr); + } +} + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s [ -i interface ]\n", + program_name); + exit(1); +} + +static void +breakme(u_char *user _U_, const struct pcap_pkthdr *h _U_, const u_char *sp _U_) +{ + warning("using pcap_breakloop()"); + pcap_breakloop(pd); +} + +int +main(int argc, char **argv) +{ + int status, op, i, ret; + char *device; + pcap_if_t *devlist; + char ebuf[PCAP_ERRBUF_SIZE]; + + device = NULL; + while ((op = getopt(argc, argv, "i:sptnq")) != -1) { + switch (op) { + + case 'i': + device = optarg; + break; + + default: + usage(); + /* NOTREACHED */ + } + } + if (device == NULL) { + if (pcap_findalldevs(&devlist, ebuf) == -1) + error("%s", ebuf); + if (devlist == NULL) + error("no interfaces available for capture"); + device = strdup(devlist->name); + warning("listening on %s", device); + pcap_freealldevs(devlist); + } + *ebuf = '\0'; + pd = pcap_create(device, ebuf); + if (pd == NULL) + error("%s", ebuf); + else if (*ebuf) + warning("%s", ebuf); + /* set nonblock before activate */ + if (pcap_setnonblock(pd, 1, ebuf) < 0) + error("pcap_setnonblock failed: %s", ebuf); + /* getnonblock just returns "not activated yet" */ + ret = pcap_getnonblock(pd, ebuf); + if (ret != PCAP_ERROR_NOT_ACTIVATED) + error("pcap_getnonblock unexpectedly succeeded"); + if ((status = pcap_activate(pd)) < 0) + error("pcap_activate failed"); + ret = pcap_getnonblock(pd, ebuf); + if (ret != 1) + error( "pcap_getnonblock did not return nonblocking" ); + + /* Set nonblock multiple times, ensure with strace that it's a noop */ + for (i=0; i<10; i++) { + if (pcap_setnonblock(pd, 1, ebuf) < 0) + error("pcap_setnonblock failed: %s", ebuf); + ret = pcap_getnonblock(pd, ebuf); + if (ret != 1) + error( "pcap_getnonblock did not return nonblocking" ); + } + /* Set block multiple times, ensure with strace that it's a noop */ + for (i=0; i<10; i++) { + if (pcap_setnonblock(pd, 0, ebuf) < 0) + error("pcap_setnonblock failed: %s", ebuf); + ret = pcap_getnonblock(pd, ebuf); + if (ret != 0) + error( "pcap_getnonblock did not return blocking" ); + } + + /* Now pcap_loop forever, with a callback that + * uses pcap_breakloop to get out of forever */ + pcap_loop(pd, -1, breakme, NULL); + + /* Now test that pcap_setnonblock fails if we can't open the + * eventfd. */ + if (pcap_setnonblock(pd, 1, ebuf) < 0) + error("pcap_setnonblock failed: %s", ebuf); + while (1) { + ret = open("/dev/null", O_RDONLY); + if (ret < 0) + break; + } + ret = pcap_setnonblock(pd, 0, ebuf); + if (ret == 0) + error("pcap_setnonblock succeeded even though file table is full"); + else + warning("pcap_setnonblock failed as expected: %s", ebuf); +} -- cgit v1.2.1