Update:
[obnox/wireshark/wip.git] / trigcap.c
1 /*
2  * trigcap
3  * a simple triggered libpcap-based capture agent
4  *
5  * $Id$
6  *
7  * (c) 2007, Luis E. Garcia Ontanon <luis@ontanon.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <signal.h>
30 #include <errno.h>
31 #include <getopt.h>
32 #include <pcap.h>
33
34 static int dumping;
35 static volatile int keep_going;
36 static pcap_t* listener;
37 static struct bpf_program stop_filter;
38 static int captured = 0;
39 static int debug_level = 0;
40
41 static void panic(int err, const char* fmt, ...) {
42         va_list ap;
43         va_start(ap,fmt);
44         vfprintf(stderr,fmt,ap);
45         va_end(ap);
46         exit(err);
47 }
48
49 static void dprintf(int lev, const char* fmt, ...) {
50         va_list ap;
51
52         if (lev <= debug_level) {
53                 va_start(ap,fmt);
54                 vfprintf(stderr,fmt,ap);
55                 va_end(ap);
56                 fflush(stderr);
57         }
58 }
59
60
61 static void usage(int err) {
62         const char* usage_str = "usage:\n"
63         "trigcap -w outfile -b begin -e end [-f capture] [-i iface] [-s snaplen] [-p] [-q] [-d [-d [-d [-d]]]]\n"
64         "   -w output file\n"
65         "   -b filter to start capturing\n"
66         "   -e filter to stop capturing\n"
67         "   -f capture filter\n"
68         "   -p promiscuous mode\n"
69         "   -s snapshot length\n"
70         "   -q quiet\n"
71         "   -d increase debug level\n"
72         "   -h prints this message\n"
73         ;
74
75         panic(err,usage_str);
76 }
77
78 static void listener_handler(u_char* u, const struct pcap_pkthdr * ph, const u_char* buf) {
79         char errbuf[PCAP_ERRBUF_SIZE];
80
81         dprintf(2,"listener handler invoked dumping=%d\n",dumping);
82
83         if (dumping) {
84                 dprintf(2,"last round\n");
85                 keep_going = 0;
86         } else {
87                 if (pcap_setfilter(listener, &stop_filter) < 0) {
88                         panic(23,"could not apply stop filter to listener: %s\n",pcap_geterr(listener));
89                 }
90
91                 dprintf(2,"apply stop filter to listener\n");
92
93                 if (pcap_setnonblock(listener, 1, errbuf) < 0) {
94                         panic(24,"could not set listener in non blocking mode: %s\n",errbuf);
95                 }
96
97                 dprintf(2,"listener -> non_blocking\n");
98                 dumping = 1;
99         }
100 }
101
102 static void capture_handler(u_char* dumper, const struct pcap_pkthdr * ph, const u_char* buf) {
103         dprintf(4,"capture handler invoked dumping=%d\n",dumping);
104         if (dumping) {
105                 captured++;
106                 pcap_dump(dumper, ph, buf);
107         }
108 }
109
110 static void sig_int(int sig) {
111         keep_going = 0;
112 }
113
114 int main(int argc, char** argv) {
115         char errbuf[PCAP_ERRBUF_SIZE];
116         char* interface = NULL;
117         char* outfile = NULL;
118         unsigned snaplen = 65536;
119         char* start_filter_str = NULL;
120         char* stop_filter_str = NULL;
121         char* capture_filter_str = NULL;
122         int promisc = 0;
123         int quiet = 0;
124         struct bpf_program start_filter;
125         struct bpf_program capture_filter;
126         pcap_t* capturer = NULL;
127         pcap_dumper_t* dumper = NULL;
128         int opt;
129
130         while ((opt = getopt(argc, argv, "i:w:s:b:e:f:phdq")) != -1) {
131                 switch (opt) {
132                         case 'i':
133                                 if (interface) panic(1,"interface already given");
134                                 interface = g_strdup(optarg);
135                                 break;
136                         case 'w':
137                                 if (outfile) panic(3,"output file already given");
138                                 outfile = g_strdup(optarg);
139                                 break;
140                         case 's':
141                                 snaplen = strtoul(optarg,NULL,10);
142                                 if ( snaplen == 0 )
143                                         panic(4,"invalid snaplen");
144                                 break;
145                         case 'b':
146                                 if (start_filter_str) panic(5,"start filter already given");
147                                 start_filter_str = g_strdup(optarg);
148                                 break;
149                         case 'e':
150                                 if (stop_filter_str) panic(6,"stop filter already given");
151                                 stop_filter_str = g_strdup(optarg);
152                                 break;
153                         case 'f':
154                                 if (capture_filter_str) panic(7,"capture filter already given");
155                                 capture_filter_str = g_strdup(optarg);
156                                 break;
157                         case 'p':
158                                 promisc = 1;
159                                 break;
160                         case 'q':
161                                 quiet = 1;
162                                 break;
163                         case 'd':
164                                 debug_level++;
165                                 break;
166                         case 'h':
167                         default:
168                                 usage(0);
169                                 break;
170                         }
171                 }
172
173         dprintf(1,"starting with:\n interface: %s\n snaplen: %d\n promisc: %d"
174                         "\n outfile: %s\n capture filter: %s\n start: %s\n stop: %s\n debug level: %d\n",
175                         interface ? interface : "to be chosen",
176                         snaplen,
177                         promisc,
178                         outfile ? outfile : "** missing **",
179                         capture_filter_str ? capture_filter_str : "** none given **",
180                         start_filter_str ? start_filter_str : "** missing **",
181                         stop_filter_str ? stop_filter_str : "** missing **",
182                         debug_level);
183
184         if (! ( start_filter_str && stop_filter_str && outfile ) ) {
185                 usage(10);
186         }
187
188         if (! interface) {
189                 interface = pcap_lookupdev(errbuf);
190                 if (!interface) {
191                         panic(11, "could not obtain an interface: %s\n",errbuf);
192                 }
193         }
194
195 #ifdef HAVE_PCAP_OPEN
196         if ( ! ( capturer = pcap_open(interface, snaplen, promisc, 1, NULL, errbuf) )) {
197 #else
198         if ( ! ( capturer = pcap_open_live(interface, snaplen, promisc, 1, errbuf) )) {
199 #endif
200                 panic(12,"could not open interface '%s' for listener: %s\n",interface,errbuf);
201         }
202
203         dprintf(1,"opened listener (%s,%d,%d)\n",interface,snaplen, promisc);
204
205         if (pcap_compile(listener, &start_filter, start_filter_str, 1, 0) < 0) {
206                 panic(13,"could not compile start filter: %s\n",pcap_geterr(listener));
207         }
208
209         dprintf(2,"compiled start filter %s\n",start_filter_str);
210
211         if (pcap_compile(listener, &stop_filter, stop_filter_str, 1, 0) < 0) {
212                 panic(14,"could not compile stop filter: %s\n",pcap_geterr(listener));
213         }
214
215         dprintf(2,"compiled stop filter %s\n",stop_filter_str);
216
217 #ifdef HAVE_PCAP_OPEN
218         if ( ! ( capturer = pcap_open(interface, snaplen, promisc, 1, NULL, errbuf) )) {
219 #else
220         if ( ! ( capturer = pcap_open_live(interface, snaplen, promisc, 1, errbuf) )) {
221 #endif
222                 panic(15,"could not open interface '%s' for capturer: %s\n",interface, errbuf);
223         }
224
225         dprintf(1,"opened capturer (%s,%d,%d)\n",interface,snaplen, promisc);
226
227         if (capture_filter_str) {
228                 if (pcap_compile(capturer, &capture_filter, capture_filter_str, 1, 0) < 0) {
229                         panic(16,"could not compile capture filter: %s\n",pcap_geterr(capturer));
230                 }
231                 if (pcap_setfilter(capturer, &capture_filter) < 0) {
232                         panic(17,"could not apply start filter to capturer: %s\n",pcap_geterr(capturer));
233                 }
234
235                 dprintf(2,"compiled and set capture filter (%s)\n",capture_filter_str);
236         }
237
238         if (pcap_setfilter(listener, &start_filter) < 0) {
239                 panic(18,"could not apply start filter to listener: %s\n",pcap_geterr(listener));
240         }
241         dprintf(2,"set start filter on listener\n");
242
243
244         if (pcap_setnonblock(listener, 0, errbuf) < 0) {
245                 panic(19,"could not set listener in blocking mode: %s\n",errbuf);
246         }
247         dprintf(2,"listener -> blocking\n");
248
249         if (pcap_setnonblock(capturer, 1, errbuf) < 0) {
250                 panic(20,"could not set capturer in non blocking mode: %s\n",errbuf);
251         }
252         dprintf(2,"capturer -> non_blocking\n");
253
254         if (! (dumper = pcap_dump_open(listener,outfile)) ) {
255                 panic(21,"open dumper file '%s': %s\n",outfile,pcap_geterr(listener));
256         }
257         dprintf(2,"opened dumper file '%s'\n",outfile);
258
259         signal(SIGINT, sig_int);
260 #ifdef SIGQUIT
261         signal(SIGQUIT, sig_int);
262 #endif
263 #ifdef SIGTERM
264         signal(SIGTERM, sig_int);
265 #endif
266 #ifdef SIGSTOP
267         signal(SIGSTOP, sig_int);
268 #endif
269
270         keep_going = 1;
271         dumping = 0;
272
273         do {
274                 if (pcap_dispatch(listener, -1, listener_handler, NULL) < 0 ) {
275                         panic(22,"pcap_dispatch(listener) failed: %s\n",pcap_geterr(listener));
276                 }
277
278                 if (pcap_dispatch(capturer, -1, capture_handler, (void*)dumper) < 0 ) {
279                         panic(23,"pcap_dispatch(capturer) failed: %s\n",pcap_geterr(capturer));
280                 }
281         } while(keep_going);
282
283         if (!quiet) {
284                 printf("%d packets captured\n",captured);
285         }
286
287         dprintf(1,"done!\n");
288
289         pcap_dump_close(dumper);
290         pcap_close(listener);
291         pcap_close(capturer);
292
293         return 0;
294 }