Skip to content

Commit 9019f53

Browse files
authored
Merge pull request #1113 from fenner/no-eventfd
Close the eventfd if we are non-blocking
2 parents 3ffafc0 + 024fd97 commit 9019f53

File tree

3 files changed

+231
-5
lines changed

3 files changed

+231
-5
lines changed

pcap-linux.c

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,10 @@ static void pcap_cleanup_linux( pcap_t *handle )
842842
handlep->device = NULL;
843843
}
844844

845-
close(handlep->poll_breakloop_fd);
845+
if (handlep->poll_breakloop_fd != -1) {
846+
close(handlep->poll_breakloop_fd);
847+
handlep->poll_breakloop_fd = -1;
848+
}
846849
pcap_cleanup_live_common(handle);
847850
}
848851

@@ -941,7 +944,8 @@ static void pcap_breakloop_linux(pcap_t *handle)
941944

942945
uint64_t value = 1;
943946
/* XXX - what if this fails? */
944-
(void)write(handlep->poll_breakloop_fd, &value, sizeof(value));
947+
if (handlep->poll_breakloop_fd != -1)
948+
(void)write(handlep->poll_breakloop_fd, &value, sizeof(value));
945949
}
946950

947951
/*
@@ -3375,7 +3379,23 @@ pcap_setnonblock_linux(pcap_t *handle, int nonblock)
33753379
*/
33763380
handlep->timeout = ~handlep->timeout;
33773381
}
3382+
if (handlep->poll_breakloop_fd != -1) {
3383+
/* Close the eventfd; we do not need it in nonblock mode. */
3384+
close(handlep->poll_breakloop_fd);
3385+
handlep->poll_breakloop_fd = -1;
3386+
}
33783387
} else {
3388+
if (handlep->poll_breakloop_fd == -1) {
3389+
/* If we did not have an eventfd, open one now that we are blocking. */
3390+
if ( ( handlep->poll_breakloop_fd = eventfd(0, EFD_NONBLOCK) ) == -1 ) {
3391+
int save_errno = errno;
3392+
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
3393+
"Could not open eventfd: %s",
3394+
strerror(errno));
3395+
errno = save_errno;
3396+
return -1;
3397+
}
3398+
}
33793399
if (handlep->timeout < 0) {
33803400
handlep->timeout = ~handlep->timeout;
33813401
}
@@ -3419,10 +3439,24 @@ static int pcap_wait_for_frames_mmap(pcap_t *handle)
34193439
struct ifreq ifr;
34203440
int ret;
34213441
struct pollfd pollinfo[2];
3442+
int numpollinfo;
34223443
pollinfo[0].fd = handle->fd;
34233444
pollinfo[0].events = POLLIN;
3424-
pollinfo[1].fd = handlep->poll_breakloop_fd;
3425-
pollinfo[1].events = POLLIN;
3445+
if ( handlep->poll_breakloop_fd == -1 ) {
3446+
numpollinfo = 1;
3447+
pollinfo[1].revents = 0;
3448+
/*
3449+
* We set pollinfo[1].revents to zero, even though
3450+
* numpollinfo = 1 meaning that poll() doesn't see
3451+
* pollinfo[1], so that we do not have to add a
3452+
* conditional of numpollinfo > 1 below when we
3453+
* test pollinfo[1].revents.
3454+
*/
3455+
} else {
3456+
pollinfo[1].fd = handlep->poll_breakloop_fd;
3457+
pollinfo[1].events = POLLIN;
3458+
numpollinfo = 2;
3459+
}
34263460

34273461
/*
34283462
* Keep polling until we either get some packets to read, see
@@ -3487,7 +3521,7 @@ static int pcap_wait_for_frames_mmap(pcap_t *handle)
34873521
if (timeout != 0)
34883522
timeout = 1;
34893523
}
3490-
ret = poll(pollinfo, 2, timeout);
3524+
ret = poll(pollinfo, numpollinfo, timeout);
34913525
if (ret < 0) {
34923526
/*
34933527
* Error. If it's not EINTR, report it.

testprogs/Makefile.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ SRC = @VALGRINDTEST_SRC@ \
8585
findalldevstest-perf.c \
8686
findalldevstest.c \
8787
opentest.c \
88+
nonblocktest.c \
8889
reactivatetest.c \
8990
selpolltest.c \
9091
threadsignaltest.c \
@@ -126,6 +127,10 @@ opentest: $(srcdir)/opentest.c ../libpcap.a
126127
$(CC) $(FULL_CFLAGS) -I. -L. -o opentest $(srcdir)/opentest.c \
127128
../libpcap.a $(LIBS)
128129

130+
nonblocktest: $(srcdir)/nonblocktest.c ../libpcap.a
131+
$(CC) $(FULL_CFLAGS) -I. -L. -o nonblocktest $(srcdir)/nonblocktest.c \
132+
../libpcap.a $(LIBS)
133+
129134
reactivatetest: $(srcdir)/reactivatetest.c ../libpcap.a
130135
$(CC) $(FULL_CFLAGS) -I. -L. -o reactivatetest \
131136
$(srcdir)/reactivatetest.c ../libpcap.a $(LIBS)

testprogs/nonblocktest.c

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
3+
* The Regents of the University of California. All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that: (1) source code distributions
7+
* retain the above copyright notice and this paragraph in its entirety, (2)
8+
* distributions including binary code include the above copyright notice and
9+
* this paragraph in its entirety in the documentation or other materials
10+
* provided with the distribution, and (3) all advertising materials mentioning
11+
* features or use of this software display the following acknowledgement:
12+
* ``This product includes software developed by the University of California,
13+
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14+
* the University nor the names of its contributors may be used to endorse
15+
* or promote products derived from this software without specific prior
16+
* written permission.
17+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18+
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19+
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20+
*/
21+
22+
#include "varattrs.h"
23+
24+
/*
25+
* Tests for pcap_set_nonblock / pcap_get_nonblock:
26+
* - idempotency
27+
* - set/get are symmetric
28+
* - get returns the same before/after activate
29+
* - pcap_breakloop works after setting nonblock on and then off
30+
*
31+
* Really this is meant to
32+
* be run manually under strace, to check for extra
33+
* calls to eventfd or close.
34+
*/
35+
#include <pcap.h>
36+
#include <stdio.h>
37+
#include <stdlib.h>
38+
#include <string.h>
39+
#include <stdarg.h>
40+
#include <unistd.h>
41+
#include <sys/types.h>
42+
#include <sys/stat.h>
43+
#include <fcntl.h>
44+
45+
static pcap_t *pd;
46+
static char *program_name = "nonblocktest";
47+
/* Forwards */
48+
static void PCAP_NORETURN usage(void);
49+
static void PCAP_NORETURN error(const char *, ...) PCAP_PRINTFLIKE(1, 2);
50+
static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2);
51+
52+
/* VARARGS */
53+
static void
54+
error(const char *fmt, ...)
55+
{
56+
va_list ap;
57+
58+
(void)fprintf(stderr, "%s: ", program_name);
59+
va_start(ap, fmt);
60+
(void)vfprintf(stderr, fmt, ap);
61+
va_end(ap);
62+
if (*fmt) {
63+
fmt += strlen(fmt);
64+
if (fmt[-1] != '\n')
65+
(void)fputc('\n', stderr);
66+
}
67+
exit(1);
68+
/* NOTREACHED */
69+
}
70+
71+
/* VARARGS */
72+
static void
73+
warning(const char *fmt, ...)
74+
{
75+
va_list ap;
76+
77+
(void)fprintf(stderr, "%s: WARNING: ", program_name);
78+
va_start(ap, fmt);
79+
(void)vfprintf(stderr, fmt, ap);
80+
va_end(ap);
81+
if (*fmt) {
82+
fmt += strlen(fmt);
83+
if (fmt[-1] != '\n')
84+
(void)fputc('\n', stderr);
85+
}
86+
}
87+
88+
static void
89+
usage(void)
90+
{
91+
(void)fprintf(stderr, "Usage: %s [ -i interface ]\n",
92+
program_name);
93+
exit(1);
94+
}
95+
96+
static void
97+
breakme(u_char *user _U_, const struct pcap_pkthdr *h _U_, const u_char *sp _U_)
98+
{
99+
warning("using pcap_breakloop()");
100+
pcap_breakloop(pd);
101+
}
102+
103+
int
104+
main(int argc, char **argv)
105+
{
106+
int status, op, i, ret;
107+
char *device;
108+
pcap_if_t *devlist;
109+
char ebuf[PCAP_ERRBUF_SIZE];
110+
111+
device = NULL;
112+
while ((op = getopt(argc, argv, "i:sptnq")) != -1) {
113+
switch (op) {
114+
115+
case 'i':
116+
device = optarg;
117+
break;
118+
119+
default:
120+
usage();
121+
/* NOTREACHED */
122+
}
123+
}
124+
if (device == NULL) {
125+
if (pcap_findalldevs(&devlist, ebuf) == -1)
126+
error("%s", ebuf);
127+
if (devlist == NULL)
128+
error("no interfaces available for capture");
129+
device = strdup(devlist->name);
130+
warning("listening on %s", device);
131+
pcap_freealldevs(devlist);
132+
}
133+
*ebuf = '\0';
134+
pd = pcap_create(device, ebuf);
135+
if (pd == NULL)
136+
error("%s", ebuf);
137+
else if (*ebuf)
138+
warning("%s", ebuf);
139+
/* set nonblock before activate */
140+
if (pcap_setnonblock(pd, 1, ebuf) < 0)
141+
error("pcap_setnonblock failed: %s", ebuf);
142+
/* getnonblock just returns "not activated yet" */
143+
ret = pcap_getnonblock(pd, ebuf);
144+
if (ret != PCAP_ERROR_NOT_ACTIVATED)
145+
error("pcap_getnonblock unexpectedly succeeded");
146+
if ((status = pcap_activate(pd)) < 0)
147+
error("pcap_activate failed");
148+
ret = pcap_getnonblock(pd, ebuf);
149+
if (ret != 1)
150+
error( "pcap_getnonblock did not return nonblocking" );
151+
152+
/* Set nonblock multiple times, ensure with strace that it's a noop */
153+
for (i=0; i<10; i++) {
154+
if (pcap_setnonblock(pd, 1, ebuf) < 0)
155+
error("pcap_setnonblock failed: %s", ebuf);
156+
ret = pcap_getnonblock(pd, ebuf);
157+
if (ret != 1)
158+
error( "pcap_getnonblock did not return nonblocking" );
159+
}
160+
/* Set block multiple times, ensure with strace that it's a noop */
161+
for (i=0; i<10; i++) {
162+
if (pcap_setnonblock(pd, 0, ebuf) < 0)
163+
error("pcap_setnonblock failed: %s", ebuf);
164+
ret = pcap_getnonblock(pd, ebuf);
165+
if (ret != 0)
166+
error( "pcap_getnonblock did not return blocking" );
167+
}
168+
169+
/* Now pcap_loop forever, with a callback that
170+
* uses pcap_breakloop to get out of forever */
171+
pcap_loop(pd, -1, breakme, NULL);
172+
173+
/* Now test that pcap_setnonblock fails if we can't open the
174+
* eventfd. */
175+
if (pcap_setnonblock(pd, 1, ebuf) < 0)
176+
error("pcap_setnonblock failed: %s", ebuf);
177+
while (1) {
178+
ret = open("/dev/null", O_RDONLY);
179+
if (ret < 0)
180+
break;
181+
}
182+
ret = pcap_setnonblock(pd, 0, ebuf);
183+
if (ret == 0)
184+
error("pcap_setnonblock succeeded even though file table is full");
185+
else
186+
warning("pcap_setnonblock failed as expected: %s", ebuf);
187+
}

0 commit comments

Comments
 (0)