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
203*f70a2e29SGleb 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