/* darkstat 3
- * copyright (c) 2001-2008 Emil Mikulic.
+ * copyright (c) 2001-2011 Emil Mikulic.
*
* cap.c: interface to libpcap.
*
* GNU General Public License version 2. (see COPYING.GPL)
*/
-#include "darkstat.h"
+#include "cdefs.h"
#include "cap.h"
+#include "config.h"
#include "conv.h"
#include "decode.h"
#include "hosts_db.h"
#include "localip.h"
+#include "opt.h"
#include <sys/ioctl.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
-extern int want_pppoe, want_macs;
-
/* The cap process life-cycle:
*
* Init - cap_init()
/* Globals - only useful within this module. */
static pcap_t *pcap = NULL;
static int pcap_fd = -1;
-static const linkhdr_t *linkhdr = NULL;
+static const struct linkhdr *linkhdr = NULL;
#define CAP_TIMEOUT 500 /* granularity of capture buffer, in milliseconds */
cap_init(const char *device, const char *filter, int promisc)
{
char errbuf[PCAP_ERRBUF_SIZE], *tmp_device;
- int linktype, caplen;
+ int linktype, snaplen, waited;
/* pcap doesn't like device being const */
tmp_device = xstrdup(device);
/* Open packet capture descriptor. */
- errbuf[0] = '\0'; /* zero length string */
- pcap = pcap_open_live(
- tmp_device,
- 1, /* snaplen, irrelevant at this point */
- 0, /* promisc, also irrelevant */
- CAP_TIMEOUT,
- errbuf);
-
- if (pcap == NULL)
- errx(1, "pcap_open_live(): %s", errbuf);
+ waited = 0;
+ for (;;) {
+ errbuf[0] = '\0'; /* zero length string */
+ pcap = pcap_open_live(
+ tmp_device,
+ 1, /* snaplen, irrelevant at this point */
+ 0, /* promisc, also irrelevant */
+ CAP_TIMEOUT,
+ errbuf);
+ if (pcap != NULL) break; /* success! */
+
+ if ((opt_wait_secs != -1) && strstr(errbuf, "device is not up")) {
+ if ((opt_wait_secs > 0) && (waited >= opt_wait_secs))
+ errx(1, "waited %d secs, giving up: pcap_open_live(): %s",
+ waited, errbuf);
+
+ verbosef("waited %d secs, interface is not up", waited);
+ sleep(1);
+ waited++;
+ }
+ else errx(1, "pcap_open_live(): %s", errbuf);
+ }
- /* Work out the linktype and what caplen it needs. */
+ /* Work out the linktype and what snaplen we need. */
linktype = pcap_datalink(pcap);
verbosef("linktype is %d", linktype);
- if ((linktype == DLT_EN10MB) && want_macs)
- show_mac_addrs = 1;
+ if ((linktype == DLT_EN10MB) && opt_want_macs)
+ hosts_db_show_macs = 1;
linkhdr = getlinkhdr(linktype);
if (linkhdr == NULL)
errx(1, "unknown linktype %d", linktype);
if (linkhdr->handler == NULL)
errx(1, "no handler for linktype %d", linktype);
- caplen = getcaplen(linkhdr);
- if (want_pppoe) {
- caplen += PPPOE_HDR_LEN;
+ snaplen = getsnaplen(linkhdr);
+ if (opt_want_pppoe) {
+ snaplen += PPPOE_HDR_LEN;
if (linktype != DLT_EN10MB)
errx(1, "can't do PPPoE decoding on a non-Ethernet linktype");
}
- verbosef("caplen is %d", caplen);
+ verbosef("calculated snaplen minimum %d", snaplen);
+#ifdef linux
+ /* Ubuntu 9.04 has a problem where requesting snaplen <= 60 will
+ * give us 42 bytes, and we need at least 54 for TCP headers.
+ *
+ * Hack to set minimum snaplen to tcpdump's default:
+ */
+ snaplen = MAX(snaplen, 96);
+#endif
+ if (opt_want_snaplen > -1)
+ snaplen = opt_want_snaplen;
+ verbosef("using snaplen %d", snaplen);
- /* Close and re-open pcap to use the new caplen. */
+ /* Close and re-open pcap to use the new snaplen. */
pcap_close(pcap);
errbuf[0] = '\0'; /* zero length string */
pcap = pcap_open_live(
tmp_device,
- caplen, /* snaplen */
+ snaplen,
promisc,
CAP_TIMEOUT,
errbuf);
pcap_fd = pcap_fileno(pcap);
/* set non-blocking */
+#ifdef linux
+ if (pcap_setnonblock(pcap, 1, errbuf) == -1)
+ errx(1, "pcap_setnonblock(): %s", errbuf);
+#else
{ int one = 1;
if (ioctl(pcap_fd, FIONBIO, &one) == -1)
err(1, "ioctl(pcap_fd, FIONBIO)"); }
+#endif
#ifdef BIOCSETWF
{
#else
/* We have a BSD-like BPF, we can select() on it. */
FD_SET(pcap_fd, read_set);
- *max_fd = max(*max_fd, pcap_fd);
+ *max_fd = MAX(*max_fd, pcap_fd);
#endif
}
-unsigned int pkts_recv = 0, pkts_drop = 0;
+unsigned int cap_pkts_recv = 0, cap_pkts_drop = 0;
static void
cap_stats_update(void)
return;
}
- pkts_recv = ps.ps_recv;
- pkts_drop = ps.ps_drop;
+ cap_pkts_recv = ps.ps_recv;
+ cap_pkts_drop = ps.ps_drop;
}
+/*
+ * Print hexdump of received packet.
+ */
+static void
+hexdump(const u_char *buf, const uint32_t len)
+{
+ uint32_t i, col;
+
+ printf("packet of %u bytes:\n", len);
+ for (i=0, col=0; i<len; i++) {
+ if (col == 0) printf(" ");
+ printf("%02x", buf[i]);
+ if (i+1 == linkhdr->hdrlen)
+ printf("[");
+ else if (i+1 == linkhdr->hdrlen + IP_HDR_LEN)
+ printf("]");
+ else printf(" ");
+ col += 3;
+ if (col >= 72) {
+ printf("\n");
+ col = 0;
+ }
+ }
+ if (col != 0) printf("\n");
+ printf("\n");
+}
+
+/*
+ * Callback function for pcap_dispatch() which chains to the decoder specified
+ * in linkhdr struct.
+ */
+static void
+callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
+{
+ if (opt_want_hexdump) hexdump(bytes, h->caplen);
+ linkhdr->handler(user, h, bytes);
+}
/*
* Process any packets currently in the capture buffer.
total = 0;
for (;;) {
+#ifndef NDEBUG
+ struct timeval t1;
+ gettimeofday(&t1, NULL);
+#endif
ret = pcap_dispatch(
pcap,
-1, /* count, -1 = entire buffer */
- linkhdr->handler, /* callback func from decode.c */
+ callback,
NULL); /* user */
if (ret < 0) {
return;
}
+#ifndef NDEBUG
+ {
+ struct timeval t2;
+ int td;
+
+ gettimeofday(&t2, NULL);
+ td = (t2.tv_sec - t1.tv_sec) * 1000000 + t2.tv_usec - t1.tv_usec;
+ if (td > CAP_TIMEOUT*1000)
+ warnx("pcap_dispatch blocked for %d usec! (expected <= %d usec)\n",
+ td, CAP_TIMEOUT*1000);
+ }
+#endif
+
/* Despite count = -1, Linux will only dispatch one packet at a time. */
total += ret;
break;
#endif
}
- /*FIXME*/if (want_verbose) fprintf(stderr, "%-20d\r", total);
cap_stats_update();
}
if (linkhdr->handler == NULL)
errx(1, "no handler for linktype %d", linktype);
if (linktype == DLT_EN10MB) /* FIXME: impossible with capfile? */
- show_mac_addrs = 1;
+ hosts_db_show_macs = 1;
/* Set filter expression, if any. */ /* FIXME: factor! */
if (filter != NULL)
ret = pcap_dispatch(
pcap,
-1, /* count, -1 = entire buffer */
- linkhdr->handler, /* callback func from decode.c */
+ callback,
NULL); /* user */
if (ret < 0)