xref: /freebsd/usr.sbin/ipfwpcap/ipfwpcap.c (revision 609169becc8289b86cd097fdac74e5616977be75)
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  * $FreeBSD$
25983e5de6SPoul-Henning Kamp  */
26983e5de6SPoul-Henning Kamp 
27983e5de6SPoul-Henning Kamp #include <stdio.h>
28983e5de6SPoul-Henning Kamp #include <errno.h>
29983e5de6SPoul-Henning Kamp #include <paths.h>
30983e5de6SPoul-Henning Kamp #include <fcntl.h>
31983e5de6SPoul-Henning Kamp #include <signal.h>
326f8f50afSEd Schouten #include <stdlib.h>
336f8f50afSEd Schouten #include <string.h>
34983e5de6SPoul-Henning Kamp #include <unistd.h>
35983e5de6SPoul-Henning Kamp #include <sys/types.h>
36983e5de6SPoul-Henning Kamp #include <sys/time.h>
37983e5de6SPoul-Henning Kamp #include <sys/param.h>		/* for MAXPATHLEN */
38983e5de6SPoul-Henning Kamp #include <sys/socket.h>
39983e5de6SPoul-Henning Kamp #include <netinet/in.h>
40983e5de6SPoul-Henning Kamp 
41983e5de6SPoul-Henning Kamp #include <netinet/in_systm.h>	/* for IP_MAXPACKET */
42983e5de6SPoul-Henning Kamp #include <netinet/ip.h>		/* for IP_MAXPACKET */
43983e5de6SPoul-Henning Kamp 
44*609169beSHans Petter Selasky #include <net/bpf.h>
45*609169beSHans Petter Selasky 
4685fb34beSSam Leffler /* XXX normally defined in config.h */
4785fb34beSSam Leffler #define HAVE_STRLCPY 1
4885fb34beSSam Leffler #define HAVE_SNPRINTF 1
4985fb34beSSam Leffler #define HAVE_VSNPRINTF 1
50983e5de6SPoul-Henning Kamp #include <pcap-int.h>	/* see pcap(3) and /usr/src/contrib/libpcap/. */
51983e5de6SPoul-Henning Kamp 
52983e5de6SPoul-Henning Kamp #ifdef IP_MAXPACKET
53983e5de6SPoul-Henning Kamp #define BUFMAX	IP_MAXPACKET
54983e5de6SPoul-Henning Kamp #else
55983e5de6SPoul-Henning Kamp #define BUFMAX	65535
56983e5de6SPoul-Henning Kamp #endif
57983e5de6SPoul-Henning Kamp 
58983e5de6SPoul-Henning Kamp #ifndef MAXPATHLEN
59983e5de6SPoul-Henning Kamp #define MAXPATHLEN	1024
60983e5de6SPoul-Henning Kamp #endif
61983e5de6SPoul-Henning Kamp 
62983e5de6SPoul-Henning Kamp static int debug = 0;
63983e5de6SPoul-Henning Kamp static int reflect = 0;		/* 1 == write packet back to socket. */
64983e5de6SPoul-Henning Kamp 
65983e5de6SPoul-Henning Kamp static ssize_t totbytes = 0, maxbytes = 0;
66983e5de6SPoul-Henning Kamp static ssize_t totpkts = 0, maxpkts = 0;
67983e5de6SPoul-Henning Kamp 
686f8f50afSEd Schouten static char *prog = NULL;
696f8f50afSEd Schouten static char pidfile[MAXPATHLEN];
70983e5de6SPoul-Henning Kamp 
71983e5de6SPoul-Henning Kamp /*
72983e5de6SPoul-Henning Kamp  * tidy up.
73983e5de6SPoul-Henning Kamp  */
746f8f50afSEd Schouten static void
756f8f50afSEd Schouten quit(int sig)
76983e5de6SPoul-Henning Kamp {
77983e5de6SPoul-Henning Kamp 	(void) unlink(pidfile);
78983e5de6SPoul-Henning Kamp 	exit(sig);
79983e5de6SPoul-Henning Kamp }
80983e5de6SPoul-Henning Kamp 
81983e5de6SPoul-Henning Kamp /*
82983e5de6SPoul-Henning Kamp  * do the "paper work"
83983e5de6SPoul-Henning Kamp  *	- save my own pid in /var/run/$0.{port#}.pid
84983e5de6SPoul-Henning Kamp  */
856f8f50afSEd Schouten static void
866f8f50afSEd Schouten okay(int pn)
87983e5de6SPoul-Henning Kamp {
886f8f50afSEd Schouten 	int fd;
89983e5de6SPoul-Henning Kamp 	char *p, numbuf[80];
90983e5de6SPoul-Henning Kamp 
91983e5de6SPoul-Henning Kamp 	if (pidfile[0] == '\0') {
92b3608ae1SEd Schouten 		p = strrchr(prog, '/');
93983e5de6SPoul-Henning Kamp 		p = (p == NULL) ? prog : p + 1;
94983e5de6SPoul-Henning Kamp 
956f8f50afSEd Schouten 		snprintf(pidfile, sizeof pidfile,
96983e5de6SPoul-Henning Kamp 			"%s%s.%d.pid", _PATH_VARRUN, p, pn);
97983e5de6SPoul-Henning Kamp 	}
98983e5de6SPoul-Henning Kamp 
99983e5de6SPoul-Henning Kamp 	fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
1006f8f50afSEd Schouten 	if (fd < 0) {
1016f8f50afSEd Schouten 		perror(pidfile);
1026f8f50afSEd Schouten 		exit(21);
1036f8f50afSEd Schouten 	}
104983e5de6SPoul-Henning Kamp 
105983e5de6SPoul-Henning Kamp 	siginterrupt(SIGTERM, 1);
106983e5de6SPoul-Henning Kamp 	siginterrupt(SIGHUP, 1);
107983e5de6SPoul-Henning Kamp 	signal(SIGTERM, quit);
108983e5de6SPoul-Henning Kamp 	signal(SIGHUP, quit);
1099195f2b3SChristian S.J. Peron 	signal(SIGINT, quit);
110983e5de6SPoul-Henning Kamp 
1116f8f50afSEd Schouten 	snprintf(numbuf, sizeof numbuf, "%d\n", getpid());
1126f8f50afSEd Schouten 	if (write(fd, numbuf, strlen(numbuf)) < 0) {
1136f8f50afSEd Schouten 		perror(pidfile);
1146f8f50afSEd Schouten 		quit(23);
1156f8f50afSEd Schouten 	}
116983e5de6SPoul-Henning Kamp 	(void) close(fd);
117983e5de6SPoul-Henning Kamp }
118983e5de6SPoul-Henning Kamp 
1196f8f50afSEd Schouten static void
1206f8f50afSEd Schouten usage(void)
121983e5de6SPoul-Henning Kamp {
1226f8f50afSEd Schouten 	fprintf(stderr,
1236f8f50afSEd Schouten "\n"
1246f8f50afSEd Schouten "usage:\n"
1256f8f50afSEd Schouten "    %s [-dr] [-b maxbytes] [-p maxpkts] [-P pidfile] portnum dumpfile\n"
1266f8f50afSEd Schouten "\n"
1276f8f50afSEd Schouten "where:\n"
1286f8f50afSEd Schouten "	'-d'  = enable debugging messages.\n"
1296f8f50afSEd Schouten "	'-r'  = reflect. write packets back to the divert socket.\n"
1306f8f50afSEd Schouten "		(ie. simulate the original intent of \"ipfw tee\").\n"
1316f8f50afSEd Schouten "	'-rr' = indicate that it is okay to quit if packet-count or\n"
1326f8f50afSEd Schouten "		byte-count limits are reached (see the NOTE below\n"
1336f8f50afSEd Schouten "		about what this implies).\n"
1346f8f50afSEd Schouten "	'-b bytcnt'   = stop dumping after {bytcnt} bytes.\n"
1356f8f50afSEd Schouten "	'-p pktcnt'   = stop dumping after {pktcnt} packets.\n"
1366f8f50afSEd Schouten "	'-P pidfile'  = alternate file to store the PID\n"
1376f8f50afSEd Schouten "			(default: /var/run/%s.{portnum}.pid).\n"
1386f8f50afSEd Schouten "\n"
1396f8f50afSEd Schouten "	portnum  = divert(4) socket port number.\n"
1406f8f50afSEd Schouten "	dumpfile = file to write captured packets (tcpdump format).\n"
1416f8f50afSEd Schouten "		   (specify '-' to write packets to stdout).\n"
1426f8f50afSEd Schouten "\n"
1436f8f50afSEd Schouten "The '-r' option should not be necessary, but because \"ipfw tee\" is broken\n"
1446f8f50afSEd Schouten "(see BUGS in ipfw(8) for details) this feature can be used along with\n"
1456f8f50afSEd Schouten "an \"ipfw divert\" rule to simulate the original intent of \"ipfw tee\".\n"
1466f8f50afSEd Schouten "\n"
1476f8f50afSEd Schouten "NOTE: With an \"ipfw divert\" rule, diverted packets will silently\n"
1486f8f50afSEd Schouten "      disappear if there is nothing listening to the divert socket.\n"
1496f8f50afSEd Schouten "\n", prog, prog);
1506f8f50afSEd Schouten 	exit(1);
151983e5de6SPoul-Henning Kamp }
152983e5de6SPoul-Henning Kamp 
1536f8f50afSEd Schouten int
1546f8f50afSEd Schouten main(int ac, char *av[])
155983e5de6SPoul-Henning Kamp {
156983e5de6SPoul-Henning Kamp 	int r, sd, portnum, l;
157983e5de6SPoul-Henning Kamp         struct sockaddr_in sin;
158983e5de6SPoul-Henning Kamp 	int errflg = 0;
159983e5de6SPoul-Henning Kamp 
160983e5de6SPoul-Henning Kamp 	int nfd;
161983e5de6SPoul-Henning Kamp 	fd_set rds;
162983e5de6SPoul-Henning Kamp 
163983e5de6SPoul-Henning Kamp 	ssize_t nr;
164983e5de6SPoul-Henning Kamp 
165983e5de6SPoul-Henning Kamp 	char *dumpf, buf[BUFMAX];
166983e5de6SPoul-Henning Kamp 
167983e5de6SPoul-Henning Kamp 	pcap_t *p;
168983e5de6SPoul-Henning Kamp 	pcap_dumper_t *dp;
169983e5de6SPoul-Henning Kamp 	struct pcap_pkthdr phd;
170983e5de6SPoul-Henning Kamp 
171983e5de6SPoul-Henning Kamp 	prog = av[0];
172983e5de6SPoul-Henning Kamp 
173983e5de6SPoul-Henning Kamp 	while ((r = getopt(ac, av, "drb:p:P:")) != -1) {
174983e5de6SPoul-Henning Kamp 		switch (r) {
175983e5de6SPoul-Henning Kamp 		case 'd':
176983e5de6SPoul-Henning Kamp 			debug++;
177983e5de6SPoul-Henning Kamp 			break;
178983e5de6SPoul-Henning Kamp 		case 'r':
179983e5de6SPoul-Henning Kamp 			reflect++;
180983e5de6SPoul-Henning Kamp 			break;
181983e5de6SPoul-Henning Kamp 		case 'b':
182983e5de6SPoul-Henning Kamp 			maxbytes = (ssize_t) atol(optarg);
183983e5de6SPoul-Henning Kamp 			break;
184983e5de6SPoul-Henning Kamp 		case 'p':
185983e5de6SPoul-Henning Kamp 			maxpkts = (ssize_t) atoi(optarg);
186983e5de6SPoul-Henning Kamp 			break;
187983e5de6SPoul-Henning Kamp 		case 'P':
188983e5de6SPoul-Henning Kamp 			strcpy(pidfile, optarg);
189983e5de6SPoul-Henning Kamp 			break;
190983e5de6SPoul-Henning Kamp 		case '?':
191983e5de6SPoul-Henning Kamp 		default:
192983e5de6SPoul-Henning Kamp 			errflg++;
193983e5de6SPoul-Henning Kamp 			break;
194983e5de6SPoul-Henning Kamp 		}
195983e5de6SPoul-Henning Kamp 	}
196983e5de6SPoul-Henning Kamp 
197983e5de6SPoul-Henning Kamp 	if ((ac - optind) != 2 || errflg)
198983e5de6SPoul-Henning Kamp 		usage();
199983e5de6SPoul-Henning Kamp 
200983e5de6SPoul-Henning Kamp 	portnum = atoi(av[optind++]);
201983e5de6SPoul-Henning Kamp 	dumpf = av[optind];
202983e5de6SPoul-Henning Kamp 
203983e5de6SPoul-Henning Kamp if (debug) fprintf(stderr, "bind to %d.\ndump to '%s'.\n", portnum, dumpf);
204983e5de6SPoul-Henning Kamp 
205983e5de6SPoul-Henning Kamp 	if ((r = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) {
206983e5de6SPoul-Henning Kamp 		perror("socket(DIVERT)");
207983e5de6SPoul-Henning Kamp 		exit(2);
208983e5de6SPoul-Henning Kamp 	}
209983e5de6SPoul-Henning Kamp 	sd = r;
210983e5de6SPoul-Henning Kamp 
211983e5de6SPoul-Henning Kamp 	sin.sin_port = htons(portnum);
212983e5de6SPoul-Henning Kamp 	sin.sin_family = AF_INET;
213983e5de6SPoul-Henning Kamp 	sin.sin_addr.s_addr = INADDR_ANY;
214983e5de6SPoul-Henning Kamp 
215983e5de6SPoul-Henning Kamp 	if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
216983e5de6SPoul-Henning Kamp 		perror("bind(divert)");
217983e5de6SPoul-Henning Kamp 		exit(3);
218983e5de6SPoul-Henning Kamp 	}
219983e5de6SPoul-Henning Kamp 
220983e5de6SPoul-Henning Kamp 	p = pcap_open_dead(DLT_RAW, BUFMAX);
221983e5de6SPoul-Henning Kamp 	dp = pcap_dump_open(p, dumpf);
222983e5de6SPoul-Henning Kamp 	if (dp == NULL) {
223983e5de6SPoul-Henning Kamp 		pcap_perror(p, dumpf);
224983e5de6SPoul-Henning Kamp 		exit(4);
225983e5de6SPoul-Henning Kamp 	}
226983e5de6SPoul-Henning Kamp 
227983e5de6SPoul-Henning Kamp 	okay(portnum);
228983e5de6SPoul-Henning Kamp 
229983e5de6SPoul-Henning Kamp 	nfd = sd + 1;
230983e5de6SPoul-Henning Kamp 	for (;;) {
231983e5de6SPoul-Henning Kamp 		FD_ZERO(&rds);
232983e5de6SPoul-Henning Kamp 		FD_SET(sd, &rds);
233983e5de6SPoul-Henning Kamp 
234983e5de6SPoul-Henning Kamp 		r = select(nfd, &rds, NULL, NULL, NULL);
235983e5de6SPoul-Henning Kamp 		if (r == -1) {
236983e5de6SPoul-Henning Kamp 			if (errno == EINTR) continue;
237983e5de6SPoul-Henning Kamp 			perror("select");
238983e5de6SPoul-Henning Kamp 			quit(11);
239983e5de6SPoul-Henning Kamp 		}
240983e5de6SPoul-Henning Kamp 
241983e5de6SPoul-Henning Kamp 		if (!FD_ISSET(sd, &rds))
242983e5de6SPoul-Henning Kamp 			/* hmm. no work. */
243983e5de6SPoul-Henning Kamp 			continue;
244983e5de6SPoul-Henning Kamp 
245983e5de6SPoul-Henning Kamp 		/*
246983e5de6SPoul-Henning Kamp 		 * use recvfrom(3 and sendto(3) as in natd(8).
247983e5de6SPoul-Henning Kamp 		 * see /usr/src/sbin/natd/natd.c
248983e5de6SPoul-Henning Kamp 		 * see ipfw(8) about using 'divert' and 'tee'.
249983e5de6SPoul-Henning Kamp 		 */
250983e5de6SPoul-Henning Kamp 
251983e5de6SPoul-Henning Kamp 		/*
252983e5de6SPoul-Henning Kamp 		 * read packet.
253983e5de6SPoul-Henning Kamp 		 */
254983e5de6SPoul-Henning Kamp 		l = sizeof(sin);
255983e5de6SPoul-Henning Kamp 		nr = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &l);
2566f8f50afSEd Schouten if (debug) fprintf(stderr, "recvfrom(%d) = %zd (%d)\n", sd, nr, l);
257983e5de6SPoul-Henning Kamp 		if (nr < 0 && errno != EINTR) {
258983e5de6SPoul-Henning Kamp 			perror("recvfrom(sd)");
259983e5de6SPoul-Henning Kamp 			quit(12);
260983e5de6SPoul-Henning Kamp 		}
261983e5de6SPoul-Henning Kamp 		if (nr <= 0) continue;
262983e5de6SPoul-Henning Kamp 
263983e5de6SPoul-Henning Kamp 		if (reflect) {
264983e5de6SPoul-Henning Kamp 			/*
265983e5de6SPoul-Henning Kamp 			 * write packet back so it can continue
266983e5de6SPoul-Henning Kamp 			 * being processed by any further IPFW rules.
267983e5de6SPoul-Henning Kamp 			 */
268983e5de6SPoul-Henning Kamp 			l = sizeof(sin);
269983e5de6SPoul-Henning Kamp 			r = sendto(sd, buf, nr, 0, (struct sockaddr *)&sin, l);
270983e5de6SPoul-Henning Kamp if (debug) fprintf(stderr, "  sendto(%d) = %d\n", sd, r);
271983e5de6SPoul-Henning Kamp 			if (r < 0) { perror("sendto(sd)"); quit(13); }
272983e5de6SPoul-Henning Kamp 		}
273983e5de6SPoul-Henning Kamp 
274983e5de6SPoul-Henning Kamp 		/*
275983e5de6SPoul-Henning Kamp 		 * check maximums, if any.
276983e5de6SPoul-Henning Kamp 		 * but don't quit if must continue reflecting packets.
277983e5de6SPoul-Henning Kamp 		 */
278983e5de6SPoul-Henning Kamp 		if (maxpkts) {
279983e5de6SPoul-Henning Kamp 			totpkts++;
280983e5de6SPoul-Henning Kamp 			if (totpkts > maxpkts) {
281983e5de6SPoul-Henning Kamp 				if (reflect == 1) continue;
282983e5de6SPoul-Henning Kamp 				quit(0);
283983e5de6SPoul-Henning Kamp 			}
284983e5de6SPoul-Henning Kamp 		}
285983e5de6SPoul-Henning Kamp 		if (maxbytes) {
286983e5de6SPoul-Henning Kamp 			totbytes += nr;
287983e5de6SPoul-Henning Kamp 			if (totbytes > maxbytes) {
288983e5de6SPoul-Henning Kamp 				if (reflect == 1) continue;
289983e5de6SPoul-Henning Kamp 				quit(0);
290983e5de6SPoul-Henning Kamp 			}
291983e5de6SPoul-Henning Kamp 		}
292983e5de6SPoul-Henning Kamp 
293983e5de6SPoul-Henning Kamp 		/*
294983e5de6SPoul-Henning Kamp 		 * save packet in tcpdump(1) format. see pcap(3).
295983e5de6SPoul-Henning Kamp 		 * divert packets are fully assembled. see ipfw(8).
296983e5de6SPoul-Henning Kamp 		 */
297983e5de6SPoul-Henning Kamp 		(void) gettimeofday(&(phd.ts), NULL);
298983e5de6SPoul-Henning Kamp 		phd.caplen = phd.len = nr;
299983e5de6SPoul-Henning Kamp 		pcap_dump((u_char *)dp, &phd, buf);
300983e5de6SPoul-Henning Kamp 		if (ferror((FILE *)dp)) { perror(dumpf); quit(14); }
301983e5de6SPoul-Henning Kamp 		(void) fflush((FILE *)dp);
302983e5de6SPoul-Henning Kamp 	}
303983e5de6SPoul-Henning Kamp 
304983e5de6SPoul-Henning Kamp 	quit(0);
305983e5de6SPoul-Henning Kamp }
306