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