summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ipfwpcap/ipfwpcap.c
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>2005-10-17 20:27:15 +0000
committerphk <phk@FreeBSD.org>2005-10-17 20:27:15 +0000
commit84406cb935270b5b267978e765af67b0b1f86e1b (patch)
treee9e4175818627de42b335bd1b0c4dcab1aa4bc2d /usr.sbin/ipfwpcap/ipfwpcap.c
parent238e8516431affd38a99039385908931ec2f759f (diff)
downloadFreeBSD-src-84406cb935270b5b267978e765af67b0b1f86e1b.zip
FreeBSD-src-84406cb935270b5b267978e765af67b0b1f86e1b.tar.gz
Add a small tool which captures packets on a DIVERT socket and writes
them as pcap (tcpdump) format data to a file or pipe. Volunters wanted for: manpage Contributed by: P Kern <pkern@cns.utoronto.ca>
Diffstat (limited to 'usr.sbin/ipfwpcap/ipfwpcap.c')
-rw-r--r--usr.sbin/ipfwpcap/ipfwpcap.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/usr.sbin/ipfwpcap/ipfwpcap.c b/usr.sbin/ipfwpcap/ipfwpcap.c
new file mode 100644
index 0000000..f279638
--- /dev/null
+++ b/usr.sbin/ipfwpcap/ipfwpcap.c
@@ -0,0 +1,301 @@
+/*
+ * 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 &
+ * # 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$
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <paths.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h> /* for MAXPATHLEN */
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <netinet/in_systm.h> /* for IP_MAXPACKET */
+#include <netinet/ip.h> /* for IP_MAXPACKET */
+
+#include <pcap-int.h> /* see pcap(3) and /usr/src/contrib/libpcap/. */
+
+#ifdef IP_MAXPACKET
+#define BUFMAX IP_MAXPACKET
+#else
+#define BUFMAX 65535
+#endif
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+static int debug = 0;
+static int reflect = 0; /* 1 == write packet back to socket. */
+
+static ssize_t totbytes = 0, maxbytes = 0;
+static ssize_t totpkts = 0, maxpkts = 0;
+
+char *prog = NULL;
+char pidfile[MAXPATHLEN] = { '\0' };
+
+/*
+ * tidy up.
+ */
+void
+quit(sig)
+int sig;
+{
+ (void) unlink(pidfile);
+ exit(sig);
+}
+
+/*
+ * do the "paper work"
+ * - save my own pid in /var/run/$0.{port#}.pid
+ */
+okay(pn)
+int pn;
+{
+ 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);
+
+ if (pidfile[0] == '\0') {
+ p = (char *)rindex(prog, '/');
+ p = (p == NULL) ? prog : p+1 ;
+
+ snprintf(pidfile, sizeof(pidfile)-1,
+ "%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); }
+
+ 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);
+}
+
+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);
+}
+
+main(ac, av)
+int ac;
+char *av[];
+{
+ int r, sd, portnum, l;
+ struct sockaddr_in sin;
+ int errflg = 0;
+
+ int nfd;
+ fd_set rds;
+
+ ssize_t nr;
+
+ char *dumpf, buf[BUFMAX];
+
+ pcap_t *p;
+ pcap_dumper_t *dp;
+ struct pcap_pkthdr phd;
+
+ prog = av[0];
+
+ while ((r = getopt(ac, av, "drb:p:P:")) != -1) {
+ switch (r) {
+ case 'd':
+ debug++;
+ break;
+ case 'r':
+ reflect++;
+ break;
+ case 'b':
+ maxbytes = (ssize_t) atol(optarg);
+ break;
+ case 'p':
+ maxpkts = (ssize_t) atoi(optarg);
+ break;
+ case 'P':
+ strcpy(pidfile, optarg);
+ break;
+ case '?':
+ default:
+ errflg++;
+ break;
+ }
+ }
+
+ if ((ac - optind) != 2 || errflg)
+ usage();
+
+ portnum = atoi(av[optind++]);
+ dumpf = av[optind];
+
+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);
+ }
+ sd = r;
+
+ sin.sin_port = htons(portnum);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+ perror("bind(divert)");
+ exit(3);
+ }
+
+ p = pcap_open_dead(DLT_RAW, BUFMAX);
+ dp = pcap_dump_open(p, dumpf);
+ if (dp == NULL) {
+ pcap_perror(p, dumpf);
+ exit(4);
+ }
+
+ okay(portnum);
+
+ nfd = sd + 1;
+ for (;;) {
+ FD_ZERO(&rds);
+ FD_SET(sd, &rds);
+
+ r = select(nfd, &rds, NULL, NULL, NULL);
+ if (r == -1) {
+ if (errno == EINTR) continue;
+ perror("select");
+ quit(11);
+ }
+
+ if (!FD_ISSET(sd, &rds))
+ /* hmm. no work. */
+ continue;
+
+ /*
+ * 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.
+ */
+ 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) {
+ perror("recvfrom(sd)");
+ quit(12);
+ }
+ if (nr <= 0) continue;
+
+ if (reflect) {
+ /*
+ * 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); }
+ }
+
+ /*
+ * check maximums, if any.
+ * but don't quit if must continue reflecting packets.
+ */
+ if (maxpkts) {
+ totpkts++;
+ if (totpkts > maxpkts) {
+ if (reflect == 1) continue;
+ quit(0);
+ }
+ }
+ if (maxbytes) {
+ totbytes += nr;
+ if (totbytes > maxbytes) {
+ if (reflect == 1) continue;
+ quit(0);
+ }
+ }
+
+ /*
+ * save packet in tcpdump(1) format. see pcap(3).
+ * divert packets are fully assembled. see ipfw(8).
+ */
+ (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);
+ }
+
+ quit(0);
+}
OpenPOWER on IntegriCloud