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