1151427Sphk/*
2151427Sphk * copy diverted (or tee'd) packets to a file in 'tcpdump' format
3151427Sphk * (ie. this uses the '-lpcap' routines).
4151427Sphk *
5151427Sphk * example usage:
6151427Sphk *	# ipfwpcap -r 8091 divt.log &
7151427Sphk *	# ipfw add 2864 divert 8091 ip from 128.432.53.82 to any
8151427Sphk *	# ipfw add 2864 divert 8091 ip from any to 128.432.53.82
9151427Sphk *
10151427Sphk *   the resulting dump file can be read with ...
11151427Sphk *	# tcpdump -nX -r divt.log
12151427Sphk */
13151427Sphk/*
14151427Sphk * Written by P Kern { pkern [AT] cns.utoronto.ca }
15151427Sphk *
16151427Sphk * Copyright (c) 2004 University of Toronto. All rights reserved.
17151427Sphk * Anyone may use or copy this software except that this copyright
18151427Sphk * notice remain intact and that credit is given where it is due.
19151427Sphk * The University of Toronto and the author make no warranty and
20151427Sphk * accept no liability for this software.
21151427Sphk *
22151427Sphk * From: Header: /local/src/local.lib/SRC/ipfwpcap/RCS/ipfwpcap.c,v 1.4 2004/01/15 16:19:07 pkern Exp
23151427Sphk *
24151427Sphk * $FreeBSD$
25151427Sphk */
26151427Sphk
27151427Sphk#include <stdio.h>
28151427Sphk#include <errno.h>
29151427Sphk#include <paths.h>
30151427Sphk#include <fcntl.h>
31151427Sphk#include <signal.h>
32193188Sed#include <stdlib.h>
33193188Sed#include <string.h>
34151427Sphk#include <unistd.h>
35151427Sphk#include <sys/types.h>
36151427Sphk#include <sys/time.h>
37151427Sphk#include <sys/param.h>		/* for MAXPATHLEN */
38151427Sphk#include <sys/socket.h>
39151427Sphk#include <netinet/in.h>
40151427Sphk
41151427Sphk#include <netinet/in_systm.h>	/* for IP_MAXPACKET */
42151427Sphk#include <netinet/ip.h>		/* for IP_MAXPACKET */
43151427Sphk
44162011Ssam/* XXX normally defined in config.h */
45162011Ssam#define HAVE_STRLCPY 1
46162011Ssam#define HAVE_SNPRINTF 1
47162011Ssam#define HAVE_VSNPRINTF 1
48151427Sphk#include <pcap-int.h>	/* see pcap(3) and /usr/src/contrib/libpcap/. */
49151427Sphk
50151427Sphk#ifdef IP_MAXPACKET
51151427Sphk#define BUFMAX	IP_MAXPACKET
52151427Sphk#else
53151427Sphk#define BUFMAX	65535
54151427Sphk#endif
55151427Sphk
56151427Sphk#ifndef MAXPATHLEN
57151427Sphk#define MAXPATHLEN	1024
58151427Sphk#endif
59151427Sphk
60151427Sphkstatic int debug = 0;
61151427Sphkstatic int reflect = 0;		/* 1 == write packet back to socket. */
62151427Sphk
63151427Sphkstatic ssize_t totbytes = 0, maxbytes = 0;
64151427Sphkstatic ssize_t totpkts = 0, maxpkts = 0;
65151427Sphk
66193188Sedstatic char *prog = NULL;
67193188Sedstatic char pidfile[MAXPATHLEN];
68151427Sphk
69151427Sphk/*
70151427Sphk * tidy up.
71151427Sphk */
72193188Sedstatic void
73193188Sedquit(int sig)
74151427Sphk{
75151427Sphk	(void) unlink(pidfile);
76151427Sphk	exit(sig);
77151427Sphk}
78151427Sphk
79151427Sphk/*
80151427Sphk * do the "paper work"
81151427Sphk *	- save my own pid in /var/run/$0.{port#}.pid
82151427Sphk */
83193188Sedstatic void
84193188Sedokay(int pn)
85151427Sphk{
86193188Sed	int fd;
87151427Sphk	char *p, numbuf[80];
88151427Sphk
89151427Sphk	if (pidfile[0] == '\0') {
90193188Sed		p = rindex(prog, '/');
91193188Sed		p = (p == NULL) ? prog : p + 1;
92151427Sphk
93193188Sed		snprintf(pidfile, sizeof pidfile,
94151427Sphk			"%s%s.%d.pid", _PATH_VARRUN, p, pn);
95151427Sphk	}
96151427Sphk
97151427Sphk	fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
98193188Sed	if (fd < 0) {
99193188Sed		perror(pidfile);
100193188Sed		exit(21);
101193188Sed	}
102151427Sphk
103193188Sed	siginterrupt(SIGTERM, 1);
104193188Sed	siginterrupt(SIGHUP, 1);
105193188Sed	signal(SIGTERM, quit);
106193188Sed	signal(SIGHUP, quit);
107193188Sed	signal(SIGINT, quit);
108151427Sphk
109193188Sed	snprintf(numbuf, sizeof numbuf, "%d\n", getpid());
110193188Sed	if (write(fd, numbuf, strlen(numbuf)) < 0) {
111193188Sed		perror(pidfile);
112193188Sed		quit(23);
113193188Sed	}
114151427Sphk	(void) close(fd);
115151427Sphk}
116151427Sphk
117193188Sedstatic void
118193188Sedusage(void)
119151427Sphk{
120193188Sed	fprintf(stderr,
121193188Sed"\n"
122193188Sed"usage:\n"
123193188Sed"    %s [-dr] [-b maxbytes] [-p maxpkts] [-P pidfile] portnum dumpfile\n"
124193188Sed"\n"
125193188Sed"where:\n"
126193188Sed"	'-d'  = enable debugging messages.\n"
127193188Sed"	'-r'  = reflect. write packets back to the divert socket.\n"
128193188Sed"		(ie. simulate the original intent of \"ipfw tee\").\n"
129193188Sed"	'-rr' = indicate that it is okay to quit if packet-count or\n"
130193188Sed"		byte-count limits are reached (see the NOTE below\n"
131193188Sed"		about what this implies).\n"
132193188Sed"	'-b bytcnt'   = stop dumping after {bytcnt} bytes.\n"
133193188Sed"	'-p pktcnt'   = stop dumping after {pktcnt} packets.\n"
134193188Sed"	'-P pidfile'  = alternate file to store the PID\n"
135193188Sed"			(default: /var/run/%s.{portnum}.pid).\n"
136193188Sed"\n"
137193188Sed"	portnum  = divert(4) socket port number.\n"
138193188Sed"	dumpfile = file to write captured packets (tcpdump format).\n"
139193188Sed"		   (specify '-' to write packets to stdout).\n"
140193188Sed"\n"
141193188Sed"The '-r' option should not be necessary, but because \"ipfw tee\" is broken\n"
142193188Sed"(see BUGS in ipfw(8) for details) this feature can be used along with\n"
143193188Sed"an \"ipfw divert\" rule to simulate the original intent of \"ipfw tee\".\n"
144193188Sed"\n"
145193188Sed"NOTE: With an \"ipfw divert\" rule, diverted packets will silently\n"
146193188Sed"      disappear if there is nothing listening to the divert socket.\n"
147193188Sed"\n", prog, prog);
148193188Sed	exit(1);
149151427Sphk}
150151427Sphk
151193188Sedint
152193188Sedmain(int ac, char *av[])
153151427Sphk{
154151427Sphk	int r, sd, portnum, l;
155151427Sphk        struct sockaddr_in sin;
156151427Sphk	int errflg = 0;
157151427Sphk
158151427Sphk	int nfd;
159151427Sphk	fd_set rds;
160151427Sphk
161151427Sphk	ssize_t nr;
162151427Sphk
163151427Sphk	char *dumpf, buf[BUFMAX];
164151427Sphk
165151427Sphk	pcap_t *p;
166151427Sphk	pcap_dumper_t *dp;
167151427Sphk	struct pcap_pkthdr phd;
168151427Sphk
169151427Sphk	prog = av[0];
170151427Sphk
171151427Sphk	while ((r = getopt(ac, av, "drb:p:P:")) != -1) {
172151427Sphk		switch (r) {
173151427Sphk		case 'd':
174151427Sphk			debug++;
175151427Sphk			break;
176151427Sphk		case 'r':
177151427Sphk			reflect++;
178151427Sphk			break;
179151427Sphk		case 'b':
180151427Sphk			maxbytes = (ssize_t) atol(optarg);
181151427Sphk			break;
182151427Sphk		case 'p':
183151427Sphk			maxpkts = (ssize_t) atoi(optarg);
184151427Sphk			break;
185151427Sphk		case 'P':
186151427Sphk			strcpy(pidfile, optarg);
187151427Sphk			break;
188151427Sphk		case '?':
189151427Sphk		default:
190151427Sphk			errflg++;
191151427Sphk			break;
192151427Sphk		}
193151427Sphk	}
194151427Sphk
195151427Sphk	if ((ac - optind) != 2 || errflg)
196151427Sphk		usage();
197151427Sphk
198151427Sphk	portnum = atoi(av[optind++]);
199151427Sphk	dumpf = av[optind];
200151427Sphk
201151427Sphkif (debug) fprintf(stderr, "bind to %d.\ndump to '%s'.\n", portnum, dumpf);
202151427Sphk
203151427Sphk	if ((r = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) {
204151427Sphk		perror("socket(DIVERT)");
205151427Sphk		exit(2);
206151427Sphk	}
207151427Sphk	sd = r;
208151427Sphk
209151427Sphk	sin.sin_port = htons(portnum);
210151427Sphk	sin.sin_family = AF_INET;
211151427Sphk	sin.sin_addr.s_addr = INADDR_ANY;
212151427Sphk
213151427Sphk	if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
214151427Sphk		perror("bind(divert)");
215151427Sphk		exit(3);
216151427Sphk	}
217151427Sphk
218151427Sphk	p = pcap_open_dead(DLT_RAW, BUFMAX);
219151427Sphk	dp = pcap_dump_open(p, dumpf);
220151427Sphk	if (dp == NULL) {
221151427Sphk		pcap_perror(p, dumpf);
222151427Sphk		exit(4);
223151427Sphk	}
224151427Sphk
225151427Sphk	okay(portnum);
226151427Sphk
227151427Sphk	nfd = sd + 1;
228151427Sphk	for (;;) {
229151427Sphk		FD_ZERO(&rds);
230151427Sphk		FD_SET(sd, &rds);
231151427Sphk
232151427Sphk		r = select(nfd, &rds, NULL, NULL, NULL);
233151427Sphk		if (r == -1) {
234151427Sphk			if (errno == EINTR) continue;
235151427Sphk			perror("select");
236151427Sphk			quit(11);
237151427Sphk		}
238151427Sphk
239151427Sphk		if (!FD_ISSET(sd, &rds))
240151427Sphk			/* hmm. no work. */
241151427Sphk			continue;
242151427Sphk
243151427Sphk		/*
244151427Sphk		 * use recvfrom(3 and sendto(3) as in natd(8).
245151427Sphk		 * see /usr/src/sbin/natd/natd.c
246151427Sphk		 * see ipfw(8) about using 'divert' and 'tee'.
247151427Sphk		 */
248151427Sphk
249151427Sphk		/*
250151427Sphk		 * read packet.
251151427Sphk		 */
252151427Sphk		l = sizeof(sin);
253151427Sphk		nr = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &l);
254193188Sedif (debug) fprintf(stderr, "recvfrom(%d) = %zd (%d)\n", sd, nr, l);
255151427Sphk		if (nr < 0 && errno != EINTR) {
256151427Sphk			perror("recvfrom(sd)");
257151427Sphk			quit(12);
258151427Sphk		}
259151427Sphk		if (nr <= 0) continue;
260151427Sphk
261151427Sphk		if (reflect) {
262151427Sphk			/*
263151427Sphk			 * write packet back so it can continue
264151427Sphk			 * being processed by any further IPFW rules.
265151427Sphk			 */
266151427Sphk			l = sizeof(sin);
267151427Sphk			r = sendto(sd, buf, nr, 0, (struct sockaddr *)&sin, l);
268151427Sphkif (debug) fprintf(stderr, "  sendto(%d) = %d\n", sd, r);
269151427Sphk			if (r < 0) { perror("sendto(sd)"); quit(13); }
270151427Sphk		}
271151427Sphk
272151427Sphk		/*
273151427Sphk		 * check maximums, if any.
274151427Sphk		 * but don't quit if must continue reflecting packets.
275151427Sphk		 */
276151427Sphk		if (maxpkts) {
277151427Sphk			totpkts++;
278151427Sphk			if (totpkts > maxpkts) {
279151427Sphk				if (reflect == 1) continue;
280151427Sphk				quit(0);
281151427Sphk			}
282151427Sphk		}
283151427Sphk		if (maxbytes) {
284151427Sphk			totbytes += nr;
285151427Sphk			if (totbytes > maxbytes) {
286151427Sphk				if (reflect == 1) continue;
287151427Sphk				quit(0);
288151427Sphk			}
289151427Sphk		}
290151427Sphk
291151427Sphk		/*
292151427Sphk		 * save packet in tcpdump(1) format. see pcap(3).
293151427Sphk		 * divert packets are fully assembled. see ipfw(8).
294151427Sphk		 */
295151427Sphk		(void) gettimeofday(&(phd.ts), NULL);
296151427Sphk		phd.caplen = phd.len = nr;
297151427Sphk		pcap_dump((u_char *)dp, &phd, buf);
298151427Sphk		if (ferror((FILE *)dp)) { perror(dumpf); quit(14); }
299151427Sphk		(void) fflush((FILE *)dp);
300151427Sphk	}
301151427Sphk
302151427Sphk	quit(0);
303151427Sphk}
304