1151427Sphk/* 2151427Sphk * copy diverted (or tee'd) packets to a file in 'tcpdump' format 3151427Sphk * (ie. this uses the '-lpcap' routines). 4151427Sphk * 5151427Sphk * example usage: 6151427Sphk * # ipfwpcap -r 8091 divt.log & 7151427Sphk * # ipfw add 2864 divert 8091 ip from 128.432.53.82 to any 8151427Sphk * # ipfw add 2864 divert 8091 ip from any to 128.432.53.82 9151427Sphk * 10151427Sphk * the resulting dump file can be read with ... 11151427Sphk * # tcpdump -nX -r divt.log 12151427Sphk */ 13151427Sphk/* 14151427Sphk * Written by P Kern { pkern [AT] cns.utoronto.ca } 15151427Sphk * 16151427Sphk * Copyright (c) 2004 University of Toronto. All rights reserved. 17151427Sphk * Anyone may use or copy this software except that this copyright 18151427Sphk * notice remain intact and that credit is given where it is due. 19151427Sphk * The University of Toronto and the author make no warranty and 20151427Sphk * accept no liability for this software. 21151427Sphk * 22151427Sphk * From: Header: /local/src/local.lib/SRC/ipfwpcap/RCS/ipfwpcap.c,v 1.4 2004/01/15 16:19:07 pkern Exp 23151427Sphk * 24151427Sphk * $FreeBSD$ 25151427Sphk */ 26151427Sphk 27151427Sphk#include <stdio.h> 28151427Sphk#include <errno.h> 29151427Sphk#include <paths.h> 30151427Sphk#include <fcntl.h> 31151427Sphk#include <signal.h> 32193188Sed#include <stdlib.h> 33193188Sed#include <string.h> 34151427Sphk#include <unistd.h> 35151427Sphk#include <sys/types.h> 36151427Sphk#include <sys/time.h> 37151427Sphk#include <sys/param.h> /* for MAXPATHLEN */ 38151427Sphk#include <sys/socket.h> 39151427Sphk#include <netinet/in.h> 40151427Sphk 41151427Sphk#include <netinet/in_systm.h> /* for IP_MAXPACKET */ 42151427Sphk#include <netinet/ip.h> /* for IP_MAXPACKET */ 43151427Sphk 44162011Ssam/* XXX normally defined in config.h */ 45162011Ssam#define HAVE_STRLCPY 1 46162011Ssam#define HAVE_SNPRINTF 1 47162011Ssam#define HAVE_VSNPRINTF 1 48151427Sphk#include <pcap-int.h> /* see pcap(3) and /usr/src/contrib/libpcap/. */ 49151427Sphk 50151427Sphk#ifdef IP_MAXPACKET 51151427Sphk#define BUFMAX IP_MAXPACKET 52151427Sphk#else 53151427Sphk#define BUFMAX 65535 54151427Sphk#endif 55151427Sphk 56151427Sphk#ifndef MAXPATHLEN 57151427Sphk#define MAXPATHLEN 1024 58151427Sphk#endif 59151427Sphk 60151427Sphkstatic int debug = 0; 61151427Sphkstatic int reflect = 0; /* 1 == write packet back to socket. */ 62151427Sphk 63151427Sphkstatic ssize_t totbytes = 0, maxbytes = 0; 64151427Sphkstatic ssize_t totpkts = 0, maxpkts = 0; 65151427Sphk 66193188Sedstatic char *prog = NULL; 67193188Sedstatic char pidfile[MAXPATHLEN]; 68151427Sphk 69151427Sphk/* 70151427Sphk * tidy up. 71151427Sphk */ 72193188Sedstatic void 73193188Sedquit(int sig) 74151427Sphk{ 75151427Sphk (void) unlink(pidfile); 76151427Sphk exit(sig); 77151427Sphk} 78151427Sphk 79151427Sphk/* 80151427Sphk * do the "paper work" 81151427Sphk * - save my own pid in /var/run/$0.{port#}.pid 82151427Sphk */ 83193188Sedstatic void 84193188Sedokay(int pn) 85151427Sphk{ 86193188Sed int fd; 87151427Sphk char *p, numbuf[80]; 88151427Sphk 89151427Sphk if (pidfile[0] == '\0') { 90193188Sed p = rindex(prog, '/'); 91193188Sed p = (p == NULL) ? prog : p + 1; 92151427Sphk 93193188Sed snprintf(pidfile, sizeof pidfile, 94151427Sphk "%s%s.%d.pid", _PATH_VARRUN, p, pn); 95151427Sphk } 96151427Sphk 97151427Sphk fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0644); 98193188Sed if (fd < 0) { 99193188Sed perror(pidfile); 100193188Sed exit(21); 101193188Sed } 102151427Sphk 103193188Sed siginterrupt(SIGTERM, 1); 104193188Sed siginterrupt(SIGHUP, 1); 105193188Sed signal(SIGTERM, quit); 106193188Sed signal(SIGHUP, quit); 107193188Sed signal(SIGINT, quit); 108151427Sphk 109193188Sed snprintf(numbuf, sizeof numbuf, "%d\n", getpid()); 110193188Sed if (write(fd, numbuf, strlen(numbuf)) < 0) { 111193188Sed perror(pidfile); 112193188Sed quit(23); 113193188Sed } 114151427Sphk (void) close(fd); 115151427Sphk} 116151427Sphk 117193188Sedstatic void 118193188Sedusage(void) 119151427Sphk{ 120193188Sed fprintf(stderr, 121193188Sed"\n" 122193188Sed"usage:\n" 123193188Sed" %s [-dr] [-b maxbytes] [-p maxpkts] [-P pidfile] portnum dumpfile\n" 124193188Sed"\n" 125193188Sed"where:\n" 126193188Sed" '-d' = enable debugging messages.\n" 127193188Sed" '-r' = reflect. write packets back to the divert socket.\n" 128193188Sed" (ie. simulate the original intent of \"ipfw tee\").\n" 129193188Sed" '-rr' = indicate that it is okay to quit if packet-count or\n" 130193188Sed" byte-count limits are reached (see the NOTE below\n" 131193188Sed" about what this implies).\n" 132193188Sed" '-b bytcnt' = stop dumping after {bytcnt} bytes.\n" 133193188Sed" '-p pktcnt' = stop dumping after {pktcnt} packets.\n" 134193188Sed" '-P pidfile' = alternate file to store the PID\n" 135193188Sed" (default: /var/run/%s.{portnum}.pid).\n" 136193188Sed"\n" 137193188Sed" portnum = divert(4) socket port number.\n" 138193188Sed" dumpfile = file to write captured packets (tcpdump format).\n" 139193188Sed" (specify '-' to write packets to stdout).\n" 140193188Sed"\n" 141193188Sed"The '-r' option should not be necessary, but because \"ipfw tee\" is broken\n" 142193188Sed"(see BUGS in ipfw(8) for details) this feature can be used along with\n" 143193188Sed"an \"ipfw divert\" rule to simulate the original intent of \"ipfw tee\".\n" 144193188Sed"\n" 145193188Sed"NOTE: With an \"ipfw divert\" rule, diverted packets will silently\n" 146193188Sed" disappear if there is nothing listening to the divert socket.\n" 147193188Sed"\n", prog, prog); 148193188Sed exit(1); 149151427Sphk} 150151427Sphk 151193188Sedint 152193188Sedmain(int ac, char *av[]) 153151427Sphk{ 154151427Sphk int r, sd, portnum, l; 155151427Sphk struct sockaddr_in sin; 156151427Sphk int errflg = 0; 157151427Sphk 158151427Sphk int nfd; 159151427Sphk fd_set rds; 160151427Sphk 161151427Sphk ssize_t nr; 162151427Sphk 163151427Sphk char *dumpf, buf[BUFMAX]; 164151427Sphk 165151427Sphk pcap_t *p; 166151427Sphk pcap_dumper_t *dp; 167151427Sphk struct pcap_pkthdr phd; 168151427Sphk 169151427Sphk prog = av[0]; 170151427Sphk 171151427Sphk while ((r = getopt(ac, av, "drb:p:P:")) != -1) { 172151427Sphk switch (r) { 173151427Sphk case 'd': 174151427Sphk debug++; 175151427Sphk break; 176151427Sphk case 'r': 177151427Sphk reflect++; 178151427Sphk break; 179151427Sphk case 'b': 180151427Sphk maxbytes = (ssize_t) atol(optarg); 181151427Sphk break; 182151427Sphk case 'p': 183151427Sphk maxpkts = (ssize_t) atoi(optarg); 184151427Sphk break; 185151427Sphk case 'P': 186151427Sphk strcpy(pidfile, optarg); 187151427Sphk break; 188151427Sphk case '?': 189151427Sphk default: 190151427Sphk errflg++; 191151427Sphk break; 192151427Sphk } 193151427Sphk } 194151427Sphk 195151427Sphk if ((ac - optind) != 2 || errflg) 196151427Sphk usage(); 197151427Sphk 198151427Sphk portnum = atoi(av[optind++]); 199151427Sphk dumpf = av[optind]; 200151427Sphk 201151427Sphkif (debug) fprintf(stderr, "bind to %d.\ndump to '%s'.\n", portnum, dumpf); 202151427Sphk 203151427Sphk if ((r = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) { 204151427Sphk perror("socket(DIVERT)"); 205151427Sphk exit(2); 206151427Sphk } 207151427Sphk sd = r; 208151427Sphk 209151427Sphk sin.sin_port = htons(portnum); 210151427Sphk sin.sin_family = AF_INET; 211151427Sphk sin.sin_addr.s_addr = INADDR_ANY; 212151427Sphk 213151427Sphk if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { 214151427Sphk perror("bind(divert)"); 215151427Sphk exit(3); 216151427Sphk } 217151427Sphk 218151427Sphk p = pcap_open_dead(DLT_RAW, BUFMAX); 219151427Sphk dp = pcap_dump_open(p, dumpf); 220151427Sphk if (dp == NULL) { 221151427Sphk pcap_perror(p, dumpf); 222151427Sphk exit(4); 223151427Sphk } 224151427Sphk 225151427Sphk okay(portnum); 226151427Sphk 227151427Sphk nfd = sd + 1; 228151427Sphk for (;;) { 229151427Sphk FD_ZERO(&rds); 230151427Sphk FD_SET(sd, &rds); 231151427Sphk 232151427Sphk r = select(nfd, &rds, NULL, NULL, NULL); 233151427Sphk if (r == -1) { 234151427Sphk if (errno == EINTR) continue; 235151427Sphk perror("select"); 236151427Sphk quit(11); 237151427Sphk } 238151427Sphk 239151427Sphk if (!FD_ISSET(sd, &rds)) 240151427Sphk /* hmm. no work. */ 241151427Sphk continue; 242151427Sphk 243151427Sphk /* 244151427Sphk * use recvfrom(3 and sendto(3) as in natd(8). 245151427Sphk * see /usr/src/sbin/natd/natd.c 246151427Sphk * see ipfw(8) about using 'divert' and 'tee'. 247151427Sphk */ 248151427Sphk 249151427Sphk /* 250151427Sphk * read packet. 251151427Sphk */ 252151427Sphk l = sizeof(sin); 253151427Sphk nr = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &l); 254193188Sedif (debug) fprintf(stderr, "recvfrom(%d) = %zd (%d)\n", sd, nr, l); 255151427Sphk if (nr < 0 && errno != EINTR) { 256151427Sphk perror("recvfrom(sd)"); 257151427Sphk quit(12); 258151427Sphk } 259151427Sphk if (nr <= 0) continue; 260151427Sphk 261151427Sphk if (reflect) { 262151427Sphk /* 263151427Sphk * write packet back so it can continue 264151427Sphk * being processed by any further IPFW rules. 265151427Sphk */ 266151427Sphk l = sizeof(sin); 267151427Sphk r = sendto(sd, buf, nr, 0, (struct sockaddr *)&sin, l); 268151427Sphkif (debug) fprintf(stderr, " sendto(%d) = %d\n", sd, r); 269151427Sphk if (r < 0) { perror("sendto(sd)"); quit(13); } 270151427Sphk } 271151427Sphk 272151427Sphk /* 273151427Sphk * check maximums, if any. 274151427Sphk * but don't quit if must continue reflecting packets. 275151427Sphk */ 276151427Sphk if (maxpkts) { 277151427Sphk totpkts++; 278151427Sphk if (totpkts > maxpkts) { 279151427Sphk if (reflect == 1) continue; 280151427Sphk quit(0); 281151427Sphk } 282151427Sphk } 283151427Sphk if (maxbytes) { 284151427Sphk totbytes += nr; 285151427Sphk if (totbytes > maxbytes) { 286151427Sphk if (reflect == 1) continue; 287151427Sphk quit(0); 288151427Sphk } 289151427Sphk } 290151427Sphk 291151427Sphk /* 292151427Sphk * save packet in tcpdump(1) format. see pcap(3). 293151427Sphk * divert packets are fully assembled. see ipfw(8). 294151427Sphk */ 295151427Sphk (void) gettimeofday(&(phd.ts), NULL); 296151427Sphk phd.caplen = phd.len = nr; 297151427Sphk pcap_dump((u_char *)dp, &phd, buf); 298151427Sphk if (ferror((FILE *)dp)) { perror(dumpf); quit(14); } 299151427Sphk (void) fflush((FILE *)dp); 300151427Sphk } 301151427Sphk 302151427Sphk quit(0); 303151427Sphk} 304