xref: /freebsd/usr.sbin/ipfwpcap/ipfwpcap.c (revision b3e76948)
1983e5de6SPoul-Henning Kamp /*
2983e5de6SPoul-Henning Kamp  * copy diverted (or tee'd) packets to a file in 'tcpdump' format
3983e5de6SPoul-Henning Kamp  * (ie. this uses the '-lpcap' routines).
4983e5de6SPoul-Henning Kamp  *
5983e5de6SPoul-Henning Kamp  * example usage:
6983e5de6SPoul-Henning Kamp  *	# ipfwpcap -r 8091 divt.log &
7983e5de6SPoul-Henning Kamp  *	# ipfw add 2864 divert 8091 ip from 128.432.53.82 to any
8983e5de6SPoul-Henning Kamp  *	# ipfw add 2864 divert 8091 ip from any to 128.432.53.82
9983e5de6SPoul-Henning Kamp  *
10983e5de6SPoul-Henning Kamp  *   the resulting dump file can be read with ...
11983e5de6SPoul-Henning Kamp  *	# tcpdump -nX -r divt.log
12983e5de6SPoul-Henning Kamp  */
13983e5de6SPoul-Henning Kamp /*
14983e5de6SPoul-Henning Kamp  * Written by P Kern { pkern [AT] cns.utoronto.ca }
15983e5de6SPoul-Henning Kamp  *
16983e5de6SPoul-Henning Kamp  * Copyright (c) 2004 University of Toronto. All rights reserved.
17983e5de6SPoul-Henning Kamp  * Anyone may use or copy this software except that this copyright
18983e5de6SPoul-Henning Kamp  * notice remain intact and that credit is given where it is due.
19983e5de6SPoul-Henning Kamp  * The University of Toronto and the author make no warranty and
20983e5de6SPoul-Henning Kamp  * accept no liability for this software.
21983e5de6SPoul-Henning Kamp  *
22983e5de6SPoul-Henning Kamp  * From: Header: /local/src/local.lib/SRC/ipfwpcap/RCS/ipfwpcap.c,v 1.4 2004/01/15 16:19:07 pkern Exp
23983e5de6SPoul-Henning Kamp  */
24983e5de6SPoul-Henning Kamp 
25983e5de6SPoul-Henning Kamp #include <stdio.h>
26983e5de6SPoul-Henning Kamp #include <errno.h>
27983e5de6SPoul-Henning Kamp #include <paths.h>
28983e5de6SPoul-Henning Kamp #include <fcntl.h>
29983e5de6SPoul-Henning Kamp #include <signal.h>
306f8f50afSEd Schouten #include <stdlib.h>
316f8f50afSEd Schouten #include <string.h>
32983e5de6SPoul-Henning Kamp #include <unistd.h>
33983e5de6SPoul-Henning Kamp #include <sys/types.h>
34983e5de6SPoul-Henning Kamp #include <sys/time.h>
35983e5de6SPoul-Henning Kamp #include <sys/param.h>		/* for MAXPATHLEN */
36983e5de6SPoul-Henning Kamp #include <sys/socket.h>
37983e5de6SPoul-Henning Kamp #include <netinet/in.h>
38983e5de6SPoul-Henning Kamp 
39983e5de6SPoul-Henning Kamp #include <netinet/in_systm.h>	/* for IP_MAXPACKET */
40983e5de6SPoul-Henning Kamp #include <netinet/ip.h>		/* for IP_MAXPACKET */
41983e5de6SPoul-Henning Kamp 
42609169beSHans Petter Selasky #include <net/bpf.h>
43609169beSHans Petter Selasky 
4485fb34beSSam Leffler /* XXX normally defined in config.h */
4585fb34beSSam Leffler #define HAVE_STRLCPY 1
4685fb34beSSam Leffler #define HAVE_SNPRINTF 1
4785fb34beSSam Leffler #define HAVE_VSNPRINTF 1
48983e5de6SPoul-Henning Kamp #include <pcap-int.h>	/* see pcap(3) and /usr/src/contrib/libpcap/. */
49983e5de6SPoul-Henning Kamp 
50983e5de6SPoul-Henning Kamp #ifdef IP_MAXPACKET
51983e5de6SPoul-Henning Kamp #define BUFMAX	IP_MAXPACKET
52983e5de6SPoul-Henning Kamp #else
53983e5de6SPoul-Henning Kamp #define BUFMAX	65535
54983e5de6SPoul-Henning Kamp #endif
55983e5de6SPoul-Henning Kamp 
56983e5de6SPoul-Henning Kamp #ifndef MAXPATHLEN
57983e5de6SPoul-Henning Kamp #define MAXPATHLEN	1024
58983e5de6SPoul-Henning Kamp #endif
59983e5de6SPoul-Henning Kamp 
60983e5de6SPoul-Henning Kamp static int debug = 0;
61983e5de6SPoul-Henning Kamp static int reflect = 0;		/* 1 == write packet back to socket. */
62983e5de6SPoul-Henning Kamp 
63983e5de6SPoul-Henning Kamp static ssize_t totbytes = 0, maxbytes = 0;
64983e5de6SPoul-Henning Kamp static ssize_t totpkts = 0, maxpkts = 0;
65983e5de6SPoul-Henning Kamp 
666f8f50afSEd Schouten static char *prog = NULL;
676f8f50afSEd Schouten static char pidfile[MAXPATHLEN];
68983e5de6SPoul-Henning Kamp 
69983e5de6SPoul-Henning Kamp /*
70983e5de6SPoul-Henning Kamp  * tidy up.
71983e5de6SPoul-Henning Kamp  */
726f8f50afSEd Schouten static void
quit(int sig)736f8f50afSEd Schouten quit(int sig)
74983e5de6SPoul-Henning Kamp {
75983e5de6SPoul-Henning Kamp 	(void) unlink(pidfile);
76983e5de6SPoul-Henning Kamp 	exit(sig);
77983e5de6SPoul-Henning Kamp }
78983e5de6SPoul-Henning Kamp 
79983e5de6SPoul-Henning Kamp /*
80983e5de6SPoul-Henning Kamp  * do the "paper work"
81983e5de6SPoul-Henning Kamp  *	- save my own pid in /var/run/$0.{port#}.pid
82983e5de6SPoul-Henning Kamp  */
836f8f50afSEd Schouten static void
okay(int pn)846f8f50afSEd Schouten okay(int pn)
85983e5de6SPoul-Henning Kamp {
866f8f50afSEd Schouten 	int fd;
87983e5de6SPoul-Henning Kamp 	char *p, numbuf[80];
88983e5de6SPoul-Henning Kamp 
89983e5de6SPoul-Henning Kamp 	if (pidfile[0] == '\0') {
90b3608ae1SEd Schouten 		p = strrchr(prog, '/');
91983e5de6SPoul-Henning Kamp 		p = (p == NULL) ? prog : p + 1;
92983e5de6SPoul-Henning Kamp 
936f8f50afSEd Schouten 		snprintf(pidfile, sizeof pidfile,
94983e5de6SPoul-Henning Kamp 			"%s%s.%d.pid", _PATH_VARRUN, p, pn);
95983e5de6SPoul-Henning Kamp 	}
96983e5de6SPoul-Henning Kamp 
97983e5de6SPoul-Henning Kamp 	fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
986f8f50afSEd Schouten 	if (fd < 0) {
996f8f50afSEd Schouten 		perror(pidfile);
1006f8f50afSEd Schouten 		exit(21);
1016f8f50afSEd Schouten 	}
102983e5de6SPoul-Henning Kamp 
103983e5de6SPoul-Henning Kamp 	siginterrupt(SIGTERM, 1);
104983e5de6SPoul-Henning Kamp 	siginterrupt(SIGHUP, 1);
105983e5de6SPoul-Henning Kamp 	signal(SIGTERM, quit);
106983e5de6SPoul-Henning Kamp 	signal(SIGHUP, quit);
1079195f2b3SChristian S.J. Peron 	signal(SIGINT, quit);
108983e5de6SPoul-Henning Kamp 
1096f8f50afSEd Schouten 	snprintf(numbuf, sizeof numbuf, "%d\n", getpid());
1106f8f50afSEd Schouten 	if (write(fd, numbuf, strlen(numbuf)) < 0) {
1116f8f50afSEd Schouten 		perror(pidfile);
1126f8f50afSEd Schouten 		quit(23);
1136f8f50afSEd Schouten 	}
114983e5de6SPoul-Henning Kamp 	(void) close(fd);
115983e5de6SPoul-Henning Kamp }
116983e5de6SPoul-Henning Kamp 
1176f8f50afSEd Schouten static void
usage(void)1186f8f50afSEd Schouten usage(void)
119983e5de6SPoul-Henning Kamp {
1206f8f50afSEd Schouten 	fprintf(stderr,
1216f8f50afSEd Schouten "\n"
1226f8f50afSEd Schouten "usage:\n"
1236f8f50afSEd Schouten "    %s [-dr] [-b maxbytes] [-p maxpkts] [-P pidfile] portnum dumpfile\n"
1246f8f50afSEd Schouten "\n"
1256f8f50afSEd Schouten "where:\n"
1266f8f50afSEd Schouten "	'-d'  = enable debugging messages.\n"
1276f8f50afSEd Schouten "	'-r'  = reflect. write packets back to the divert socket.\n"
1286f8f50afSEd Schouten "		(ie. simulate the original intent of \"ipfw tee\").\n"
1296f8f50afSEd Schouten "	'-rr' = indicate that it is okay to quit if packet-count or\n"
1306f8f50afSEd Schouten "		byte-count limits are reached (see the NOTE below\n"
1316f8f50afSEd Schouten "		about what this implies).\n"
1326f8f50afSEd Schouten "	'-b bytcnt'   = stop dumping after {bytcnt} bytes.\n"
1336f8f50afSEd Schouten "	'-p pktcnt'   = stop dumping after {pktcnt} packets.\n"
1346f8f50afSEd Schouten "	'-P pidfile'  = alternate file to store the PID\n"
1356f8f50afSEd Schouten "			(default: /var/run/%s.{portnum}.pid).\n"
1366f8f50afSEd Schouten "\n"
1376f8f50afSEd Schouten "	portnum  = divert(4) socket port number.\n"
1386f8f50afSEd Schouten "	dumpfile = file to write captured packets (tcpdump format).\n"
1396f8f50afSEd Schouten "		   (specify '-' to write packets to stdout).\n"
1406f8f50afSEd Schouten "\n"
1416f8f50afSEd Schouten "The '-r' option should not be necessary, but because \"ipfw tee\" is broken\n"
1426f8f50afSEd Schouten "(see BUGS in ipfw(8) for details) this feature can be used along with\n"
1436f8f50afSEd Schouten "an \"ipfw divert\" rule to simulate the original intent of \"ipfw tee\".\n"
1446f8f50afSEd Schouten "\n"
1456f8f50afSEd Schouten "NOTE: With an \"ipfw divert\" rule, diverted packets will silently\n"
1466f8f50afSEd Schouten "      disappear if there is nothing listening to the divert socket.\n"
1476f8f50afSEd Schouten "\n", prog, prog);
1486f8f50afSEd Schouten 	exit(1);
149983e5de6SPoul-Henning Kamp }
150983e5de6SPoul-Henning Kamp 
1516f8f50afSEd Schouten int
main(int ac,char * av[])1526f8f50afSEd Schouten main(int ac, char *av[])
153983e5de6SPoul-Henning Kamp {
154983e5de6SPoul-Henning Kamp 	int r, sd, portnum, l;
155983e5de6SPoul-Henning Kamp         struct sockaddr_in sin;
156983e5de6SPoul-Henning Kamp 	int errflg = 0;
157983e5de6SPoul-Henning Kamp 
158983e5de6SPoul-Henning Kamp 	int nfd;
159983e5de6SPoul-Henning Kamp 	fd_set rds;
160983e5de6SPoul-Henning Kamp 
161983e5de6SPoul-Henning Kamp 	ssize_t nr;
162983e5de6SPoul-Henning Kamp 
163983e5de6SPoul-Henning Kamp 	char *dumpf, buf[BUFMAX];
164983e5de6SPoul-Henning Kamp 
165983e5de6SPoul-Henning Kamp 	pcap_t *p;
166983e5de6SPoul-Henning Kamp 	pcap_dumper_t *dp;
167983e5de6SPoul-Henning Kamp 	struct pcap_pkthdr phd;
168983e5de6SPoul-Henning Kamp 
169983e5de6SPoul-Henning Kamp 	prog = av[0];
170983e5de6SPoul-Henning Kamp 
171983e5de6SPoul-Henning Kamp 	while ((r = getopt(ac, av, "drb:p:P:")) != -1) {
172983e5de6SPoul-Henning Kamp 		switch (r) {
173983e5de6SPoul-Henning Kamp 		case 'd':
174983e5de6SPoul-Henning Kamp 			debug++;
175983e5de6SPoul-Henning Kamp 			break;
176983e5de6SPoul-Henning Kamp 		case 'r':
177983e5de6SPoul-Henning Kamp 			reflect++;
178983e5de6SPoul-Henning Kamp 			break;
179983e5de6SPoul-Henning Kamp 		case 'b':
180983e5de6SPoul-Henning Kamp 			maxbytes = (ssize_t) atol(optarg);
181983e5de6SPoul-Henning Kamp 			break;
182983e5de6SPoul-Henning Kamp 		case 'p':
183983e5de6SPoul-Henning Kamp 			maxpkts = (ssize_t) atoi(optarg);
184983e5de6SPoul-Henning Kamp 			break;
185983e5de6SPoul-Henning Kamp 		case 'P':
186983e5de6SPoul-Henning Kamp 			strcpy(pidfile, optarg);
187983e5de6SPoul-Henning Kamp 			break;
188983e5de6SPoul-Henning Kamp 		case '?':
189983e5de6SPoul-Henning Kamp 		default:
190983e5de6SPoul-Henning Kamp 			errflg++;
191983e5de6SPoul-Henning Kamp 			break;
192983e5de6SPoul-Henning Kamp 		}
193983e5de6SPoul-Henning Kamp 	}
194983e5de6SPoul-Henning Kamp 
195983e5de6SPoul-Henning Kamp 	if ((ac - optind) != 2 || errflg)
196983e5de6SPoul-Henning Kamp 		usage();
197983e5de6SPoul-Henning Kamp 
198983e5de6SPoul-Henning Kamp 	portnum = atoi(av[optind++]);
199983e5de6SPoul-Henning Kamp 	dumpf = av[optind];
200983e5de6SPoul-Henning Kamp 
201983e5de6SPoul-Henning Kamp if (debug) fprintf(stderr, "bind to %d.\ndump to '%s'.\n", portnum, dumpf);
202983e5de6SPoul-Henning Kamp 
203f70a2e29SGleb Smirnoff 	if ((r = socket(PF_DIVERT, SOCK_RAW, 0)) == -1) {
204983e5de6SPoul-Henning Kamp 		perror("socket(DIVERT)");
205983e5de6SPoul-Henning Kamp 		exit(2);
206983e5de6SPoul-Henning Kamp 	}
207983e5de6SPoul-Henning Kamp 	sd = r;
208983e5de6SPoul-Henning Kamp 
209983e5de6SPoul-Henning Kamp 	sin.sin_port = htons(portnum);
210983e5de6SPoul-Henning Kamp 	sin.sin_family = AF_INET;
211983e5de6SPoul-Henning Kamp 	sin.sin_addr.s_addr = INADDR_ANY;
212983e5de6SPoul-Henning Kamp 
213983e5de6SPoul-Henning Kamp 	if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
214983e5de6SPoul-Henning Kamp 		perror("bind(divert)");
215983e5de6SPoul-Henning Kamp 		exit(3);
216983e5de6SPoul-Henning Kamp 	}
217983e5de6SPoul-Henning Kamp 
218983e5de6SPoul-Henning Kamp 	p = pcap_open_dead(DLT_RAW, BUFMAX);
219983e5de6SPoul-Henning Kamp 	dp = pcap_dump_open(p, dumpf);
220983e5de6SPoul-Henning Kamp 	if (dp == NULL) {
221983e5de6SPoul-Henning Kamp 		pcap_perror(p, dumpf);
222983e5de6SPoul-Henning Kamp 		exit(4);
223983e5de6SPoul-Henning Kamp 	}
224983e5de6SPoul-Henning Kamp 
225983e5de6SPoul-Henning Kamp 	okay(portnum);
226983e5de6SPoul-Henning Kamp 
227983e5de6SPoul-Henning Kamp 	nfd = sd + 1;
228983e5de6SPoul-Henning Kamp 	for (;;) {
229983e5de6SPoul-Henning Kamp 		FD_ZERO(&rds);
230983e5de6SPoul-Henning Kamp 		FD_SET(sd, &rds);
231983e5de6SPoul-Henning Kamp 
232983e5de6SPoul-Henning Kamp 		r = select(nfd, &rds, NULL, NULL, NULL);
233983e5de6SPoul-Henning Kamp 		if (r == -1) {
234983e5de6SPoul-Henning Kamp 			if (errno == EINTR) continue;
235983e5de6SPoul-Henning Kamp 			perror("select");
236983e5de6SPoul-Henning Kamp 			quit(11);
237983e5de6SPoul-Henning Kamp 		}
238983e5de6SPoul-Henning Kamp 
239983e5de6SPoul-Henning Kamp 		if (!FD_ISSET(sd, &rds))
240983e5de6SPoul-Henning Kamp 			/* hmm. no work. */
241983e5de6SPoul-Henning Kamp 			continue;
242983e5de6SPoul-Henning Kamp 
243983e5de6SPoul-Henning Kamp 		/*
244983e5de6SPoul-Henning Kamp 		 * use recvfrom(3 and sendto(3) as in natd(8).
245983e5de6SPoul-Henning Kamp 		 * see /usr/src/sbin/natd/natd.c
246983e5de6SPoul-Henning Kamp 		 * see ipfw(8) about using 'divert' and 'tee'.
247983e5de6SPoul-Henning Kamp 		 */
248983e5de6SPoul-Henning Kamp 
249983e5de6SPoul-Henning Kamp 		/*
250983e5de6SPoul-Henning Kamp 		 * read packet.
251983e5de6SPoul-Henning Kamp 		 */
252983e5de6SPoul-Henning Kamp 		l = sizeof(sin);
253983e5de6SPoul-Henning Kamp 		nr = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &l);
2546f8f50afSEd Schouten if (debug) fprintf(stderr, "recvfrom(%d) = %zd (%d)\n", sd, nr, l);
255983e5de6SPoul-Henning Kamp 		if (nr < 0 && errno != EINTR) {
256983e5de6SPoul-Henning Kamp 			perror("recvfrom(sd)");
257983e5de6SPoul-Henning Kamp 			quit(12);
258983e5de6SPoul-Henning Kamp 		}
259983e5de6SPoul-Henning Kamp 		if (nr <= 0) continue;
260983e5de6SPoul-Henning Kamp 
261983e5de6SPoul-Henning Kamp 		if (reflect) {
262983e5de6SPoul-Henning Kamp 			/*
263983e5de6SPoul-Henning Kamp 			 * write packet back so it can continue
264983e5de6SPoul-Henning Kamp 			 * being processed by any further IPFW rules.
265983e5de6SPoul-Henning Kamp 			 */
266983e5de6SPoul-Henning Kamp 			l = sizeof(sin);
267983e5de6SPoul-Henning Kamp 			r = sendto(sd, buf, nr, 0, (struct sockaddr *)&sin, l);
268983e5de6SPoul-Henning Kamp if (debug) fprintf(stderr, "  sendto(%d) = %d\n", sd, r);
269983e5de6SPoul-Henning Kamp 			if (r < 0) { perror("sendto(sd)"); quit(13); }
270983e5de6SPoul-Henning Kamp 		}
271983e5de6SPoul-Henning Kamp 
272983e5de6SPoul-Henning Kamp 		/*
273983e5de6SPoul-Henning Kamp 		 * check maximums, if any.
274983e5de6SPoul-Henning Kamp 		 * but don't quit if must continue reflecting packets.
275983e5de6SPoul-Henning Kamp 		 */
276983e5de6SPoul-Henning Kamp 		if (maxpkts) {
277983e5de6SPoul-Henning Kamp 			totpkts++;
278983e5de6SPoul-Henning Kamp 			if (totpkts > maxpkts) {
279983e5de6SPoul-Henning Kamp 				if (reflect == 1) continue;
280983e5de6SPoul-Henning Kamp 				quit(0);
281983e5de6SPoul-Henning Kamp 			}
282983e5de6SPoul-Henning Kamp 		}
283983e5de6SPoul-Henning Kamp 		if (maxbytes) {
284983e5de6SPoul-Henning Kamp 			totbytes += nr;
285983e5de6SPoul-Henning Kamp 			if (totbytes > maxbytes) {
286983e5de6SPoul-Henning Kamp 				if (reflect == 1) continue;
287983e5de6SPoul-Henning Kamp 				quit(0);
288983e5de6SPoul-Henning Kamp 			}
289983e5de6SPoul-Henning Kamp 		}
290983e5de6SPoul-Henning Kamp 
291983e5de6SPoul-Henning Kamp 		/*
292983e5de6SPoul-Henning Kamp 		 * save packet in tcpdump(1) format. see pcap(3).
293983e5de6SPoul-Henning Kamp 		 * divert packets are fully assembled. see ipfw(8).
294983e5de6SPoul-Henning Kamp 		 */
295983e5de6SPoul-Henning Kamp 		(void) gettimeofday(&(phd.ts), NULL);
296983e5de6SPoul-Henning Kamp 		phd.caplen = phd.len = nr;
297983e5de6SPoul-Henning Kamp 		pcap_dump((u_char *)dp, &phd, buf);
298983e5de6SPoul-Henning Kamp 		if (ferror((FILE *)dp)) { perror(dumpf); quit(14); }
299983e5de6SPoul-Henning Kamp 		(void) fflush((FILE *)dp);
300983e5de6SPoul-Henning Kamp 	}
301983e5de6SPoul-Henning Kamp 
302983e5de6SPoul-Henning Kamp 	quit(0);
303983e5de6SPoul-Henning Kamp }
304