xref: /freebsd/contrib/libpcap/testprogs/selpolltest.c (revision c14a5a8800a0f7a007f8cd197b4cad4d26a78f8c)
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 #ifndef lint
25 static const char copyright[] _U_ =
26     "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\
27 The Regents of the University of California.  All rights reserved.\n";
28 #endif
29 
30 /*
31  * Tests how select() and poll() behave on the selectable file descriptor
32  * for a pcap_t.
33  *
34  * This would be significantly different on Windows, as it'd test
35  * how WaitForMultipleObjects() would work on the event handle for a
36  * pcap_t.
37  */
38 #include <pcap.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <stdarg.h>
43 #include <unistd.h>
44 #include <errno.h>
45 #include <sys/types.h>
46 #ifdef HAVE_SYS_SELECT_H
47 #include <sys/select.h>
48 #else
49 #include <sys/time.h>	/* older UN*Xes */
50 #endif
51 #include <poll.h>
52 
53 #include "pcap/funcattrs.h"
54 
55 static char *program_name;
56 
57 /* Forwards */
58 static void countme(u_char *, const struct pcap_pkthdr *, const u_char *);
59 static void PCAP_NORETURN usage(void);
60 static void PCAP_NORETURN error(const char *, ...) PCAP_PRINTFLIKE(1, 2);
61 static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2);
62 static char *copy_argv(char **);
63 
64 static pcap_t *pd;
65 
66 int
67 main(int argc, char **argv)
68 {
69 	register int op;
70 	bpf_u_int32 localnet, netmask;
71 	register char *cp, *cmdbuf, *device;
72 	int doselect, dopoll, dotimeout, dononblock;
73 	const char *mechanism;
74 	struct bpf_program fcode;
75 	char ebuf[PCAP_ERRBUF_SIZE];
76 	pcap_if_t *devlist;
77 	int selectable_fd;
78 	struct timeval *required_timeout;
79 	int status;
80 	int packet_count;
81 
82 	device = NULL;
83 	doselect = 0;
84 	dopoll = 0;
85 	mechanism = NULL;
86 	dotimeout = 0;
87 	dononblock = 0;
88 	if ((cp = strrchr(argv[0], '/')) != NULL)
89 		program_name = cp + 1;
90 	else
91 		program_name = argv[0];
92 
93 	opterr = 0;
94 	while ((op = getopt(argc, argv, "i:sptn")) != -1) {
95 		switch (op) {
96 
97 		case 'i':
98 			device = optarg;
99 			break;
100 
101 		case 's':
102 			doselect = 1;
103 			mechanism = "select() and pcap_dispatch()";
104 			break;
105 
106 		case 'p':
107 			dopoll = 1;
108 			mechanism = "poll() and pcap_dispatch()";
109 			break;
110 
111 		case 't':
112 			dotimeout = 1;
113 			break;
114 
115 		case 'n':
116 			dononblock = 1;
117 			break;
118 
119 		default:
120 			usage();
121 			/* NOTREACHED */
122 		}
123 	}
124 
125 	if (doselect && dopoll) {
126 		fprintf(stderr, "selpolltest: choose select (-s) or poll (-p), but not both\n");
127 		return 1;
128 	}
129 	if (dotimeout && !doselect && !dopoll) {
130 		fprintf(stderr, "selpolltest: timeout (-t) requires select (-s) or poll (-p)\n");
131 		return 1;
132 	}
133 	if (device == NULL) {
134 		if (pcap_findalldevs(&devlist, ebuf) == -1)
135 			error("%s", ebuf);
136 		if (devlist == NULL)
137 			error("no interfaces available for capture");
138 		device = strdup(devlist->name);
139 		pcap_freealldevs(devlist);
140 	}
141 	*ebuf = '\0';
142 	pd = pcap_open_live(device, 65535, 0, 1000, ebuf);
143 	if (pd == NULL)
144 		error("%s", ebuf);
145 	else if (*ebuf)
146 		warning("%s", ebuf);
147 	if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) {
148 		localnet = 0;
149 		netmask = 0;
150 		warning("%s", ebuf);
151 	}
152 	cmdbuf = copy_argv(&argv[optind]);
153 
154 	if (pcap_compile(pd, &fcode, cmdbuf, 1, netmask) < 0)
155 		error("%s", pcap_geterr(pd));
156 	if (pcap_setfilter(pd, &fcode) < 0)
157 		error("%s", pcap_geterr(pd));
158 
159 	if (doselect || dopoll) {
160 		/*
161 		 * We need either an FD on which to do select()/poll()
162 		 * or, if there isn't one, a timeout to use in select()/
163 		 * poll().
164 		 */
165 		selectable_fd = pcap_get_selectable_fd(pd);
166 		if (selectable_fd == -1) {
167 			printf("Listening on %s, using %s, with a timeout\n",
168 			    device, mechanism);
169 			required_timeout = pcap_get_required_select_timeout(pd);
170 			if (required_timeout == NULL)
171 				error("select()/poll() isn't supported on %s, even with a timeout",
172 				    device);
173 
174 			/*
175 			 * As we won't be notified by select() or poll()
176 			 * that a read can be done, we'll have to periodically
177 			 * try reading from the device every time the required
178 			 * timeout expires, and we don't want those attempts
179 			 * to block if nothing has arrived in that interval,
180 			 * so we want to force non-blocking mode.
181 			 */
182 			dononblock = 1;
183 		} else {
184 			printf("Listening on %s, using %s\n", device,
185 			    mechanism);
186 			required_timeout = NULL;
187 		}
188 	} else
189 		printf("Listening on %s, using pcap_dispatch()\n", device);
190 
191 	if (dononblock) {
192 		if (pcap_setnonblock(pd, 1, ebuf) == -1)
193 			error("pcap_setnonblock failed: %s", ebuf);
194 	}
195 	if (doselect) {
196 		for (;;) {
197 			fd_set setread, setexcept;
198 			struct timeval seltimeout;
199 
200 			FD_ZERO(&setread);
201 			if (selectable_fd != -1) {
202 				FD_SET(selectable_fd, &setread);
203 				FD_ZERO(&setexcept);
204 				FD_SET(selectable_fd, &setexcept);
205 			}
206 			if (dotimeout) {
207 				seltimeout.tv_sec = 0;
208 				if (required_timeout != NULL &&
209 				    required_timeout->tv_usec < 1000)
210 					seltimeout.tv_usec = required_timeout->tv_usec;
211 				else
212 					seltimeout.tv_usec = 1000;
213 				status = select(selectable_fd + 1, &setread,
214 				    NULL, &setexcept, &seltimeout);
215 			} else if (required_timeout != NULL) {
216 				seltimeout = *required_timeout;
217 				status = select(selectable_fd + 1, &setread,
218 				    NULL, &setexcept, &seltimeout);
219 			} else {
220 				status = select((selectable_fd == -1) ?
221 				    0 : selectable_fd + 1, &setread,
222 				    NULL, &setexcept, NULL);
223 			}
224 			if (status == -1) {
225 				printf("Select returns error (%s)\n",
226 				    strerror(errno));
227 			} else {
228 				if (selectable_fd == -1) {
229 					if (status != 0)
230 						printf("Select returned a descriptor\n");
231 				} else {
232 					if (status == 0)
233 						printf("Select timed out: ");
234 					else
235 						printf("Select returned a descriptor: ");
236 					if (FD_ISSET(selectable_fd, &setread))
237 						printf("readable, ");
238 					else
239 						printf("not readable, ");
240 					if (FD_ISSET(selectable_fd, &setexcept))
241 						printf("exceptional condition\n");
242 					else
243 						printf("no exceptional condition\n");
244 				}
245 				packet_count = 0;
246 				status = pcap_dispatch(pd, -1, countme,
247 				    (u_char *)&packet_count);
248 				if (status < 0)
249 					break;
250 				/*
251 				 * Don't report this if we're using a
252 				 * required timeout and we got no packets,
253 				 * because that could be a very short timeout,
254 				 * and we don't want to spam the user with
255 				 * a ton of "no packets" reports.
256 				 */
257 				if (status != 0 || packet_count != 0 ||
258 				    required_timeout != NULL) {
259 					printf("%d packets seen, %d packets counted after select returns\n",
260 					    status, packet_count);
261 				}
262 			}
263 		}
264 	} else if (dopoll) {
265 		for (;;) {
266 			struct pollfd fd;
267 			int polltimeout;
268 
269 			fd.fd = selectable_fd;
270 			fd.events = POLLIN;
271 			if (dotimeout)
272 				polltimeout = 1;
273 			else if (required_timeout != NULL &&
274 			    required_timeout->tv_usec >= 1000)
275 				polltimeout = required_timeout->tv_usec/1000;
276 			else
277 				polltimeout = -1;
278 			status = poll(&fd, (selectable_fd == -1) ? 0 : 1, polltimeout);
279 			if (status == -1) {
280 				printf("Poll returns error (%s)\n",
281 				    strerror(errno));
282 			} else {
283 				if (selectable_fd == -1) {
284 					if (status != 0)
285 						printf("Poll returned a descriptor\n");
286 				} else {
287 					if (status == 0)
288 						printf("Poll timed out\n");
289 					else {
290 						printf("Poll returned a descriptor: ");
291 						if (fd.revents & POLLIN)
292 							printf("readable, ");
293 						else
294 							printf("not readable, ");
295 						if (fd.revents & POLLERR)
296 							printf("exceptional condition, ");
297 						else
298 							printf("no exceptional condition, ");
299 						if (fd.revents & POLLHUP)
300 							printf("disconnect, ");
301 						else
302 							printf("no disconnect, ");
303 						if (fd.revents & POLLNVAL)
304 							printf("invalid\n");
305 						else
306 							printf("not invalid\n");
307 					}
308 				}
309 				packet_count = 0;
310 				status = pcap_dispatch(pd, -1, countme,
311 				    (u_char *)&packet_count);
312 				if (status < 0)
313 					break;
314 				/*
315 				 * Don't report this if we're using a
316 				 * required timeout and we got no packets,
317 				 * because that could be a very short timeout,
318 				 * and we don't want to spam the user with
319 				 * a ton of "no packets" reports.
320 				 */
321 				if (status != 0 || packet_count != 0 ||
322 				    required_timeout != NULL) {
323 					printf("%d packets seen, %d packets counted after poll returns\n",
324 					    status, packet_count);
325 				}
326 			}
327 		}
328 	} else {
329 		for (;;) {
330 			packet_count = 0;
331 			status = pcap_dispatch(pd, -1, countme,
332 			    (u_char *)&packet_count);
333 			if (status < 0)
334 				break;
335 			printf("%d packets seen, %d packets counted after pcap_dispatch returns\n",
336 			    status, packet_count);
337 		}
338 	}
339 	if (status == -2) {
340 		/*
341 		 * We got interrupted, so perhaps we didn't
342 		 * manage to finish a line we were printing.
343 		 * Print an extra newline, just in case.
344 		 */
345 		putchar('\n');
346 	}
347 	(void)fflush(stdout);
348 	if (status == -1) {
349 		/*
350 		 * Error.  Report it.
351 		 */
352 		(void)fprintf(stderr, "%s: pcap_loop: %s\n",
353 		    program_name, pcap_geterr(pd));
354 	}
355 	pcap_close(pd);
356 	exit(status == -1 ? 1 : 0);
357 }
358 
359 static void
360 countme(u_char *user, const struct pcap_pkthdr *h _U_, const u_char *sp _U_)
361 {
362 	int *counterp = (int *)user;
363 
364 	(*counterp)++;
365 }
366 
367 static void
368 usage(void)
369 {
370 	(void)fprintf(stderr, "Usage: %s [ -sptn ] [ -i interface ] [expression]\n",
371 	    program_name);
372 	exit(1);
373 }
374 
375 /* VARARGS */
376 static void
377 error(const char *fmt, ...)
378 {
379 	va_list ap;
380 
381 	(void)fprintf(stderr, "%s: ", program_name);
382 	va_start(ap, fmt);
383 	(void)vfprintf(stderr, fmt, ap);
384 	va_end(ap);
385 	if (*fmt) {
386 		fmt += strlen(fmt);
387 		if (fmt[-1] != '\n')
388 			(void)fputc('\n', stderr);
389 	}
390 	exit(1);
391 	/* NOTREACHED */
392 }
393 
394 /* VARARGS */
395 static void
396 warning(const char *fmt, ...)
397 {
398 	va_list ap;
399 
400 	(void)fprintf(stderr, "%s: WARNING: ", program_name);
401 	va_start(ap, fmt);
402 	(void)vfprintf(stderr, fmt, ap);
403 	va_end(ap);
404 	if (*fmt) {
405 		fmt += strlen(fmt);
406 		if (fmt[-1] != '\n')
407 			(void)fputc('\n', stderr);
408 	}
409 }
410 
411 /*
412  * Copy arg vector into a new buffer, concatenating arguments with spaces.
413  */
414 static char *
415 copy_argv(register char **argv)
416 {
417 	register char **p;
418 	register u_int len = 0;
419 	char *buf;
420 	char *src, *dst;
421 
422 	p = argv;
423 	if (*p == 0)
424 		return 0;
425 
426 	while (*p)
427 		len += strlen(*p++) + 1;
428 
429 	buf = (char *)malloc(len);
430 	if (buf == NULL)
431 		error("copy_argv: malloc");
432 
433 	p = argv;
434 	dst = buf;
435 	while ((src = *p++) != NULL) {
436 		while ((*dst++ = *src++) != '\0')
437 			;
438 		dst[-1] = ' ';
439 	}
440 	dst[-1] = '\0';
441 
442 	return buf;
443 }
444