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