diff options
| author | Joseph Mingrone <jrm@FreeBSD.org> | 2023-03-27 18:45:17 +0000 |
|---|---|---|
| committer | Joseph Mingrone <jrm@FreeBSD.org> | 2023-03-27 18:45:17 +0000 |
| commit | 35af88c96350eb786f1198dfb6b29a171016e6bf (patch) | |
| tree | e883c1f8391d5ca1afd57abd8ed9d2cd7c274b0b /testprogs | |
| parent | 20616273d52132557e786a8aea1637be4c218a08 (diff) | |
Diffstat (limited to 'testprogs')
| -rw-r--r-- | testprogs/CMakeLists.txt | 13 | ||||
| -rw-r--r-- | testprogs/Makefile.in | 54 | ||||
| -rw-r--r-- | testprogs/can_set_rfmon_test.c | 1 | ||||
| -rw-r--r-- | testprogs/capturetest.c | 89 | ||||
| -rw-r--r-- | testprogs/filtertest.c | 44 | ||||
| -rw-r--r-- | testprogs/findalldevstest-perf.c | 97 | ||||
| -rw-r--r-- | testprogs/findalldevstest.c | 29 | ||||
| -rw-r--r-- | testprogs/fuzz/CMakeLists.txt | 43 | ||||
| -rw-r--r-- | testprogs/fuzz/fuzz_both.c | 101 | ||||
| -rw-r--r-- | testprogs/fuzz/fuzz_both.options | 2 | ||||
| -rw-r--r-- | testprogs/fuzz/fuzz_filter.c | 43 | ||||
| -rw-r--r-- | testprogs/fuzz/fuzz_filter.options | 2 | ||||
| -rw-r--r-- | testprogs/fuzz/fuzz_pcap.c | 80 | ||||
| -rw-r--r-- | testprogs/fuzz/fuzz_pcap.options | 2 | ||||
| -rw-r--r-- | testprogs/fuzz/onefile.c | 54 | ||||
| -rw-r--r-- | testprogs/nonblocktest.c | 187 | ||||
| -rw-r--r-- | testprogs/opentest.c | 19 | ||||
| -rw-r--r-- | testprogs/reactivatetest.c | 1 | ||||
| -rw-r--r-- | testprogs/selpolltest.c | 66 | ||||
| -rw-r--r-- | testprogs/threadsignaltest.c | 58 | ||||
| -rw-r--r-- | testprogs/valgrindtest.c | 34 | ||||
| -rwxr-xr-x | testprogs/visopts.py | 317 | ||||
| -rw-r--r-- | testprogs/writecaptest.c | 556 |
23 files changed, 1794 insertions, 98 deletions
diff --git a/testprogs/CMakeLists.txt b/testprogs/CMakeLists.txt index b8ef9b7d0510..567f42aa6ed7 100644 --- a/testprogs/CMakeLists.txt +++ b/testprogs/CMakeLists.txt @@ -19,6 +19,10 @@ macro(add_test_executable _executable) target_link_libraries(${_executable} ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) endif(WIN32) + if(NOT "${LINKER_FLAGS}" STREQUAL "") + set_target_properties(${_executable} PROPERTIES + LINK_FLAGS "${LINKER_FLAGS}") + endif() add_dependencies(testprogs ${_executable}) endmacro() @@ -26,8 +30,10 @@ add_test_executable(can_set_rfmon_test) add_test_executable(capturetest) add_test_executable(filtertest) add_test_executable(findalldevstest) +add_test_executable(findalldevstest-perf) add_test_executable(opentest) add_test_executable(reactivatetest) +add_test_executable(writecaptest) if(NOT WIN32) add_test_executable(selpolltest) @@ -35,6 +41,11 @@ endif() add_test_executable(threadsignaltest ${CMAKE_THREAD_LIBS_INIT}) -if(NOT WIN32) +# Same as in configure.ac. +if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR + CMAKE_SYSTEM_NAME STREQUAL "Linux") add_test_executable(valgrindtest) endif() + +add_subdirectory(fuzz) diff --git a/testprogs/Makefile.in b/testprogs/Makefile.in index ec0a47208464..f195693713f9 100644 --- a/testprogs/Makefile.in +++ b/testprogs/Makefile.in @@ -38,6 +38,7 @@ mandir = @mandir@ # VPATH srcdir = @srcdir@ +top_srcdir = @top_srcdir@ VPATH = @srcdir@ # @@ -62,7 +63,6 @@ LDFLAGS = @LDFLAGS@ ${CROSSFLAGS} DYEXT = @DYEXT@ V_RPATH_OPT = @V_RPATH_OPT@ DEPENDENCY_CFLAG = @DEPENDENCY_CFLAG@ -EXTRA_NETWORK_LIBS=@EXTRA_NETWORK_LIBS@ # Standard CFLAGS for building test programs FULL_CFLAGS = $(CCOPT) $(INCLS) $(DEFS) $(CFLAGS) @@ -79,14 +79,17 @@ INSTALL_DATA = @INSTALL_DATA@ $(CC) $(FULL_CFLAGS) -c $(srcdir)/$*.c SRC = @VALGRINDTEST_SRC@ \ - capturetest.c \ can_set_rfmon_test.c \ + capturetest.c \ filtertest.c \ + findalldevstest-perf.c \ findalldevstest.c \ opentest.c \ + nonblocktest.c \ reactivatetest.c \ selpolltest.c \ - threadsignaltest.c + threadsignaltest.c \ + writecaptest.c TESTS = $(SRC:.c=) @@ -98,31 +101,56 @@ CLEANFILES = $(OBJ) $(TESTS) all: $(TESTS) capturetest: $(srcdir)/capturetest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o capturetest $(srcdir)/capturetest.c ../libpcap.a $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o capturetest $(srcdir)/capturetest.c \ + ../libpcap.a $(LIBS) can_set_rfmon_test: $(srcdir)/can_set_rfmon_test.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o can_set_rfmon_test $(srcdir)/can_set_rfmon_test.c ../libpcap.a $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o can_set_rfmon_test \ + $(srcdir)/can_set_rfmon_test.c \ + ../libpcap.a $(LIBS) filtertest: $(srcdir)/filtertest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o filtertest $(srcdir)/filtertest.c ../libpcap.a $(EXTRA_NETWORK_LIBS) $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o filtertest $(srcdir)/filtertest.c \ + ../libpcap.a $(LIBS) findalldevstest: $(srcdir)/findalldevstest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o findalldevstest $(srcdir)/findalldevstest.c ../libpcap.a $(EXTRA_NETWORK_LIBS) $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o findalldevstest \ + $(srcdir)/findalldevstest.c \ + ../libpcap.a $(LIBS) + +findalldevstest-perf: $(srcdir)/findalldevstest-perf.c ../libpcap.a + $(CC) $(FULL_CFLAGS) -I. -L. -o findalldevstest-perf \ + $(srcdir)/findalldevstest-perf.c \ + ../libpcap.a $(LIBS) opentest: $(srcdir)/opentest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o opentest $(srcdir)/opentest.c ../libpcap.a $(LIBS) + $(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) + $(CC) $(FULL_CFLAGS) -I. -L. -o reactivatetest \ + $(srcdir)/reactivatetest.c ../libpcap.a $(LIBS) selpolltest: $(srcdir)/selpolltest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o selpolltest $(srcdir)/selpolltest.c ../libpcap.a $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o selpolltest $(srcdir)/selpolltest.c \ + ../libpcap.a $(LIBS) threadsignaltest: $(srcdir)/threadsignaltest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o threadsignaltest $(srcdir)/threadsignaltest.c ../libpcap.a $(LIBS) $(PTHREAD_LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o threadsignaltest \ + $(srcdir)/threadsignaltest.c \ + ../libpcap.a $(LIBS) $(PTHREAD_LIBS) valgrindtest: $(srcdir)/valgrindtest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o valgrindtest $(srcdir)/valgrindtest.c ../libpcap.a $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o valgrindtest $(srcdir)/valgrindtest.c \ + ../libpcap.a $(LIBS) + +writecaptest: $(srcdir)/writecaptest.c ../libpcap.a + $(CC) $(FULL_CFLAGS) -I. -L. -o writecaptest $(srcdir)/writecaptest.c \ + ../libpcap.a $(LIBS) clean: rm -f $(CLEANFILES) @@ -141,4 +169,4 @@ tags: $(TAGFILES) ctags -wtd $(TAGFILES) depend: - ../$(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC) + $(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" -s "$(srcdir)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC) diff --git a/testprogs/can_set_rfmon_test.c b/testprogs/can_set_rfmon_test.c index f6188ba1399e..96816f9b977c 100644 --- a/testprogs/can_set_rfmon_test.c +++ b/testprogs/can_set_rfmon_test.c @@ -70,7 +70,6 @@ main(int argc, char **argv) else error("%s: pcap_can_set_rfmon failed: %s", argv[1], pcap_statustostr(status)); - return 1; } printf("%s: Monitor mode %s be set\n", argv[1], status ? "can" : "cannot"); return 0; diff --git a/testprogs/capturetest.c b/testprogs/capturetest.c index d625cb4ab2b7..4eadd3367968 100644 --- a/testprogs/capturetest.c +++ b/testprogs/capturetest.c @@ -38,6 +38,9 @@ The Regents of the University of California. All rights reserved.\n"; #include <unistd.h> #endif #include <errno.h> +#ifndef _WIN32 + #include <signal.h> +#endif #include <sys/types.h> #include <pcap.h> @@ -58,6 +61,37 @@ static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2); static char *copy_argv(char **); static pcap_t *pd; +#ifndef _WIN32 +static int breaksigint = 0; +#endif + +#ifndef _WIN32 +static void +sigint_handler(int signum _U_) +{ + if (breaksigint) + pcap_breakloop(pd); +} +#endif + +#ifdef _WIN32 +/* + * We don't have UN*X-style signals, so we don't have anything to test. + */ +#define B_OPTION "" +#define R_OPTION "" +#define S_OPTION "" +#else +/* + * We do have UN*X-style signals (we assume that "not Windows" means "UN*X"). + */ +#define B_OPTION "b" +#define R_OPTION "r" +#define S_OPTION "s" +#endif + +#define COMMAND_OPTIONS B_OPTION "i:mn" R_OPTION S_OPTION "t:" +#define USAGE_OPTIONS "-" B_OPTION "mn" R_OPTION S_OPTION int main(int argc, char **argv) @@ -69,6 +103,10 @@ main(int argc, char **argv) int timeout = 1000; int immediate = 0; int nonblock = 0; +#ifndef _WIN32 + int sigrestart = 0; + int catchsigint = 0; +#endif pcap_if_t *devlist; bpf_u_int32 localnet, netmask; struct bpf_program fcode; @@ -83,9 +121,15 @@ main(int argc, char **argv) program_name = argv[0]; opterr = 0; - while ((op = getopt(argc, argv, "i:mnt:")) != -1) { + while ((op = getopt(argc, argv, COMMAND_OPTIONS)) != -1) { switch (op) { +#ifndef _WIN32 + case 'b': + breaksigint = 1; + break; +#endif + case 'i': device = optarg; break; @@ -98,6 +142,16 @@ main(int argc, char **argv) nonblock = 1; break; +#ifndef _WIN32 + case 'r': + sigrestart = 1; + break; + + case 's': + catchsigint = 1; + break; +#endif + case 't': longarg = strtol(optarg, &p, 10); if (p == optarg || *p != '\0') { @@ -132,6 +186,28 @@ main(int argc, char **argv) pcap_freealldevs(devlist); } *ebuf = '\0'; + +#ifndef _WIN32 + /* + * If we were told to catch SIGINT, do so. + */ + if (catchsigint) { + struct sigaction action; + + action.sa_handler = sigint_handler; + sigemptyset(&action.sa_mask); + + /* + * Should SIGINT interrupt, or restart, system calls? + */ + action.sa_flags = sigrestart ? SA_RESTART : 0; + + if (sigaction(SIGINT, &action, NULL) == -1) + error("Can't catch SIGINT: %s\n", + strerror(errno)); + } +#endif + pd = pcap_create(device, ebuf); if (pd == NULL) error("%s", ebuf); @@ -188,6 +264,10 @@ main(int argc, char **argv) if (status != 0) { printf("%d packets seen, %d packets counted after pcap_dispatch returns\n", status, packet_count); + struct pcap_stat ps; + pcap_stats(pd, &ps); + printf("%d ps_recv, %d ps_drop, %d ps_ifdrop\n", + ps.ps_recv, ps.ps_drop, ps.ps_ifdrop); } } if (status == -2) { @@ -197,13 +277,14 @@ main(int argc, char **argv) * Print an extra newline, just in case. */ putchar('\n'); + printf("Broken out of loop from SIGINT handler\n"); } (void)fflush(stdout); if (status == -1) { /* * Error. Report it. */ - (void)fprintf(stderr, "%s: pcap_loop: %s\n", + (void)fprintf(stderr, "%s: pcap_dispatch: %s\n", program_name, pcap_geterr(pd)); } pcap_close(pd); @@ -223,7 +304,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 [ -mn ] [ -i interface ] [ -t timeout] [expression]\n", + (void)fprintf(stderr, "Usage: %s [ " USAGE_OPTIONS " ] [ -i interface ] [ -t timeout] [expression]\n", program_name); exit(1); } @@ -271,7 +352,7 @@ static char * copy_argv(register char **argv) { register char **p; - register u_int len = 0; + register size_t len = 0; char *buf; char *src, *dst; diff --git a/testprogs/filtertest.c b/testprogs/filtertest.c index 7e2d6d6e186d..15556d045cf7 100644 --- a/testprogs/filtertest.c +++ b/testprogs/filtertest.c @@ -36,6 +36,7 @@ The Regents of the University of California. All rights reserved.\n"; #include <stdlib.h> #include <string.h> #include <stdarg.h> +#include <limits.h> #ifdef _WIN32 #include "getopt.h" #include "unix.h" @@ -56,6 +57,8 @@ The Regents of the University of California. All rights reserved.\n"; #include "pcap/funcattrs.h" +#define MAXIMUM_SNAPLEN 262144 + #ifdef BDEBUG /* * We have pcap_set_optimizer_debug() and pcap_set_print_dot_graph() in @@ -99,11 +102,21 @@ read_infile(char *fname) if (fstat(fd, &buf) < 0) error("can't stat %s: %s", fname, pcap_strerror(errno)); + /* + * _read(), on Windows, has an unsigned int byte count and an + * int return value, so we can't handle a file bigger than + * INT_MAX - 1 bytes (and have no reason to do so; a filter *that* + * big will take forever to compile). (The -1 is for the '\0' at + * the end of the string.) + */ + if (buf.st_size > INT_MAX - 1) + error("%s is larger than %d bytes; that's too large", fname, + INT_MAX - 1); cp = malloc((u_int)buf.st_size + 1); if (cp == NULL) error("malloc(%d) for %s: %s", (u_int)buf.st_size + 1, fname, pcap_strerror(errno)); - cc = read(fd, cp, (u_int)buf.st_size); + cc = (int)read(fd, cp, (u_int)buf.st_size); if (cc < 0) error("read %s: %s", fname, pcap_strerror(errno)); if (cc != buf.st_size) @@ -163,7 +176,7 @@ static char * copy_argv(register char **argv) { register char **p; - register u_int len = 0; + register size_t len = 0; char *buf; char *src, *dst; @@ -196,13 +209,14 @@ main(int argc, char **argv) char *cp; int op; int dflag; +#ifdef BDEBUG int gflag; +#endif char *infile; int Oflag; - long snaplen; + int snaplen; char *p; int dlt; - int have_fcode = 0; bpf_u_int32 netmask = PCAP_NETMASK_UNKNOWN; char *cmdbuf; pcap_t *pd; @@ -214,11 +228,13 @@ main(int argc, char **argv) #endif /* _WIN32 */ dflag = 1; +#ifdef BDEBUG gflag = 0; +#endif infile = NULL; Oflag = 1; - snaplen = 68; + snaplen = MAXIMUM_SNAPLEN; if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; @@ -272,13 +288,19 @@ main(int argc, char **argv) case 's': { char *end; + long long_snaplen; - snaplen = strtol(optarg, &end, 0); + long_snaplen = strtol(optarg, &end, 0); if (optarg == end || *end != '\0' - || snaplen < 0 || snaplen > 65535) + || long_snaplen < 0 + || long_snaplen > MAXIMUM_SNAPLEN) error("invalid snaplen %s", optarg); - else if (snaplen == 0) - snaplen = 65535; + else { + if (snaplen == 0) + snaplen = MAXIMUM_SNAPLEN; + else + snaplen = (int)long_snaplen; + } break; } @@ -317,7 +339,6 @@ main(int argc, char **argv) if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) error("%s", pcap_geterr(pd)); - have_fcode = 1; if (!bpf_validate(fcode.bf_insns, fcode.bf_len)) warn("Filter doesn't pass validation"); @@ -337,8 +358,7 @@ main(int argc, char **argv) bpf_dump(&fcode, dflag); free(cmdbuf); - if (have_fcode) - pcap_freecode (&fcode); + pcap_freecode (&fcode); pcap_close(pd); exit(0); } diff --git a/testprogs/findalldevstest-perf.c b/testprogs/findalldevstest-perf.c new file mode 100644 index 000000000000..16f53cdc5967 --- /dev/null +++ b/testprogs/findalldevstest-perf.c @@ -0,0 +1,97 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifdef _WIN32 + #include <winsock2.h> + #include <ws2tcpip.h> + #include <windows.h> +#else + #include <unistd.h> + #include <sys/resource.h> +#endif + +#include <pcap.h> + +#include "varattrs.h" +#include "pcap/funcattrs.h" +#include "portability.h" + +int main(int argc _U_, char **argv _U_) +{ + pcap_if_t *alldevs; + int exit_status = 0; + char errbuf[PCAP_ERRBUF_SIZE+1]; +#ifdef _WIN32 + FILETIME start_ktime, start_utime, end_ktime, end_utime; + FILETIME dummy1, dummy2; + ULARGE_INTEGER start_kticks, end_kticks, start_uticks, end_uticks; + ULONGLONG ktime, utime, tottime; +#else + struct rusage start_rusage, end_rusage; + struct timeval ktime, utime, tottime; +#endif + +#ifdef _WIN32 + if (!GetProcessTimes(GetCurrentProcess(), &dummy1, &dummy2, + &start_ktime, &start_utime)) + { + fprintf(stderr, "GetProcessTimes() fails at start\n"); + exit(1); + } + start_kticks.LowPart = start_ktime.dwLowDateTime; + start_kticks.HighPart = start_ktime.dwHighDateTime; + start_uticks.LowPart = start_utime.dwLowDateTime; + start_uticks.HighPart = start_utime.dwHighDateTime; +#else + if (getrusage(RUSAGE_SELF, &start_rusage) == -1) { + fprintf(stderr, "getrusage() fails at start\n"); + exit(1); + } +#endif + for (int i = 0; i < 500; i++) + { + if (pcap_findalldevs(&alldevs, errbuf) == -1) + { + fprintf(stderr,"Error in pcap_findalldevs: %s\n",errbuf); + exit(1); + } + pcap_freealldevs(alldevs); + } + +#ifdef _WIN32 + if (!GetProcessTimes(GetCurrentProcess(), &dummy1, &dummy2, + &end_ktime, &end_utime)) + { + fprintf(stderr, "GetProcessTimes() fails at end\n"); + exit(1); + } + end_kticks.LowPart = end_ktime.dwLowDateTime; + end_kticks.HighPart = end_ktime.dwHighDateTime; + end_uticks.LowPart = end_utime.dwLowDateTime; + end_uticks.HighPart = end_utime.dwHighDateTime; + ktime = end_kticks.QuadPart - start_kticks.QuadPart; + utime = end_uticks.QuadPart - start_uticks.QuadPart; + tottime = ktime + utime; + printf("Total CPU secs: kernel %g, user %g, total %g\n", + ((double)ktime) / 10000000.0, + ((double)utime) / 10000000.0, + ((double)tottime) / 10000000.0); +#else + if (getrusage(RUSAGE_SELF, &end_rusage) == -1) { + fprintf(stderr, "getrusage() fails at end\n"); + exit(1); + } + timersub(&end_rusage.ru_stime, &start_rusage.ru_stime, &ktime); + timersub(&end_rusage.ru_utime, &start_rusage.ru_utime, &utime); + timeradd(&ktime, &utime, &tottime); + printf("Total CPU secs: kernel %g, user %g, total %g\n", + (double)ktime.tv_sec + ((double)ktime.tv_usec / 1000000.0), + (double)utime.tv_sec + ((double)utime.tv_usec / 1000000.0), + (double)tottime.tv_sec + ((double)tottime.tv_usec / 1000000.0)); +#endif + exit(exit_status); +} diff --git a/testprogs/findalldevstest.c b/testprogs/findalldevstest.c index e535e25462fb..062932090381 100644 --- a/testprogs/findalldevstest.c +++ b/testprogs/findalldevstest.c @@ -19,6 +19,7 @@ #include <pcap.h> +#include "varattrs.h" #include "pcap/funcattrs.h" static int ifprint(pcap_if_t *d); @@ -94,7 +95,11 @@ getpass(const char *prompt) } #endif +#ifdef ENABLE_REMOTE int main(int argc, char **argv) +#else +int main(int argc _U_, char **argv _U_) +#endif { pcap_if_t *alldevs; pcap_if_t *d; @@ -152,8 +157,24 @@ int main(int argc, char **argv) { if (pcap_lookupnet(alldevs->name, &net, &mask, errbuf) < 0) { - fprintf(stderr,"Error in pcap_lookupnet: %s\n",errbuf); - exit_status = 2; + /* + * XXX - this doesn't distinguish between "a real error + * occurred" and "this interface doesn't *have* an IPv4 + * address". The latter shouldn't be treated as an error. + * + * We look for the interface name, followed by a colon and + * a space, and, if we find it,w e see if what follows it + * is "no IPv4 address assigned". + */ + size_t devnamelen = strlen(alldevs->name); + if (strncmp(errbuf, alldevs->name, devnamelen) == 0 && + strncmp(errbuf + devnamelen, ": ", 2) == 0 && + strcmp(errbuf + devnamelen + 2, "no IPv4 address assigned") == 0) + printf("Preferred device is not on an IPv4 network\n"); + else { + fprintf(stderr,"Error in pcap_lookupnet: %s\n",errbuf); + exit_status = 2; + } } else { @@ -300,12 +321,12 @@ static int ifprint(pcap_if_t *d) #define IPTOSBUFFERS 12 static char *iptos(bpf_u_int32 in) { - static char output[IPTOSBUFFERS][3*4+3+1]; + static char output[IPTOSBUFFERS][sizeof("255.255.255.255")]; static short which; u_char *p; p = (u_char *)∈ which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1); - sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + snprintf(output[which], sizeof(output[which]), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); return output[which]; } diff --git a/testprogs/fuzz/CMakeLists.txt b/testprogs/fuzz/CMakeLists.txt new file mode 100644 index 000000000000..67250cca4349 --- /dev/null +++ b/testprogs/fuzz/CMakeLists.txt @@ -0,0 +1,43 @@ +add_executable(fuzz_pcap onefile.c fuzz_pcap.c) +target_link_libraries(fuzz_pcap ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(fuzz_pcap PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") +endif() + +add_executable(fuzz_filter onefile.c fuzz_filter.c) +target_link_libraries(fuzz_filter ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(fuzz_filter PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") +endif() + +add_executable(fuzz_both onefile.c fuzz_both.c) +target_link_libraries(fuzz_both ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(fuzz_both PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") +endif() + +if(ENABLE_REMOTE AND "$ENV{CFLAGS}" MATCHES "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION") +add_executable(fuzz_rclient onefile.c fuzz_rclient.c) +target_link_libraries(fuzz_rclient ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(fuzz_rclient PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") +endif() + +add_executable(fuzz_rserver onefile.c fuzz_rserver.c ../../rpcapd/daemon.c) +check_function_exists(crypt HAVE_CRYPT_IN_SYSTEM_LIBRARIES) +if(HAVE_CRYPT_IN_SYSTEM_LIBRARIES) + set(HAVE_CRYPT TRUE) +else(HAVE_CRYPT_IN_SYSTEM_LIBRARIES) + set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} crypt) +endif(HAVE_CRYPT_IN_SYSTEM_LIBRARIES) +target_link_libraries(fuzz_rserver ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) + +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(fuzz_rserver PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") +endif() +endif(ENABLE_REMOTE AND "$ENV{CFLAGS}" MATCHES "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION") diff --git a/testprogs/fuzz/fuzz_both.c b/testprogs/fuzz/fuzz_both.c new file mode 100644 index 000000000000..59e3d404103d --- /dev/null +++ b/testprogs/fuzz/fuzz_both.c @@ -0,0 +1,101 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#include <pcap/pcap.h> + +FILE * outfile = NULL; + +static int bufferToFile(const char * name, const uint8_t *Data, size_t Size) { + FILE * fd; + if (remove(name) != 0) { + if (errno != ENOENT) { + printf("failed remove, errno=%d\n", errno); + return -1; + } + } + fd = fopen(name, "wb"); + if (fd == NULL) { + printf("failed open, errno=%d\n", errno); + return -2; + } + if (fwrite (Data, 1, Size, fd) != Size) { + fclose(fd); + return -3; + } + fclose(fd); + return 0; +} + +void fuzz_openFile(const char * name) { + if (outfile != NULL) { + fclose(outfile); + } + outfile = fopen(name, "w"); +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + pcap_t * pkts; + char errbuf[PCAP_ERRBUF_SIZE]; + const u_char *pkt; + struct pcap_pkthdr *header; + int r; + size_t filterSize; + char * filter; + struct bpf_program bpf; + + + //initialize output file + if (outfile == NULL) { + outfile = fopen("/dev/null", "w"); + if (outfile == NULL) { + return 0; + } + } + + if (Size < 1) { + return 0; + } + filterSize = Data[0]; + if (Size < 1+filterSize || filterSize == 0) { + return 0; + } + + //rewrite buffer to a file as libpcap does not have buffer inputs + if (bufferToFile("/tmp/fuzz.pcap", Data+1+filterSize, Size-(1+filterSize)) < 0) { + return 0; + } + + //initialize structure + pkts = pcap_open_offline("/tmp/fuzz.pcap", errbuf); + if (pkts == NULL) { + fprintf(outfile, "Couldn't open pcap file %s\n", errbuf); + return 0; + } + + filter = malloc(filterSize); + memcpy(filter, Data+1, filterSize); + //null terminate string + filter[filterSize-1] = 0; + + if (pcap_compile(pkts, &bpf, filter, 1, PCAP_NETMASK_UNKNOWN) == 0) { + //loop over packets + r = pcap_next_ex(pkts, &header, &pkt); + while (r > 0) { + //checks filter + fprintf(outfile, "packet length=%d/%d filter=%d\n",header->caplen, header->len, pcap_offline_filter(&bpf, header, pkt)); + r = pcap_next_ex(pkts, &header, &pkt); + } + //close structure + pcap_close(pkts); + pcap_freecode(&bpf); + } + else { + pcap_close(pkts); + } + free(filter); + + return 0; +} diff --git a/testprogs/fuzz/fuzz_both.options b/testprogs/fuzz/fuzz_both.options new file mode 100644 index 000000000000..0824b19fab47 --- /dev/null +++ b/testprogs/fuzz/fuzz_both.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65535 diff --git a/testprogs/fuzz/fuzz_filter.c b/testprogs/fuzz/fuzz_filter.c new file mode 100644 index 000000000000..de350672797f --- /dev/null +++ b/testprogs/fuzz/fuzz_filter.c @@ -0,0 +1,43 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <pcap/pcap.h> + +void fuzz_openFile(const char * name){ + //do nothing +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + pcap_t * pkts; + struct bpf_program bpf; + char * filter; + + //we need at least 1 byte for linktype + if (Size < 1) { + return 0; + } + + //initialize structure snaplen = 65535 + pkts = pcap_open_dead(Data[Size-1], 0xFFFF); + if (pkts == NULL) { + printf("pcap_open_dead failed\n"); + return 0; + } + filter = malloc(Size); + memcpy(filter, Data, Size); + //null terminate string + filter[Size-1] = 0; + + if (pcap_compile(pkts, &bpf, filter, 1, PCAP_NETMASK_UNKNOWN) == 0) { + pcap_setfilter(pkts, &bpf); + pcap_close(pkts); + pcap_freecode(&bpf); + } + else { + pcap_close(pkts); + } + free(filter); + + return 0; +} diff --git a/testprogs/fuzz/fuzz_filter.options b/testprogs/fuzz/fuzz_filter.options new file mode 100644 index 000000000000..9fda93fcb340 --- /dev/null +++ b/testprogs/fuzz/fuzz_filter.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 4096 diff --git a/testprogs/fuzz/fuzz_pcap.c b/testprogs/fuzz/fuzz_pcap.c new file mode 100644 index 000000000000..fba5312fcaf2 --- /dev/null +++ b/testprogs/fuzz/fuzz_pcap.c @@ -0,0 +1,80 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> + +#include <pcap/pcap.h> + +FILE * outfile = NULL; + +static int bufferToFile(const char * name, const uint8_t *Data, size_t Size) { + FILE * fd; + if (remove(name) != 0) { + if (errno != ENOENT) { + printf("failed remove, errno=%d\n", errno); + return -1; + } + } + fd = fopen(name, "wb"); + if (fd == NULL) { + printf("failed open, errno=%d\n", errno); + return -2; + } + if (fwrite (Data, 1, Size, fd) != Size) { + fclose(fd); + return -3; + } + fclose(fd); + return 0; +} + +void fuzz_openFile(const char * name) { + if (outfile != NULL) { + fclose(outfile); + } + outfile = fopen(name, "w"); +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + pcap_t * pkts; + char errbuf[PCAP_ERRBUF_SIZE]; + const u_char *pkt; + struct pcap_pkthdr *header; + struct pcap_stat stats; + int r; + + //initialize output file + if (outfile == NULL) { + outfile = fopen("/dev/null", "w"); + if (outfile == NULL) { + return 0; + } + } + + //rewrite buffer to a file as libpcap does not have buffer inputs + if (bufferToFile("/tmp/fuzz.pcap", Data, Size) < 0) { + return 0; + } + + //initialize structure + pkts = pcap_open_offline("/tmp/fuzz.pcap", errbuf); + if (pkts == NULL) { + fprintf(outfile, "Couldn't open pcap file %s\n", errbuf); + return 0; + } + + //loop over packets + r = pcap_next_ex(pkts, &header, &pkt); + while (r > 0) { + //TODO pcap_offline_filter + fprintf(outfile, "packet length=%d/%d\n",header->caplen, header->len); + r = pcap_next_ex(pkts, &header, &pkt); + } + if (pcap_stats(pkts, &stats) == 0) { + fprintf(outfile, "number of packets=%d\n", stats.ps_recv); + } + //close structure + pcap_close(pkts); + + return 0; +} diff --git a/testprogs/fuzz/fuzz_pcap.options b/testprogs/fuzz/fuzz_pcap.options new file mode 100644 index 000000000000..0824b19fab47 --- /dev/null +++ b/testprogs/fuzz/fuzz_pcap.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65535 diff --git a/testprogs/fuzz/onefile.c b/testprogs/fuzz/onefile.c new file mode 100644 index 000000000000..690a63bdc433 --- /dev/null +++ b/testprogs/fuzz/onefile.c @@ -0,0 +1,54 @@ +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +void fuzz_openFile(const char * name); + +int main(int argc, char** argv) +{ + FILE * fp; + uint8_t *Data; + size_t Size; + + if (argc == 3) { + fuzz_openFile(argv[2]); + } else if (argc != 2) { + return 1; + } + //opens the file, get its size, and reads it into a buffer + fp = fopen(argv[1], "rb"); + if (fp == NULL) { + return 2; + } + if (fseek(fp, 0L, SEEK_END) != 0) { + fclose(fp); + return 2; + } + Size = ftell(fp); + if (Size == (size_t) -1) { + fclose(fp); + return 2; + } + if (fseek(fp, 0L, SEEK_SET) != 0) { + fclose(fp); + return 2; + } + Data = malloc(Size); + if (Data == NULL) { + fclose(fp); + return 2; + } + if (fread(Data, Size, 1, fp) != 1) { + fclose(fp); + free(Data); + return 2; + } + + //launch fuzzer + LLVMFuzzerTestOneInput(Data, Size); + free(Data); + fclose(fp); + return 0; +} + diff --git a/testprogs/nonblocktest.c b/testprogs/nonblocktest.c new file mode 100644 index 000000000000..72700a3b64a1 --- /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 <pcap.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +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); +} diff --git a/testprogs/opentest.c b/testprogs/opentest.c index bad38eb0e9fb..a441dda1268f 100644 --- a/testprogs/opentest.c +++ b/testprogs/opentest.c @@ -45,7 +45,7 @@ The Regents of the University of California. All rights reserved.\n"; #include "portability.h" #endif -#define MAXIMUM_SNAPLEN 65535 +#define MAXIMUM_SNAPLEN 262144 static char *program_name; @@ -81,7 +81,7 @@ main(int argc, char **argv) switch (op) { case 'i': - device = optarg; + device = strdup(optarg); break; case 'I': @@ -95,13 +95,19 @@ main(int argc, char **argv) case 's': { char *end; + long long_snaplen; - snaplen = strtol(optarg, &end, 0); + long_snaplen = strtol(optarg, &end, 0); if (optarg == end || *end != '\0' - || snaplen < 0 || snaplen > MAXIMUM_SNAPLEN) + || long_snaplen < 0 + || long_snaplen > MAXIMUM_SNAPLEN) error("invalid snaplen %s", optarg); - else if (snaplen == 0) - snaplen = MAXIMUM_SNAPLEN; + else { + if (snaplen == 0) + snaplen = MAXIMUM_SNAPLEN; + else + snaplen = (int)long_snaplen; + } break; } @@ -186,6 +192,7 @@ main(int argc, char **argv) else printf("%s opened successfully\n", device); } + free(device); pcap_close(pd); exit(status < 0 ? 1 : 0); } diff --git a/testprogs/reactivatetest.c b/testprogs/reactivatetest.c index d7f3e322c175..a9c987aa5015 100644 --- a/testprogs/reactivatetest.c +++ b/testprogs/reactivatetest.c @@ -51,7 +51,6 @@ main(void) if (pd == NULL) { error("Neither lo0 nor lo could be opened: %s", ebuf); - return 2; } } status = pcap_activate(pd); diff --git a/testprogs/selpolltest.c b/testprogs/selpolltest.c index 329281dc24bc..ab7f8f462a0a 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; + int selectable_fd = -1; + 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,11 +272,12 @@ 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 && required_timeout->tv_usec >= 1000) - polltimeout = required_timeout->tv_usec/1000; + polltimeout = (int)(required_timeout->tv_usec/1000); else polltimeout = -1; status = poll(&fd, (selectable_fd == -1) ? 0 : 1, polltimeout); @@ -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 { @@ -349,7 +351,7 @@ main(int argc, char **argv) /* * Error. Report it. */ - (void)fprintf(stderr, "%s: pcap_loop: %s\n", + (void)fprintf(stderr, "%s: pcap_dispatch: %s\n", program_name, pcap_geterr(pd)); } pcap_close(pd); @@ -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); } @@ -415,7 +417,7 @@ static char * copy_argv(register char **argv) { register char **p; - register u_int len = 0; + register size_t len = 0; char *buf; char *src, *dst; diff --git a/testprogs/threadsignaltest.c b/testprogs/threadsignaltest.c index a60bb49523fb..c9ade76fe5b5 100644 --- a/testprogs/threadsignaltest.c +++ b/testprogs/threadsignaltest.c @@ -157,7 +157,7 @@ capture_thread_func(THREAD_FUNC_ARG_TYPE arg) } else printf("No packets seen by pcap_dispatch\n"); } - if (status == -2) { + if (status == PCAP_ERROR_BREAK) { /* * We got interrupted, so perhaps we didn't * manage to finish a line we were printing. @@ -167,11 +167,11 @@ capture_thread_func(THREAD_FUNC_ARG_TYPE arg) printf("Loop got broken\n"); } (void)fflush(stdout); - if (status == -1) { + if (status == PCAP_ERROR) { /* * Error. Report it. */ - (void)fprintf(stderr, "%s: pcap_loop: %s\n", + (void)fprintf(stderr, "%s: pcap_dispatch: %s\n", program_name, pcap_geterr(pd)); } return 0; @@ -182,7 +182,7 @@ main(int argc, char **argv) { register int op; register char *cp, *cmdbuf, *device; - int immediate = 0; + int do_wakeup = 1; pcap_if_t *devlist; bpf_u_int32 localnet, netmask; struct bpf_program fcode; @@ -200,13 +200,17 @@ main(int argc, char **argv) program_name = argv[0]; opterr = 0; - while ((op = getopt(argc, argv, "i:")) != -1) { + while ((op = getopt(argc, argv, "i:n")) != -1) { switch (op) { case 'i': device = optarg; break; + case 'n': + do_wakeup = 0; + break; + default: usage(); /* NOTREACHED */ @@ -229,12 +233,6 @@ main(int argc, char **argv) if (status != 0) error("%s: pcap_set_snaplen failed: %s", device, pcap_statustostr(status)); - if (immediate) { - status = pcap_set_immediate_mode(pd, 1); - if (status != 0) - error("%s: pcap_set_immediate_mode failed: %s", - device, pcap_statustostr(status)); - } status = pcap_set_timeout(pd, 5*60*1000); if (status != 0) error("%s: pcap_set_timeout failed: %s", @@ -280,21 +278,39 @@ main(int argc, char **argv) error("Can't create capture thread: %s", strerror(status)); #endif sleep_secs(60); + printf("Doing pcap_breakloop()\n"); pcap_breakloop(pd); + if (do_wakeup) { + /* + * Force a wakeup in the capture thread. + * + * On some platforms, with some devices,, pcap_breakloop() + * can't do that itself. On Windows, poke the device's + * event handle; on UN*X, send a SIGUSR1 to the thread. + */ +#ifdef _WIN32 + printf("Setting event\n"); + if (!SetEvent(pcap_getevent(pd))) + error("Can't set event for pcap_t: %s", + win32_strerror(GetLastError())); +#else + printf("Sending SIGUSR1\n"); + status = pthread_kill(capture_thread, SIGUSR1); + if (status != 0) + warning("Can't interrupt capture thread: %s", + strerror(status)); +#endif + } + + /* + * Now wait for the capture thread to terminate. + */ #ifdef _WIN32 - printf("Setting event\n"); - if (!SetEvent(pcap_getevent(pd))) - error("Can't set event for pcap_t: %s", - win32_strerror(GetLastError())); if (WaitForSingleObject(capture_thread, INFINITE) == WAIT_FAILED) error("Wait for thread termination failed: %s", win32_strerror(GetLastError())); CloseHandle(capture_thread); #else - printf("Sending SIGUSR1\n"); - status = pthread_kill(capture_thread, SIGUSR1); - if (status != 0) - warning("Can't interrupt capture thread: %s", strerror(status)); status = pthread_join(capture_thread, &retval); if (status != 0) error("Wait for thread termination failed: %s", @@ -317,7 +333,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 [ -m ] [ -i interface ] [ -t timeout] [expression]\n", + (void)fprintf(stderr, "Usage: %s [ -n ] [ -i interface ] [ expression ]\n", program_name); exit(1); } @@ -365,7 +381,7 @@ static char * copy_argv(register char **argv) { register char **p; - register u_int len = 0; + register size_t len = 0; char *buf; char *src, *dst; diff --git a/testprogs/valgrindtest.c b/testprogs/valgrindtest.c index 104ef6a9feaa..55055ca3d9ba 100644 --- a/testprogs/valgrindtest.c +++ b/testprogs/valgrindtest.c @@ -59,6 +59,7 @@ The Regents of the University of California. All rights reserved.\n"; #include <stdlib.h> #include <string.h> #include <stdarg.h> +#include <limits.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> @@ -99,6 +100,23 @@ The Regents of the University of California. All rights reserved.\n"; #endif +/* + * Squelch a warning. + * + * We include system headers to be able to directly set the filter to + * a program with uninitialized content, to make sure what we're testing + * is Valgrind's checking of the system call to set the filter, and we + * also include <pcap.h> to open the device in the first place, and that + * means that we may get collisions between their definitions of + * BPF_STMT and BPF_JUMP - and do, in fact, get them on Linux (the + * definitions may be semantically the same, but that's not sufficient to + * avoid the warnings, as the preprocessor doesn't know that u_short is + * just unsigned short). + * + * So we undefine BPF_STMT and BPF_JUMP to avoid the warning. + */ +#undef BPF_STMT +#undef BPF_JUMP #include <pcap.h> static char *program_name; @@ -132,11 +150,21 @@ read_infile(char *fname) if (fstat(fd, &buf) < 0) error("can't stat %s: %s", fname, pcap_strerror(errno)); + /* + * _read(), on Windows, has an unsigned int byte count and an + * int return value, so we can't handle a file bigger than + * INT_MAX - 1 bytes (and have no reason to do so; a filter *that* + * big will take forever to compile). (The -1 is for the '\0' at + * the end of the string.) + */ + if (buf.st_size > INT_MAX - 1) + error("%s is larger than %d bytes; that's too large", fname, + INT_MAX - 1); cp = malloc((u_int)buf.st_size + 1); if (cp == NULL) error("malloc(%d) for %s: %s", (u_int)buf.st_size + 1, fname, pcap_strerror(errno)); - cc = read(fd, cp, (u_int)buf.st_size); + cc = (int)read(fd, cp, (u_int)buf.st_size); if (cc < 0) error("read %s: %s", fname, pcap_strerror(errno)); if (cc != buf.st_size) @@ -196,7 +224,7 @@ static char * copy_argv(register char **argv) { register char **p; - register u_int len = 0; + register size_t len = 0; char *buf; char *src, *dst; @@ -421,7 +449,7 @@ usage(void) (void)fprintf(stderr, "%s, with %s\n", program_name, pcap_lib_version()); (void)fprintf(stderr, - "Usage: %s [-aI] [ -F file ] [ -I interface ] [ expression ]\n", + "Usage: %s [-aI] [ -F file ] [ -i interface ] [ expression ]\n", program_name); exit(1); } diff --git a/testprogs/visopts.py b/testprogs/visopts.py new file mode 100755 index 000000000000..97eafffff4f0 --- /dev/null +++ b/testprogs/visopts.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python + +""" +This program parses the output from pcap_compile() to visualize the CFG after +each optimize phase. + +Usage guide: +1. Enable optimizer debugging code when configure libpcap, + and build libpcap & the test programs + ./configure --enable-optimizer-dbg + make + make testprogs +2. Run filtertest to compile BPF expression and produce the CFG as a + DOT graph, save to output a.txt + testprogs/filtertest -g EN10MB host 192.168.1.1 > a.txt +3. Send a.txt to this program's standard input + cat a.txt | testprogs/visopts.py + (Graphviz must be installed) +4. Step 2&3 can be merged: + testprogs/filtertest -g EN10MB host 192.168.1.1 | testprogs/visopts.py +5. The standard output is something like this: + generated files under directory: /tmp/visopts-W9ekBw + the directory will be removed when this programs finished. + open this link: http://localhost:39062/expr1.html +6. Open the URL at the 3rd line in a browser. + +Note: +1. The CFG is translated to SVG images, expr1.html embeds them as external + documents. If you open expr1.html as local file using file:// protocol, some + browsers will deny such requests so the web page will not work properly. + For Chrome, you can run it using the following command to avoid this: + chromium --disable-web-security + That's why this program starts a localhost HTTP server. +2. expr1.html uses jQuery from https://ajax.googleapis.com, so it needs Internet + access to work. +""" + +import sys, os +import string +import subprocess +import json + +html_template = string.Template(""" +<html> + <head> + <title>BPF compiler optimization phases for $expr </title> + <style type="text/css"> + .hc { + /* half width container */ + display: inline-block; + float: left; + width: 50%; + } + </style> + + <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"/></script> + <!--script type="text/javascript" src="./jquery.min.js"/></script--> + <script type="text/javascript"> + var expr = '$expr'; + var exprid = 1; + var gcount = $gcount; + var logs = JSON.parse('$logs'); + logs[gcount] = ""; + + var leftsvg = null; + var rightsvg = null; + + function gurl(index) { + index += 1; + if (index < 10) + s = "00" + index; + else if (index < 100) + s = "0" + index; + else + s = "" + index; + return "./expr" + exprid + "_g" + s + ".svg" + } + + function annotate_svgs() { + if (!leftsvg || !rightsvg) return; + + $$.each([$$(leftsvg), $$(rightsvg)], function() { + $$(this).find("[id|='block'][opacity]").each(function() { + $$(this).removeAttr('opacity'); + }); + }); + + $$(leftsvg).find("[id|='block']").each(function() { + var has = $$(rightsvg).find("#" + this.id).length != 0; + if (!has) $$(this).attr("opacity", "0.4"); + else { + $$(this).click(function() { + var target = $$(rightsvg).find("#" + this.id); + var offset = $$("#rightsvgc").offset().top + target.position().top; + window.scrollTo(0, offset); + target.focus(); + }); + } + }); + $$(rightsvg).find("[id|='block']").each(function() { + var has = $$(leftsvg).find("#" + this.id).length != 0; + if (!has) $$(this).attr("opacity", "0.4"); + else { + $$(this).click(function() { + var target = $$(leftsvg).find("#" + this.id); + var offset = $$("#leftsvgc").offset().top + target.position().top; + window.scrollTo(0, offset); + target.focus(); + }); + } + }); + } + + function init_svgroot(svg) { + svg.setAttribute("width", "100%"); + svg.setAttribute("height", "100%"); + } + function wait_leftsvg() { + if (leftsvg) return; + var doc = document.getElementById("leftsvgc").getSVGDocument(); + if (doc == null) { + setTimeout(wait_leftsvg, 500); + return; + } + leftsvg = doc.documentElement; + //console.log(leftsvg); + // initialize it + init_svgroot(leftsvg); + annotate_svgs(); + } + function wait_rightsvg() { + if (rightsvg) return; + var doc = document.getElementById("rightsvgc").getSVGDocument(); + if (doc == null) { + setTimeout(wait_rightsvg, 500); + return; + } + rightsvg = doc.documentElement; + //console.log(rightsvg); + // initialize it + init_svgroot(rightsvg); + annotate_svgs(); + } + function load_left(index) { + var url = gurl(index); + var frag = "<embed id='leftsvgc' type='image/svg+xml' pluginspage='https://www.adobe.com/svg/viewer/install/' src='" + url + "'/>"; + $$("#lsvg").html(frag); + $$("#lcomment").html(logs[index]); + $$("#lsvglink").attr("href", url); + leftsvg = null; + wait_leftsvg(); + } + function load_right(index) { + var url = gurl(index); + var frag = "<embed id='rightsvgc' type='image/svg+xml' pluginspage='https://www.adobe.com/svg/viewer/install/' src='" + url + "'/>"; + $$("#rsvg").html(frag); + $$("#rcomment").html(logs[index]); + $$("#rsvglink").attr("href", url); + rightsvg = null; + wait_rightsvg(); + } + + $$(document).ready(function() { + for (var i = 0; i < gcount; i++) { + var opt = "<option value='" + i + "'>loop" + i + " -- " + logs[i] + "</option>"; + $$("#lselect").append(opt); + $$("#rselect").append(opt); + } + var on_selected = function() { + var index = parseInt($$(this).children("option:selected").val()); + if (this.id == "lselect") + load_left(index); + else + load_right(index); + } + $$("#lselect").change(on_selected); + $$("#rselect").change(on_selected); + + $$("#backward").click(function() { + var index = parseInt($$("#lselect option:selected").val()); + if (index <= 0) return; + $$("#lselect").val(index - 1).change(); + $$("#rselect").val(index).change(); + }); + $$("#forward").click(function() { + var index = parseInt($$("#rselect option:selected").val()); + if (index >= gcount - 1) return; + $$("#lselect").val(index).change(); + $$("#rselect").val(index + 1).change(); + }); + + if (gcount >= 1) $$("#lselect").val(0).change(); + if (gcount >= 2) $$("#rselect").val(1).change(); + }); + </script> + </head> + <body style="width: 96%"> + <div> + <h1>$expr</h1> + <div style="text-align: center;"> + <button id="backward" type="button"><<</button> + + <button id="forward" type="button">>></button> + </div> + </div> + <br/> + <div style="clear: both;"> + <div class="hc lc"> + <select id="lselect"></select> + <a id="lsvglink" target="_blank">open this svg in browser</a> + <p id="lcomment"></p> + </div> + <div class="hc rc"> + <select id="rselect"></select> + <a id="rsvglink" target="_blank">open this svg in browser</a> + <p id="rcomment"></p> + </div> + </div> + <br/> + <div style="clear: both;"> + <div id="lsvg" class="hc lc"></div> + <div id="rsvg" class="hc rc"></div> + </div> + </body> +</html> +""") + +def write_html(expr, gcount, logs): + logs = map(lambda s: s.strip().replace("\n", "<br/>"), logs) + + global html_template + html = html_template.safe_substitute(expr=expr.encode("string-escape"), gcount=gcount, logs=json.dumps(logs).encode("string-escape")) + with file("expr1.html", "wt") as f: + f.write(html) + +def render_on_html(infile): + expr = None + gid = 1 + log = "" + dot = "" + indot = 0 + logs = [] + + for line in infile: + if line.startswith("machine codes for filter:"): + expr = line[len("machine codes for filter:"):].strip() + break + elif line.startswith("digraph BPF {"): + indot = 1 + dot = line + elif indot: + dot += line + if line.startswith("}"): + indot = 2 + else: + log += line + + if indot == 2: + try: + p = subprocess.Popen(['dot', '-Tsvg'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + except OSError as ose: + print "Failed to run 'dot':", ose + print "(Is Graphviz installed?)" + exit(1) + + svg = p.communicate(dot)[0] + with file("expr1_g%03d.svg" % (gid), "wt") as f: + f.write(svg) + + logs.append(log) + gid += 1 + log = "" + dot = "" + indot = 0 + + if indot != 0: + #unterminated dot graph for expression + return False + if expr is None: + # BPF parser encounter error(s) + return False + write_html(expr, gid - 1, logs) + return True + +def run_httpd(): + import SimpleHTTPServer + import SocketServer + + class MySocketServer(SocketServer.TCPServer): + allow_reuse_address = True + Handler = SimpleHTTPServer.SimpleHTTPRequestHandler + httpd = MySocketServer(("localhost", 0), Handler) + print "open this link: http://localhost:%d/expr1.html" % (httpd.server_address[1]) + try: + httpd.serve_forever() + except KeyboardInterrupt as e: + pass + +def main(): + import tempfile + import atexit + import shutil + os.chdir(tempfile.mkdtemp(prefix="visopts-")) + atexit.register(shutil.rmtree, os.getcwd()) + print "generated files under directory: %s" % os.getcwd() + print " the directory will be removed when this program has finished." + + if not render_on_html(sys.stdin): + return 1 + run_httpd() + return 0 + +if __name__ == "__main__": + if '-h' in sys.argv or '--help' in sys.argv: + print __doc__ + exit(0) + exit(main()) diff --git a/testprogs/writecaptest.c b/testprogs/writecaptest.c new file mode 100644 index 000000000000..4db532c6e621 --- /dev/null +++ b/testprogs/writecaptest.c @@ -0,0 +1,556 @@ +/* + * 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" + +#ifndef lint +static const char copyright[] _U_ = + "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\ +The Regents of the University of California. All rights reserved.\n"; +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <limits.h> +#ifdef _WIN32 + #include "getopt.h" +#else + #include <unistd.h> +#endif +#include <errno.h> +#ifndef _WIN32 + #include <signal.h> +#endif +#include <sys/types.h> + +#include <pcap.h> + +#include "pcap/funcattrs.h" + +#ifdef _WIN32 + #include "portability.h" +#endif + +static char *program_name; + +/* 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); +static char *copy_argv(char **); + +static pcap_t *pd; + +#ifdef _WIN32 +static BOOL WINAPI +stop_capture(DWORD ctrltype _U_) +{ + pcap_breakloop(pd); + return TRUE; +} +#else +static void +stop_capture(int signum _U_) +{ + pcap_breakloop(pd); +} +#endif + +static long +parse_interface_number(const char *device) +{ + const char *p; + long devnum; + char *end; + + /* + * Search for a colon, terminating any scheme at the beginning + * of the device. + */ + p = strchr(device, ':'); + if (p != NULL) { + /* + * We found it. Is it followed by "//"? + */ + p++; /* skip the : */ + if (strncmp(p, "//", 2) == 0) { + /* + * Yes. Search for the next /, at the end of the + * authority part of the URL. + */ + p += 2; /* skip the // */ + p = strchr(p, '/'); + if (p != NULL) { + /* + * OK, past the / is the path. + */ + device = p + 1; + } + } + } + devnum = strtol(device, &end, 10); + if (device != end && *end == '\0') { + /* + * It's all-numeric, but is it a valid number? + */ + if (devnum <= 0) { + /* + * No, it's not an ordinal. + */ + error("Invalid adapter index"); + } + return (devnum); + } else { + /* + * It's not all-numeric; return -1, so our caller + * knows that. + */ + return (-1); + } +} + +static char * +find_interface_by_number(long devnum) +{ + pcap_if_t *dev, *devlist; + long i; + char ebuf[PCAP_ERRBUF_SIZE]; + char *device; + int status; + + status = pcap_findalldevs(&devlist, ebuf); + if (status < 0) + error("%s", ebuf); + /* + * Look for the devnum-th entry in the list of devices (1-based). + */ + for (i = 0, dev = devlist; i < devnum-1 && dev != NULL; + i++, dev = dev->next) + ; + if (dev == NULL) + error("Invalid adapter index"); + device = strdup(dev->name); + pcap_freealldevs(devlist); + return (device); +} + +static pcap_t * +open_interface(const char *device, int snaplen_set, int snaplen, char *ebuf) +{ + pcap_t *pc; + int status; + char *cp; + + pc = pcap_create(device, ebuf); + if (pc == NULL) { + /* + * If this failed with "No such device", that means + * the interface doesn't exist; return NULL, so that + * the caller can see whether the device name is + * actually an interface index. + */ + if (strstr(ebuf, "No such device") != NULL) + return (NULL); + error("%s", ebuf); + } + if (snaplen_set) { + status = pcap_set_snaplen(pc, snaplen); + if (status != 0) + error("%s: pcap_set_snaplen failed: %s", + device, pcap_statustostr(status)); + } + status = pcap_set_timeout(pc, 100); + if (status != 0) + error("%s: pcap_set_timeout failed: %s", + device, pcap_statustostr(status)); + status = pcap_activate(pc); + if (status < 0) { + /* + * pcap_activate() failed. + */ + cp = pcap_geterr(pc); + if (status == PCAP_ERROR) + error("%s", cp); + else if (status == PCAP_ERROR_NO_SUCH_DEVICE) { + /* + * Return an error for our caller to handle. + */ + snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s\n(%s)", + device, pcap_statustostr(status), cp); + } else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0') + error("%s: %s\n(%s)", device, + pcap_statustostr(status), cp); + else + error("%s: %s", device, + pcap_statustostr(status)); + pcap_close(pc); + return (NULL); + } else if (status > 0) { + /* + * pcap_activate() succeeded, but it's warning us + * of a problem it had. + */ + cp = pcap_geterr(pc); + if (status == PCAP_WARNING) + warning("%s", cp); + else if (status == PCAP_WARNING_PROMISC_NOTSUP && + *cp != '\0') + warning("%s: %s\n(%s)", device, + pcap_statustostr(status), cp); + else + warning("%s: %s", device, + pcap_statustostr(status)); + } + return (pc); +} + +#define COMMAND_OPTIONS "DLi:s:w:y:" + +int +main(int argc, char **argv) +{ + int op; + char *cp, *cmdbuf = NULL, *device, *end, *savefile = NULL; + int snaplen = 0; + int snaplen_set = 0; + pcap_if_t *devlist; + long devnum; + int show_interfaces = 0; + int show_dlt_types = 0; + int ndlts; + int *dlts; + bpf_u_int32 localnet, netmask; + struct bpf_program fcode; + char ebuf[PCAP_ERRBUF_SIZE]; +#ifndef _WIN32 + struct sigaction action; +#endif + int dlt; + const char *dlt_name = NULL; + int status; + pcap_dumper_t *pdd; + + device = NULL; + if ((cp = strrchr(argv[0], '/')) != NULL) + program_name = cp + 1; + else + program_name = argv[0]; + + opterr = 0; + while ((op = getopt(argc, argv, COMMAND_OPTIONS)) != -1) { + switch (op) { + + case 'D': + show_interfaces = 1; + break; + + case 'L': + show_dlt_types = 1; + break; + + case 'i': + device = optarg; + break; + + case 's': + snaplen = (int)strtol(optarg, &end, 0); + if (optarg == end || *end != '\0' || snaplen < 0) + error("invalid snaplen %s (must be >= 0)", + optarg); + snaplen_set = 1; + break; + + case 'w': + savefile = optarg; + break; + + case 'y': + dlt_name = optarg; + break; + + default: + usage(); + /* NOTREACHED */ + } + } + + if (show_interfaces) { + pcap_if_t *dev; + int i; + + if (pcap_findalldevs(&devlist, ebuf) < 0) + error("%s", ebuf); + for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) { + printf("%d.%s", i+1, dev->name); + if (dev->description != NULL) + printf(" (%s)", dev->description); + printf("\n"); + } + pcap_freealldevs(devlist); + return (0); + } + + 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); + pcap_freealldevs(devlist); + } + if (show_dlt_types) { + pd = pcap_create(device, ebuf); + if (pd == NULL) + error("%s", ebuf); + status = pcap_activate(pd); + if (status < 0) { + /* + * pcap_activate() failed. + */ + error("%s: %s\n(%s)", device, + pcap_statustostr(status), pcap_geterr(pd)); + } + ndlts = pcap_list_datalinks(pd, &dlts); + if (ndlts < 0) { + /* + * pcap_list_datalinks() failed. + */ + error("%s: %s\n(%s)", device, + pcap_statustostr(status), pcap_geterr(pd)); + } + for (int i = 0; i < ndlts; i++) { + dlt_name = pcap_datalink_val_to_name(dlts[i]); + if (dlt_name == NULL) + printf("DLT %d", dlts[i]); + else + printf("%s", dlt_name); + printf("\n"); + } + pcap_free_datalinks(dlts); + pcap_close(pd); + return 0; + } + + if (savefile == NULL) + error("no savefile specified"); + + *ebuf = '\0'; + + pd = open_interface(device, snaplen_set, snaplen, ebuf); + if (pd == NULL) { + /* + * That failed because the interface couldn't be found. + * + * If we can get a list of interfaces, and the interface name + * is purely numeric, try to use it as a 1-based index + * in the list of interfaces. + */ + devnum = parse_interface_number(device); + if (devnum == -1) { + /* + * It's not a number; just report + * the open error and fail. + */ + error("%s", ebuf); + } + + /* + * OK, it's a number; try to find the + * interface with that index, and try + * to open it. + * + * find_interface_by_number() exits if it + * couldn't be found. + */ + device = find_interface_by_number(devnum); + pd = open_interface(device, snaplen_set, snaplen, ebuf); + if (pd == NULL) + error("%s", ebuf); + } + + if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) { + localnet = 0; + netmask = 0; + warning("%s", ebuf); + } + + if (dlt_name != NULL) { + dlt = pcap_datalink_name_to_val(dlt_name); + if (dlt == PCAP_ERROR) + error("%s isn't a valid DLT name", dlt_name); + if (pcap_set_datalink(pd, dlt) == PCAP_ERROR) + error("%s: %s", device, pcap_geterr(pd)); + } + + /* + * Don't set a filter unless we were given one on the + * command line; if capturing doesn't work, or doesn't + * use the snapshot length, without a filter, that's + * a bug. + */ + if (optind < argc) { + cmdbuf = copy_argv(&argv[optind]); + + if (pcap_compile(pd, &fcode, cmdbuf, 1, netmask) < 0) + error("%s", pcap_geterr(pd)); + + if (pcap_setfilter(pd, &fcode) < 0) + error("%s", pcap_geterr(pd)); + } + + pdd = pcap_dump_open(pd, savefile); + if (pdd == NULL) + error("%s", pcap_geterr(pd)); + +#ifdef _WIN32 + SetConsoleCtrlHandler(stop_capture, TRUE); +#else + action.sa_handler = stop_capture; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + if (sigaction(SIGINT, &action, NULL) == -1) + error("Can't catch SIGINT: %s\n", strerror(errno)); +#endif + + printf("Listening on %s, link-type ", device); + dlt = pcap_datalink(pd); + dlt_name = pcap_datalink_val_to_name(dlt); + if (dlt_name == NULL) + printf("DLT %d", dlt); + else + printf("%s", dlt_name); + printf("\n"); + for (;;) { + status = pcap_dispatch(pd, -1, pcap_dump, (u_char *)pdd); + if (status < 0) + break; + if (status != 0) { + printf("%d packets seen\n", status); + struct pcap_stat ps; + pcap_stats(pd, &ps); + printf("%d ps_recv, %d ps_drop, %d ps_ifdrop\n", + ps.ps_recv, ps.ps_drop, ps.ps_ifdrop); + } + } + if (status == -2) { + /* + * We got interrupted, so perhaps we didn't + * manage to finish a line we were printing. + * Print an extra newline, just in case. + */ + putchar('\n'); + printf("Broken out of loop from SIGINT handler\n"); + } + (void)fflush(stdout); + if (status == -1) { + /* + * Error. Report it. + */ + (void)fprintf(stderr, "%s: pcap_dispatch: %s\n", + program_name, pcap_geterr(pd)); + } + pcap_close(pd); + if (cmdbuf != NULL) { + pcap_freecode(&fcode); + free(cmdbuf); + } + exit(status == -1 ? 1 : 0); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s -D -L [ -i interface ] [ -s snaplen ] [ -w file ] [ -y dlt ] [expression]\n", + program_name); + exit(1); +} + +/* 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); + } +} + +/* + * Copy arg vector into a new buffer, concatenating arguments with spaces. + */ +static char * +copy_argv(register char **argv) +{ + register char **p; + register size_t len = 0; + char *buf; + char *src, *dst; + + p = argv; + if (*p == 0) + return 0; + + while (*p) + len += strlen(*p++) + 1; + + buf = (char *)malloc(len); + if (buf == NULL) + error("copy_argv: malloc"); + + p = argv; + dst = buf; + while ((src = *p++) != NULL) { + while ((*dst++ = *src++) != '\0') + ; + dst[-1] = ' '; + } + dst[-1] = '\0'; + + return buf; +} |
