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 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 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 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') { 906f8f50afSEd Schouten p = rindex(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 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 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 203983e5de6SPoul-Henning Kamp if ((r = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -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