Be explicit that per-host pages are directories.
[darkstat.git] / 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 "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: */