]> The Tcpdump Group git mirrors - libpcap/blob - testprogs/filtertest.c
d136e62799bffb77d3add8a92cc942327d036313
[libpcap] / testprogs / filtertest.c
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 #include <config.h>
31
32 #include <pcap.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <stdarg.h>
37 #include <limits.h>
38 #ifdef _WIN32
39 #include "getopt.h"
40 #include "unix.h"
41 #else
42 #include <unistd.h>
43 #include <sysexits.h>
44 #endif
45 #include <fcntl.h>
46 #include <errno.h>
47 #ifdef _WIN32
48 #include <winsock2.h>
49 #include <ws2tcpip.h>
50 #else
51 #include <sys/socket.h>
52 #include <arpa/inet.h>
53 #endif
54 #include <sys/types.h>
55 #include <sys/stat.h>
56
57 #include "pcap/funcattrs.h"
58
59 #define MAXIMUM_SNAPLEN 262144
60 #define MAX_STDIN (64 * 1024)
61
62 #ifdef BDEBUG
63 /*
64 * We have pcap_set_optimizer_debug() and pcap_set_print_dot_graph() in
65 * libpcap; declare them (they're not declared by any libpcap header,
66 * because they're special hacks, only available if libpcap was configured
67 * to include them, and only intended for use by libpcap developers trying
68 * to debug the optimizer for filter expressions).
69 */
70 PCAP_API void pcap_set_optimizer_debug(int);
71 PCAP_API void pcap_set_print_dot_graph(int);
72 #endif
73
74 #ifdef __linux__
75 #include <linux/filter.h> // SKF_AD_VLAN_TAG_PRESENT
76 /*
77 * pcap-int.h is a private header and should not be included by programs that
78 * use libpcap. This test program uses a special hack because it is the
79 * simplest way to test internal code paths that otherwise would require
80 * elevated privileges. Do not do this in normal code.
81 */
82 #include <pcap-int.h>
83 #endif // __linux__
84
85 static char *program_name;
86
87 /* Forwards */
88 static void PCAP_NORETURN usage(FILE *);
89 static void PCAP_NORETURN error(const int, const char *, ...) PCAP_PRINTFLIKE(2, 3);
90 static void warn(const char *, ...) PCAP_PRINTFLIKE(1, 2);
91
92 /*
93 * On Windows, we need to open the file in binary mode, so that
94 * we get all the bytes specified by the size we get from "fstat()".
95 * On UNIX, that's not necessary. O_BINARY is defined on Windows;
96 * we define it as 0 if it's not defined, so it does nothing.
97 */
98 #ifndef O_BINARY
99 #define O_BINARY 0
100 #endif
101
102 // Replace "# comment" with spaces.
103 static void
104 blank_comments(char *cp, const size_t size)
105 {
106 for (size_t i = 0; i < size; i++) {
107 if (cp[i] == '#')
108 while (i < size && cp[i] != '\n')
109 cp[i++] = ' ';
110 }
111 }
112
113 static char *
114 read_infile(char *fname)
115 {
116 int fd, cc;
117 register char *cp;
118 struct stat buf;
119
120 fd = open(fname, O_RDONLY|O_BINARY);
121 if (fd < 0)
122 error(EX_NOINPUT, "can't open %s: %s", fname, pcap_strerror(errno));
123
124 if (fstat(fd, &buf) < 0)
125 error(EX_NOINPUT, "can't stat %s: %s", fname, pcap_strerror(errno));
126
127 /*
128 * _read(), on Windows, has an unsigned int byte count and an
129 * int return value, so we can't handle a file bigger than
130 * INT_MAX - 1 bytes (and have no reason to do so; a filter *that*
131 * big will take forever to compile). (The -1 is for the '\0' at
132 * the end of the string.)
133 */
134 if (buf.st_size > INT_MAX - 1)
135 error(EX_DATAERR, "%s is larger than %d bytes; that's too large", fname,
136 INT_MAX - 1);
137 cp = malloc((u_int)buf.st_size + 1);
138 if (cp == NULL)
139 error(EX_OSERR, "malloc(%d) for %s: %s", (u_int)buf.st_size + 1,
140 fname, pcap_strerror(errno));
141 cc = (int)read(fd, cp, (u_int)buf.st_size);
142 if (cc < 0)
143 error(EX_IOERR, "read %s: %s", fname, pcap_strerror(errno));
144 if (cc != buf.st_size)
145 error(EX_IOERR, "short read %s (%d != %d)", fname, cc, (int)buf.st_size);
146
147 close(fd);
148 blank_comments(cp, (size_t)cc);
149 cp[cc] = '\0';
150 return (cp);
151 }
152
153 // Copy stdin into a size-limited buffer.
154 static char *
155 read_stdin(void)
156 {
157 char *buf = calloc(1, MAX_STDIN + 1);
158 if (buf == NULL)
159 error(EX_OSERR, "%s: calloc", __func__);
160 size_t readsize = fread(buf, 1, MAX_STDIN, stdin);
161 if (! feof(stdin)) {
162 free(buf);
163 error(EX_DATAERR, "received more than %u bytes on stdin", MAX_STDIN);
164 }
165 if (ferror(stdin)) {
166 free(buf);
167 error(EX_IOERR, "failed reading from stdin after %zd bytes", readsize);
168 }
169 fclose(stdin);
170 // No error, all data is within the buffer and NUL-terminated.
171 blank_comments(buf, readsize);
172 return buf;
173 }
174
175 /* VARARGS */
176 static void
177 error(const int status, const char *fmt, ...)
178 {
179 va_list ap;
180
181 (void)fprintf(stderr, "%s: ", program_name);
182 va_start(ap, fmt);
183 (void)vfprintf(stderr, fmt, ap);
184 va_end(ap);
185 if (*fmt) {
186 fmt += strlen(fmt);
187 if (fmt[-1] != '\n')
188 (void)fputc('\n', stderr);
189 }
190 exit(status);
191 /* NOTREACHED */
192 }
193
194 /* VARARGS */
195 static void
196 warn(const char *fmt, ...)
197 {
198 va_list ap;
199
200 (void)fprintf(stderr, "%s: WARNING: ", program_name);
201 va_start(ap, fmt);
202 (void)vfprintf(stderr, fmt, ap);
203 va_end(ap);
204 if (*fmt) {
205 fmt += strlen(fmt);
206 if (fmt[-1] != '\n')
207 (void)fputc('\n', stderr);
208 }
209 }
210
211 /*
212 * Copy arg vector into a new buffer, concatenating arguments with spaces.
213 */
214 static char *
215 copy_argv(register char **argv)
216 {
217 register char **p;
218 register size_t len = 0;
219 char *buf;
220 char *src, *dst;
221
222 p = argv;
223 if (*p == 0)
224 return 0;
225
226 while (*p)
227 len += strlen(*p++) + 1;
228
229 buf = (char *)malloc(len);
230 if (buf == NULL)
231 error(EX_OSERR, "%s: malloc", __func__);
232
233 p = argv;
234 dst = buf;
235 while ((src = *p++) != NULL) {
236 while ((*dst++ = *src++) != '\0')
237 ;
238 dst[-1] = ' ';
239 }
240 dst[-1] = '\0';
241
242 return buf;
243 }
244
245 int
246 main(int argc, char **argv)
247 {
248 char *cp;
249 int op;
250 int dflag = 1;
251 #ifdef BDEBUG
252 int gflag = 0;
253 #endif
254 char *infile = NULL;
255 char *insavefile = NULL;
256 int Oflag = 1;
257 #ifdef __linux__
258 int lflag = 0;
259 #endif
260 int snaplen = MAXIMUM_SNAPLEN;
261 char *p;
262 bpf_u_int32 netmask = PCAP_NETMASK_UNKNOWN;
263 char *cmdbuf;
264 pcap_t *pd;
265 struct bpf_program fcode;
266
267 #ifdef _WIN32
268 WSADATA wsaData;
269 if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData))
270 return 1;
271 #endif /* _WIN32 */
272
273 if ((cp = strrchr(argv[0], '/')) != NULL)
274 program_name = cp + 1;
275 else
276 program_name = argv[0];
277
278 opterr = 0;
279 while ((op = getopt(argc, argv, "hdF:gm:Os:lr:")) != -1) {
280 switch (op) {
281
282 case 'h':
283 usage(stdout);
284 /* NOTREACHED */
285
286 case 'd':
287 ++dflag;
288 break;
289
290 case 'g':
291 #ifdef BDEBUG
292 ++gflag;
293 break;
294 #else
295 error(EX_USAGE, "libpcap and filtertest not built with optimizer debugging enabled");
296 #endif
297
298 case 'F':
299 infile = optarg;
300 break;
301
302 case 'r':
303 insavefile = optarg;
304 break;
305
306 case 'O':
307 Oflag = 0;
308 break;
309
310 case 'm': {
311 bpf_u_int32 addr;
312
313 switch (inet_pton(AF_INET, optarg, &addr)) {
314
315 case 0:
316 error(EX_DATAERR, "invalid netmask %s", optarg);
317
318 case -1:
319 error(EX_DATAERR, "invalid netmask %s: %s", optarg,
320 pcap_strerror(errno));
321
322 case 1:
323 // inet_pton(): network byte order, pcap_compile(): host byte order.
324 netmask = ntohl(addr);
325 break;
326 }
327 break;
328 }
329
330 case 's': {
331 char *end;
332 long long_snaplen;
333
334 long_snaplen = strtol(optarg, &end, 0);
335 if (optarg == end || *end != '\0'
336 || long_snaplen < 0
337 || long_snaplen > MAXIMUM_SNAPLEN)
338 error(EX_DATAERR, "invalid snaplen %s", optarg);
339 else {
340 if (snaplen == 0)
341 snaplen = MAXIMUM_SNAPLEN;
342 else
343 snaplen = (int)long_snaplen;
344 }
345 break;
346 }
347
348 case 'l':
349 #ifdef __linux__
350 // Enable Linux BPF extensions.
351 lflag = 1;
352 break;
353 #else
354 error(EX_USAGE, "libpcap and filtertest built without Linux BPF extensions");
355 #endif
356
357 default:
358 usage(stderr);
359 /* NOTREACHED */
360 }
361 }
362
363 if (insavefile) {
364 if (dflag > 1)
365 warn("-d is a no-op with -r");
366 #ifdef BDEBUG
367 if (gflag)
368 warn("-g is a no-op with -r");
369 #endif
370 #ifdef __linux__
371 if (lflag)
372 warn("-l is a no-op with -r");
373 #endif
374
375 char errbuf[PCAP_ERRBUF_SIZE];
376 if (NULL == (pd = pcap_open_offline(insavefile, errbuf)))
377 error(EX_NOINPUT, "Failed opening: %s", errbuf);
378 } else {
379 // Must have at least one command-line argument for the DLT.
380 if (optind >= argc) {
381 usage(stderr);
382 /* NOTREACHED */
383 }
384 int dlt = pcap_datalink_name_to_val(argv[optind]);
385 if (dlt < 0) {
386 dlt = (int)strtol(argv[optind], &p, 10);
387 if (p == argv[optind] || *p != '\0')
388 error(EX_DATAERR, "invalid data link type %s", argv[optind]);
389 }
390 optind++;
391
392 pd = pcap_open_dead(dlt, snaplen);
393 if (pd == NULL)
394 error(EX_SOFTWARE, "Can't open fake pcap_t");
395 #ifdef __linux__
396 if (lflag) {
397 #ifdef SKF_AD_VLAN_TAG_PRESENT
398 /*
399 * Generally speaking, the fact the header defines the
400 * symbol does not necessarily mean the running kernel
401 * supports what is known as [vlanp] and everything
402 * before it, but in this use case the filter program
403 * is not meant for the kernel.
404 */
405 pd->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING;
406 #endif // SKF_AD_VLAN_TAG_PRESENT
407 pd->bpf_codegen_flags |= BPF_SPECIAL_BASIC_HANDLING;
408 }
409 #endif // __linux__
410 #ifdef BDEBUG
411 pcap_set_optimizer_debug(dflag);
412 pcap_set_print_dot_graph(gflag);
413 #endif
414 }
415
416 if (infile)
417 cmdbuf = strcmp(infile, "-") ? read_infile(infile) : read_stdin();
418 else
419 cmdbuf = copy_argv(&argv[optind]);
420
421 if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0)
422 error(EX_DATAERR, "%s", pcap_geterr(pd));
423
424 if (!bpf_validate(fcode.bf_insns, fcode.bf_len))
425 warn("Filter doesn't pass validation");
426
427 if (! insavefile) {
428 #ifdef BDEBUG
429 // replace line feed with space
430 for (cp = cmdbuf; *cp != '\0'; ++cp) {
431 if (*cp == '\r' || *cp == '\n') {
432 *cp = ' ';
433 }
434 }
435 // only show machine code if BDEBUG defined, since dflag > 3
436 printf("machine codes for filter: %s\n", cmdbuf);
437 #endif
438 bpf_dump(&fcode, dflag);
439 } else {
440 struct pcap_pkthdr *h;
441 const u_char *d;
442 int ret;
443 while (PCAP_ERROR_BREAK != (ret = pcap_next_ex(pd, &h, &d))) {
444 if (ret == PCAP_ERROR)
445 error(EX_IOERR, "pcap_next_ex() failed: %s", pcap_geterr(pd));
446 if (ret == 1)
447 printf("%d\n", pcap_offline_filter(&fcode, h, d));
448 else
449 error(EX_IOERR, "pcap_next_ex() failed: %d", ret);
450 }
451 }
452 free(cmdbuf);
453 pcap_freecode (&fcode);
454 pcap_close(pd);
455 #ifdef _WIN32
456 WSACleanup();
457 #endif
458 exit(EX_OK);
459 }
460
461 static void
462 usage(FILE *f)
463 {
464 (void)fprintf(f, "%s, with %s\n", program_name,
465 pcap_lib_version());
466 (void)fprintf(f,
467 "Usage: %s [-d"
468 #ifdef BDEBUG
469 "g"
470 #endif
471 "O"
472 #ifdef __linux__
473 "l"
474 #endif
475 "] [ -F file ] [ -m netmask] [ -s snaplen ] dlt [ expr ]\n",
476 program_name);
477 (void)fprintf(f, " (print the filter program bytecode)\n");
478 (void)fprintf(f,
479 " or: %s [-O] [ -F file ] [ -m netmask] -r file [ expression ]\n",
480 program_name);
481 (void)fprintf(f, " (print the filter program result for each packet)\n");
482 (void)fprintf(f, " or: %s -h\n", program_name);
483 (void)fprintf(f, " (print the detailed help screen)\n");
484 if (f == stdout) {
485 (void)fprintf(f, "\nOptions specific to %s:\n", program_name);
486 (void)fprintf(f, " <dlt> a valid DLT name, e.g. 'EN10MB'\n");
487 (void)fprintf(f, " <expr> a valid filter expression, e.g. 'tcp port 80'\n");
488 #ifdef __linux__
489 (void)fprintf(f, " -l allow the use of Linux BPF extensions\n");
490 #endif
491 #ifdef BDEBUG
492 (void)fprintf(f, " -g print Graphviz dot graphs for the optimizer steps\n");
493 #endif
494 (void)fprintf(f, " -m <netmask> use this netmask for pcap_compile(), e.g. 255.255.255.0\n");
495 (void)fprintf(f, "\n");
496 (void)fprintf(f, "Options common with tcpdump:\n");
497 (void)fprintf(f, " -d change output format (accumulates, one -d is implicit)\n");
498 (void)fprintf(f, " -O do not optimize the filter program\n");
499 (void)fprintf(f, " -F <file> read the filter expression from the specified file\n");
500 (void)fprintf(f, " (\"-\" means stdin and allows at most %u characters)\n", MAX_STDIN);
501 (void)fprintf(f, " -s <snaplen> set the snapshot length\n");
502 (void)fprintf(f, " -r <file> read the packets from this savefile\n");
503 (void)fprintf(f, "\nIf no filter expression is specified, it defaults to an empty string, which\n");
504 (void)fprintf(f, "accepts all packets. If the -F option is in use, it replaces any filter\n");
505 (void)fprintf(f, "expression specified as a command-line argument.\n");
506 }
507 exit(f == stdout ? EX_OK : EX_USAGE);
508 }