Use the host compiler for build tool c-ify.
[darkstat-debian] / cap.c
1 /* darkstat 3
2 * copyright (c) 2001-2014 Emil Mikulic.
3 *
4 * cap.c: capture packets, and hand them off to decode and acct.
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 "acct.h"
11 #include "cdefs.h"
12 #include "cap.h"
13 #include "config.h"
14 #include "conv.h"
15 #include "decode.h"
16 #include "err.h"
17 #include "hosts_db.h"
18 #include "localip.h"
19 #include "now.h"
20 #include "opt.h"
21 #include "queue.h"
22 #include "str.h"
23
24 #include <sys/ioctl.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/wait.h>
28 #ifdef HAVE_SYS_FILIO_H
29 # include <sys/filio.h> /* Solaris' FIONBIO hides here */
30 #endif
31 #include <assert.h>
32 #include <pcap.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 char *title_interfaces = NULL; /* for html.c */
39
40 /* The cap process life-cycle:
41 * - cap_add_ifname() one or more times
42 * - cap_add_filter() zero or more times
43 * - cap_start() once to start listening
44 * Once per main loop:
45 * - cap_fd_set() to update the select() set
46 * - cap_poll() to read from ready pcap fds
47 * Shutdown:
48 * - cap_stop()
49 */
50
51 struct strnode {
52 STAILQ_ENTRY(strnode) entries;
53 const char *str;
54 };
55
56 struct cap_iface {
57 STAILQ_ENTRY(cap_iface) entries;
58
59 const char *name;
60 const char *filter;
61 pcap_t *pcap;
62 int fd;
63 const struct linkhdr *linkhdr;
64 struct local_ips local_ips;
65 };
66
67 static STAILQ_HEAD(cli_ifnames_head, strnode) cli_ifnames =
68 STAILQ_HEAD_INITIALIZER(cli_ifnames);
69
70 static STAILQ_HEAD(cli_filters_head, strnode) cli_filters =
71 STAILQ_HEAD_INITIALIZER(cli_filters);
72
73 static STAILQ_HEAD(cap_ifs_head, cap_iface) cap_ifs =
74 STAILQ_HEAD_INITIALIZER(cap_ifs);
75
76 /* The read timeout passed to pcap_open_live() */
77 #define CAP_TIMEOUT_MSEC 500
78
79 void cap_add_ifname(const char *ifname) {
80 struct strnode *n = xmalloc(sizeof(*n));
81 n->str = ifname;
82 STAILQ_INSERT_TAIL(&cli_ifnames, n, entries);
83 }
84
85 void cap_add_filter(const char *filter) {
86 struct strnode *n = xmalloc(sizeof(*n));
87 n->str = filter;
88 STAILQ_INSERT_TAIL(&cli_filters, n, entries);
89 }
90
91 static void cap_set_filter(pcap_t *pcap, const char *filter) {
92 struct bpf_program prog;
93 char *tmp_filter;
94
95 if (filter == NULL)
96 return;
97
98 tmp_filter = xstrdup(filter);
99 if (pcap_compile(
100 pcap,
101 &prog,
102 tmp_filter,
103 1, /* optimize */
104 0) /* netmask */
105 == -1)
106 errx(1, "pcap_compile(): %s", pcap_geterr(pcap));
107
108 if (pcap_setfilter(pcap, &prog) == -1)
109 errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap));
110
111 pcap_freecode(&prog);
112 free(tmp_filter);
113 }
114
115 /* Start capturing on just one interface. Called from cap_start(). */
116 static void cap_start_one(struct cap_iface *iface, const int promisc) {
117 char errbuf[PCAP_ERRBUF_SIZE], *tmp_device;
118 int linktype, snaplen, waited;
119
120 /* pcap wants a non-const interface name string */
121 tmp_device = xstrdup(iface->name);
122 if (iface->filter)
123 verbosef("capturing on interface '%s' with filter '%s'",
124 tmp_device, iface->filter);
125 else
126 verbosef("capturing on interface '%s' with no filter", tmp_device);
127
128 /* Open packet capture descriptor. */
129 waited = 0;
130 for (;;) {
131 errbuf[0] = '\0'; /* zero length string */
132 iface->pcap = pcap_open_live(
133 tmp_device,
134 1, /* snaplen, irrelevant at this point */
135 0, /* promisc, also irrelevant */
136 CAP_TIMEOUT_MSEC,
137 errbuf);
138 if (iface->pcap != NULL)
139 break; /* success! */
140
141 if ((opt_wait_secs != -1) && strstr(errbuf, "device is not up")) {
142 if ((opt_wait_secs > 0) && (waited >= opt_wait_secs))
143 errx(1, "waited %d secs, giving up: pcap_open_live(): %s",
144 waited, errbuf);
145
146 verbosef("waited %d secs, interface is not up", waited);
147 sleep(1);
148 waited++;
149 }
150 else errx(1, "pcap_open_live(): %s", errbuf);
151 }
152
153 /* Work out the linktype and what snaplen we need. */
154 linktype = pcap_datalink(iface->pcap);
155 verbosef("linktype is %d", linktype);
156 if ((linktype == DLT_EN10MB) && opt_want_macs)
157 hosts_db_show_macs = 1;
158 iface->linkhdr = getlinkhdr(linktype);
159 if (iface->linkhdr == NULL)
160 errx(1, "unknown linktype %d", linktype);
161 if (iface->linkhdr->decoder == NULL)
162 errx(1, "no decoder for linktype %d", linktype);
163 snaplen = getsnaplen(iface->linkhdr);
164 if (opt_want_pppoe) {
165 snaplen += PPPOE_HDR_LEN;
166 if (linktype != DLT_EN10MB)
167 errx(1, "can't do PPPoE decoding on a non-Ethernet linktype");
168 }
169 verbosef("calculated snaplen minimum %d", snaplen);
170 #ifdef linux
171 /* FIXME: actually due to libpcap moving to mmap (!!!)
172 * work out which version and fix the way we do capture
173 * on linux:
174 */
175
176 /* Ubuntu 9.04 has a problem where requesting snaplen <= 60 will
177 * give us 42 bytes, and we need at least 54 for TCP headers.
178 *
179 * Hack to set minimum snaplen to tcpdump's default:
180 */
181 snaplen = MAX(snaplen, 96);
182 #endif
183 if (opt_want_snaplen > -1)
184 snaplen = opt_want_snaplen;
185 verbosef("using snaplen %d", snaplen);
186
187 /* Close and re-open pcap to use the new snaplen. */
188 pcap_close(iface->pcap);
189 errbuf[0] = '\0'; /* zero length string */
190 iface->pcap = pcap_open_live(
191 tmp_device,
192 snaplen,
193 promisc,
194 CAP_TIMEOUT_MSEC,
195 errbuf);
196
197 if (iface->pcap == NULL)
198 errx(1, "pcap_open_live(): %s", errbuf);
199
200 if (errbuf[0] != '\0') /* not zero length anymore -> warning */
201 warnx("pcap_open_live() warning: %s", errbuf);
202
203 free(tmp_device);
204
205 if (promisc)
206 verbosef("capturing in promiscuous mode");
207 else
208 verbosef("capturing in non-promiscuous mode");
209
210 cap_set_filter(iface->pcap, iface->filter);
211 iface->fd = pcap_fileno(iface->pcap);
212
213 /* set non-blocking */
214 #ifdef linux
215 if (pcap_setnonblock(iface->pcap, 1, errbuf) == -1)
216 errx(1, "pcap_setnonblock(): %s", errbuf);
217 #else
218 {
219 int one = 1;
220 if (ioctl(iface->fd, FIONBIO, &one) == -1)
221 err(1, "ioctl(iface->fd, FIONBIO)");
222 }
223 #endif
224
225 #ifdef BIOCSETWF
226 {
227 /* Deny all writes to the socket */
228 struct bpf_insn bpf_wfilter[] = { BPF_STMT(BPF_RET+BPF_K, 0) };
229 int wf_len = sizeof(bpf_wfilter) / sizeof(struct bpf_insn);
230 struct bpf_program pr;
231
232 pr.bf_len = wf_len;
233 pr.bf_insns = bpf_wfilter;
234
235 if (ioctl(iface->fd, BIOCSETWF, &pr) == -1)
236 err(1, "ioctl(iface->fd, BIOCSETFW)");
237 verbosef("filtered out BPF writes");
238 }
239 #endif
240
241 #ifdef BIOCLOCK
242 /* set "locked" flag (no reset) */
243 if (ioctl(iface->fd, BIOCLOCK) == -1)
244 err(1, "ioctl(iface->fd, BIOCLOCK)");
245 verbosef("locked down BPF for security");
246 #endif
247 }
248
249 void cap_start(const int promisc) {
250 struct str *ifs = str_make();
251
252 assert(STAILQ_EMPTY(&cap_ifs));
253 if (STAILQ_EMPTY(&cli_ifnames))
254 errx(1, "no interfaces specified");
255
256 /* For each ifname */
257 while (!STAILQ_EMPTY(&cli_ifnames)) {
258 struct strnode *ifname, *filter = NULL;
259 struct cap_iface *iface = xmalloc(sizeof(*iface));
260
261 ifname = STAILQ_FIRST(&cli_ifnames);
262 STAILQ_REMOVE_HEAD(&cli_ifnames, entries);
263
264 if (!STAILQ_EMPTY(&cli_filters)) {
265 filter = STAILQ_FIRST(&cli_filters);
266 STAILQ_REMOVE_HEAD(&cli_filters, entries);
267 }
268
269 iface->name = ifname->str;
270 iface->filter = (filter == NULL) ? NULL : filter->str;
271 iface->pcap = NULL;
272 iface->fd = -1;
273 iface->linkhdr = NULL;
274 localip_init(&iface->local_ips);
275 STAILQ_INSERT_TAIL(&cap_ifs, iface, entries);
276 cap_start_one(iface, promisc);
277
278 free(ifname);
279 if (filter) free(filter);
280
281 if (str_len(ifs) == 0)
282 str_append(ifs, iface->name);
283 else
284 str_appendf(ifs, ", %s", iface->name);
285 }
286 verbosef("all capture interfaces prepared");
287
288 /* Deallocate extra filters, if any. */
289 while (!STAILQ_EMPTY(&cli_filters)) {
290 struct strnode *filter = STAILQ_FIRST(&cli_filters);
291
292 verbosef("ignoring extraneous filter '%s'", filter->str);
293 STAILQ_REMOVE_HEAD(&cli_filters, entries);
294 free(filter);
295 }
296
297 str_appendn(ifs, "", 1); /* NUL terminate */
298 {
299 size_t _;
300 str_extract(ifs, &_, &title_interfaces);
301 }
302 }
303
304 #ifdef linux
305 # define _unused_on_linux_ _unused_
306 # define _unused_otherwise_
307 #else
308 # define _unused_on_linux_
309 # define _unused_otherwise_ _unused_
310 #endif
311
312 /*
313 * Set pcap_fd in the given fd_set.
314 */
315 void cap_fd_set(fd_set *read_set _unused_on_linux_,
316 int *max_fd _unused_on_linux_,
317 struct timeval *timeout _unused_otherwise_,
318 int *need_timeout) {
319 assert(*need_timeout == 0); /* we're first to get a shot at the fd_set */
320
321 #ifdef linux
322 /*
323 * Linux's BPF is immediate, so don't select() as it will lead to horrible
324 * performance. Instead, use a timeout for buffering.
325 */
326 *need_timeout = 1;
327 timeout->tv_sec = 0;
328 timeout->tv_usec = CAP_TIMEOUT_MSEC * 1000;
329 #else
330 {
331 struct cap_iface *iface;
332 STAILQ_FOREACH(iface, &cap_ifs, entries) {
333 /* We have a BSD-like BPF, we can select() on it. */
334 FD_SET(iface->fd, read_set);
335 *max_fd = MAX(*max_fd, iface->fd);
336 }
337 }
338 #endif
339 }
340
341 unsigned int cap_pkts_recv = 0, cap_pkts_drop = 0;
342
343 static void cap_stats_update(void) {
344 struct cap_iface *iface;
345
346 cap_pkts_recv = 0;
347 cap_pkts_drop = 0;
348 STAILQ_FOREACH(iface, &cap_ifs, entries) {
349 struct pcap_stat ps;
350 if (pcap_stats(iface->pcap, &ps) != 0) {
351 warnx("pcap_stats('%s'): %s", iface->name, pcap_geterr(iface->pcap));
352 return;
353 }
354 cap_pkts_recv += ps.ps_recv;
355 cap_pkts_drop += ps.ps_drop;
356 }
357 }
358
359 /* Print hexdump of received packet to stdout, for debugging. */
360 static void hexdump(const u_char *buf,
361 const uint32_t len,
362 const struct linkhdr *linkhdr) {
363 uint32_t i, col;
364
365 printf("packet of %u bytes:\n", len);
366 for (i=0, col=0; i<len; i++) {
367 if (col == 0) printf(" ");
368 printf("%02x", buf[i]);
369 if (i+1 == linkhdr->hdrlen)
370 printf("|"); /* marks end of link headers (e.g. ethernet) */
371 else
372 printf(" ");
373 col += 3;
374 if (col >= 72) {
375 printf("\n");
376 col = 0;
377 }
378 }
379 if (col != 0) printf("\n");
380 printf("\n");
381 }
382
383 /* Callback function for pcap_dispatch() which chains to the decoder specified
384 * in the linkhdr struct.
385 */
386 static void callback(u_char *user,
387 const struct pcap_pkthdr *pheader,
388 const u_char *pdata) {
389 const struct cap_iface * const iface = (struct cap_iface *)user;
390 struct pktsummary sm;
391
392 if (opt_want_hexdump)
393 hexdump(pdata, pheader->caplen, iface->linkhdr);
394 memset(&sm, 0, sizeof(sm));
395 if (iface->linkhdr->decoder(pheader, pdata, &sm))
396 acct_for(&sm, &iface->local_ips);
397 }
398
399 /* Process any packets currently in the capture buffer.
400 * Returns 0 on error (usually means the interface went down).
401 */
402 int cap_poll(fd_set *read_set _unused_on_linux_) {
403 struct cap_iface *iface;
404 static int told = 0;
405
406 STAILQ_FOREACH(iface, &cap_ifs, entries) {
407 /* Once per capture poll, check our IP address. It's used in accounting
408 * for traffic graphs.
409 */
410 localip_update(iface->name, &iface->local_ips);
411 if (!told && iface->local_ips.num_addrs == 0) {
412 verbosef("interface '%s' has no addresses, "
413 "your graphs will be blank",
414 iface->name);
415 verbosef("please read the darkstat manpage, "
416 "and consider using the -l option");
417 told = 1;
418 }
419
420 for (;;) {
421 struct timespec t;
422 int ret;
423
424 timer_start(&t);
425 ret = pcap_dispatch(
426 iface->pcap,
427 -1, /* count = entire buffer */
428 callback,
429 (u_char*)iface); /* user = struct to pass to callback */
430 timer_stop(&t,
431 2 * CAP_TIMEOUT_MSEC * 1000000,
432 "pcap_dispatch took too long");
433
434 if (ret < 0) {
435 warnx("pcap_dispatch('%s'): %s",
436 iface->name, pcap_geterr(iface->pcap));
437 return 0;
438 }
439
440 #if 0 /* debugging */
441 verbosef("iface '%s' got %d pkts", iface->name, ret);
442 #endif
443
444 #ifdef linux
445 /* keep looping until we've dispatched all the outstanding packets */
446 if (ret == 0)
447 break;
448 #else
449 /* we get them all on the first shot */
450 break;
451 #endif
452 }
453 }
454 cap_stats_update();
455 return 1;
456 }
457
458 void cap_stop(void) {
459 while (!STAILQ_EMPTY(&cap_ifs)) {
460 struct cap_iface *iface = STAILQ_FIRST(&cap_ifs);
461
462 STAILQ_REMOVE_HEAD(&cap_ifs, entries);
463 pcap_close(iface->pcap);
464 localip_free(&iface->local_ips);
465 free(iface);
466 }
467 free(title_interfaces);
468 title_interfaces = NULL;
469 }
470
471 /* This is only needed by the DNS child. In the main process, the deallocation
472 * happens in cap_start().
473 */
474 void cap_free_args(void) {
475 while (!STAILQ_EMPTY(&cli_ifnames)) {
476 struct strnode *ifname = STAILQ_FIRST(&cli_ifnames);
477 STAILQ_REMOVE_HEAD(&cli_ifnames, entries);
478 free(ifname);
479 }
480
481 while (!STAILQ_EMPTY(&cli_filters)) {
482 struct strnode *filter = STAILQ_FIRST(&cli_filters);
483 STAILQ_REMOVE_HEAD(&cli_filters, entries);
484 free(filter);
485 }
486 }
487
488 /* Run through entire capfile. */
489 void cap_from_file(const char *capfile) {
490 char errbuf[PCAP_ERRBUF_SIZE];
491 int linktype, ret;
492 struct cap_iface iface;
493
494 iface.name = NULL;
495 iface.filter = NULL;
496 iface.pcap = NULL;
497 iface.fd = -1;
498 iface.linkhdr = NULL;
499 localip_init(&iface.local_ips);
500
501 /* Process cmdline filters. */
502 if (!STAILQ_EMPTY(&cli_filters))
503 iface.filter = STAILQ_FIRST(&cli_filters)->str;
504 while (!STAILQ_EMPTY(&cli_filters)) {
505 struct strnode *n = STAILQ_FIRST(&cli_filters);
506 STAILQ_REMOVE_HEAD(&cli_filters, entries);
507 free(n);
508 }
509
510 /* Open packet capture descriptor. */
511 errbuf[0] = '\0'; /* zero length string */
512 iface.pcap = pcap_open_offline(capfile, errbuf);
513
514 if (iface.pcap == NULL)
515 errx(1, "pcap_open_offline(): %s", errbuf);
516
517 if (errbuf[0] != '\0') /* not zero length anymore -> warning */
518 warnx("pcap_open_offline() warning: %s", errbuf);
519
520 /* Work out the linktype. */
521 linktype = pcap_datalink(iface.pcap);
522 iface.linkhdr = getlinkhdr(linktype);
523 if (iface.linkhdr == NULL)
524 errx(1, "unknown linktype %d", linktype);
525 if (iface.linkhdr->decoder == NULL)
526 errx(1, "no decoder for linktype %d", linktype);
527
528 cap_set_filter(iface.pcap, iface.filter);
529
530 /* Process file. */
531 ret = pcap_dispatch(
532 iface.pcap,
533 -1, /* count, -1 = entire buffer */
534 callback,
535 (u_char*)&iface); /* user */
536
537 if (ret < 0)
538 errx(1, "pcap_dispatch(): %s", pcap_geterr(iface.pcap));
539
540 localip_free(&iface.local_ips);
541 pcap_close(iface.pcap);
542 }
543
544 /* vim:set ts=3 sw=3 tw=78 expandtab: */