Prefix all global variables to aid debugging.
[darkstat] / cap.c
1 /* darkstat 3
2 * copyright (c) 2001-2010 Emil Mikulic.
3 *
4 * cap.c: interface to libpcap.
5 *
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
8 */
9
10 #include "darkstat.h"
11 #include "cap.h"
12 #include "conv.h"
13 #include "decode.h"
14 #include "hosts_db.h"
15 #include "localip.h"
16
17 #include <sys/ioctl.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/wait.h>
21 #ifdef HAVE_SYS_FILIO_H
22 # include <sys/filio.h> /* Solaris' FIONBIO hides here */
23 #endif
24 #include <assert.h>
25 #include "err.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 /* The cap process life-cycle:
32 *
33 * Init - cap_init()
34 * Fill fd_set - cap_fd_set()
35 * Poll - cap_poll()
36 * Stop - cap_stop()
37 */
38
39 /* Globals - only useful within this module. */
40 static pcap_t *pcap = NULL;
41 static int pcap_fd = -1;
42 static const struct linkhdr *linkhdr = NULL;
43
44 #define CAP_TIMEOUT 500 /* granularity of capture buffer, in milliseconds */
45
46 /* ---------------------------------------------------------------------------
47 * Init pcap. Exits on failure.
48 */
49 void
50 cap_init(const char *device, const char *filter, int promisc)
51 {
52 char errbuf[PCAP_ERRBUF_SIZE], *tmp_device;
53 int linktype, snaplen, waited;
54
55 /* pcap doesn't like device being const */
56 tmp_device = xstrdup(device);
57
58 /* Open packet capture descriptor. */
59 waited = 0;
60 for (;;) {
61 errbuf[0] = '\0'; /* zero length string */
62 pcap = pcap_open_live(
63 tmp_device,
64 1, /* snaplen, irrelevant at this point */
65 0, /* promisc, also irrelevant */
66 CAP_TIMEOUT,
67 errbuf);
68 if (pcap != NULL) break; /* success! */
69
70 if ((opt_wait_secs != -1) && strstr(errbuf, "device is not up")) {
71 if ((opt_wait_secs > 0) && (waited >= opt_wait_secs))
72 errx(1, "waited %d secs, giving up: pcap_open_live(): %s",
73 waited, errbuf);
74
75 verbosef("waited %d secs, interface is not up", waited);
76 sleep(1);
77 waited++;
78 }
79 else errx(1, "pcap_open_live(): %s", errbuf);
80 }
81
82 /* Work out the linktype and what snaplen we need. */
83 linktype = pcap_datalink(pcap);
84 verbosef("linktype is %d", linktype);
85 if ((linktype == DLT_EN10MB) && opt_want_macs)
86 hosts_db_show_macs = 1;
87 linkhdr = getlinkhdr(linktype);
88 if (linkhdr == NULL)
89 errx(1, "unknown linktype %d", linktype);
90 if (linkhdr->handler == NULL)
91 errx(1, "no handler for linktype %d", linktype);
92 snaplen = getsnaplen(linkhdr);
93 if (opt_want_pppoe) {
94 snaplen += PPPOE_HDR_LEN;
95 if (linktype != DLT_EN10MB)
96 errx(1, "can't do PPPoE decoding on a non-Ethernet linktype");
97 }
98 verbosef("calculated snaplen minimum %d", snaplen);
99 #ifdef linux
100 /* Ubuntu 9.04 has a problem where requesting snaplen <= 60 will
101 * give us 42 bytes, and we need at least 54 for TCP headers.
102 *
103 * Hack to set minimum snaplen to tcpdump's default:
104 */
105 snaplen = max(snaplen, 96);
106 #endif
107 if (opt_want_snaplen > -1)
108 snaplen = opt_want_snaplen;
109 verbosef("using snaplen %d", snaplen);
110
111 /* Close and re-open pcap to use the new snaplen. */
112 pcap_close(pcap);
113 errbuf[0] = '\0'; /* zero length string */
114 pcap = pcap_open_live(
115 tmp_device,
116 snaplen,
117 promisc,
118 CAP_TIMEOUT,
119 errbuf);
120
121 if (pcap == NULL)
122 errx(1, "pcap_open_live(): %s", errbuf);
123
124 if (errbuf[0] != '\0') /* not zero length anymore -> warning */
125 warnx("pcap_open_live() warning: %s", errbuf);
126
127 free(tmp_device);
128
129 if (promisc)
130 verbosef("capturing in promiscuous mode");
131 else
132 verbosef("capturing in non-promiscuous mode");
133
134 /* Set filter expression, if any. */
135 if (filter != NULL)
136 {
137 struct bpf_program prog;
138 char *tmp_filter = xstrdup(filter);
139 if (pcap_compile(
140 pcap,
141 &prog,
142 tmp_filter,
143 1, /* optimize */
144 0) /* netmask */
145 == -1)
146 errx(1, "pcap_compile(): %s", pcap_geterr(pcap));
147
148 if (pcap_setfilter(pcap, &prog) == -1)
149 errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap));
150
151 pcap_freecode(&prog);
152 free(tmp_filter);
153 }
154
155 pcap_fd = pcap_fileno(pcap);
156
157 /* set non-blocking */
158 #ifdef linux
159 if (pcap_setnonblock(pcap, 1, errbuf) == -1)
160 errx(1, "pcap_setnonblock(): %s", errbuf);
161 #else
162 { int one = 1;
163 if (ioctl(pcap_fd, FIONBIO, &one) == -1)
164 err(1, "ioctl(pcap_fd, FIONBIO)"); }
165 #endif
166
167 #ifdef BIOCSETWF
168 {
169 /* Deny all writes to the socket */
170 struct bpf_insn bpf_wfilter[] = { BPF_STMT(BPF_RET+BPF_K, 0) };
171 int wf_len = sizeof(bpf_wfilter) / sizeof(struct bpf_insn);
172 struct bpf_program pr;
173
174 pr.bf_len = wf_len;
175 pr.bf_insns = bpf_wfilter;
176
177 if (ioctl(pcap_fd, BIOCSETWF, &pr) == -1)
178 err(1, "ioctl(pcap_fd, BIOCSETFW)");
179 verbosef("filtered out BPF writes");
180 }
181 #endif
182
183 #ifdef BIOCLOCK
184 /* set "locked" flag (no reset) */
185 if (ioctl(pcap_fd, BIOCLOCK) == -1)
186 err(1, "ioctl(pcap_fd, BIOCLOCK)");
187 verbosef("locked down BPF for security");
188 #endif
189 }
190
191 /*
192 * Set pcap_fd in the given fd_set.
193 */
194 void
195 cap_fd_set(
196 #ifdef linux
197 fd_set *read_set _unused_,
198 int *max_fd _unused_,
199 struct timeval *timeout,
200 #else
201 fd_set *read_set,
202 int *max_fd,
203 struct timeval *timeout _unused_,
204 #endif
205 int *need_timeout)
206 {
207 assert(*need_timeout == 0); /* we're first to get a shot at this */
208 #ifdef linux
209 /*
210 * Linux's BPF is immediate, so don't select() as it will lead to horrible
211 * performance. Instead, use a timeout for buffering.
212 */
213 *need_timeout = 1;
214 timeout->tv_sec = 0;
215 timeout->tv_usec = CAP_TIMEOUT * 1000; /* msec->usec */
216 #else
217 /* We have a BSD-like BPF, we can select() on it. */
218 FD_SET(pcap_fd, read_set);
219 *max_fd = max(*max_fd, pcap_fd);
220 #endif
221 }
222
223 unsigned int cap_pkts_recv = 0, cap_pkts_drop = 0;
224
225 static void
226 cap_stats_update(void)
227 {
228 struct pcap_stat ps;
229
230 if (pcap_stats(pcap, &ps) != 0) {
231 warnx("pcap_stats(): %s", pcap_geterr(pcap));
232 return;
233 }
234
235 cap_pkts_recv = ps.ps_recv;
236 cap_pkts_drop = ps.ps_drop;
237 }
238
239 /*
240 * Print hexdump of received packet.
241 */
242 static void
243 hexdump(const u_char *buf, const uint32_t len)
244 {
245 uint32_t i, col;
246
247 printf("packet of %u bytes:\n", len);
248 for (i=0, col=0; i<len; i++) {
249 if (col == 0) printf(" ");
250 printf("%02x", buf[i]);
251 if (i+1 == linkhdr->hdrlen)
252 printf("[");
253 else if (i+1 == linkhdr->hdrlen + IP_HDR_LEN)
254 printf("]");
255 else printf(" ");
256 col += 3;
257 if (col >= 72) {
258 printf("\n");
259 col = 0;
260 }
261 }
262 if (col != 0) printf("\n");
263 printf("\n");
264 }
265
266 /*
267 * Callback function for pcap_dispatch() which chains to the decoder specified
268 * in linkhdr struct.
269 */
270 static void
271 callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
272 {
273 if (opt_want_hexdump) hexdump(bytes, h->caplen);
274 linkhdr->handler(user, h, bytes);
275 }
276
277 /*
278 * Process any packets currently in the capture buffer.
279 */
280 void
281 cap_poll(fd_set *read_set
282 #ifdef linux
283 _unused_
284 #endif
285 )
286 {
287 int total, ret;
288
289 #ifndef linux /* We don't use select() on Linux. */
290 if (!FD_ISSET(pcap_fd, read_set)) {
291 verbosef("cap_poll premature");
292 return;
293 }
294 #endif
295
296 /*
297 * Once per capture poll, check our IP address. It's used in accounting
298 * for traffic graphs.
299 */
300 localip_update(); /* FIXME: this might even be too often */
301
302 total = 0;
303 for (;;) {
304 #ifndef NDEBUG
305 struct timeval t1;
306 gettimeofday(&t1, NULL);
307 #endif
308 ret = pcap_dispatch(
309 pcap,
310 -1, /* count, -1 = entire buffer */
311 callback,
312 NULL); /* user */
313
314 if (ret < 0) {
315 warnx("pcap_dispatch(): %s", pcap_geterr(pcap));
316 return;
317 }
318
319 #ifndef NDEBUG
320 {
321 struct timeval t2;
322 int td;
323
324 gettimeofday(&t2, NULL);
325 td = (t2.tv_sec - t1.tv_sec) * 1000000 + t2.tv_usec - t1.tv_usec;
326 if (td > CAP_TIMEOUT*1000)
327 warnx("pcap_dispatch blocked for %d usec! (expected <= %d usec)\n",
328 td, CAP_TIMEOUT*1000);
329 }
330 #endif
331
332 /* Despite count = -1, Linux will only dispatch one packet at a time. */
333 total += ret;
334
335 #ifdef linux
336 /* keep looping until we've dispatched all the outstanding packets */
337 if (ret == 0) break;
338 #else
339 /* we get them all on the first shot */
340 break;
341 #endif
342 }
343 cap_stats_update();
344 }
345
346 void
347 cap_stop(void)
348 {
349 pcap_close(pcap);
350 }
351
352 /* Run through entire capfile. */
353 void
354 cap_from_file(const char *capfile, const char *filter)
355 {
356 char errbuf[PCAP_ERRBUF_SIZE];
357 int linktype, ret;
358
359 /* Open packet capture descriptor. */
360 errbuf[0] = '\0'; /* zero length string */
361 pcap = pcap_open_offline(capfile, errbuf);
362
363 if (pcap == NULL)
364 errx(1, "pcap_open_offline(): %s", errbuf);
365
366 if (errbuf[0] != '\0') /* not zero length anymore -> warning */
367 warnx("pcap_open_offline() warning: %s", errbuf);
368
369 /* Work out the linktype. */
370 linktype = pcap_datalink(pcap);
371 linkhdr = getlinkhdr(linktype);
372 if (linkhdr == NULL)
373 errx(1, "unknown linktype %d", linktype);
374 if (linkhdr->handler == NULL)
375 errx(1, "no handler for linktype %d", linktype);
376 if (linktype == DLT_EN10MB) /* FIXME: impossible with capfile? */
377 hosts_db_show_macs = 1;
378
379 /* Set filter expression, if any. */ /* FIXME: factor! */
380 if (filter != NULL)
381 {
382 struct bpf_program prog;
383 char *tmp_filter = xstrdup(filter);
384 if (pcap_compile(
385 pcap,
386 &prog,
387 tmp_filter,
388 1, /* optimize */
389 0) /* netmask */
390 == -1)
391 errx(1, "pcap_compile(): %s", pcap_geterr(pcap));
392
393 if (pcap_setfilter(pcap, &prog) == -1)
394 errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap));
395
396 pcap_freecode(&prog);
397 free(tmp_filter);
398 }
399
400 /* Process file. */
401 ret = pcap_dispatch(
402 pcap,
403 -1, /* count, -1 = entire buffer */
404 callback,
405 NULL); /* user */
406
407 if (ret < 0)
408 errx(1, "pcap_dispatch(): %s", pcap_geterr(pcap));
409 }
410
411 /* vim:set ts=3 sw=3 tw=78 expandtab: */