--- ipfwpcap.c.orig Sat Nov 11 05:22:06 2006 +++ ipfwpcap.c Tue Dec 19 08:30:00 2006 @@ -1,95 +1,120 @@ +/*- + * Copyright (c) 2004 University of Toronto. All rights reserved. + * Anyone may use or copy this software except that this copyright + * notice remain intact and that credit is given where it is due. + * The University of Toronto and the author make no warranty and + * accept no liability for this software. + * + * $FreeBSD: /repoman/r/ncvs/src/usr.sbin/ipfwpcap/ipfwpcap.c,v 1.2 2006/09/04 19:30:44 sam Exp $ + */ + /* - * copy diverted (or tee'd) packets to a file in 'tcpdump' format + * Copy diverted (or tee'd) packets to a file in 'tcpdump' format * (ie. this uses the '-lpcap' routines). * - * example usage: - * # ipfwpcap -r 8091 divt.log & + * Example usage: + * # ipfwpcap -r 8091 divt.log * # ipfw add 2864 divert 8091 ip from 128.432.53.82 to any * # ipfw add 2864 divert 8091 ip from any to 128.432.53.82 * * the resulting dump file can be read with ... * # tcpdump -nX -r divt.log - */ -/* - * Written by P Kern { pkern [AT] cns.utoronto.ca } - * - * Copyright (c) 2004 University of Toronto. All rights reserved. - * Anyone may use or copy this software except that this copyright - * notice remain intact and that credit is given where it is due. - * The University of Toronto and the author make no warranty and - * accept no liability for this software. * - * From: Header: /local/src/local.lib/SRC/ipfwpcap/RCS/ipfwpcap.c,v 1.4 2004/01/15 16:19:07 pkern Exp - * - * $FreeBSD: /repoman/r/ncvs/src/usr.sbin/ipfwpcap/ipfwpcap.c,v 1.2 2006/09/04 19:30:44 sam Exp $ + * Written by P Kern { pkern [AT] cns.utoronto.ca } + * Adopted by V Pavluk (vladvic_r@mail.ru) + * - changed sighup handler to reopen log file + * - added sigalrm handler to flush data + * - some of exit() codes changed to sysexits(3) + * Major code reworking by Vadim Goncharov + * - signals and daemonizing rewritten + * - style(9) reformat, more sysexits(3) and other cleanups + * - enabled own alarm sending, changed options and updated man page */ +#include +#include +#include /* For MAXPATHLEN */ +#include +#include + +#include +#include /* For IP_MAXPACKET */ +#include /* For IP_MAXPACKET */ + #include #include #include #include #include #include -#include -#include -#include /* for MAXPATHLEN */ -#include -#include - -#include /* for IP_MAXPACKET */ -#include /* for IP_MAXPACKET */ +#include /* XXX normally defined in config.h */ -#define HAVE_STRLCPY 1 -#define HAVE_SNPRINTF 1 -#define HAVE_VSNPRINTF 1 -#include /* see pcap(3) and /usr/src/contrib/libpcap/. */ +#define HAVE_STRLCPY 1 +#define HAVE_SNPRINTF 1 +#define HAVE_VSNPRINTF 1 +#include /* See pcap(3) and /usr/src/contrib/libpcap/. */ #ifdef IP_MAXPACKET -#define BUFMAX IP_MAXPACKET +#define BUFMAX IP_MAXPACKET #else -#define BUFMAX 65535 +#define BUFMAX 65535 #endif #ifndef MAXPATHLEN #define MAXPATHLEN 1024 #endif -static int debug = 0; -static int reflect = 0; /* 1 == write packet back to socket. */ +#define MAXPIDLEN 9 /* Max decimal pid length in characters. */ +#define DEFINTERVAL 60 /* How often to do flush (in seconds). */ -static ssize_t totbytes = 0, maxbytes = 0; -static ssize_t totpkts = 0, maxpkts = 0; +static char *prog = NULL; +static char pidfile[MAXPATHLEN] = { '\0' }; -char *prog = NULL; -char pidfile[MAXPATHLEN] = { '\0' }; +static int quit = 0; /* Is it time to exit? */ +static int do_flush = 0; /* Time to flush log. */ +static int do_reopen = 0; /* Time for log rotating. */ +static int flush_interval = DEFINTERVAL; /* - * tidy up. + * Tidy up macro. */ -void -quit(sig) -int sig; +#define QUIT(code) do { \ + pcap_dump_flush(dp); \ + (void)unlink(pidfile); \ + exit(code); \ +} while(0); + +void +quit_sig(int sig) +{ + quit = 1; +} + +void +flush_log(int sigalrm) +{ + do_flush = 1; + alarm(flush_interval); +} + +void +reopen_log(int sighup) { - (void) unlink(pidfile); - exit(sig); + do_reopen = 1; } /* - * do the "paper work" - * - save my own pid in /var/run/$0.{port#}.pid + * Do the "paper work". + * - fork and detach from terminal, if needed. + * - save my own pid in /var/run/$0.{port#}.pid. */ -okay(pn) -int pn; +void +okay(int pn, int detach, int nochdir) { - FILE *fp; - int fd, numlen, n; - char *p, numbuf[80]; - - numlen = sizeof(numbuf); - bzero(numbuf, numlen); - snprintf(numbuf, numlen-1, "%ld\n", getpid()); - numlen = strlen(numbuf); + int pf; + char *p, strpid[MAXPIDLEN + 1]; + pid_t pid; if (pidfile[0] == '\0') { p = (char *)rindex(prog, '/'); @@ -99,93 +124,158 @@ "%s%s.%d.pid", _PATH_VARRUN, p, pn); } - fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0644); - if (fd < 0) { perror(pidfile); exit(21); } + pf = open(pidfile, O_WRONLY | O_CREAT | O_EXCL | O_EXLOCK, 0644); - siginterrupt(SIGTERM, 1); - siginterrupt(SIGHUP, 1); - signal (SIGTERM, quit); - signal (SIGHUP, quit); - - n = write(fd, numbuf, numlen); - if (n < 0) { perror(pidfile); quit(23); } - (void) close(fd); + /* + * We couldn't create pid file + */ + if (pf == -1) { + if (errno == EEXIST) { + /* + * If it is because it's already exists + */ + bzero(strpid, MAXPIDLEN + 1); + fprintf(stderr, "PID file already exists!\n"); + + /* + * Try to read the PID stored in the existing file + */ + pf = open(pidfile, O_RDONLY); + if (pf == -1) { + perror("Error opening PID file for reading"); + exit(EX_IOERR); + } + if (read(pf, strpid, MAXPIDLEN) < 0) { + perror("Error reading PID file"); + exit(EX_IOERR); + } + pid = atol(strpid); + close(pf); + + /* + * We found PID, try to determine, whether process + * is running + */ + if (kill(pid, 0) == 0) { + /* + * Signal is delivered, though process with + * such PID exists + */ + fprintf(stderr, "%s already running with PID=%d, exiting...\n", prog, pid); + exit(1); + } else { + /* + * It seems, like the process is killed, so + * we can proceed... + */ + fprintf(stderr, "Stale PID file, overwriting...\n"); + pf = open(pidfile, O_WRONLY | O_TRUNC | O_EXLOCK); + if (pf == -1) { + perror("Error opening PID file for writing"); + exit(EX_IOERR); + } + } + } else { + perror("Error creating PID file"); + exit(EX_IOERR); + } + } + + if (detach) { + if (daemon(nochdir, 0) != 0) { + close(pf); + (void)unlink(pidfile); + perror("daemon"); + exit(EX_OSERR); + } + } + + /* + * Set signal handlers and system behaviour. This must be done + * before saving PID to prevent small, but possible race condition + * when another instance failed to create PID, reads it and tries + * to send signal to us. + */ + siginterrupt(SIGTERM, 1); + siginterrupt(SIGHUP, 1); + + /* Ignore 0th signal, or process may be killed with it by default... */ + signal(0, SIG_IGN); + signal(SIGINT, quit_sig); + signal(SIGTERM, quit_sig); + signal(SIGHUP, reopen_log); + signal(SIGALRM, flush_log); + + /* Save our PID to pidfile. */ + bzero(strpid, MAXPIDLEN + 1); + snprintf(strpid, MAXPIDLEN, "%ld\n", getpid()); + if (write(pf, strpid, strlen(strpid)) < 0) { + perror("Error writing PID file"); + exit(EX_IOERR); + } + close(pf); } +void usage() { - fprintf(stderr, "\ -\n\ -usage:\n\ - %s [-dr] [-b maxbytes] [-p maxpkts] [-P pidfile] portnum dumpfile\n\ -\n\ -where:\n\ - '-d' = enable debugging messages.\n\ - '-r' = reflect. write packets back to the divert socket.\n\ - (ie. simulate the original intent of \"ipfw tee\").\n\ - '-rr' = indicate that it is okay to quit if packet-count or\n\ - byte-count limits are reached (see the NOTE below\n\ - about what this implies).\n\ - '-b bytcnt' = stop dumping after {bytcnt} bytes.\n\ - '-p pktcnt' = stop dumping after {pktcnt} packets.\n\ - '-P pidfile' = alternate file to store the PID\n\ - (default: /var/run/%s.{portnum}.pid).\n\ -\n\ - portnum = divert(4) socket port number.\n\ - dumpfile = file to write captured packets (tcpdump format).\n\ - (specify '-' to write packets to stdout).\n\ -\n\ -", prog, prog); - - fprintf(stderr, "\ -The '-r' option should not be necessary, but because \"ipfw tee\" is broken\n\ -(see BUGS in ipfw(8) for details) this feature can be used along with\n\ -an \"ipfw divert\" rule to simulate the original intent of \"ipfw tee\".\n\ -\n\ -NOTE: With an \"ipfw divert\" rule, diverted packets will silently\n\ - disappear if there is nothing listening to the divert socket.\n\ -\n\ -"); - exit(-1); + fprintf(stderr, + "usage: %s [-dz] [-r | -rr] [-i flush_interval] [-b maxbytes] [-p maxpkts] [-P pidfile] portnum dumpfile\n", + prog); + + exit(EX_USAGE); } -main(ac, av) -int ac; -char *av[]; +main(int argc, char *argv[]) { int r, sd, portnum, l; - struct sockaddr_in sin; - int errflg = 0; + struct sockaddr_in sin; + int errflg = 0, zeroize = 0; int nfd; fd_set rds; ssize_t nr; - char *dumpf, buf[BUFMAX]; + char buf[BUFMAX]; + + int debug = 0; + int reflect = 0; /* 1 == write packet back to socket. */ + + ssize_t totbytes = 0, maxbytes = 0; + ssize_t totpkts = 0, maxpkts = 0; - pcap_t *p; - pcap_dumper_t *dp; struct pcap_pkthdr phd; + pcap_t *p; + pcap_dumper_t *dp; /* Global, as signal handlers may want it. */ + char *dumpf; - prog = av[0]; + prog = argv[0]; - while ((r = getopt(ac, av, "drb:p:P:")) != -1) { + while ((r = getopt(argc, argv, "drzb:i:p:P:")) != -1) { switch (r) { case 'd': - debug++; + debug = 1; break; case 'r': reflect++; break; + case 'i': + flush_interval = atoi(optarg); + if ((flush_interval < 5) || (flush_interval > 3600)) + flush_interval = DEFINTERVAL; + break; case 'b': - maxbytes = (ssize_t) atol(optarg); + maxbytes = (ssize_t)atol(optarg); break; case 'p': - maxpkts = (ssize_t) atoi(optarg); + maxpkts = (ssize_t)atoi(optarg); + break; + case 'z': + zeroize = 1; break; case 'P': - strcpy(pidfile, optarg); + strlcpy(pidfile, optarg, sizeof(pidfile)); break; case '?': default: @@ -194,17 +284,18 @@ } } - if ((ac - optind) != 2 || errflg) + if (((argc - optind) != 2) || errflg) usage(); - portnum = atoi(av[optind++]); - dumpf = av[optind]; + portnum = atoi(argv[optind++]); + dumpf = argv[optind]; -if (debug) fprintf(stderr, "bind to %d.\ndump to '%s'.\n", portnum, dumpf); + if (debug) + fprintf(stderr, "bind to %d.\ndump to '%s'.\n", portnum, dumpf); if ((r = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) { perror("socket(DIVERT)"); - exit(2); + exit(EX_OSERR); } sd = r; @@ -214,92 +305,136 @@ if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { perror("bind(divert)"); - exit(3); + exit(EX_OSERR); } p = pcap_open_dead(DLT_RAW, BUFMAX); dp = pcap_dump_open(p, dumpf); if (dp == NULL) { pcap_perror(p, dumpf); - exit(4); + exit(EX_OSFILE); } + + /* + * We will not chdir() to root directory if user specified + * non-absolute pathname to logfile, because in this case + * logfile will be created in another directory after first + * reopening on SIGHUP. + */ + okay(portnum, !debug, dumpf[0] == '/' ? 0 : 1); - okay(portnum); + alarm(flush_interval); /* Start timer. */ nfd = sd + 1; - for (;;) { + while (!quit) { + /* + * Handle signal actions on next iteration after select()'s EINTR. + */ + if (do_flush) { + if (debug) + fprintf(stderr, "Flushing log.\n"); + pcap_dump_flush(dp); + do_flush = 0; + } + + if (do_reopen) { + if (debug) + fprintf(stderr, "Reopening log.\n"); + pcap_dump_close(dp); + dp = pcap_dump_open(p, dumpf); + if (zeroize) { + totbytes = 0; + totpkts = 0; + } + do_reopen = 0; + } + + /* Prepare for select(). */ FD_ZERO(&rds); FD_SET(sd, &rds); r = select(nfd, &rds, NULL, NULL, NULL); if (r == -1) { - if (errno == EINTR) continue; + if (errno == EINTR) + continue; perror("select"); - quit(11); + QUIT(EX_OSERR); } - if (!FD_ISSET(sd, &rds)) - /* hmm. no work. */ - continue; + continue; /* Hmm. No work. */ /* - * use recvfrom(3 and sendto(3) as in natd(8). - * see /usr/src/sbin/natd/natd.c - * see ipfw(8) about using 'divert' and 'tee'. + * Use recvfrom(3 and sendto(3) as in natd(8). + * See /usr/src/sbin/natd/natd.c. + * See ipfw(8) about using 'divert' and 'tee'. */ /* - * read packet. + * Read packet. */ l = sizeof(sin); nr = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &l); -if (debug) fprintf(stderr, "recvfrom(%d) = %d (%d)\n", sd, nr, l); - if (nr < 0 && errno != EINTR) { + + if (debug) + fprintf(stderr, "recvfrom(%d) = %d (%d)\n", sd, nr, l); + + if ((nr < 0) && (errno != EINTR)) { perror("recvfrom(sd)"); - quit(12); + QUIT(EX_IOERR); } - if (nr <= 0) continue; + if (nr <= 0) + continue; - if (reflect) { + if (reflect > 0) { /* - * write packet back so it can continue - * being processed by any further IPFW rules. + * Write packet back so it can continue being + * processed by any further IPFW rules. */ l = sizeof(sin); r = sendto(sd, buf, nr, 0, (struct sockaddr *)&sin, l); -if (debug) fprintf(stderr, " sendto(%d) = %d\n", sd, r); - if (r < 0) { perror("sendto(sd)"); quit(13); } + if (debug) + fprintf(stderr, "sendto(%d) = %d\n", sd, r); + if (r < 0) { + perror("sendto(sd)"); + QUIT(EX_IOERR); + } } /* - * check maximums, if any. - * but don't quit if must continue reflecting packets. + * Check maximums, if any. But don't quit if must continue + * reflecting packets. However, it's ok to exit when + * reflect > 1. */ if (maxpkts) { totpkts++; if (totpkts > maxpkts) { - if (reflect == 1) continue; - quit(0); + if (reflect == 1) + continue; + QUIT(EX_OK); } } if (maxbytes) { totbytes += nr; if (totbytes > maxbytes) { - if (reflect == 1) continue; - quit(0); + if (reflect == 1) + continue; + QUIT(EX_OK); } } /* - * save packet in tcpdump(1) format. see pcap(3). - * divert packets are fully assembled. see ipfw(8). + * Save packet in tcpdump(1) format. See pcap(3). Divert + * packets are fully assembled, see ipfw(8). */ - (void) gettimeofday(&(phd.ts), NULL); + (void)gettimeofday(&(phd.ts), NULL); phd.caplen = phd.len = nr; pcap_dump((u_char *)dp, &phd, buf); - if (ferror((FILE *)dp)) { perror(dumpf); quit(14); } - (void) fflush((FILE *)dp); + if (ferror((FILE *)dp)) { + perror(dumpf); + QUIT(EX_IOERR); + } + } - quit(0); + QUIT(EX_OK); } --- ipfwpcap.8.orig Sat Nov 11 05:08:21 2006 +++ ipfwpcap.8 Thu Dec 21 09:04:28 2006 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD: /repoman/r/ncvs/src/usr.sbin/ipfwpcap/ipfwpcap.8,v 1.3 2006/09/30 19:07:03 ru Exp $ .\" -.Dd May 22, 2006 +.Dd Dec 20, 2006 .Dt IPFWPCAP 8 .Os .Sh NAME @@ -32,7 +32,9 @@ .Nd "copy diverted packets to a file in tcpdump format" .Sh SYNOPSIS .Nm -.Op Fl dr +.Op Fl dz +.Op Fl r | rr +.Op Fl i Ar flush_interval .Op Fl b Ar maxbytes .Op Fl p Ar maxpkts .Op Fl P Ar pidfile @@ -48,19 +50,44 @@ .Xr ipfw 8 to a port on which .Nm -listens. +daemon listens. The packets are then dropped unless .Fl r is used. .Pp +.Nm +closes and then re-opens the dump file when it receives +.Dv SIGHUP , +permitting +.Xr newsyslog 8 +to rotate dump logfiles automatically. +Note that already existing file will be truncated on open or re-open. +Receiving +.Dv SIGALRM +causes +.Nm +to flush the current logfile buffers to the disk, thus making the most +recent logs available. +The buffers are also flushed every +.Ar flush_interval +seconds. +.Pp The options are as follows: .Bl -tag -width indent .It Fl d -Turns on extra debugging messages. +Turns on debugging messages and prevents +.Nm +from making itself a background daemon. .It Fl r Writes packets back to the .Xr divert 4 socket. +This option can be used to reflect packets back to +.Xr ipfw 8 +if you for some reasons want to use +.Dq divert +rule action instead of usually more suitable +.Dq tee . .It Fl rr Indicates that it is okay to quit if .Ar maxbytes @@ -74,6 +101,17 @@ Stop dumping after .Ar maxbytes bytes. +Note that size of resulting +.Ar dumpfile +will be greater than +.Ar maxbytes +because +.Xr pcap 3 +stores additional headers for each packet in the file. +.It Fl i Ar flush_interval +Time in seconds to delay between automatic flushes of the file. +This may be specified with a value between 5 and 3600 seconds. +If not specified, the default is 60 seconds. .It Fl p Ar maxpkts Stop dumping after .Ar maxpkt @@ -81,7 +119,10 @@ .It Fl P Ar pidfile File to store PID number in. Default is -.Pa /var/run/ipwfpcap.portnr.pid . +.Pa /var/run/ipwfpcap. Ns Ao Ar portnum Ac Ns Pa .pid . +.It Fl z +Reset byte and packet counters to zero after each reopening of the +.Ar dumpfile . .El .Pp The @@ -98,7 +139,7 @@ .Sh EXIT STATUS .Ex -std .Sh EXAMPLES -.Dl "ipfwpcap -r 8091 divt.log &" +.Dl "ipfwpcap -r 8091 divt.log" .Pp Starts .Nm @@ -117,12 +158,13 @@ .Xr tcpdump 1 , .Xr pcap 3 , .Xr divert 4 , -.Xr ipfw 8 +.Xr ipfw 8 , +.Xr pflogd 8 .Sh HISTORY The .Nm utility first appeared in -.Fx 7.0 . +.Fx 6.3 . .Sh AUTHORS .An -nosplit .Nm