1 /* 2 * copy diverted (or tee'd) packets to a file in 'tcpdump' format 3 * (ie. this uses the '-lpcap' routines). 4 * 5 * example usage: 6 * # ipfwpcap -r 8091 divt.log & 7 * # ipfw add 2864 divert 8091 ip from 128.432.53.82 to any 8 * # ipfw add 2864 divert 8091 ip from any to 128.432.53.82 9 * 10 * the resulting dump file can be read with ... 11 * # tcpdump -nX -r divt.log 12 */ 13 /* 14 * Written by P Kern { pkern [AT] cns.utoronto.ca } 15 * 16 * Copyright (c) 2004 University of Toronto. All rights reserved. 17 * Anyone may use or copy this software except that this copyright 18 * notice remain intact and that credit is given where it is due. 19 * The University of Toronto and the author make no warranty and 20 * accept no liability for this software. 21 * 22 * From: Header: /local/src/local.lib/SRC/ipfwpcap/RCS/ipfwpcap.c,v 1.4 2004/01/15 16:19:07 pkern Exp 23 * 24 * $FreeBSD$ 25 */ 26 27 #include <stdio.h> 28 #include <errno.h> 29 #include <paths.h> 30 #include <fcntl.h> 31 #include <signal.h> 32 #include <unistd.h> 33 #include <sys/types.h> 34 #include <sys/time.h> 35 #include <sys/param.h> /* for MAXPATHLEN */ 36 #include <sys/socket.h> 37 #include <netinet/in.h> 38 39 #include <netinet/in_systm.h> /* for IP_MAXPACKET */ 40 #include <netinet/ip.h> /* for IP_MAXPACKET */ 41 42 #include <pcap-int.h> /* see pcap(3) and /usr/src/contrib/libpcap/. */ 43 44 #ifdef IP_MAXPACKET 45 #define BUFMAX IP_MAXPACKET 46 #else 47 #define BUFMAX 65535 48 #endif 49 50 #ifndef MAXPATHLEN 51 #define MAXPATHLEN 1024 52 #endif 53 54 static int debug = 0; 55 static int reflect = 0; /* 1 == write packet back to socket. */ 56 57 static ssize_t totbytes = 0, maxbytes = 0; 58 static ssize_t totpkts = 0, maxpkts = 0; 59 60 char *prog = NULL; 61 char pidfile[MAXPATHLEN] = { '\0' }; 62 63 /* 64 * tidy up. 65 */ 66 void 67 quit(sig) 68 int sig; 69 { 70 (void) unlink(pidfile); 71 exit(sig); 72 } 73 74 /* 75 * do the "paper work" 76 * - save my own pid in /var/run/$0.{port#}.pid 77 */ 78 okay(pn) 79 int pn; 80 { 81 FILE *fp; 82 int fd, numlen, n; 83 char *p, numbuf[80]; 84 85 numlen = sizeof(numbuf); 86 bzero(numbuf, numlen); 87 snprintf(numbuf, numlen-1, "%ld\n", getpid()); 88 numlen = strlen(numbuf); 89 90 if (pidfile[0] == '\0') { 91 p = (char *)rindex(prog, '/'); 92 p = (p == NULL) ? prog : p+1 ; 93 94 snprintf(pidfile, sizeof(pidfile)-1, 95 "%s%s.%d.pid", _PATH_VARRUN, p, pn); 96 } 97 98 fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0644); 99 if (fd < 0) { perror(pidfile); exit(21); } 100 101 siginterrupt(SIGTERM, 1); 102 siginterrupt(SIGHUP, 1); 103 signal (SIGTERM, quit); 104 signal (SIGHUP, quit); 105 106 n = write(fd, numbuf, numlen); 107 if (n < 0) { perror(pidfile); quit(23); } 108 (void) close(fd); 109 } 110 111 usage() 112 { 113 fprintf(stderr, "\ 114 \n\ 115 usage:\n\ 116 %s [-dr] [-b maxbytes] [-p maxpkts] [-P pidfile] portnum dumpfile\n\ 117 \n\ 118 where:\n\ 119 '-d' = enable debugging messages.\n\ 120 '-r' = reflect. write packets back to the divert socket.\n\ 121 (ie. simulate the original intent of \"ipfw tee\").\n\ 122 '-rr' = indicate that it is okay to quit if packet-count or\n\ 123 byte-count limits are reached (see the NOTE below\n\ 124 about what this implies).\n\ 125 '-b bytcnt' = stop dumping after {bytcnt} bytes.\n\ 126 '-p pktcnt' = stop dumping after {pktcnt} packets.\n\ 127 '-P pidfile' = alternate file to store the PID\n\ 128 (default: /var/run/%s.{portnum}.pid).\n\ 129 \n\ 130 portnum = divert(4) socket port number.\n\ 131 dumpfile = file to write captured packets (tcpdump format).\n\ 132 (specify '-' to write packets to stdout).\n\ 133 \n\ 134 ", prog, prog); 135 136 fprintf(stderr, "\ 137 The '-r' option should not be necessary, but because \"ipfw tee\" is broken\n\ 138 (see BUGS in ipfw(8) for details) this feature can be used along with\n\ 139 an \"ipfw divert\" rule to simulate the original intent of \"ipfw tee\".\n\ 140 \n\ 141 NOTE: With an \"ipfw divert\" rule, diverted packets will silently\n\ 142 disappear if there is nothing listening to the divert socket.\n\ 143 \n\ 144 "); 145 exit(-1); 146 } 147 148 main(ac, av) 149 int ac; 150 char *av[]; 151 { 152 int r, sd, portnum, l; 153 struct sockaddr_in sin; 154 int errflg = 0; 155 156 int nfd; 157 fd_set rds; 158 159 ssize_t nr; 160 161 char *dumpf, buf[BUFMAX]; 162 163 pcap_t *p; 164 pcap_dumper_t *dp; 165 struct pcap_pkthdr phd; 166 167 prog = av[0]; 168 169 while ((r = getopt(ac, av, "drb:p:P:")) != -1) { 170 switch (r) { 171 case 'd': 172 debug++; 173 break; 174 case 'r': 175 reflect++; 176 break; 177 case 'b': 178 maxbytes = (ssize_t) atol(optarg); 179 break; 180 case 'p': 181 maxpkts = (ssize_t) atoi(optarg); 182 break; 183 case 'P': 184 strcpy(pidfile, optarg); 185 break; 186 case '?': 187 default: 188 errflg++; 189 break; 190 } 191 } 192 193 if ((ac - optind) != 2 || errflg) 194 usage(); 195 196 portnum = atoi(av[optind++]); 197 dumpf = av[optind]; 198 199 if (debug) fprintf(stderr, "bind to %d.\ndump to '%s'.\n", portnum, dumpf); 200 201 if ((r = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) { 202 perror("socket(DIVERT)"); 203 exit(2); 204 } 205 sd = r; 206 207 sin.sin_port = htons(portnum); 208 sin.sin_family = AF_INET; 209 sin.sin_addr.s_addr = INADDR_ANY; 210 211 if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { 212 perror("bind(divert)"); 213 exit(3); 214 } 215 216 p = pcap_open_dead(DLT_RAW, BUFMAX); 217 dp = pcap_dump_open(p, dumpf); 218 if (dp == NULL) { 219 pcap_perror(p, dumpf); 220 exit(4); 221 } 222 223 okay(portnum); 224 225 nfd = sd + 1; 226 for (;;) { 227 FD_ZERO(&rds); 228 FD_SET(sd, &rds); 229 230 r = select(nfd, &rds, NULL, NULL, NULL); 231 if (r == -1) { 232 if (errno == EINTR) continue; 233 perror("select"); 234 quit(11); 235 } 236 237 if (!FD_ISSET(sd, &rds)) 238 /* hmm. no work. */ 239 continue; 240 241 /* 242 * use recvfrom(3 and sendto(3) as in natd(8). 243 * see /usr/src/sbin/natd/natd.c 244 * see ipfw(8) about using 'divert' and 'tee'. 245 */ 246 247 /* 248 * read packet. 249 */ 250 l = sizeof(sin); 251 nr = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &l); 252 if (debug) fprintf(stderr, "recvfrom(%d) = %d (%d)\n", sd, nr, l); 253 if (nr < 0 && errno != EINTR) { 254 perror("recvfrom(sd)"); 255 quit(12); 256 } 257 if (nr <= 0) continue; 258 259 if (reflect) { 260 /* 261 * write packet back so it can continue 262 * being processed by any further IPFW rules. 263 */ 264 l = sizeof(sin); 265 r = sendto(sd, buf, nr, 0, (struct sockaddr *)&sin, l); 266 if (debug) fprintf(stderr, " sendto(%d) = %d\n", sd, r); 267 if (r < 0) { perror("sendto(sd)"); quit(13); } 268 } 269 270 /* 271 * check maximums, if any. 272 * but don't quit if must continue reflecting packets. 273 */ 274 if (maxpkts) { 275 totpkts++; 276 if (totpkts > maxpkts) { 277 if (reflect == 1) continue; 278 quit(0); 279 } 280 } 281 if (maxbytes) { 282 totbytes += nr; 283 if (totbytes > maxbytes) { 284 if (reflect == 1) continue; 285 quit(0); 286 } 287 } 288 289 /* 290 * save packet in tcpdump(1) format. see pcap(3). 291 * divert packets are fully assembled. see ipfw(8). 292 */ 293 (void) gettimeofday(&(phd.ts), NULL); 294 phd.caplen = phd.len = nr; 295 pcap_dump((u_char *)dp, &phd, buf); 296 if (ferror((FILE *)dp)) { perror(dumpf); quit(14); } 297 (void) fflush((FILE *)dp); 298 } 299 300 quit(0); 301 } 302