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