Merge tag 'upstream/3.0.717'
authorEmil Mikulic <emikulic@gmail.com>
Sun, 18 Aug 2013 06:34:48 +0000 (16:34 +1000)
committerEmil Mikulic <emikulic@gmail.com>
Sun, 18 Aug 2013 06:34:48 +0000 (16:34 +1000)
Upstream version 3.0.717

32 files changed:
ChangeLog
Makefile.in
NEWS
acct.c
acct.h
cap.c
cap.h
config.h.in
configure
configure.ac
darkstat.c
daylog.c
db.c
db.h
decode.c
decode.h
dns.c
err.c
graph_db.c
hosts_db.c
hosts_db.h
hosts_sort.c
html.c
http.c
localip.c
localip.h
now.c [new file with mode: 0644]
now.h
opt.h
queue.h
str.c
str.h

index f14816c..561c567 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+v3.0.717 (14 August 2013)
+       - (OS X only) Work around lack of clock_gettime().
+       - Fix crash due to str_appendf() not understanding %ld.
+
+v3.0.716 (8 August 2013)
+       - Implement support for multiple capture interfaces.
+       - Support multiple local IPs on an interface.
+       - Only error out if we fail to create all HTTP sockets.
+         In particular, this helps on IPv6-incapable platforms.
+       - Use monotonic time over wall time where appropriate.
+       - Portability fixes for NetBSD and OpenBSD.
+
 v3.0.715 (January 2012)
        - Compatibility fixes for Hurd and Solaris.
        - Use link-time optimization and automake-like silent rules.
@@ -5,7 +17,7 @@ v3.0.715 (January 2012)
        - Continuing fixes for IPv6 support.
        - Only update lastseen time for sender, not recipient.
        - Implement --local-only: accounting for hosts on the local net.
-        - Make failure to bind() a socket non-fatal.
+       - Make failure to bind() a socket non-fatal.
        - Make failure to get local IP non-fatal.
        - Fall back to gethostbyaddr() if getnameinfo() fails.
        - Fix detection of IPv4 multicast addresses.
@@ -166,3 +178,5 @@ v3.0.471 (Jun 2006)
 v2.6 (Nov 2003)
 
        End of the line for darkstat 2
+
+vim:set noet ts=8 sw=8 tw=72:
index 27e971d..b0dbf2e 100644 (file)
@@ -43,6 +43,7 @@ html.c                \
 http.c         \
 localip.c      \
 ncache.c       \
+now.c          \
 pidfile.c      \
 str.c
 
@@ -124,16 +125,15 @@ acct.o: acct.c acct.h decode.h addr.h conv.h daylog.h graph_db.h err.h \
  cdefs.h hosts_db.h localip.h now.h opt.h
 addr.o: addr.c addr.h
 bsd.o: bsd.c bsd.h config.h cdefs.h
-cap.o: cap.c cdefs.h cap.h config.h conv.h decode.h addr.h hosts_db.h \
localip.h opt.h err.h
+cap.o: cap.c acct.h cdefs.h cap.h config.h conv.h decode.h addr.h err.h \
hosts_db.h localip.h now.h opt.h queue.h str.h
 conv.o: conv.c conv.h err.h cdefs.h
 darkstat.o: darkstat.c acct.h cap.h cdefs.h config.h conv.h daylog.h \
  graph_db.h db.h dns.h err.h http.h hosts_db.h addr.h localip.h ncache.h \
pidfile.h now.h
now.h pidfile.h
 daylog.o: daylog.c err.h cdefs.h daylog.h graph_db.h str.h now.h
 db.o: db.c cdefs.h err.h hosts_db.h addr.h graph_db.h db.h
-decode.o: decode.c cdefs.h acct.h cap.h config.h decode.h addr.h err.h \
- opt.h
+decode.o: decode.c cdefs.h decode.h addr.h err.h opt.h
 dns.o: dns.c cdefs.h conv.h decode.h addr.h dns.h err.h hosts_db.h \
  queue.h str.h tree.h bsd.h config.h
 err.o: err.c cdefs.h err.h opt.h pidfile.h bsd.h config.h
@@ -145,7 +145,9 @@ hosts_sort.o: hosts_sort.c cdefs.h err.h hosts_db.h addr.h
 html.o: html.c config.h str.h html.h opt.h
 http.o: http.c cdefs.h config.h conv.h err.h graph_db.h hosts_db.h addr.h \
  http.h now.h queue.h str.h stylecss.h graphjs.h
-localip.o: localip.c addr.h config.h err.h cdefs.h localip.h bsd.h
+localip.o: localip.c addr.h bsd.h config.h conv.h err.h cdefs.h localip.h \
+ now.h
 ncache.o: ncache.c conv.h err.h cdefs.h ncache.h tree.h bsd.h config.h
+now.o: now.c err.h cdefs.h now.h
 pidfile.o: pidfile.c err.h cdefs.h str.h pidfile.h
 str.o: str.c conv.h err.h cdefs.h str.h
diff --git a/NEWS b/NEWS
index 0ec2855..c40c9de 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,19 +1,19 @@
-darkstat v3.0.540 and earlier defaulted to running in the foreground
-and had a "-d" commandline argument to get darkstat to daemonize,
-detach from the controlling terminal, and run in the background.
+Changes to defaults, most recent first:
 
-Since r540, the default has been inverted.  darkstat will daemonize by
-default.
+- After v3.0.708, --debug was split into --verbose and --no-daemon.
 
-Since r626, this behaviour can be suppressed with the "--debug"
-commandline argument, to force darkstat to stay in the foreground for
-debugging purposes.
+- Since v3.0.694, darkstat is able to save its internal database into a file and
+  reload it on startup.  The file format is, by design, incompatible with the
+  format from darkstat v2.
 
-The "-d" argument is no longer recognized, and will prevent darkstat
-from starting, so make sure you adjust any startup scripts you may have.
+- After v3.0.626, daemonizing can be suppressed with the "--debug" commandline
+  argument, to force darkstat to stay in the foreground for debugging purposes.
 
-After 3.0.708, --debug was split into --verbose and --no-daemon.
+  The "-d" argument is no longer recognized, and will prevent darkstat
+  from starting, so make sure you adjust any startup scripts you may have.
 
-Since v3.0.694, darkstat is able to save its internal database into a
-file and reload it on startup.  The file format is, by design,
-incompatible with the format from darkstat v2.
+- v3.0.540 and earlier defaulted to running in the foreground and had a "-d"
+  commandline argument to get darkstat to daemonize, detach from the controlling
+  terminal, and run in the background.
+
+  After 540, the default has been inverted.  darkstat will daemonize by default.
diff --git a/acct.c b/acct.c
index 62ad076..17aa4f0 100644 (file)
--- a/acct.c
+++ b/acct.c
@@ -133,34 +133,23 @@ acct_init_localnet(const char *spec)
    verbosef("   local network mask: %s", addr_to_str(&localmask));
 }
 
-static int
-addr_is_local(const struct addr * const a)
-{
-   if (a->family == IPv4) {
-      if (using_localnet4) {
-         if (addr_inside(a, &localnet4, &localmask4))
-            return 1;
-      } else {
-         if (addr_equal(a, &localip4))
-            return 1;
-      }
-   } else {
-      assert(a->family == IPv6);
-      if (using_localnet6) {
-         if (addr_inside(a, &localnet6, &localmask6))
-            return 1;
-      } else {
-         if (addr_equal(a, &localip6))
-            return 1;
-      }
+static int addr_is_local(const struct addr * const a,
+                         const struct local_ips *local_ips) {
+   if (is_localip(a, local_ips))
+      return 1;
+   if (a->family == IPv4 && using_localnet4) {
+      if (addr_inside(a, &localnet4, &localmask4))
+         return 1;
+   } else if (a->family == IPv6 && using_localnet6) {
+      if (addr_inside(a, &localnet6, &localmask6))
+         return 1;
    }
    return 0;
 }
 
 /* Account for the given packet summary. */
-void
-acct_for(const struct pktsummary * const sm)
-{
+void acct_for(const struct pktsummary * const sm,
+              const struct local_ips * const local_ips) {
    struct bucket *hs = NULL, *hd = NULL;
    struct bucket *ps, *pd;
    int dir_in, dir_out;
@@ -189,18 +178,15 @@ acct_for(const struct pktsummary * const sm)
    acct_total_bytes += sm->len;
 
    /* Graphs. */
-   dir_out = addr_is_local(&(sm->src));
-   dir_in  = addr_is_local(&(sm->dst));
+   dir_out = addr_is_local(&sm->src, local_ips);
+   dir_in  = addr_is_local(&sm->dst, local_ips);
 
    /* Traffic staying within the network isn't counted. */
-   if (dir_in == 1 && dir_out == 1)
-      dir_in = dir_out = 0;
-
-   if (dir_out) {
+   if (dir_out && !dir_in) {
       daylog_acct((uint64_t)sm->len, GRAPH_OUT);
       graph_acct((uint64_t)sm->len, GRAPH_OUT);
    }
-   if (dir_in) {
+   if (dir_in && !dir_out) {
       daylog_acct((uint64_t)sm->len, GRAPH_IN);
       graph_acct((uint64_t)sm->len, GRAPH_IN);
    }
@@ -209,15 +195,15 @@ acct_for(const struct pktsummary * const sm)
 
    /* Hosts. */
    hosts_db_reduce();
-   if (!opt_want_local_only || addr_is_local(&sm->src)) {
+   if (!opt_want_local_only || dir_out) {
       hs = host_get(&(sm->src));
       hs->out   += sm->len;
       hs->total += sm->len;
       memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac));
-      hs->u.host.lastseen = now;
+      hs->u.host.last_seen_mono = now_mono();
    }
 
-   if (!opt_want_local_only || addr_is_local(&sm->dst)) {
+   if (!opt_want_local_only || dir_in) {
       hd = host_get(&(sm->dst));
       hd->in    += sm->len;
       hd->total += sm->len;
@@ -274,16 +260,12 @@ acct_for(const struct pktsummary * const sm)
       }
       break;
 
-   case IPPROTO_ICMP:
-   case IPPROTO_ICMPV6:
-   case IPPROTO_AH:
-   case IPPROTO_ESP:
-   case IPPROTO_OSPF:
-      /* known protocol, don't complain about it */
+   case IPPROTO_INVALID:
+      /* proto decoding failed, don't complain in accounting */
       break;
 
    default:
-      verbosef("unknown IP proto (%04x)", sm->proto);
+      verbosef("acct_for: unknown IP protocol 0x%02x", sm->proto);
    }
 }
 
diff --git a/acct.h b/acct.h
index 3c2828b..b4edb7f 100644 (file)
--- a/acct.h
+++ b/acct.h
@@ -7,10 +7,12 @@
 #include <stdint.h>
 
 struct pktsummary;
+struct local_ips;
 
 extern uint64_t acct_total_packets, acct_total_bytes;
 
 void acct_init_localnet(const char *spec);
-void acct_for(const struct pktsummary * const sm);
+void acct_for(const struct pktsummary * const sm,
+              const struct local_ips * const local_ips);
 
-/* vim:set ts=3 sw=3 tw=78 expandtab: */
+/* vim:set ts=3 sw=3 tw=80 expandtab: */
diff --git a/cap.c b/cap.c
index d07bf9c..7e1c41b 100644 (file)
--- a/cap.c
+++ b/cap.c
@@ -1,20 +1,25 @@
 /* darkstat 3
  * copyright (c) 2001-2011 Emil Mikulic.
  *
- * cap.c: interface to libpcap.
+ * cap.c: capture packets, and hand them off to decode and acct.
  *
  * You may use, modify and redistribute this file under the terms of the
  * GNU General Public License version 2. (see COPYING.GPL)
  */
 
+#include "acct.h"
 #include "cdefs.h"
 #include "cap.h"
 #include "config.h"
 #include "conv.h"
 #include "decode.h"
+#include "err.h"
 #include "hosts_db.h"
 #include "localip.h"
+#include "now.h"
 #include "opt.h"
+#include "queue.h"
+#include "str.h"
 
 #include <sys/ioctl.h>
 #include <sys/types.h>
 # include <sys/filio.h> /* Solaris' FIONBIO hides here */
 #endif
 #include <assert.h>
-#include "err.h"
+#include <pcap.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
+char *title_interfaces = NULL; /* for html.c */
+
 /* The cap process life-cycle:
- *
- * Init           - cap_init()
- * Fill fd_set    - cap_fd_set()
- * Poll           - cap_poll()
- * Stop           - cap_stop()
+ *  - cap_add_ifname() one or more times
+ *  - cap_add_filter() zero or more times
+ *  - cap_start() once to start listening
+ * Once per main loop:
+ *  - cap_fd_set() to update the select() set
+ *  - cap_poll() to read from ready pcap fds
+ * Shutdown:
+ *  - cap_stop()
  */
 
-/* Globals - only useful within this module. */
-static pcap_t *pcap = NULL;
-static int pcap_fd = -1;
-static const struct linkhdr *linkhdr = NULL;
+struct strnode {
+   STAILQ_ENTRY(strnode) entries;
+   const char *str;
+};
 
-#define CAP_TIMEOUT 500 /* granularity of capture buffer, in milliseconds */
+struct cap_iface {
+   STAILQ_ENTRY(cap_iface) entries;
 
-/* ---------------------------------------------------------------------------
- * Init pcap.  Exits on failure.
- */
-void
-cap_init(const char *device, const char *filter, int promisc)
-{
+   const char *name;
+   const char *filter;
+   pcap_t *pcap;
+   int fd;
+   const struct linkhdr *linkhdr;
+   struct local_ips local_ips;
+};
+
+static STAILQ_HEAD(cli_ifnames_head, strnode) cli_ifnames =
+   STAILQ_HEAD_INITIALIZER(cli_ifnames);
+
+static STAILQ_HEAD(cli_filters_head, strnode) cli_filters =
+   STAILQ_HEAD_INITIALIZER(cli_filters);
+
+static STAILQ_HEAD(cap_ifs_head, cap_iface) cap_ifs =
+   STAILQ_HEAD_INITIALIZER(cap_ifs);
+
+/* The read timeout passed to pcap_open_live() */
+#define CAP_TIMEOUT_MSEC 500
+
+void cap_add_ifname(const char *ifname) {
+   struct strnode *n = xmalloc(sizeof(*n));
+   n->str = ifname;
+   STAILQ_INSERT_TAIL(&cli_ifnames, n, entries);
+}
+
+void cap_add_filter(const char *filter) {
+   struct strnode *n = xmalloc(sizeof(*n));
+   n->str = filter;
+   STAILQ_INSERT_TAIL(&cli_filters, n, entries);
+}
+
+static void cap_set_filter(pcap_t *pcap, const char *filter) {
+   struct bpf_program prog;
+   char *tmp_filter;
+
+   if (filter == NULL)
+      return;
+
+   tmp_filter = xstrdup(filter);
+   if (pcap_compile(
+         pcap,
+         &prog,
+         tmp_filter,
+         1,          /* optimize */
+         0)          /* netmask */
+         == -1)
+      errx(1, "pcap_compile(): %s", pcap_geterr(pcap));
+
+   if (pcap_setfilter(pcap, &prog) == -1)
+      errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap));
+
+   pcap_freecode(&prog);
+   free(tmp_filter);
+}
+
+static void cap_start_one(struct cap_iface *iface, const int promisc) {
    char errbuf[PCAP_ERRBUF_SIZE], *tmp_device;
    int linktype, snaplen, waited;
 
-   /* pcap doesn't like device being const */
-   tmp_device = xstrdup(device);
+   /* pcap wants a non-const interface name string */
+   tmp_device = xstrdup(iface->name);
+   if (iface->filter)
+      verbosef("capturing on interface '%s' with filter '%s'",
+         tmp_device, iface->filter);
+   else
+      verbosef("capturing on interface '%s' with no filter", tmp_device);
 
    /* Open packet capture descriptor. */
    waited = 0;
    for (;;) {
       errbuf[0] = '\0'; /* zero length string */
-      pcap = pcap_open_live(
+      iface->pcap = pcap_open_live(
          tmp_device,
          1,          /* snaplen, irrelevant at this point */
          0,          /* promisc, also irrelevant */
-         CAP_TIMEOUT,
+         CAP_TIMEOUT_MSEC,
          errbuf);
-      if (pcap != NULL) break; /* success! */
+      if (iface->pcap != NULL)
+         break; /* success! */
 
       if ((opt_wait_secs != -1) && strstr(errbuf, "device is not up")) {
          if ((opt_wait_secs > 0) && (waited >= opt_wait_secs))
@@ -82,16 +150,16 @@ cap_init(const char *device, const char *filter, int promisc)
    }
 
    /* Work out the linktype and what snaplen we need. */
-   linktype = pcap_datalink(pcap);
+   linktype = pcap_datalink(iface->pcap);
    verbosef("linktype is %d", linktype);
    if ((linktype == DLT_EN10MB) && opt_want_macs)
       hosts_db_show_macs = 1;
-   linkhdr = getlinkhdr(linktype);
-   if (linkhdr == NULL)
+   iface->linkhdr = getlinkhdr(linktype);
+   if (iface->linkhdr == NULL)
       errx(1, "unknown linktype %d", linktype);
-   if (linkhdr->handler == NULL)
-      errx(1, "no handler for linktype %d", linktype);
-   snaplen = getsnaplen(linkhdr);
+   if (iface->linkhdr->decoder == NULL)
+      errx(1, "no decoder for linktype %d", linktype);
+   snaplen = getsnaplen(iface->linkhdr);
    if (opt_want_pppoe) {
       snaplen += PPPOE_HDR_LEN;
       if (linktype != DLT_EN10MB)
@@ -99,6 +167,11 @@ cap_init(const char *device, const char *filter, int promisc)
    }
    verbosef("calculated snaplen minimum %d", snaplen);
 #ifdef linux
+   /* FIXME: actually due to libpcap moving to mmap (!!!)
+    * work out which version and fix the way we do capture
+    * on 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.
     *
@@ -111,16 +184,16 @@ cap_init(const char *device, const char *filter, int promisc)
    verbosef("using snaplen %d", snaplen);
 
    /* Close and re-open pcap to use the new snaplen. */
-   pcap_close(pcap);
+   pcap_close(iface->pcap);
    errbuf[0] = '\0'; /* zero length string */
-   pcap = pcap_open_live(
+   iface->pcap = pcap_open_live(
       tmp_device,
       snaplen,
       promisc,
-      CAP_TIMEOUT,
+      CAP_TIMEOUT_MSEC,
       errbuf);
 
-   if (pcap == NULL)
+   if (iface->pcap == NULL)
       errx(1, "pcap_open_live(): %s", errbuf);
 
    if (errbuf[0] != '\0') /* not zero length anymore -> warning */
@@ -133,80 +206,117 @@ cap_init(const char *device, const char *filter, int promisc)
    else
       verbosef("capturing in non-promiscuous mode");
 
-   /* Set filter expression, if any. */
-   if (filter != NULL)
-   {
-      struct bpf_program prog;
-      char *tmp_filter = xstrdup(filter);
-      if (pcap_compile(
-            pcap,
-            &prog,
-            tmp_filter,
-            1,          /* optimize */
-            0)          /* netmask */
-            == -1)
-         errx(1, "pcap_compile(): %s", pcap_geterr(pcap));
-
-      if (pcap_setfilter(pcap, &prog) == -1)
-         errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap));
-
-      pcap_freecode(&prog);
-      free(tmp_filter);
-   }
-
-   pcap_fd = pcap_fileno(pcap);
+   cap_set_filter(iface->pcap, iface->filter);
+   iface->fd = pcap_fileno(iface->pcap);
 
    /* set non-blocking */
 #ifdef linux
-   if (pcap_setnonblock(pcap, 1, errbuf) == -1)
+   if (pcap_setnonblock(iface->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)"); }
+   {
+      int one = 1;
+      if (ioctl(iface->fd, FIONBIO, &one) == -1)
+         err(1, "ioctl(iface->fd, FIONBIO)");
+   }
 #endif
 
 #ifdef BIOCSETWF
-{
-   /* Deny all writes to the socket */
-   struct bpf_insn bpf_wfilter[] = { BPF_STMT(BPF_RET+BPF_K, 0) };
-   int wf_len = sizeof(bpf_wfilter) / sizeof(struct bpf_insn);
-   struct bpf_program pr;
-
-   pr.bf_len = wf_len;
-   pr.bf_insns = bpf_wfilter;
-
-   if (ioctl(pcap_fd, BIOCSETWF, &pr) == -1)
-      err(1, "ioctl(pcap_fd, BIOCSETFW)");
-   verbosef("filtered out BPF writes");
-}
+   {
+      /* Deny all writes to the socket */
+      struct bpf_insn bpf_wfilter[] = { BPF_STMT(BPF_RET+BPF_K, 0) };
+      int wf_len = sizeof(bpf_wfilter) / sizeof(struct bpf_insn);
+      struct bpf_program pr;
+
+      pr.bf_len = wf_len;
+      pr.bf_insns = bpf_wfilter;
+
+      if (ioctl(iface->fd, BIOCSETWF, &pr) == -1)
+         err(1, "ioctl(iface->fd, BIOCSETFW)");
+      verbosef("filtered out BPF writes");
+   }
 #endif
 
 #ifdef BIOCLOCK
    /* set "locked" flag (no reset) */
-   if (ioctl(pcap_fd, BIOCLOCK) == -1)
-      err(1, "ioctl(pcap_fd, BIOCLOCK)");
+   if (ioctl(iface->fd, BIOCLOCK) == -1)
+      err(1, "ioctl(iface->fd, BIOCLOCK)");
    verbosef("locked down BPF for security");
 #endif
 }
 
-/*
- * Set pcap_fd in the given fd_set.
- */
-void
-cap_fd_set(
+void cap_start(const int promisc) {
+   struct str *ifs = str_make();
+
+   assert(STAILQ_EMPTY(&cap_ifs));
+   if (STAILQ_EMPTY(&cli_ifnames))
+      errx(1, "no interfaces specified");
+
+   /* For each ifname */
+   while (!STAILQ_EMPTY(&cli_ifnames)) {
+      struct strnode *ifname, *filter = NULL;
+      struct cap_iface *iface = xmalloc(sizeof(*iface));
+
+      ifname = STAILQ_FIRST(&cli_ifnames);
+      STAILQ_REMOVE_HEAD(&cli_ifnames, entries);
+
+      if (!STAILQ_EMPTY(&cli_filters)) {
+         filter = STAILQ_FIRST(&cli_filters);
+         STAILQ_REMOVE_HEAD(&cli_filters, entries);
+      }
+
+      iface->name = ifname->str;
+      iface->filter = (filter == NULL) ? NULL : filter->str;
+      iface->pcap = NULL;
+      iface->fd = -1;
+      iface->linkhdr = NULL;
+      localip_init(&iface->local_ips);
+      STAILQ_INSERT_TAIL(&cap_ifs, iface, entries);
+      cap_start_one(iface, promisc);
+
+      free(ifname);
+      if (filter) free(filter);
+
+      if (str_len(ifs) == 0)
+         str_append(ifs, iface->name);
+      else
+         str_appendf(ifs, ", %s", iface->name);
+   }
+   verbosef("all capture interfaces prepared");
+
+   /* Deallocate extra filters, if any. */
+   while (!STAILQ_EMPTY(&cli_filters)) {
+      struct strnode *filter = STAILQ_FIRST(&cli_filters);
+
+      verbosef("ignoring extraneous filter '%s'", filter->str);
+      STAILQ_REMOVE_HEAD(&cli_filters, entries);
+      free(filter);
+   }
+
+   str_appendn(ifs, "", 1); /* NUL terminate */
+   {
+      size_t _;
+      str_extract(ifs, &_, &title_interfaces);
+   }
+}
+
 #ifdef linux
-   fd_set *read_set _unused_,
-   int *max_fd _unused_,
-   struct timeval *timeout,
+# define _unused_on_linux_ _unused_
+# define _unused_otherwise_
 #else
-   fd_set *read_set,
-   int *max_fd,
-   struct timeval *timeout _unused_,
+# define _unused_on_linux_
+# define _unused_otherwise_ _unused_
 #endif
-   int *need_timeout)
-{
-   assert(*need_timeout == 0); /* we're first to get a shot at this */
+
+/*
+ * Set pcap_fd in the given fd_set.
+ */
+void cap_fd_set(fd_set *read_set _unused_on_linux_,
+                int *max_fd _unused_on_linux_,
+                struct timeval *timeout _unused_otherwise_,
+                int *need_timeout) {
+   assert(*need_timeout == 0); /* we're first to get a shot at the fd_set */
+
 #ifdef linux
    /*
     * Linux's BPF is immediate, so don't select() as it will lead to horrible
@@ -214,36 +324,41 @@ cap_fd_set(
     */
    *need_timeout = 1;
    timeout->tv_sec = 0;
-   timeout->tv_usec = CAP_TIMEOUT * 1000; /* msec->usec */
+   timeout->tv_usec = CAP_TIMEOUT_MSEC * 1000;
 #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);
+   {
+      struct cap_iface *iface;
+      STAILQ_FOREACH(iface, &cap_ifs, entries) {
+         /* We have a BSD-like BPF, we can select() on it. */
+         FD_SET(iface->fd, read_set);
+         *max_fd = MAX(*max_fd, iface->fd);
+      }
+   }
 #endif
 }
 
 unsigned int cap_pkts_recv = 0, cap_pkts_drop = 0;
 
-static void
-cap_stats_update(void)
-{
-   struct pcap_stat ps;
+static void cap_stats_update(void) {
+   struct cap_iface *iface;
 
-   if (pcap_stats(pcap, &ps) != 0) {
-      warnx("pcap_stats(): %s", pcap_geterr(pcap));
-      return;
+   cap_pkts_recv = 0;
+   cap_pkts_drop = 0;
+   STAILQ_FOREACH(iface, &cap_ifs, entries) {
+      struct pcap_stat ps;
+      if (pcap_stats(iface->pcap, &ps) != 0) {
+         warnx("pcap_stats('%s'): %s", iface->name, pcap_geterr(iface->pcap));
+         return;
+      }
+      cap_pkts_recv += ps.ps_recv;
+      cap_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)
-{
+/* Print hexdump of received packet to stdout, for debugging. */
+static void hexdump(const u_char *buf,
+                    const uint32_t len,
+                    const struct linkhdr *linkhdr) {
    uint32_t i, col;
 
    printf("packet of %u bytes:\n", len);
@@ -251,10 +366,9 @@ hexdump(const u_char *buf, const uint32_t len)
       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(" ");
+         printf("|"); /* marks end of link headers (e.g. ethernet) */
+      else
+         printf(" ");
       col += 3;
       if (col >= 72) {
          printf("\n");
@@ -265,149 +379,135 @@ hexdump(const u_char *buf, const uint32_t len)
    printf("\n");
 }
 
-/*
- * Callback function for pcap_dispatch() which chains to the decoder specified
- * in linkhdr struct.
+/* Callback function for pcap_dispatch() which chains to the decoder specified
+ * in the 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);
+static void callback(u_char *user,
+                     const struct pcap_pkthdr *pheader,
+                     const u_char *pdata) {
+   const struct cap_iface * const iface = (struct cap_iface *)user;
+   struct pktsummary sm;
+
+   if (opt_want_hexdump)
+      hexdump(pdata, pheader->caplen, iface->linkhdr);
+   memset(&sm, 0, sizeof(sm));
+   if (iface->linkhdr->decoder(pheader, pdata, &sm))
+      acct_for(&sm, &iface->local_ips);
 }
 
-/*
- * Process any packets currently in the capture buffer.
- */
-void
-cap_poll(fd_set *read_set
-#ifdef linux
-   _unused_
-#endif
-)
-{
-   int total, ret;
-
-#ifndef linux /* We don't use select() on Linux. */
-   if (!FD_ISSET(pcap_fd, read_set)) {
-      verbosef("cap_poll premature");
-      return;
-   }
-#endif
-
-   /*
-    * Once per capture poll, check our IP address.  It's used in accounting
-    * for traffic graphs.
-    */
-   localip_update(); /* FIXME: this might even be too often */
-
-   total = 0;
-   for (;;) {
-#ifndef NDEBUG
-      struct timeval t1;
-      gettimeofday(&t1, NULL);
-#endif
-      ret = pcap_dispatch(
-            pcap,
-            -1,               /* count, -1 = entire buffer */
-            callback,
-            NULL);            /* user */
-
-      if (ret < 0) {
-         warnx("pcap_dispatch(): %s", pcap_geterr(pcap));
-         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;
+/* Process any packets currently in the capture buffer. */
+void cap_poll(fd_set *read_set _unused_on_linux_) {
+   struct cap_iface *iface;
+
+   STAILQ_FOREACH(iface, &cap_ifs, entries) {
+      /* Once per capture poll, check our IP address.  It's used in accounting
+       * for traffic graphs.
+       */
+      localip_update(iface->name, &iface->local_ips);
+
+      for (;;) {
+         struct timespec t;
+         int ret;
+
+         timer_start(&t);
+         ret = pcap_dispatch(
+               iface->pcap,
+               -1, /* count = entire buffer */
+               callback,
+               (u_char*)iface); /* user = struct to pass to callback */
+
+         if (ret < 0) {
+            warnx("pcap_dispatch('%s'): %s",
+               iface->name, pcap_geterr(iface->pcap));
+            continue;
+         }
+         timer_stop(&t,
+                    2 * CAP_TIMEOUT_MSEC * 1000000,
+                    "pcap_dispatch took too long");
+
+         if (0) /* debugging */
+            verbosef("iface '%s' got %d pkts", iface->name, ret);
 
 #ifdef linux
-      /* keep looping until we've dispatched all the outstanding packets */
-      if (ret == 0) break;
+         /* keep looping until we've dispatched all the outstanding packets */
+         if (ret == 0)
+            break;
 #else
-      /* we get them all on the first shot */
-      break;
+         /* we get them all on the first shot */
+         break;
 #endif
+      }
    }
    cap_stats_update();
 }
 
-void
-cap_stop(void)
-{
-   pcap_close(pcap);
+void cap_stop(void) {
+   while (!STAILQ_EMPTY(&cap_ifs)) {
+      struct cap_iface *iface = STAILQ_FIRST(&cap_ifs);
+
+      STAILQ_REMOVE_HEAD(&cap_ifs, entries);
+      pcap_close(iface->pcap);
+      localip_free(&iface->local_ips);
+      free(iface);
+   }
+   free(title_interfaces);
+   title_interfaces = NULL;
 }
 
 /* Run through entire capfile. */
-void
-cap_from_file(const char *capfile, const char *filter)
-{
+void cap_from_file(const char *capfile) {
    char errbuf[PCAP_ERRBUF_SIZE];
    int linktype, ret;
+   struct cap_iface iface;
+
+   iface.name = NULL;
+   iface.filter = NULL;
+   iface.pcap = NULL;
+   iface.fd = -1;
+   iface.linkhdr = NULL;
+   localip_init(&iface.local_ips);
+
+   /* Process cmdline filters. */
+   if (!STAILQ_EMPTY(&cli_filters))
+      iface.filter = STAILQ_FIRST(&cli_filters)->str;
+   while (!STAILQ_EMPTY(&cli_filters)) {
+      struct strnode *n = STAILQ_FIRST(&cli_filters);
+      STAILQ_REMOVE_HEAD(&cli_filters, entries);
+      free(n);
+   }
 
    /* Open packet capture descriptor. */
    errbuf[0] = '\0'; /* zero length string */
-   pcap = pcap_open_offline(capfile, errbuf);
+   iface.pcap = pcap_open_offline(capfile, errbuf);
 
-   if (pcap == NULL)
+   if (iface.pcap == NULL)
       errx(1, "pcap_open_offline(): %s", errbuf);
 
    if (errbuf[0] != '\0') /* not zero length anymore -> warning */
       warnx("pcap_open_offline() warning: %s", errbuf);
 
    /* Work out the linktype. */
-   linktype = pcap_datalink(pcap);
-   linkhdr = getlinkhdr(linktype);
-   if (linkhdr == NULL)
+   linktype = pcap_datalink(iface.pcap);
+   iface.linkhdr = getlinkhdr(linktype);
+   if (iface.linkhdr == NULL)
       errx(1, "unknown linktype %d", linktype);
-   if (linkhdr->handler == NULL)
-      errx(1, "no handler for linktype %d", linktype);
-   if (linktype == DLT_EN10MB) /* FIXME: impossible with capfile? */
-      hosts_db_show_macs = 1;
+   if (iface.linkhdr->decoder == NULL)
+      errx(1, "no decoder for linktype %d", linktype);
 
-   /* Set filter expression, if any. */ /* FIXME: factor! */
-   if (filter != NULL)
-   {
-      struct bpf_program prog;
-      char *tmp_filter = xstrdup(filter);
-      if (pcap_compile(
-            pcap,
-            &prog,
-            tmp_filter,
-            1,          /* optimize */
-            0)          /* netmask */
-            == -1)
-         errx(1, "pcap_compile(): %s", pcap_geterr(pcap));
-
-      if (pcap_setfilter(pcap, &prog) == -1)
-         errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap));
-
-      pcap_freecode(&prog);
-      free(tmp_filter);
-   }
+   cap_set_filter(iface.pcap, iface.filter);
 
    /* Process file. */
    ret = pcap_dispatch(
-         pcap,
+         iface.pcap,
          -1,               /* count, -1 = entire buffer */
          callback,
-         NULL);            /* user */
+         (u_char*)&iface); /* user */
 
    if (ret < 0)
-      errx(1, "pcap_dispatch(): %s", pcap_geterr(pcap));
+      errx(1, "pcap_dispatch(): %s", pcap_geterr(iface.pcap));
+
+   localip_free(&iface.local_ips);
+   pcap_close(iface.pcap);
 }
 
 /* vim:set ts=3 sw=3 tw=78 expandtab: */
diff --git a/cap.h b/cap.h
index 18f2425..e1a77cf 100644 (file)
--- a/cap.h
+++ b/cap.h
 
 extern unsigned int cap_pkts_recv, cap_pkts_drop;
 
-void cap_init(const char *device, const char *filter, int promisc);
+void cap_add_ifname(const char *ifname); /* call one or more times */
+void cap_add_filter(const char *filter); /* call zero or more times */
+void cap_start(const int promisc);
 void cap_fd_set(fd_set *read_set, int *max_fd,
    struct timeval *timeout, int *need_timeout);
 void cap_poll(fd_set *read_set);
 void cap_stop(void);
 
-void cap_from_file(const char *capfile, const char *filter);
+void cap_from_file(const char *capfile);
 
 /* vim:set ts=3 sw=3 tw=78 expandtab: */
index 09fdf95..6a34856 100644 (file)
@@ -24,9 +24,6 @@
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
-/* Define to 1 if you have the <net/if_ether.h> header file. */
-#undef HAVE_NET_IF_ETHER_H
-
 /* Define to 1 if you have the <pcap.h> header file. */
 #undef HAVE_PCAP_H
 
index 5552f80..520dc65 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,11 +1,9 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for darkstat 3.0.715.
+# Generated by GNU Autoconf 2.69 for darkstat 3.0.717.
 #
 #
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
-# Foundation, Inc.
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
 #
 #
 # This configure script is free software; the Free Software Foundation
@@ -134,6 +132,31 @@ export LANGUAGE
 # CDPATH.
 (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
 
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -167,7 +190,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
 else
   exitcode=1; echo positional parameters were not saved.
 fi
-test x\$exitcode = x0 || exit 1"
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
   as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
   as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
   eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
@@ -212,21 +236,25 @@ IFS=$as_save_IFS
 
 
       if test "x$CONFIG_SHELL" != x; then :
-  # We cannot yet assume a decent shell, so we have to provide a
-       # neutralization value for shells without unset; and this also
-       # works around shells that cannot unset nonexistent variables.
-       # Preserve -v and -x to the replacement shell.
-       BASH_ENV=/dev/null
-       ENV=/dev/null
-       (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
-       export CONFIG_SHELL
-       case $- in # ((((
-         *v*x* | *x*v* ) as_opts=-vx ;;
-         *v* ) as_opts=-v ;;
-         *x* ) as_opts=-x ;;
-         * ) as_opts= ;;
-       esac
-       exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
 fi
 
     if test x$as_have_required = xno; then :
@@ -328,6 +356,14 @@ $as_echo X"$as_dir" |
 
 
 } # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
 # as_fn_append VAR VALUE
 # ----------------------
 # Append the text in VALUE to the end of the definition contained in VAR. Take
@@ -449,6 +485,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
   chmod +x "$as_me.lineno" ||
     { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
 
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
   # Don't try to exec as it changes $[0], causing all sort of problems
   # (the dirname of $[0] is not the place where we might find the
   # original and so on.  Autoconf is especially sensitive to this).
@@ -483,16 +523,16 @@ if (echo >conf$$.file) 2>/dev/null; then
     # ... but there are two gotchas:
     # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
     # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
-    # In both cases, we have to default to `cp -p'.
+    # In both cases, we have to default to `cp -pR'.
     ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
-      as_ln_s='cp -p'
+      as_ln_s='cp -pR'
   elif ln conf$$.file conf$$ 2>/dev/null; then
     as_ln_s=ln
   else
-    as_ln_s='cp -p'
+    as_ln_s='cp -pR'
   fi
 else
-  as_ln_s='cp -p'
+  as_ln_s='cp -pR'
 fi
 rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
 rmdir conf$$.dir 2>/dev/null
@@ -504,28 +544,8 @@ else
   as_mkdir_p=false
 fi
 
-if test -x / >/dev/null 2>&1; then
-  as_test_x='test -x'
-else
-  if ls -dL / >/dev/null 2>&1; then
-    as_ls_L_option=L
-  else
-    as_ls_L_option=
-  fi
-  as_test_x='
-    eval sh -c '\''
-      if test -d "$1"; then
-       test -d "$1/.";
-      else
-       case $1 in #(
-       -*)set "./$1";;
-       esac;
-       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
-       ???[sx]*):;;*)false;;esac;fi
-    '\'' sh
-  '
-fi
-as_executable_p=$as_test_x
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
 
 # Sed expression to map a string onto a valid CPP name.
 as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
@@ -557,8 +577,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='darkstat'
 PACKAGE_TARNAME='darkstat'
-PACKAGE_VERSION='3.0.715'
-PACKAGE_STRING='darkstat 3.0.715'
+PACKAGE_VERSION='3.0.717'
+PACKAGE_STRING='darkstat 3.0.717'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL='http://unix4lyfe.org/darkstat/'
 
@@ -660,7 +680,7 @@ with_chroot_dir
 with_privdrop_user
 enable_silent_rules
 enable_debug
-enable_mad_warnings
+enable_warnings
 with_pcap
 '
       ac_precious_vars='build_alias
@@ -1127,8 +1147,6 @@ target=$target_alias
 if test "x$host_alias" != x; then
   if test "x$build_alias" = x; then
     cross_compiling=maybe
-    $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
-    If a cross compiler is detected then cross compile mode will be used" >&2
   elif test "x$build_alias" != "x$host_alias"; then
     cross_compiling=yes
   fi
@@ -1214,7 +1232,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures darkstat 3.0.715 to adapt to many kinds of systems.
+\`configure' configures darkstat 3.0.717 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1275,7 +1293,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of darkstat 3.0.715:";;
+     short | recursive ) echo "Configuration of darkstat 3.0.717:";;
    esac
   cat <<\_ACEOF
 
@@ -1286,8 +1304,8 @@ Optional Features:
   --enable-silent-rules          less verbose build output (undo: 'make V=1')
   --disable-silent-rules         verbose build output (undo: 'make V=0')
   --disable-debug         turn off debugging code and asserts
-  --enable-mad-warnings   turn on lots of compile-time warnings, these are
-                          GCC-specific and only really useful for development
+  --enable-warnings       turn on lots of compile-time warnings, these are
+                          only useful for development
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -1374,10 +1392,10 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-darkstat configure 3.0.715
-generated by GNU Autoconf 2.68
+darkstat configure 3.0.717
+generated by GNU Autoconf 2.69
 
-Copyright (C) 2010 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
 This configure script is free software; the Free Software Foundation
 gives unlimited permission to copy, distribute and modify it.
 _ACEOF
@@ -1453,7 +1471,7 @@ $as_echo "$ac_try_echo"; } >&5
         test ! -s conftest.err
        } && test -s conftest$ac_exeext && {
         test "$cross_compiling" = yes ||
-        $as_test_x conftest$ac_exeext
+        test -x conftest$ac_exeext
        }; then :
   ac_retval=0
 else
@@ -1672,8 +1690,8 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by darkstat $as_me 3.0.715, which was
-generated by GNU Autoconf 2.68.  Invocation command line was
+It was created by darkstat $as_me 3.0.717, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
 
@@ -2147,7 +2165,7 @@ case $as_dir/ in #((
     # by default.
     for ac_prog in ginstall scoinst install; do
       for ac_exec_ext in '' $ac_executable_extensions; do
-       if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+       if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
          if test $ac_prog = install &&
            grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
            # AIX install.  It has an incompatible calling convention.
@@ -2225,7 +2243,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_CC="${ac_tool_prefix}gcc"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -2265,7 +2283,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_CC="gcc"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -2318,7 +2336,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_CC="${ac_tool_prefix}cc"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -2359,7 +2377,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
        ac_prog_rejected=yes
        continue
@@ -2417,7 +2435,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -2461,7 +2479,7 @@ do
   IFS=$as_save_IFS
   test -z "$as_dir" && as_dir=.
     for ac_exec_ext in '' $ac_executable_extensions; do
-  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
     ac_cv_prog_ac_ct_CC="$ac_prog"
     $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
     break 2
@@ -2907,8 +2925,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <stdarg.h>
 #include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+struct stat;
 /* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
 struct buf { int x; };
 FILE * (*rcsopen) (struct buf *, struct stat *, int);
@@ -3063,125 +3080,29 @@ else
 $as_echo "skipped" >&6; }
 fi
 
-# Check whether --enable-mad-warnings was given.
-if test "${enable_mad_warnings+set}" = set; then :
-  enableval=$enable_mad_warnings; if test "x$enableval" = "xyes" ; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler can emit certain warnings" >&5
-$as_echo_n "checking if your C compiler can emit certain warnings... " >&6; }
+# Check whether --enable-warnings was given.
+if test "${enable_warnings+set}" = set; then :
+  enableval=$enable_warnings; if test "x$enableval" = "xyes" ; then
+   { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler has gcc-like --extra-warnings" >&5
+$as_echo_n "checking if your C compiler has gcc-like --extra-warnings... " >&6; }
    save_cflags="$CFLAGS"
-   CFLAGS="$CFLAGS -fdiagnostics-show-option \
--Waddress \
--Waggregate-return \
--Wall \
--Wbad-function-cast \
--Wcast-align \
--Wcast-qual \
--Wchar-subscripts \
--Wcomment \
--Wdeclaration-after-statement \
--Wdisabled-optimization \
--Wextra \
--Wfloat-equal \
--Wformat \
--Wformat=2 \
--Wformat-nonliteral \
--Wformat-security \
--Wformat-y2k \
--Wimplicit \
--Wimplicit-function-declaration \
--Wimplicit-int \
--Winit-self \
--Winline \
--Winvalid-pch \
--Wmain \
--Wmissing-braces \
--Wmissing-declarations \
--Wmissing-field-initializers \
--Wmissing-format-attribute \
--Wmissing-include-dirs \
--Wmissing-noreturn \
--Wmissing-prototypes \
--Wnested-externs \
--Wnonnull \
--Wold-style-definition \
--Wpacked \
--Wparentheses \
--Wpointer-arith \
--Wpointer-sign \
--Wredundant-decls \
--Wreturn-type \
--Wsequence-point \
--Wshadow \
--Wsign-compare \
--Wstrict-aliasing -fstrict-aliasing \
--Wstrict-overflow=5 -fstrict-overflow \
--Wstrict-prototypes \
--Wswitch \
--Wswitch-default \
--Wswitch-enum \
--Wtrigraphs \
--Wundef \
--Wuninitialized \
--Wunknown-pragmas \
--Wunsafe-loop-optimizations \
--Wunused \
--Wunused-function \
--Wunused-label \
--Wunused-parameter \
--Wunused-value \
--Wunused-variable \
--Wvariadic-macros \
--Wvolatile-register-var \
--Wwrite-strings \
-"
-# The above are valid for gcc version 4.2.1.
-
+   CFLAGS="$CFLAGS -fdiagnostics-show-option --all-warnings --extra-warnings"
    if (eval $ac_link) 2>/dev/null; then
     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler can emit certain other warnings" >&5
-$as_echo_n "checking if your C compiler can emit certain other warnings... " >&6; }
-    save_cflags="$CFLAGS"
-    CFLAGS="$CFLAGS \
--Warray-bounds \
--Wclobbered \
--Wcoverage-mismatch \
--Wempty-body \
--Wignored-qualifiers \
--Wlogical-op \
--Wmissing-parameter-type \
--Wold-style-declaration \
--Wpacked-bitfield-compat \
--Wsync-nand \
--Wtype-limits \
--Wvla \
-"
-# The above are valid for gcc version 4.4.3.
-# We skip the following entirely:
-#-pedantic
-#-Wabi
-#-Wc++-compat
-#-Wconversion
-#-Wfatal-errors
-#-Wlong-long
-#-Wpadded
-#-Wsign-conversion
-#-Wstack-protector
-#-Wsystem-headers
-#-Wtraditional
-#-Wtraditional-conversion
-#-Wunreachable-code
-
-    if (eval $ac_link) 2>/dev/null; then
-     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-    else
-     { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+   else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-     CFLAGS="$save_cflags"
-    fi
+    CFLAGS="$save_cflags"
+   fi
 
+   { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler has clang-like -Weverything" >&5
+$as_echo_n "checking if your C compiler has clang-like -Weverything... " >&6; }
+   save_cflags="$CFLAGS"
+   CFLAGS="$CFLAGS -Weverything"
+   if (eval $ac_link) 2>/dev/null; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
    else
     { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
@@ -3593,7 +3514,7 @@ do
     for ac_prog in grep ggrep; do
     for ac_exec_ext in '' $ac_executable_extensions; do
       ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
-      { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+      as_fn_executable_p "$ac_path_GREP" || continue
 # Check for GNU ac_path_GREP and select it if it is found.
   # Check for GNU $ac_path_GREP
 case `"$ac_path_GREP" --version 2>&1` in
@@ -3659,7 +3580,7 @@ do
     for ac_prog in egrep; do
     for ac_exec_ext in '' $ac_executable_extensions; do
       ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
-      { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+      as_fn_executable_p "$ac_path_EGREP" || continue
 # Check for GNU ac_path_EGREP and select it if it is found.
   # Check for GNU $ac_path_EGREP
 case `"$ac_path_EGREP" --version 2>&1` in
@@ -3849,22 +3770,61 @@ fi
 done
 
 
-# Debian GNU/kFreeBSD needs net/if_ether.h for ETH_P_IPV6
-for ac_header in net/if_ether.h
-do :
-  ac_fn_c_check_header_mongrel "$LINENO" "net/if_ether.h" "ac_cv_header_net_if_ether_h" "$ac_includes_default"
-if test "x$ac_cv_header_net_if_ether_h" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_NET_IF_ETHER_H 1
-_ACEOF
+# This is the modern way.  Older systems use the ioctl method.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing getifaddrs" >&5
+$as_echo_n "checking for library containing getifaddrs... " >&6; }
+if ${ac_cv_search_getifaddrs+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
 
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char getifaddrs ();
+int
+main ()
+{
+return getifaddrs ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' c; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_getifaddrs=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_getifaddrs+:} false; then :
+  break
 fi
-
 done
+if ${ac_cv_search_getifaddrs+:} false; then :
 
-
-# This is the modern way.  Older systems use the ioctl method.
-for ac_header in ifaddrs.h
+else
+  ac_cv_search_getifaddrs=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_getifaddrs" >&5
+$as_echo "$ac_cv_search_getifaddrs" >&6; }
+ac_res=$ac_cv_search_getifaddrs
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+  for ac_header in ifaddrs.h
 do :
   ac_fn_c_check_header_mongrel "$LINENO" "ifaddrs.h" "ac_cv_header_ifaddrs_h" "$ac_includes_default"
 if test "x$ac_cv_header_ifaddrs_h" = xyes; then :
@@ -3876,6 +3836,10 @@ fi
 
 done
 
+fi
+
+
+
 
 # Some OSes (Solaris) need sys/sockio.h for SIOCGIFADDR
 for ac_header in sys/sockio.h
@@ -4000,19 +3964,16 @@ if test -n "$PCAP_BROKEN" ; then
  cat <<END_MSG
 $RULE
 
-darkstat absolutely requires libpcap to be installed.  If
-it's installed into a prefix that isn't being picked up by
+darkstat absolutely requires libpcap to be installed.  On Debian
+systems, apt-get install libpcap-dev.  On RPM systems like RedHat, try
+installing the libpcap-devel package.
+
+If libpcap is installed into a prefix that isn't being picked up by
 configure, for example /usr/local, re-run configure and add
 --with-pcap=/usr/local
 
-If you are using a package-based operating system (like
-something with RPMs), see if there is a pcap-devel or
-libpcap-devel package that you can install, to provide the
-pcap headers and libraries.
-
-Failing all of the above, go to http://www.tcpdump.org/ and
-download the source distribution of libpcap and build it
-yourself.
+Failing all of the above, go to http://www.tcpdump.org/ and download the
+source distribution of libpcap and build it yourself.
 
 $RULE
 END_MSG
@@ -4220,6 +4181,63 @@ fi
 done
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
+$as_echo_n "checking for library containing clock_gettime... " >&6; }
+if ${ac_cv_search_clock_gettime+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' rt; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_clock_gettime=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_clock_gettime+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_clock_gettime+:} false; then :
+
+else
+  ac_cv_search_clock_gettime=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
+$as_echo "$ac_cv_search_clock_gettime" >&6; }
+ac_res=$ac_cv_search_clock_gettime
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
 ac_config_files="$ac_config_files Makefile darkstat.8"
 
 cat >confcache <<\_ACEOF
@@ -4629,16 +4647,16 @@ if (echo >conf$$.file) 2>/dev/null; then
     # ... but there are two gotchas:
     # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
     # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
-    # In both cases, we have to default to `cp -p'.
+    # In both cases, we have to default to `cp -pR'.
     ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
-      as_ln_s='cp -p'
+      as_ln_s='cp -pR'
   elif ln conf$$.file conf$$ 2>/dev/null; then
     as_ln_s=ln
   else
-    as_ln_s='cp -p'
+    as_ln_s='cp -pR'
   fi
 else
-  as_ln_s='cp -p'
+  as_ln_s='cp -pR'
 fi
 rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
 rmdir conf$$.dir 2>/dev/null
@@ -4698,28 +4716,16 @@ else
   as_mkdir_p=false
 fi
 
-if test -x / >/dev/null 2>&1; then
-  as_test_x='test -x'
-else
-  if ls -dL / >/dev/null 2>&1; then
-    as_ls_L_option=L
-  else
-    as_ls_L_option=
-  fi
-  as_test_x='
-    eval sh -c '\''
-      if test -d "$1"; then
-       test -d "$1/.";
-      else
-       case $1 in #(
-       -*)set "./$1";;
-       esac;
-       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
-       ???[sx]*):;;*)false;;esac;fi
-    '\'' sh
-  '
-fi
-as_executable_p=$as_test_x
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
 
 # Sed expression to map a string onto a valid CPP name.
 as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
@@ -4740,8 +4746,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by darkstat $as_me 3.0.715, which was
-generated by GNU Autoconf 2.68.  Invocation command line was
+This file was extended by darkstat $as_me 3.0.717, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
   CONFIG_HEADERS  = $CONFIG_HEADERS
@@ -4803,11 +4809,11 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-darkstat config.status 3.0.715
-configured by $0, generated by GNU Autoconf 2.68,
+darkstat config.status 3.0.717
+configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
-Copyright (C) 2010 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
 This config.status script is free software; the Free Software Foundation
 gives unlimited permission to copy, distribute and modify it."
 
@@ -4896,7 +4902,7 @@ fi
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 if \$ac_cs_recheck; then
-  set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
   shift
   \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
   CONFIG_SHELL='$SHELL'
index 15c356a..7af8316 100644 (file)
@@ -1,4 +1,6 @@
-AC_INIT(darkstat, 3.0.715, , , http://unix4lyfe.org/darkstat/)
+# Need at least 2.64 for PACKAGE_URL
+AC_PREREQ([2.64])
+AC_INIT(darkstat, 3.0.717, , , http://unix4lyfe.org/darkstat/)
 AC_CONFIG_SRCDIR([darkstat.c])
 AC_CONFIG_HEADER([config.h])
 
@@ -92,121 +94,25 @@ else
  AC_MSG_RESULT(skipped)
 fi
 
-AC_ARG_ENABLE(mad-warnings, AS_HELP_STRING([--enable-mad-warnings],
- [turn on lots of compile-time warnings, these are GCC-specific and only
really useful for development]),
+AC_ARG_ENABLE(warnings, AS_HELP_STRING([--enable-warnings],
+ [turn on lots of compile-time warnings,
 these are only useful for development]),
  [if test "x$enableval" = "xyes" ; then
-   AC_MSG_CHECKING(if your C compiler can emit certain warnings)
+   AC_MSG_CHECKING(if your C compiler has gcc-like --extra-warnings)
    save_cflags="$CFLAGS"
-   CFLAGS="$CFLAGS -fdiagnostics-show-option \
--Waddress \
--Waggregate-return \
--Wall \
--Wbad-function-cast \
--Wcast-align \
--Wcast-qual \
--Wchar-subscripts \
--Wcomment \
--Wdeclaration-after-statement \
--Wdisabled-optimization \
--Wextra \
--Wfloat-equal \
--Wformat \
--Wformat=2 \
--Wformat-nonliteral \
--Wformat-security \
--Wformat-y2k \
--Wimplicit \
--Wimplicit-function-declaration \
--Wimplicit-int \
--Winit-self \
--Winline \
--Winvalid-pch \
--Wmain \
--Wmissing-braces \
--Wmissing-declarations \
--Wmissing-field-initializers \
--Wmissing-format-attribute \
--Wmissing-include-dirs \
--Wmissing-noreturn \
--Wmissing-prototypes \
--Wnested-externs \
--Wnonnull \
--Wold-style-definition \
--Wpacked \
--Wparentheses \
--Wpointer-arith \
--Wpointer-sign \
--Wredundant-decls \
--Wreturn-type \
--Wsequence-point \
--Wshadow \
--Wsign-compare \
--Wstrict-aliasing -fstrict-aliasing \
--Wstrict-overflow=5 -fstrict-overflow \
--Wstrict-prototypes \
--Wswitch \
--Wswitch-default \
--Wswitch-enum \
--Wtrigraphs \
--Wundef \
--Wuninitialized \
--Wunknown-pragmas \
--Wunsafe-loop-optimizations \
--Wunused \
--Wunused-function \
--Wunused-label \
--Wunused-parameter \
--Wunused-value \
--Wunused-variable \
--Wvariadic-macros \
--Wvolatile-register-var \
--Wwrite-strings \
-"
-# The above are valid for gcc version 4.2.1.
-
+   CFLAGS="$CFLAGS -fdiagnostics-show-option --all-warnings --extra-warnings"
    if (eval $ac_link) 2>/dev/null; then
     AC_MSG_RESULT(yes)
+   else
+    AC_MSG_RESULT(no)
+    CFLAGS="$save_cflags"
+   fi
 
-    AC_MSG_CHECKING(if your C compiler can emit certain other warnings)
-    save_cflags="$CFLAGS"
-    CFLAGS="$CFLAGS \
--Warray-bounds \
--Wclobbered \
--Wcoverage-mismatch \
--Wempty-body \
--Wignored-qualifiers \
--Wlogical-op \
--Wmissing-parameter-type \
--Wold-style-declaration \
--Wpacked-bitfield-compat \
--Wsync-nand \
--Wtype-limits \
--Wvla \
-"
-# The above are valid for gcc version 4.4.3.
-# We skip the following entirely:
-#-pedantic
-#-Wabi
-#-Wc++-compat
-#-Wconversion
-#-Wfatal-errors
-#-Wlong-long
-#-Wpadded
-#-Wsign-conversion
-#-Wstack-protector
-#-Wsystem-headers
-#-Wtraditional
-#-Wtraditional-conversion
-#-Wunreachable-code
-
-    if (eval $ac_link) 2>/dev/null; then
-     AC_MSG_RESULT(yes)
-    else
-     AC_MSG_RESULT(no)
-     CFLAGS="$save_cflags"
-    fi
-
+   AC_MSG_CHECKING(if your C compiler has clang-like -Weverything)
+   save_cflags="$CFLAGS"
+   CFLAGS="$CFLAGS -Weverything"
+   if (eval $ac_link) 2>/dev/null; then
+    AC_MSG_RESULT(yes)
    else
     AC_MSG_RESULT(no)
     CFLAGS="$save_cflags"
@@ -248,11 +154,11 @@ AC_SEARCH_LIBS(hstrerror, [resolv], [],
 # Solaris need sys/filio.h for FIONBIO
 AC_CHECK_HEADERS(sys/filio.h)
 
-# Debian GNU/kFreeBSD needs net/if_ether.h for ETH_P_IPV6
-AC_CHECK_HEADERS(net/if_ether.h)
-
 # This is the modern way.  Older systems use the ioctl method.
-AC_CHECK_HEADERS(ifaddrs.h)
+AC_SEARCH_LIBS(getifaddrs, [c],
+  [AC_CHECK_HEADERS(ifaddrs.h)])
+
+
 
 # Some OSes (Solaris) need sys/sockio.h for SIOCGIFADDR
 AC_CHECK_HEADERS(sys/sockio.h)
@@ -292,19 +198,16 @@ if test -n "$PCAP_BROKEN" ; then
  cat <<END_MSG
 $RULE
 
-darkstat absolutely requires libpcap to be installed.  If
-it's installed into a prefix that isn't being picked up by
+darkstat absolutely requires libpcap to be installed.  On Debian
+systems, apt-get install libpcap-dev.  On RPM systems like RedHat, try
+installing the libpcap-devel package.
+
+If libpcap is installed into a prefix that isn't being picked up by
 configure, for example /usr/local, re-run configure and add
 --with-pcap=/usr/local
 
-If you are using a package-based operating system (like
-something with RPMs), see if there is a pcap-devel or
-libpcap-devel package that you can install, to provide the
-pcap headers and libraries.
-
-Failing all of the above, go to http://www.tcpdump.org/ and
-download the source distribution of libpcap and build it
-yourself.
+Failing all of the above, go to http://www.tcpdump.org/ and download the
+source distribution of libpcap and build it yourself.
 
 $RULE
 END_MSG
@@ -325,5 +228,7 @@ AC_SEARCH_LIBS(strlcat, [bsd],
 AC_CHECK_HEADERS(bsd/string.h)
 AC_CHECK_HEADERS(bsd/unistd.h)
 
+AC_SEARCH_LIBS(clock_gettime, rt)
+
 AC_CONFIG_FILES([Makefile darkstat.8])
 AC_OUTPUT
index 0f4b70d..2213986 100644 (file)
@@ -20,6 +20,7 @@
 #include "hosts_db.h"
 #include "localip.h"
 #include "ncache.h"
+#include "now.h"
 #include "pidfile.h"
 
 #include <assert.h>
@@ -33,9 +34,6 @@
 #include <unistd.h>
 #include <pcap.h>
 
-#include "now.h"
-time_t now;
-
 #ifndef INADDR_NONE
 # define INADDR_NONE (-1) /* Solaris */
 #endif
@@ -45,8 +43,7 @@ static volatile int running = 1;
 static void sig_shutdown(int signum _unused_) { running = 0; }
 
 static volatile int reset_pending = 0, export_pending = 0;
-static void sig_reset(int signum _unused_)
-{
+static void sig_reset(int signum _unused_) {
    reset_pending = 1;
    export_pending = 1;
 }
@@ -54,9 +51,8 @@ static void sig_reset(int signum _unused_)
 static void sig_export(int signum _unused_) { export_pending = 1; }
 
 /* --- Commandline parsing --- */
-static unsigned long
-parsenum(const char *str, unsigned long max /* 0 for no max */)
-{
+static unsigned long parsenum(const char *str,
+                              unsigned long max /* 0 for no max */) {
    unsigned long n;
    char *end;
 
@@ -71,8 +67,13 @@ parsenum(const char *str, unsigned long max /* 0 for no max */)
    return n;
 }
 
-const char *opt_interface = NULL;
-static void cb_interface(const char *arg) { opt_interface = arg; }
+static int opt_iface_seen = 0;
+static void cb_interface(const char *arg) {
+   cap_add_ifname(arg);
+   opt_iface_seen = 1;
+}
+
+static void cb_filter(const char *arg) { cap_add_filter(arg); }
 
 const char *opt_capfile = NULL;
 static void cb_capfile(const char *arg) { opt_capfile = arg; }
@@ -111,9 +112,6 @@ static void cb_port(const char *arg)
 
 static void cb_bindaddr(const char *arg) { http_add_bindaddr(arg); }
 
-const char *opt_filter = NULL;
-static void cb_filter(const char *arg) { opt_filter = arg; }
-
 static int is_localnet_specified = 0;
 static void cb_local(const char *arg)
 {
@@ -214,11 +212,11 @@ struct cmdline_arg {
 };
 
 static struct cmdline_arg cmdline_args[] = {
-   {"-i",             "interface",       cb_interface,    0},
-   {"-r",             "file",            cb_capfile,      0},
+   {"-i",             "interface",       cb_interface,   -1},
+   {"-f",             "filter",          cb_filter,      -1},
+   {"-r",             "capfile",         cb_capfile,      0},
    {"-p",             "port",            cb_port,         0},
    {"-b",             "bindaddr",        cb_bindaddr,    -1},
-   {"-f",             "filter",          cb_filter,       0},
    {"-l",             "network/netmask", cb_local,        0},
    {"--local-only",   NULL,              cb_local_only,   0},
    {"--snaplen",      "bytes",           cb_snaplen,      0},
@@ -248,12 +246,8 @@ static struct cmdline_arg cmdline_args[] = {
    {NULL,             NULL,              NULL,            0}
 };
 
-/*
- * We autogenerate the usage statement from the cmdline_args data structure.
- */
-static void
-usage(void)
-{
+/* We autogenerate the usage statement from the cmdline_args data structure. */
+static void usage(void) {
    static char intro[] = "usage: darkstat ";
    char indent[sizeof(intro)];
    struct cmdline_arg *arg;
@@ -278,9 +272,7 @@ usage(void)
 "documentation and usage examples.\n");
 }
 
-static void
-parse_sub_cmdline(const int argc, char * const *argv)
-{
+static void parse_sub_cmdline(const int argc, char * const *argv) {
    struct cmdline_arg *arg;
 
    if (argc == 0) return;
@@ -319,9 +311,7 @@ parse_sub_cmdline(const int argc, char * const *argv)
    exit(EXIT_FAILURE);
 }
 
-static void
-parse_cmdline(const int argc, char * const *argv)
-{
+static void parse_cmdline(const int argc, char * const *argv) {
    if (argc < 1) {
       /* Not enough args. */
       usage();
@@ -336,17 +326,20 @@ parse_cmdline(const int argc, char * const *argv)
    }
 
    /* start syslogging as early as possible */
-   if (opt_want_syslog) openlog("darkstat", LOG_NDELAY | LOG_PID, LOG_DAEMON);
+   if (opt_want_syslog)
+      openlog("darkstat", LOG_NDELAY | LOG_PID, LOG_DAEMON);
 
    /* some default values */
-   if (opt_chroot_dir == NULL) opt_chroot_dir = CHROOT_DIR;
-   if (opt_privdrop_user == NULL) opt_privdrop_user = PRIVDROP_USER;
+   if (opt_chroot_dir == NULL)
+      opt_chroot_dir = CHROOT_DIR;
+   if (opt_privdrop_user == NULL)
+      opt_privdrop_user = PRIVDROP_USER;
 
    /* sanity check args */
-   if ((opt_interface == NULL) && (opt_capfile == NULL))
+   if (!opt_iface_seen && opt_capfile == NULL)
       errx(1, "must specify either interface (-i) or capture file (-r)");
 
-   if ((opt_interface != NULL) && (opt_capfile != NULL))
+   if (opt_iface_seen && opt_capfile != NULL)
       errx(1, "can't specify both interface (-i) and capture file (-r)");
 
    if ((opt_hosts_max != 0) && (opt_hosts_keep >= opt_hosts_max)) {
@@ -379,22 +372,17 @@ parse_cmdline(const int argc, char * const *argv)
       verbosef("WARNING: --local-only without -l only matches the local host");
 }
 
-static void
-run_from_capfile(void)
-{
+static void run_from_capfile(void) {
+   now_init();
    graph_init();
    hosts_db_init();
-   cap_from_file(opt_capfile, opt_filter);
-   cap_stop();
+   cap_from_file(opt_capfile);
    if (export_fn != NULL) db_export(export_fn);
    hosts_db_free();
    graph_free();
-#ifndef PRIu64
-#warning "PRIu64 is not defined, using qu instead"
-#define PRIu64 "qu"
-#endif
-   verbosef("Total packets: %"PRIu64", bytes: %"PRIu64,
-      acct_total_packets, acct_total_bytes);
+   verbosef("Total packets: %llu, bytes: %llu",
+            (unsigned long long)acct_total_packets,
+            (unsigned long long)acct_total_bytes);
 }
 
 /* --- Program body --- */
@@ -405,10 +393,6 @@ main(int argc, char **argv)
    parse_cmdline(argc-1, argv+1);
 
    if (opt_capfile) {
-      /*
-       * This is very different from a regular run against a network
-       * interface.
-       */
       run_from_capfile();
       return 0;
    }
@@ -426,19 +410,18 @@ main(int argc, char **argv)
 
    /* do this first as it forks - minimize memory use */
    if (opt_want_dns) dns_init(opt_privdrop_user);
-   cap_init(opt_interface, opt_filter, opt_want_promisc); /* needs root */
+   cap_start(opt_want_promisc); /* needs root */
    http_listen(opt_bindport);
    ncache_init(); /* must do before chroot() */
 
    privdrop(opt_chroot_dir, opt_privdrop_user);
 
    /* Don't need root privs for these: */
-   now = time(NULL);
+   now_init();
    if (daylog_fn != NULL) daylog_init(daylog_fn);
    graph_init();
    hosts_db_init();
    if (import_fn != NULL) db_import(import_fn);
-   localip_init(opt_interface);
 
    if (signal(SIGTERM, sig_shutdown) == SIG_ERR)
       errx(1, "signal(SIGTERM) failed");
@@ -455,48 +438,47 @@ main(int argc, char **argv)
    while (running) {
       int select_ret, max_fd = -1, use_timeout = 0;
       struct timeval timeout;
+      struct timespec t;
       fd_set rs, ws;
 
-      now = time(NULL);
-
-      if (export_pending) {
-         if (export_fn != NULL)
-            db_export(export_fn);
-         export_pending = 0;
-      }
-
-      if (reset_pending) {
-         if (export_pending)
-            continue; /* export before reset */
-         hosts_db_reset();
-         graph_reset();
-         reset_pending = 0;
-      }
-
       FD_ZERO(&rs);
       FD_ZERO(&ws);
-
       cap_fd_set(&rs, &max_fd, &timeout, &use_timeout);
       http_fd_set(&rs, &ws, &max_fd, &timeout, &use_timeout);
 
       select_ret = select(max_fd+1, &rs, &ws, NULL,
          (use_timeout) ? &timeout : NULL);
-
-      if ((select_ret == 0) && (!use_timeout))
+      if (select_ret == 0 && !use_timeout)
             errx(1, "select() erroneously timed out");
-
       if (select_ret == -1) {
          if (errno == EINTR)
             continue;
          else
             err(1, "select()");
       }
-      else {
-         graph_rotate();
-         cap_poll(&rs);
-         dns_poll();
-         http_poll(&rs, &ws);
+
+      timer_start(&t);
+      now_update();
+
+      if (export_pending) {
+         if (export_fn != NULL)
+            db_export(export_fn);
+         export_pending = 0;
+      }
+
+      if (reset_pending) {
+         if (export_pending)
+            continue; /* export before reset */
+         hosts_db_reset();
+         graph_reset();
+         reset_pending = 0;
       }
+
+      graph_rotate();
+      cap_poll(&rs);
+      dns_poll();
+      http_poll(&rs, &ws);
+      timer_stop(&t, 1000000000, "event processing took longer than a second");
    }
 
    verbosef("shutting down");
index a0b517e..fe1928f 100644 (file)
--- a/daylog.c
+++ b/daylog.c
 
 #define _GNU_SOURCE 1 /* for O_NOFOLLOW on Linux */
 
-#include <sys/types.h>
+#include "err.h"
+#include "daylog.h"
+#include "str.h"
+#include "now.h"
+
 #include <assert.h>
 #include <fcntl.h>
+#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
-#include "err.h"
-#include "daylog.h"
-#include "str.h"
-#include "now.h"
-
 static const char *daylog_fn = NULL;
-static time_t today_time, tomorrow_time;
+static long today_real, tomorrow_real;
 static uint64_t bytes_in, bytes_out, pkts_in, pkts_out;
 
 #define DAYLOG_DATE_LEN 26 /* strlen("1900-01-01 00:00:00 +1234") + 1 */
 static char datebuf[DAYLOG_DATE_LEN];
 
-static char *
-fmt_date(const time_t when)
-{
-    time_t tmp = when;
-    if (strftime(datebuf, DAYLOG_DATE_LEN,
-        "%Y-%m-%d %H:%M:%S %z", localtime(&tmp) ) == 0)
+static char *fmt_date(time_t when) {
+    if (strftime(datebuf,
+                 DAYLOG_DATE_LEN,
+                 "%Y-%m-%d %H:%M:%S %z",
+                 localtime(&when)) == 0)
             errx(1, "strftime() failed in fmt_date()");
-    return (datebuf);
+    return datebuf;
 }
 
 /* Given some time today, find the first second of tomorrow. */
-static time_t
-tomorrow(const time_t today)
-{
-   time_t tmp = today;
+static time_t tomorrow(time_t t_before) {
+   time_t t_after;
    struct tm tm, *lt;
 
-   lt = localtime(&tmp);
+   lt = localtime(&t_before);
    memcpy(&tm, lt, sizeof(tm));
    tm.tm_sec = 0;
    tm.tm_min = 0;
    tm.tm_hour = 0;
    tm.tm_mday = lt->tm_mday + 1; /* tomorrow */
-   return mktime(&tm);
-}
-
-static int
-daylog_open(void)
-{
-   return open(daylog_fn, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600);
-}
-
-static void
-daylog_emit(void)
-{
-   int fd = daylog_open();
-
-   if (fd != -1) {
-      struct str *buf = str_make();
-      char *s;
-      size_t len;
-      str_appendf(buf, "%s|%u|%qu|%qu|%qu|%qu\n",
-         fmt_date(today_time), (unsigned int)today_time,
-         bytes_in, bytes_out, pkts_in, pkts_out);
-      str_extract(buf, &len, &s);
-
-      (void)write(fd, s, len); /* ignore write errors */
-      close(fd);
-      free(s);
-   }
+   t_after = mktime(&tm);
+   assert(t_after > t_before);
+   return t_after;
 }
 
-void
-daylog_init(const char *filename)
-{
+/* Warns on error. */
+static void daylog_write(const char *format, ...) {
    int fd;
+   ssize_t wr;
+   va_list va;
    struct str *buf;
-   char *s;
-   size_t len;
-
-   daylog_fn = filename;
-   today_time = time(NULL);
-   tomorrow_time = tomorrow(today_time);
-   verbosef("today is %u, tomorrow is %u",
-      (unsigned int)today_time, (unsigned int)tomorrow_time);
-   bytes_in = bytes_out = pkts_in = pkts_out = 0;
 
-   fd = daylog_open();
-   if (fd == -1)
-      err(1, "couldn't open(\"%s\") for append", filename);
+   assert(daylog_fn != NULL);
+   fd = open(daylog_fn, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600);
+   if (fd == -1) {
+      warn("daylog_write: couldn't open '%s' for append", daylog_fn);
+      return;
+   }
 
    buf = str_make();
-   str_appendf(buf, "# logging started at %s (%u)\n",
-      fmt_date(today_time), (unsigned int)today_time);
-   str_extract(buf, &len, &s);
-   (void)write(fd, s, len); /* ignore write errors */
+   va_start(va, format);
+   str_vappendf(buf, format, va);
+   va_end(va);
+
+   wr = str_write(buf, fd);
+   if (wr == -1)
+      warn("daylog_write: couldn't write to '%s'", daylog_fn);
+   else if (wr != (ssize_t)str_len(buf))
+      warnx("daylog_write: truncated write to '%s': wrote %d of %d bytes",
+           daylog_fn,
+           (int)wr,
+           (int)str_len(buf));
    close(fd);
-   free(s);
+   str_free(buf);
 }
 
-void daylog_free(void)
-{
-   int fd;
-   struct str *buf;
-   char *s;
-   size_t len;
-
-   today_time = time(NULL);
+static void daylog_emit(void) {
+   daylog_write("%s|%qu|%qu|%qu|%qu|%qu\n",
+                fmt_date(today_real), (uint64_t)today_real,
+                bytes_in, bytes_out, pkts_in, pkts_out);
+}
 
-   /* Emit what's currently accumulated. */
-   daylog_emit();
+void daylog_init(const char *filename) {
+   daylog_fn = filename;
+   today_real = now_real();
+   tomorrow_real = tomorrow(today_real);
+   verbosef("today is %ld, tomorrow is %ld",
+            (long)today_real, (long)tomorrow_real);
+   bytes_in = bytes_out = pkts_in = pkts_out = 0;
 
-   fd = daylog_open();
-   if (fd == -1) return;
+   daylog_write("# logging started at %s (%qu)\n",
+                fmt_date(today_real), (uint64_t)today_real);
+}
 
-   buf = str_make();
-   str_appendf(buf, "# logging stopped at %s (%u)\n",
-      fmt_date(today_time), (unsigned int)today_time);
-   str_extract(buf, &len, &s);
-   (void)write(fd, s, len); /* ignore write errors */
-   close(fd);
-   free(s);
+void daylog_free(void) {
+   today_real = now_real();
+   daylog_emit(); /* Emit what's currently accumulated before we exit. */
+   daylog_write("# logging stopped at %s (%qu)\n",
+                fmt_date(today_real), (uint64_t)today_real);
 }
 
-void
-daylog_acct(uint64_t amount, enum graph_dir dir)
-{
-   if (daylog_fn == NULL) return; /* disabled */
+void daylog_acct(uint64_t amount, enum graph_dir dir) {
+   if (daylog_fn == NULL)
+      return; /* daylogging disabled */
 
-   /* Check if we need to rotate. */
-   if (now >= tomorrow_time) {
+   /* Check if we need to update the log. */
+   if (now_real() >= tomorrow_real) {
       daylog_emit();
 
-      today_time = now;
-      tomorrow_time = tomorrow(today_time);
+      today_real = now_real();
+      tomorrow_real = tomorrow(today_real);
       bytes_in = bytes_out = pkts_in = pkts_out = 0;
-      verbosef("rotated daylog, tomorrow = %u",
-         (unsigned int)tomorrow_time);
+      verbosef("updated daylog, tomorrow = %ld", (long)tomorrow_real);
    }
 
    /* Accounting. */
diff --git a/db.c b/db.c
index 5a82d90..cf50da9 100644 (file)
--- a/db.c
+++ b/db.c
@@ -27,9 +27,7 @@ static const unsigned char export_tag_hosts_ver1[] = {0xDA, 'H', 'S', 0x01};
 static const unsigned char export_tag_graph_ver1[] = {0xDA, 'G', 'R', 0x01};
 
 #ifndef swap64
-static inline uint64_t
-swap64(uint64_t _x)
-{
+static uint64_t swap64(uint64_t _x) {
    /* this is __bswap64 from:
     * $FreeBSD: src/sys/i386/include/endian.h,v 1.41$
     */
@@ -40,17 +38,12 @@ swap64(uint64_t _x)
 }
 #endif
 
-uint64_t
-hton64(const uint64_t ho)
-{
-   if (ntohs(0x1234) == 0x1234) return ho;
-   else return swap64(ho);
-}
-
-uint64_t
-ntoh64(const uint64_t no)
-{
-   return hton64(no);
+#define ntoh64 hton64
+static inline uint64_t hton64(const uint64_t ho) {
+   if (ntohs(0x1234) == 0x1234)
+      return ho;
+   else
+      return swap64(ho);
 }
 
 void
diff --git a/db.h b/db.h
index 5f2eba6..6f9d28a 100644 (file)
--- a/db.h
+++ b/db.h
@@ -11,10 +11,6 @@ struct addr;
 
 void db_import(const char *filename);
 void db_export(const char *filename);
-
-/* byteswap */
-uint64_t hton64(const uint64_t ho);
-uint64_t ntoh64(const uint64_t no);
 void test_64order(void);
 
 /* read helpers */
index def8c29..e8e5f35 100644 (file)
--- a/decode.c
+++ b/decode.c
@@ -11,9 +11,6 @@
  */
 
 #include "cdefs.h"
-#include "acct.h"
-#include "cap.h"
-#include "config.h"
 #include "decode.h"
 #include "err.h"
 #include "opt.h"
 #include <string.h>
 #include <unistd.h>
 #include <arpa/inet.h> /* inet_ntoa() */
+#include <net/if.h> /* struct ifreq */
 
 /* need struct ether_header */
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-# include <sys/queue.h>
-# include <net/if.h>
-# include <net/if_arp.h>
+#ifdef __NetBSD__ /* works for NetBSD 5.1.2 */
 # include <netinet/if_ether.h>
 #else
-# ifdef __sun
-#  include <sys/ethernet.h>
-#  define ETHER_HDR_LEN 14
+# ifdef __OpenBSD__
+#  include <sys/queue.h>
+#  include <net/if_arp.h>
+#  include <netinet/if_ether.h>
 # else
-#  ifdef _AIX
-#   include <netinet/if_ether.h>
+#  ifdef __sun
+#   include <sys/ethernet.h>
 #   define ETHER_HDR_LEN 14
 #  else
-#   include <net/ethernet.h>
+#   ifdef _AIX
+#    include <netinet/if_ether.h>
+#    define ETHER_HDR_LEN 14
+#   else
+#    include <net/ethernet.h>
+#   endif
 #  endif
 # endif
 #endif
+
 #ifndef ETHERTYPE_PPPOE
-#define ETHERTYPE_PPPOE 0x8864
+# define ETHERTYPE_PPPOE 0x8864
 #endif
-
 #ifndef ETHERTYPE_IPV6
-# ifdef HAVE_NET_IF_ETHER_H
-#  include <net/if_ether.h>    /* ETH_P_IPV6 for GNU/kfreebsd */
-# endif
-# ifdef ETH_P_IPV6
-#  define ETHERTYPE_IPV6 ETH_P_IPV6
-# endif
+# define ETHERTYPE_IPV6 0x86DD
 #endif
 
-#include <net/if.h> /* struct ifreq */
 #include <netinet/in_systm.h> /* n_long */
 #include <netinet/ip.h> /* struct ip */
 #include <netinet/ip6.h> /* struct ip6_hdr */
 #include <netinet/tcp.h> /* struct tcphdr */
 #include <netinet/udp.h> /* struct udphdr */
 
+#define PPP_HDR_LEN     4
+#define FDDI_HDR_LEN    21
+#define IP_HDR_LEN      sizeof(struct ip)
+#define IPV6_HDR_LEN    sizeof(struct ip6_hdr)
+#define TCP_HDR_LEN     sizeof(struct tcphdr)
+#define UDP_HDR_LEN     sizeof(struct udphdr)
+#define NULL_HDR_LEN    4
+#define SLL_HDR_LEN     16
+#define RAW_HDR_LEN     0
+
 #ifndef IPV6_VERSION
-#define IPV6_VERSION 0x60
+# define IPV6_VERSION 0x60
 #endif
 
 #ifndef IPV6_VERSION_MASK
-#define IPV6_VERSION_MASK 0xf0
+# define IPV6_VERSION_MASK 0xF0
 #endif
 
-static void decode_ether(u_char *, const struct pcap_pkthdr *,
-   const u_char *);
-static void decode_loop(u_char *, const struct pcap_pkthdr *,
-   const u_char *);
-static void decode_null(u_char *, const struct pcap_pkthdr *,
-   const u_char *);
-static void decode_ppp(u_char *, const struct pcap_pkthdr *,
-   const u_char *);
-static void decode_pppoe(u_char *, const struct pcap_pkthdr *,
-   const u_char *);
-static void decode_pppoe_real(const u_char *pdata, const uint32_t len,
-   struct pktsummary *sm);
-static void decode_linux_sll(u_char *, const struct pcap_pkthdr *,
-   const u_char *);
-static void decode_raw(u_char *, const struct pcap_pkthdr *,
-   const u_char *);
-static void decode_ip(const u_char *pdata, const uint32_t len,
-   struct pktsummary *sm);
-static void decode_ipv6(const u_char *pdata, const uint32_t len,
-   struct pktsummary *sm);
+static int decode_ether(DECODER_ARGS);
+static int decode_loop(DECODER_ARGS);
+static int decode_null(DECODER_ARGS);
+static int decode_ppp(DECODER_ARGS);
+static int decode_pppoe(DECODER_ARGS);
+#ifdef DLT_LINUX_SLL
+static int decode_linux_sll(DECODER_ARGS);
+#endif
+static int decode_raw(DECODER_ARGS);
+
+#define HELPER_ARGS const u_char *pdata, \
+                    const uint32_t len, \
+                    struct pktsummary *sm
+
+static int helper_pppoe(HELPER_ARGS);
+static int helper_ip(HELPER_ARGS);
+static int helper_ipv6(HELPER_ARGS);
+static void helper_ip_deeper(HELPER_ARGS); /* protocols like TCP/UDP */
 
 /* Link-type header information */
 static const struct linkhdr linkhdrs[] = {
@@ -114,208 +117,130 @@ static const struct linkhdr linkhdrs[] = {
    { -1, 0, NULL }
 };
 
-/*
- * Returns a pointer to the linkhdr record matching the given linktype, or
+/* Returns a pointer to the linkhdr record matching the given linktype, or
  * NULL if no matching entry found.
  */
-const struct linkhdr *
-getlinkhdr(const int linktype)
-{
+const struct linkhdr *getlinkhdr(const int linktype) {
    size_t i;
 
    for (i=0; linkhdrs[i].linktype != -1; i++)
       if (linkhdrs[i].linktype == linktype)
          return (&(linkhdrs[i]));
-   return (NULL);
+   return NULL;
 }
 
-/*
- * Returns the minimum snaplen needed to decode everything up to the TCP/UDP
- * packet headers.  The IPv6 header is normative.  The argument lh is not
- * allowed to be NULL.
+/* Returns the minimum snaplen needed to decode everything up to and including
+ * the TCP/UDP packet headers.
  */
-int
-getsnaplen(const struct linkhdr *lh)
-{
+int getsnaplen(const struct linkhdr *lh) {
    return (int)(lh->hdrlen + IPV6_HDR_LEN + MAX(TCP_HDR_LEN, UDP_HDR_LEN));
 }
 
-/* Decoding functions. */
-static void
-decode_ether(u_char *user _unused_,
-      const struct pcap_pkthdr *pheader,
-      const u_char *pdata)
-{
+static int decode_ether(DECODER_ARGS) {
    u_short type;
    const struct ether_header *hdr = (const struct ether_header *)pdata;
-   struct pktsummary sm;
-   memset(&sm, 0, sizeof(sm));
-   sm.time = pheader->ts.tv_sec;
 
    if (pheader->caplen < ETHER_HDR_LEN) {
       verbosef("ether: packet too short (%u bytes)", pheader->caplen);
-      return;
+      return 0;
    }
-
 #ifdef __sun
-   memcpy(sm.src_mac, hdr->ether_shost.ether_addr_octet, sizeof(sm.src_mac));
-   memcpy(sm.dst_mac, hdr->ether_dhost.ether_addr_octet, sizeof(sm.dst_mac));
+   memcpy(sm->src_mac, hdr->ether_shost.ether_addr_octet, sizeof(sm->src_mac));
+   memcpy(sm->dst_mac, hdr->ether_dhost.ether_addr_octet, sizeof(sm->dst_mac));
 #else
-   memcpy(sm.src_mac, hdr->ether_shost, sizeof(sm.src_mac));
-   memcpy(sm.dst_mac, hdr->ether_dhost, sizeof(sm.dst_mac));
+   memcpy(sm->src_mac, hdr->ether_shost, sizeof(sm->src_mac));
+   memcpy(sm->dst_mac, hdr->ether_dhost, sizeof(sm->dst_mac));
 #endif
-
-   type = ntohs( hdr->ether_type );
+   type = ntohs(hdr->ether_type);
    switch (type) {
-   case ETHERTYPE_IP:
-   case ETHERTYPE_IPV6:
-      if (!opt_want_pppoe) {
-         decode_ip(pdata + ETHER_HDR_LEN,
-                   pheader->caplen - ETHER_HDR_LEN, &sm);
-         acct_for(&sm);
-      } else
+      case ETHERTYPE_IP:
+      case ETHERTYPE_IPV6:
+         if (!opt_want_pppoe)
+            return helper_ip(pdata + ETHER_HDR_LEN,
+                             pheader->caplen - ETHER_HDR_LEN,
+                             sm);
          verbosef("ether: discarded IP packet, expecting PPPoE instead");
-      break;
-   case ETHERTYPE_ARP:
-      /* known protocol, don't complain about it. */
-      break;
-   case ETHERTYPE_PPPOE:
-      if (opt_want_pppoe)
-         decode_pppoe_real(pdata + ETHER_HDR_LEN,
-                           pheader->caplen - ETHER_HDR_LEN, &sm);
-      else
+         return 0;
+      case ETHERTYPE_PPPOE:
+         if (opt_want_pppoe)
+            return helper_pppoe(pdata + ETHER_HDR_LEN,
+                                pheader->caplen - ETHER_HDR_LEN,
+                                sm);
          verbosef("ether: got PPPoE frame: maybe you want --pppoe");
-      break;
-   default:
-      verbosef("ether: unknown protocol (0x%04x)", type);
+         return 0;
+      case ETHERTYPE_ARP:
+         /* known protocol, don't complain about it. */
+         return 0;
+      default:
+         verbosef("ether: unknown protocol (0x%04x)", type);
+         return 0;
    }
 }
 
 /* Very similar to decode_null, except on OpenBSD we need to think
  * about family endianness.
  */
-static void
-decode_loop(u_char *user _unused_,
-      const struct pcap_pkthdr *pheader,
-      const u_char *pdata)
-{
+static int decode_loop(DECODER_ARGS) {
    uint32_t family;
-   struct pktsummary sm;
-   memset(&sm, 0, sizeof(sm));
 
    if (pheader->caplen < NULL_HDR_LEN) {
       verbosef("loop: packet too short (%u bytes)", pheader->caplen);
-      return;
+      return 0;
    }
    family = *(const uint32_t *)pdata;
 #ifdef __OpenBSD__
    family = ntohl(family);
 #endif
-   if (family == AF_INET) {
-      decode_ip(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm);
-      sm.time = pheader->ts.tv_sec;
-      acct_for(&sm);
-   }
-   else if (family == AF_INET6) {
-      decode_ipv6(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm);
-      sm.time = pheader->ts.tv_sec;
-      acct_for(&sm);
-   }
-   else
-      verbosef("loop: unknown family (%x)", family);
+   if (family == AF_INET)
+      return helper_ip(pdata + NULL_HDR_LEN,
+                       pheader->caplen - NULL_HDR_LEN, sm);
+   if (family == AF_INET6)
+      return helper_ipv6(pdata + NULL_HDR_LEN,
+                         pheader->caplen - NULL_HDR_LEN, sm);
+   verbosef("loop: unknown family (0x%04x)", family);
+   return 0;
 }
 
-static void
-decode_null(u_char *user _unused_,
-      const struct pcap_pkthdr *pheader,
-      const u_char *pdata)
-{
+static int decode_null(DECODER_ARGS) {
    uint32_t family;
-   struct pktsummary sm;
-   memset(&sm, 0, sizeof(sm));
 
    if (pheader->caplen < NULL_HDR_LEN) {
       verbosef("null: packet too short (%u bytes)", pheader->caplen);
-      return;
+      return 0;
    }
    family = *(const uint32_t *)pdata;
-   if (family == AF_INET) {
-      decode_ip(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm);
-      sm.time = pheader->ts.tv_sec;
-      acct_for(&sm);
-   }
-   else if (family == AF_INET6) {
-      decode_ipv6(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm);
-      sm.time = pheader->ts.tv_sec;
-      acct_for(&sm);
-   }
-   else
-      verbosef("null: unknown family (%x)", family);
+   if (family == AF_INET)
+      return helper_ip(pdata + NULL_HDR_LEN,
+                       pheader->caplen - NULL_HDR_LEN,
+                       sm);
+   if (family == AF_INET6)
+      return helper_ipv6(pdata + NULL_HDR_LEN,
+                         pheader->caplen - NULL_HDR_LEN,
+                         sm);
+   verbosef("null: unknown family (0x%04x)", family);
+   return 0;
 }
 
-static void
-decode_ppp(u_char *user _unused_,
-      const struct pcap_pkthdr *pheader,
-      const u_char *pdata)
-{
-   struct pktsummary sm;
-   memset(&sm, 0, sizeof(sm));
-
+static int decode_ppp(DECODER_ARGS) {
    if (pheader->caplen < PPPOE_HDR_LEN) {
       verbosef("ppp: packet too short (%u bytes)", pheader->caplen);
-      return;
+      return 0;
    }
-
-   if (pdata[2] == 0x00 && pdata[3] == 0x21) {
-         decode_ip(pdata + PPP_HDR_LEN, pheader->caplen - PPP_HDR_LEN, &sm);
-         sm.time = pheader->ts.tv_sec;
-         acct_for(&sm);
-   } else
-      verbosef("non-IP PPP packet; ignoring.");
+   if (pdata[2] == 0x00 && pdata[3] == 0x21)
+      return helper_ip(pdata + PPP_HDR_LEN,
+                       pheader->caplen - PPP_HDR_LEN,
+                       sm);
+   verbosef("ppp: non-IP PPP packet; ignoring.");
+   return 0;
 }
 
-static void
-decode_pppoe(u_char *user _unused_,
-      const struct pcap_pkthdr *pheader,
-      const u_char *pdata)
-{
-   struct pktsummary sm;
-   memset(&sm, 0, sizeof(sm));
-   sm.time = pheader->ts.tv_sec;
-   decode_pppoe_real(pdata, pheader->caplen, &sm);
-}
-
-static void
-decode_pppoe_real(const u_char *pdata, const uint32_t len,
-   struct pktsummary *sm)
-{
-   if (len < PPPOE_HDR_LEN) {
-      verbosef("pppoe: packet too short (%u bytes)", len);
-      return;
-   }
-
-   if (pdata[1] != 0x00) {
-      verbosef("pppoe: code = 0x%02x, expecting 0; ignoring.", pdata[1]);
-      return;
-   }
-
-   if ((pdata[6] == 0xc0) && (pdata[7] == 0x21)) return; /* LCP */
-   if ((pdata[6] == 0xc0) && (pdata[7] == 0x25)) return; /* LQR */
-
-   if ((pdata[6] == 0x00) && (pdata[7] == 0x21)) {
-      decode_ip(pdata + PPPOE_HDR_LEN, len - PPPOE_HDR_LEN, sm);
-      acct_for(sm);
-   } else
-      verbosef("pppoe: non-IP PPPoE packet (0x%02x%02x); ignoring.",
-         pdata[6], pdata[7]);
+static int decode_pppoe(DECODER_ARGS) {
+   return helper_pppoe(pdata, pheader->caplen, sm);
 }
 
+#ifdef DLT_LINUX_SLL
 /* very similar to decode_ether ... */
-static void
-decode_linux_sll(u_char *user _unused_,
-      const struct pcap_pkthdr *pheader,
-      const u_char *pdata)
-{
+static int decode_linux_sll(DECODER_ARGS) {
    const struct sll_header {
       uint16_t packet_type;
       uint16_t device_type;
@@ -325,63 +250,67 @@ decode_linux_sll(u_char *user _unused_,
       uint16_t ether_type;
    } *hdr = (const struct sll_header *)pdata;
    u_short type;
-   struct pktsummary sm;
-   memset(&sm, 0, sizeof(sm));
 
    if (pheader->caplen < SLL_HDR_LEN) {
       verbosef("linux_sll: packet too short (%u bytes)", pheader->caplen);
-      return;
+      return 0;
    }
-
-   type = ntohs( hdr->ether_type );
+   type = ntohs(hdr->ether_type);
    switch (type) {
    case ETHERTYPE_IP:
    case ETHERTYPE_IPV6:
-      decode_ip(pdata + SLL_HDR_LEN, pheader->caplen - SLL_HDR_LEN, &sm);
-      sm.time = pheader->ts.tv_sec;
-      acct_for(&sm);
-      break;
+      return helper_ip(pdata + SLL_HDR_LEN,
+                       pheader->caplen - SLL_HDR_LEN,
+                       sm);
    case ETHERTYPE_ARP:
       /* known protocol, don't complain about it. */
-      break;
+      return 0;
    default:
-      verbosef("linux_sll: unknown protocol (%04x)", type);
+      verbosef("linux_sll: unknown protocol (0x%04x)", type);
+      return 0;
    }
 }
+#endif /* DLT_LINUX_SLL */
 
-static void
-decode_raw(u_char *user _unused_,
-      const struct pcap_pkthdr *pheader,
-      const u_char *pdata)
-{
-   struct pktsummary sm;
-   memset(&sm, 0, sizeof(sm));
-
-   decode_ip(pdata, pheader->caplen, &sm);
-   sm.time = pheader->ts.tv_sec;
-   acct_for(&sm);
+static int decode_raw(DECODER_ARGS) {
+   return helper_ip(pdata, pheader->caplen, sm);
 }
 
-static void decode_ip_payload(const u_char *pdata, const uint32_t len,
-      struct pktsummary *sm);
+static int helper_pppoe(HELPER_ARGS) {
+   if (len < PPPOE_HDR_LEN) {
+      verbosef("pppoe: packet too short (%u bytes)", len);
+      return 0;
+   }
 
-static void
-decode_ip(const u_char *pdata, const uint32_t len, struct pktsummary *sm)
-{
+   if (pdata[1] != 0x00) {
+      verbosef("pppoe: code = 0x%02x, expecting 0; ignoring.", pdata[1]);
+      return 0;
+   }
+
+   if ((pdata[6] == 0xc0) && (pdata[7] == 0x21)) return 0; /* LCP */
+   if ((pdata[6] == 0xc0) && (pdata[7] == 0x25)) return 0; /* LQR */
+
+   if ((pdata[6] == 0x00) && (pdata[7] == 0x21))
+      return helper_ip(pdata + PPPOE_HDR_LEN, len - PPPOE_HDR_LEN, sm);
+
+   verbosef("pppoe: ignoring non-IP PPPoE packet (0x%02x%02x)",
+            pdata[6], pdata[7]);
+   return 0;
+}
+
+static int helper_ip(HELPER_ARGS) {
    const struct ip *hdr = (const struct ip *)pdata;
 
-   if (hdr->ip_v == 6) {
-      /* Redirect parsing of IPv6 packets. */
-      decode_ipv6(pdata, len, sm);
-      return;
-   }
    if (len < IP_HDR_LEN) {
       verbosef("ip: packet too short (%u bytes)", len);
-      return;
+      return 0;
+   }
+   if (hdr->ip_v == 6) {
+      return helper_ipv6(pdata, len, sm);
    }
    if (hdr->ip_v != 4) {
       verbosef("ip: version %d (expecting 4 or 6)", hdr->ip_v);
-      return;
+      return 0;
    }
 
    sm->len = ntohs(hdr->ip_len);
@@ -393,42 +322,57 @@ decode_ip(const u_char *pdata, const uint32_t len, struct pktsummary *sm)
    sm->dst.family = IPv4;
    sm->dst.ip.v4 = hdr->ip_dst.s_addr;
 
-   decode_ip_payload(pdata + IP_HDR_LEN, len - IP_HDR_LEN, sm);
+   helper_ip_deeper(pdata + IP_HDR_LEN, len - IP_HDR_LEN, sm);
+   return 1;
 }
 
-static void
-decode_ipv6(const u_char *pdata, const uint32_t len, struct pktsummary *sm)
-{
+static int helper_ipv6(HELPER_ARGS) {
    const struct ip6_hdr *hdr = (const struct ip6_hdr *)pdata;
 
    if (len < IPV6_HDR_LEN) {
       verbosef("ipv6: packet too short (%u bytes)", len);
-      return;
+      return 0;
    }
-
    if ((hdr->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
       verbosef("ipv6: bad version (%02x, expecting %02x)",
-         hdr->ip6_vfc & IPV6_VERSION_MASK,
-         IPV6_VERSION);
-      return;
+               hdr->ip6_vfc & IPV6_VERSION_MASK, IPV6_VERSION);
+      return 0;
    }
 
+   /* IPv4 has "total length," but IPv6 has "payload length" which doesn't
+    * count the header bytes.
+    */
    sm->len = ntohs(hdr->ip6_plen) + IPV6_HDR_LEN;
    sm->proto = hdr->ip6_nxt;
-
    sm->src.family = IPv6;
    memcpy(&sm->src.ip.v6, &hdr->ip6_src, sizeof(sm->src.ip.v6));
-
    sm->dst.family = IPv6;
    memcpy(&sm->dst.ip.v6, &hdr->ip6_dst, sizeof(sm->dst.ip.v6));
 
-   decode_ip_payload(pdata + IPV6_HDR_LEN, len - IPV6_HDR_LEN, sm);
+   /* Don't do proto accounting if this packet uses extension headers. */
+   switch (sm->proto) {
+      case 0: /* Hop-by-Hop Options */
+      case IPPROTO_NONE:
+      case IPPROTO_DSTOPTS:
+      case IPPROTO_ROUTING:
+      case IPPROTO_FRAGMENT:
+      case IPPROTO_AH:
+      case IPPROTO_ESP:
+      case 135: /* Mobility */
+         sm->proto = IPPROTO_INVALID;
+         return 1; /* but we have addresses, so host accounting is ok */
+
+      default:
+         helper_ip_deeper(pdata + IPV6_HDR_LEN, len - IPV6_HDR_LEN, sm);
+         return 1;
+   }
 }
 
-static void
-decode_ip_payload(const u_char *pdata, const uint32_t len,
-      struct pktsummary *sm)
-{
+static void helper_ip_deeper(HELPER_ARGS) {
+   /* At this stage we have IP addresses so we can do host accounting.
+    * If proto decode fails, we set IPPROTO_INVALID to skip proto accounting.
+    * We don't need to "return 0" like other helpers.
+    */
    switch (sm->proto) {
       case IPPROTO_TCP: {
          const struct tcphdr *thdr = (const struct tcphdr *)pdata;
@@ -441,7 +385,7 @@ decode_ip_payload(const u_char *pdata, const uint32_t len,
          sm->dst_port = ntohs(thdr->th_dport);
          sm->tcp_flags = thdr->th_flags &
             (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG);
-         break;
+         return;
       }
 
       case IPPROTO_UDP: {
@@ -453,19 +397,19 @@ decode_ip_payload(const u_char *pdata, const uint32_t len,
          }
          sm->src_port = ntohs(uhdr->uh_sport);
          sm->dst_port = ntohs(uhdr->uh_dport);
-         break;
+         return;
       }
 
       case IPPROTO_ICMP:
+      case IPPROTO_IGMP:
       case IPPROTO_ICMPV6:
-      case IPPROTO_AH:
-      case IPPROTO_ESP:
       case IPPROTO_OSPF:
          /* known protocol, don't complain about it */
+         sm->proto = IPPROTO_INVALID; /* also don't do accounting */
          break;
 
       default:
-         verbosef("ip: unknown protocol %d", sm->proto);
+         verbosef("ip_deeper: unknown protocol 0x%02x", sm->proto);
    }
 }
 
index 35cb42a..cc4bd55 100644 (file)
--- a/decode.h
+++ b/decode.h
@@ -9,55 +9,49 @@
 #ifndef __DARKSTAT_DECODE_H
 #define __DARKSTAT_DECODE_H
 
-#include <pcap.h>
-#include <netinet/in_systm.h>  /* n_time */
-#define __USE_GNU 1
-#include <netinet/in.h> /* for <netinet/ip.h>  */
-#include <netinet/ip.h> /* struct ip */
-
 #include "addr.h"
 
-#define PPP_HDR_LEN     4
-#define FDDI_HDR_LEN    21
-#define IP_HDR_LEN      sizeof(struct ip)
-#define IPV6_HDR_LEN    sizeof(struct ip6_hdr)
-#define TCP_HDR_LEN     sizeof(struct tcphdr)
-#define UDP_HDR_LEN     sizeof(struct udphdr)
-#define NULL_HDR_LEN    4
-#define PPPOE_HDR_LEN   8
-#define SLL_HDR_LEN     16
-#define RAW_HDR_LEN     0
-
 #ifndef ETHER_ADDR_LEN
-#define ETHER_ADDR_LEN 6
+# define ETHER_ADDR_LEN 6
 #endif
 
+#define IPPROTO_INVALID 254 /* special: means don't do proto accounting */
+
 #ifndef IPPROTO_OSPF
-#define IPPROTO_OSPF 89
+# define IPPROTO_OSPF 89
 #endif
 
-#define IPPROTO_INVALID 254 /* don't do proto accounting */
-
-struct linkhdr {
-   int linktype;
-   unsigned int hdrlen;
-   pcap_handler handler;
-};
-
-const struct linkhdr *getlinkhdr(const int linktype);
-int getsnaplen(const struct linkhdr *lh);
+#define PPPOE_HDR_LEN 8
 
+/* Decoding creates a summary which is passed to accounting. */
 struct pktsummary {
    /* Fields are in host byte order (except IPs) */
    struct addr src, dst;
-   time_t time;
    uint16_t len;
-   uint8_t proto;               /* IPPROTO_{TCP, UDP, ICMP} */
+   uint8_t proto; /* IPPROTO_INVALID means don't do proto accounting */
    uint8_t tcp_flags;           /* only for TCP */
    uint16_t src_port, dst_port; /* only for TCP, UDP */
-   uint8_t src_mac[ETHER_ADDR_LEN],
+   uint8_t src_mac[ETHER_ADDR_LEN], /* only for Ethernet */
            dst_mac[ETHER_ADDR_LEN]; /* only for Ethernet */
 };
 
+struct pcap_pkthdr; /* from pcap.h */
+
+#define DECODER_ARGS const struct pcap_pkthdr *pheader, \
+                     const u_char *pdata, \
+                     struct pktsummary *sm
+
+/* Returns 0 on decode failure (meaning accounting should not be performed) */
+typedef int (decoder_fn)(DECODER_ARGS);
+
+struct linkhdr {
+   int linktype;
+   unsigned int hdrlen;
+   decoder_fn *decoder;
+};
+
+const struct linkhdr *getlinkhdr(const int linktype);
+int getsnaplen(const struct linkhdr *lh);
+
 #endif /* __DARKSTAT_DECODE_H */
 /* vim:set ts=3 sw=3 tw=78 expandtab: */
diff --git a/dns.c b/dns.c
index e8bdbb4..c89dc1b 100644 (file)
--- a/dns.c
+++ b/dns.c
 #include <string.h>
 #include <unistd.h>
 
+#ifdef __NetBSD__
+# define gethostbyaddr(addr, len, type) \
+         gethostbyaddr((const char *)(addr), len, type)
+#endif
+
 static void dns_main(void) _noreturn_; /* the child process runs this */
 
 #define CHILD 0 /* child process uses this socket */
diff --git a/err.c b/err.c
index 358e564..3b42d18 100644 (file)
--- a/err.c
+++ b/err.c
@@ -29,7 +29,6 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
-#include <unistd.h>
 
 static void to_syslog(const char *type, const int want_err,
           const char *format, va_list va) _printflike_(3, 0);
index 8c72102..c0875d4 100644 (file)
@@ -23,6 +23,7 @@
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h> /* for memcpy() */
+#include <time.h>
 
 #define GRAPH_WIDTH "320"
 #define GRAPH_HEIGHT "200"
@@ -46,63 +47,54 @@ static struct graph *graph_db[] = {
 };
 
 static unsigned int graph_db_size = sizeof(graph_db)/sizeof(*graph_db);
+static long start_mono, start_real, last_real;
 
-static time_t start_time, last_time;
-
-void
-graph_init(void)
-{
+void graph_init(void) {
    unsigned int i;
    for (i=0; i<graph_db_size; i++) {
       graph_db[i]->in  = xmalloc(sizeof(uint64_t) * graph_db[i]->num_bars);
       graph_db[i]->out = xmalloc(sizeof(uint64_t) * graph_db[i]->num_bars);
    }
-   start_time = time(NULL);
+   start_mono = now_mono();
+   start_real = now_real();
    graph_reset();
 }
 
-static void
-zero_graph(struct graph *g)
-{
+static void zero_graph(struct graph *g) {
    memset(g->in,  0, sizeof(uint64_t) * g->num_bars);
    memset(g->out, 0, sizeof(uint64_t) * g->num_bars);
 }
 
-void
-graph_reset(void)
-{
+void graph_reset(void) {
    unsigned int i;
+
    for (i=0; i<graph_db_size; i++)
       zero_graph(graph_db[i]);
-   last_time = 0;
+   last_real = 0;
 }
 
-void
-graph_free(void)
-{
+void graph_free(void) {
    unsigned int i;
+
    for (i=0; i<graph_db_size; i++) {
       free(graph_db[i]->in);
       free(graph_db[i]->out);
    }
 }
 
-void
-graph_acct(uint64_t amount, enum graph_dir dir)
-{
+void graph_acct(uint64_t amount, enum graph_dir dir) {
    unsigned int i;
    for (i=0; i<graph_db_size; i++)
-    switch (dir) {
-     case GRAPH_IN:  graph_db[i]->in[  graph_db[i]->pos ] += amount; break;
-     case GRAPH_OUT: graph_db[i]->out[ graph_db[i]->pos ] += amount; break;
-     default: errx(1, "unknown graph_dir in graph_acct: %d", dir);
-    }
+      if (dir == GRAPH_IN) {
+         graph_db[i]->in[  graph_db[i]->pos ] += amount;
+      } else {
+         assert(dir == GRAPH_OUT);
+         graph_db[i]->out[ graph_db[i]->pos ] += amount;
+      }
 }
 
 /* Advance a graph: advance the pos, zeroing out bars as we move. */
-static void
-advance(struct graph *g, const unsigned int pos)
-{
+static void advance(struct graph *g, const unsigned int pos) {
    if (g->pos == pos)
       return; /* didn't need to advance */
    do {
@@ -112,10 +104,9 @@ advance(struct graph *g, const unsigned int pos)
 }
 
 /* Rotate a graph: rotate all bars so that the bar at the current pos is moved
- * to the newly given pos.  This is non-destructive. */
-static void
-rotate(struct graph *g, const unsigned int pos)
-{
+ * to the newly given pos.
+ */
+static void rotate(struct graph *g, const unsigned int pos) {
    uint64_t *tmp;
    unsigned int i, ofs;
    size_t size;
@@ -140,13 +131,11 @@ rotate(struct graph *g, const unsigned int pos)
    g->pos = pos;
 }
 
-static void
-graph_resync(const time_t new_time)
-{
+static void graph_resync(const time_t new_real) {
    struct tm *tm;
    /*
-    * If time went backwards, we assume that real time is continuous and that
-    * the time adjustment should only affect display.  i.e., if we have:
+    * If real time went backwards, we assume that the time adjustment should
+    * only affect display.  i.e., if we have:
     *
     * second 15: 12  bytes
     * second 16: 345 bytes
@@ -159,13 +148,15 @@ graph_resync(const time_t new_time)
     * second 7: 345 bytes
     * second 8: <-- current pos
     *
-    * Note that we don't make any corrections for time being stepped forward.
+    * We don't make any corrections for time being stepped forward,
+    * it's treated as though there was no traffic during that time.
+    *
     * We rely on graph advancement to happen at the correct real time to
     * account for, for example, bandwidth used per day.
     */
-   assert(new_time < last_time);
+   assert(new_real < last_real);
 
-   tm = localtime(&new_time);
+   tm = localtime(&new_real);
    if (tm->tm_sec == 60)
       tm->tm_sec = 59; /* mis-handle leap seconds */
 
@@ -174,25 +165,21 @@ graph_resync(const time_t new_time)
    rotate(&graph_hrs, tm->tm_hour);
    rotate(&graph_days, tm->tm_mday - 1);
 
-   last_time = new_time;
+   last_real = new_real;
 }
 
-void
-graph_rotate(void)
-{
-   time_t t, td;
+void graph_rotate(void) {
+   long t, td;
    struct tm *tm;
    unsigned int i;
 
-   t = now;
+   t = now_real();
+   td = t - last_real;
 
-   if (last_time == 0) {
+   if (last_real == 0) {
       verbosef("first rotate");
-      last_time = t;
+      last_real = t;
       tm = localtime(&t);
-      if (tm->tm_sec == 60)
-         tm->tm_sec = 59; /* mis-handle leap seconds */
-
       graph_secs.pos = tm->tm_sec;
       graph_mins.pos = tm->tm_min;
       graph_hrs.pos = tm->tm_hour;
@@ -200,22 +187,20 @@ graph_rotate(void)
       return;
    }
 
-   if (t == last_time)
-      return; /* superfluous rotate */
+   if (t == last_real)
+      return; /* time has not advanced a full second, don't rotate */
 
-   if (t < last_time) {
-      verbosef("time went backwards! (from %u to %u, offset is %d)",
-         (unsigned int)last_time, (unsigned int)t, (int)(t - last_time));
+   if (t < last_real) {
+      verbosef("graph_db: realtime went backwards! "
+               "(from %ld to %ld, offset is %ld)",
+               last_real, t, td);
       graph_resync(t);
       return;
    }
 
    /* else, normal rotation */
-   td = t - last_time;
-   last_time = t;
+   last_real = t;
    tm = localtime(&t);
-   if (tm->tm_sec == 60)
-      tm->tm_sec = 59; /* mis-handle leap seconds */
 
    /* zero out graphs which have been completely rotated through */
    for (i=0; i<graph_db_size; i++)
@@ -236,14 +221,12 @@ graph_rotate(void)
  * to have validated the header of the segment, and left the file position at
  * the start of the data.
  */
-int
-graph_import(const int fd)
-{
+int graph_import(const int fd) {
    uint64_t last;
    unsigned int i, j;
 
    if (!read64(fd, &last)) return 0;
-   last_time = (time_t)last;
+   last_real = last;
 
    for (i=0; i<graph_db_size; i++) {
       unsigned char num_bars, pos;
@@ -281,12 +264,10 @@ graph_import(const int fd)
  * Database Export: Dump hosts_db into a file provided by the caller.
  * The caller is responsible for writing out the header first.
  */
-int
-graph_export(const int fd)
-{
+int graph_export(const int fd) {
    unsigned int i, j;
 
-   if (!write64(fd, (uint64_t)last_time)) return 0;
+   if (!write64(fd, (uint64_t)last_real)) return 0;
    for (i=0; i<graph_db_size; i++) {
       if (!write8(fd, graph_db[i]->num_bars)) return 0;
       if (!write8(fd, graph_db[i]->pos)) return 0;
@@ -302,26 +283,29 @@ graph_export(const int fd)
 /* ---------------------------------------------------------------------------
  * Web interface: front page!
  */
-struct str *
-html_front_page(void)
-{
+struct str *html_front_page(void) {
    struct str *buf, *rf;
    unsigned int i;
    char start_when[100];
+   long d_real, d_mono;
 
    buf = str_make();
    html_open(buf, "Graphs", /*path_depth=*/0, /*want_graph_js=*/1);
 
+   d_mono = now_mono() - start_mono;
+   d_real = now_real() - start_real;
    str_append(buf, "<p>\n");
    str_append(buf, "<b>Running for</b> <span id=\"rf\">");
-   rf = length_of_time(now - start_time);
-   /* FIXME: use a more monotonic clock perhaps? */
+   rf = length_of_time(d_mono);
    str_appendstr(buf, rf);
    str_free(rf);
    str_append(buf, "</span>");
+   if (abs(d_real - d_mono) > 1)
+      str_appendf(buf, " (real time is off by %qd sec)",
+                  (int64_t)d_real - (int64_t)d_mono);
 
    if (strftime(start_when, sizeof(start_when),
-      "%Y-%m-%d %H:%M:%S %Z%z", localtime(&start_time)) != 0)
+      "%Y-%m-%d %H:%M:%S %Z%z", localtime(&start_real)) != 0)
       str_appendf(buf, "<b>, since</b> %s", start_when);
 
    str_appendf(buf,"<b>.</b><br>\n"
@@ -372,15 +356,13 @@ html_front_page(void)
 /* ---------------------------------------------------------------------------
  * Web interface: graphs.xml
  */
-struct str *
-xml_graphs(void)
-{
+struct str *xml_graphs(void) {
    unsigned int i, j;
    struct str *buf = str_make(), *rf;
 
    str_appendf(buf, "<graphs tp=\"%qu\" tb=\"%qu\" pc=\"%u\" pd=\"%u\" rf=\"",
       acct_total_packets, acct_total_bytes, cap_pkts_recv, cap_pkts_drop);
-   rf = length_of_time(now - start_time);
+   rf = length_of_time(now_real() - start_real);
    str_appendstr(buf, rf);
    str_free(rf);
    str_append(buf, "\">\n");
@@ -402,4 +384,4 @@ xml_graphs(void)
    return (buf);
 }
 
-/* vim:set ts=3 sw=3 tw=78 expandtab: */
+/* vim:set ts=3 sw=3 tw=80 et: */
index 754dacc..c2d7ae4 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h> /* memset(), strcmp() */
+#include <time.h>
 #include <unistd.h>
 
 int hosts_db_show_macs = 0;
@@ -236,7 +237,7 @@ make_func_host(const void *key)
    MAKE_BUCKET(b, h, host);
    h->addr = CASTKEY(struct addr);
    h->dns = NULL;
-   h->lastseen = 0;
+   h->last_seen_mono = 0;
    memset(&h->mac_addr, 0, sizeof(h->mac_addr));
    h->ports_tcp = NULL;
    h->ports_udp = NULL;
@@ -344,16 +345,15 @@ format_row_host(struct str *buf, const struct bucket *b,
       b->in, b->out, b->total);
 
    if (opt_want_lastseen) {
-      time_t last_t = b->u.host.lastseen;
+      long last = b->u.host.last_seen_mono;
       struct str *last_str = NULL;
 
-      if ((now >= last_t) && (last_t > 0))
-         last_str = length_of_time(now - last_t);
+      if ((now_mono() >= last) && (last > 0))
+         last_str = length_of_time(now_mono() - last);
 
-      str_append(buf,
-         " <td class=\"num\">");
+      str_append(buf, " <td class=\"num\">");
       if (last_str == NULL) {
-         if (last_t == 0)
+         if (last == 0)
             str_append(buf, "(never)");
          else
             str_append(buf, "(clock error)");
@@ -361,12 +361,10 @@ format_row_host(struct str *buf, const struct bucket *b,
          str_appendstr(buf, last_str);
          str_free(last_str);
       }
-      str_append(buf,
-         "</td>");
+      str_append(buf, "</td>");
    }
 
-   str_appendf(buf,
-      "</tr>\n");
+   str_appendf(buf, "</tr>\n");
 
    /* Only resolve hosts "on demand" */
    if (b->u.host.dns == NULL)
@@ -1024,14 +1022,12 @@ done:
 /* ---------------------------------------------------------------------------
  * Web interface: detailed view of a single host.
  */
-static struct str *
-html_hosts_detail(const char *ip)
-{
+static struct str *html_hosts_detail(const char *ip) {
    struct bucket *h;
    struct str *buf, *ls_len;
    char ls_when[100];
    const char *canonical;
-   time_t ls;
+   time_t last_real;
 
    h = host_search(ip);
    if (h == NULL)
@@ -1069,13 +1065,13 @@ html_hosts_detail(const char *ip)
       "<p>\n"
       "<b>Last seen:</b> ");
 
-   ls = h->u.host.lastseen;
+   last_real = mono_to_real(h->u.host.last_seen_mono);
    if (strftime(ls_when, sizeof(ls_when),
-      "%Y-%m-%d %H:%M:%S %Z%z", localtime(&ls)) != 0)
+      "%Y-%m-%d %H:%M:%S %Z%z", localtime(&last_real)) != 0)
          str_append(buf, ls_when);
 
-   if (h->u.host.lastseen <= now) {
-      ls_len = length_of_time(now - h->u.host.lastseen);
+   if (h->u.host.last_seen_mono <= now_mono()) {
+      ls_len = length_of_time(now_mono() - h->u.host.last_seen_mono);
       str_append(buf, " (");
       str_appendstr(buf, ls_len);
       str_free(ls_len);
@@ -1103,7 +1099,7 @@ html_hosts_detail(const char *ip)
    format_table(buf, h->u.host.ip_protos, 0,TOTAL,0);
 
    html_close(buf);
-   return (buf);
+   return buf;
 }
 
 /* ---------------------------------------------------------------------------
@@ -1263,7 +1259,7 @@ hosts_db_import_host(const int fd)
    if (ver > 1) {
       uint64_t t;
       if (!read64(fd, &t)) return 0;
-      host->u.host.lastseen = (time_t)t;
+      host->u.host.last_seen_mono = real_to_mono(t);
    }
 
    assert(sizeof(host->u.host.mac_addr) == 6);
@@ -1336,9 +1332,11 @@ int hosts_db_export(const int fd)
       if (!writen(fd, export_tag_host_ver3, sizeof(export_tag_host_ver3)))
          return 0;
 
-      if (!writeaddr(fd, &(b->u.host.addr))) return 0;
+      if (!writeaddr(fd, &(b->u.host.addr)))
+         return 0;
 
-      if (!write64(fd, (uint64_t)(b->u.host.lastseen))) return 0;
+      if (!write64(fd, (uint64_t)mono_to_real(b->u.host.last_seen_mono)))
+         return 0;
 
       assert(sizeof(b->u.host.mac_addr) == 6);
       if (!writen(fd, b->u.host.mac_addr, sizeof(b->u.host.mac_addr)))
index f2f389a..3f869f1 100644 (file)
@@ -9,7 +9,7 @@
 #ifndef __DARKSTAT_HOSTS_DB_H
 #define __DARKSTAT_HOSTS_DB_H
 
-#include <sys/types.h> /* for time_t and uint64_t (esp on FreeBSD) */
+#include <sys/types.h> /* for uint64_t */
 
 #include "addr.h"
 
@@ -19,7 +19,7 @@ struct host {
    struct addr addr;
    char *dns;
    uint8_t mac_addr[6];
-   time_t lastseen;
+   long last_seen_mono;
    struct hashtable *ports_tcp, *ports_udp, *ip_protos;
 };
 
index d17a21f..b6f32dd 100644 (file)
@@ -34,8 +34,8 @@ cmp(const struct bucket * const *x, const struct bucket * const *y,
       b = (*y)->total;
       break;
    case LASTSEEN:
-      a = (*x)->u.host.lastseen;
-      b = (*y)->u.host.lastseen;
+      a = (*x)->u.host.last_seen_mono;
+      b = (*y)->u.host.last_seen_mono;
       break;
    default:
       errx(1, "cmp: unknown direction: %d", dir);
diff --git a/html.c b/html.c
index 2d2b7f7..fe5bb68 100644 (file)
--- a/html.c
+++ b/html.c
@@ -32,11 +32,11 @@ void html_open(struct str *buf, const char *title,
         "<!DOCTYPE html>\n"
         "<html>\n"
         "<head>\n"
-         "<title>%s (darkstat3 %s)</title>\n"
+         "<title>%s (darkstat %s)</title>\n"
          "<meta name=\"generator\" content=\"" PACKAGE_STRING "\">\n"
          "<meta name=\"robots\" content=\"noindex, noarchive\">\n"
-         "<link rel=\"stylesheet\" href=\"%s/style.css\" type=\"text/css\">\n"
-        , title, opt_interface, root);
+         "<link rel=\"stylesheet\" href=\"%s/style.css\" type=\"text/css\">\n",
+        title, title_interfaces, root);
 
     if (want_graph_js)
         str_appendf(buf,
@@ -67,4 +67,4 @@ void html_close(struct str *buf)
         "</html>\n");
 }
 
-/* vim:set ts=4 sw=4 tw=78 expandtab: */
+/* vim:set ts=4 sw=4 tw=80 et: */
diff --git a/http.c b/http.c
index 2835e8e..e39f16b 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,5 +1,5 @@
 /* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2012 Emil Mikulic.
  *
  * http.c: embedded webserver.
  * This borrows a lot of code from darkhttpd.
@@ -33,6 +33,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 #include <zlib.h>
 
@@ -55,7 +56,7 @@ struct connection {
 
     int socket;
     struct sockaddr_storage client;
-    time_t last_active;
+    long last_active_mono;
     enum {
         RECV_REQUEST,          /* receiving request */
         SEND_HEADER_AND_REPLY, /* try to send header+reply together */
@@ -266,7 +267,7 @@ static struct connection *new_connection(void)
 
     conn->socket = -1;
     memset(&conn->client, 0, sizeof(conn->client));
-    conn->last_active = now;
+    conn->last_active_mono = now_mono();
     conn->request = NULL;
     conn->request_length = 0;
     conn->accept_gzip = 0;
@@ -363,13 +364,11 @@ static void free_connection(struct connection *conn)
  * buffer is returned for convenience.
  */
 #define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */
-static char *rfc1123_date(char *dest, const time_t when)
-{
-    time_t tmp = when;
+static char *rfc1123_date(char *dest, time_t when) {
     if (strftime(dest, DATE_LEN,
-        "%a, %d %b %Y %H:%M:%S %Z", gmtime(&tmp) ) == 0)
+        "%a, %d %b %Y %H:%M:%S %Z", gmtime(&when) ) == 0)
             errx(1, "strftime() failed [%s]", dest);
-    return (dest);
+    return dest;
 }
 
 static void generate_header(struct connection *conn,
@@ -382,7 +381,7 @@ static void generate_header(struct connection *conn,
     if (conn->encoding == NULL)
         conn->encoding = encoding_identity;
 
-    verbosef("http: %d %s (%s: %d bytes)", code, text,
+    verbosef("http: %d %s (%s: %zu bytes)", code, text,
         conn->encoding, conn->reply_length);
     conn->header_length = xasprintf(&(conn->header),
         "HTTP/1.1 %d %s\r\n"
@@ -397,7 +396,7 @@ static void generate_header(struct connection *conn,
         "\r\n"
         ,
         code, text,
-        rfc1123_date(date, now), server,
+        rfc1123_date(date, now_real()), server,
         conn->mime_type, conn->reply_length, conn->encoding,
         conn->header_extra);
     conn->http_code = code;
@@ -719,7 +718,7 @@ static void poll_recv_request(struct connection *conn)
         conn->state = DONE;
         return;
     }
-    conn->last_active = now;
+    conn->last_active_mono = now_mono();
 
     /* append to conn->request */
     conn->request = xrealloc(conn->request, conn->request_length+recvd+1);
@@ -772,7 +771,7 @@ static void poll_send_header_and_reply(struct connection *conn)
     iov[1].iov_len = conn->reply_length;
 
     sent = writev(conn->socket, iov, 2);
-    conn->last_active = now;
+    conn->last_active_mono = now_mono();
 
     /* handle any errors (-1) or closure (0) in send() */
     if (sent < 1) {
@@ -814,7 +813,7 @@ static void poll_send_header(struct connection *conn)
 
     sent = send(conn->socket, conn->header + conn->header_sent,
         conn->header_length - conn->header_sent, 0);
-    conn->last_active = now;
+    conn->last_active_mono = now_mono();
     dverbosef("poll_send_header(%d) sent %d bytes", conn->socket, (int)sent);
 
     /* handle any errors (-1) or closure (0) in send() */
@@ -850,7 +849,7 @@ static void poll_send_reply(struct connection *conn)
     sent = send(conn->socket,
         conn->reply + conn->reply_sent,
         conn->reply_length - conn->reply_sent, 0);
-    conn->last_active = now;
+    conn->last_active_mono = now_mono();
     dverbosef("poll_send_reply(%d) sent %d: [%d-%d] of %d",
         conn->socket, (int)sent,
         (int)conn->reply_sent,
@@ -921,14 +920,18 @@ static void http_listen_one(struct addrinfo *ai,
 
     /* create incoming socket */
     if ((sockin = socket(ai->ai_family, ai->ai_socktype,
-            ai->ai_protocol)) == -1)
-        err(1, "http_listen_one(%s, %u): socket(%d (%s), %d, %d) failed",
+            ai->ai_protocol)) == -1) {
+        warn("http_listen_one(%s, %u): socket(%d (%s), %d, %d) failed",
           ipaddr, (unsigned int)bindport,
           ai->ai_family,
           (ai->ai_family == AF_INET6) ? "AF_INET6" :
           (ai->ai_family == AF_INET) ? "AF_INET" :
           "?",
           ai->ai_socktype,  ai->ai_protocol);
+        return;
+    }
+
+    fd_set_nonblock(sockin);
 
     /* reuse address */
     sockopt = 1;
@@ -951,11 +954,12 @@ static void http_listen_one(struct addrinfo *ai,
     /* bind socket */
     if (bind(sockin, ai->ai_addr, ai->ai_addrlen) == -1) {
         warn("bind(\"%s\") failed", ipaddr);
+        close(sockin);
         return;
     }
 
     /* listen on socket */
-    if (listen(sockin, -1) == -1)
+    if (listen(sockin, 128) == -1)
         err(1, "listen() failed");
 
     verbosef("listening on http://%s%s%s:%u/",
@@ -993,7 +997,7 @@ void http_listen(const unsigned short bindport)
         free(bindaddr);
     }
 
-    if (insocks == 0)
+    if (insocks == NULL)
         errx(1, "was not able to bind any ports for http interface");
 
     /* ignore SIGPIPE */
@@ -1022,7 +1026,7 @@ http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd,
 
     LIST_FOREACH_SAFE(conn, &connlist, entries, next)
     {
-        int idlefor = now - conn->last_active;
+        int idlefor = now_mono() - conn->last_active_mono;
 
         /* Time out dead connections. */
         if (idlefor >= idletime) {
index 0c15409..f7b7f93 100644 (file)
--- a/localip.c
+++ b/localip.c
@@ -1,22 +1,27 @@
 /* darkstat 3
  * copyright (c) 2001-2011 Emil Mikulic.
  *
- * localip.c: determine local IP of our capture interface
+ * localip.c: determine local IPs of an interface
  *
  * You may use, modify and redistribute this file under the terms of the
  * GNU General Public License version 2. (see COPYING.GPL)
  */
 
 #include "addr.h"
+#include "bsd.h" /* for strlcpy */
 #include "config.h" /* for HAVE_IFADDRS_H */
+#include "conv.h"
 #include "err.h"
 #include "localip.h"
-#include "bsd.h" /* for strlcpy */
+#include "now.h"
 
 #include <sys/socket.h>
 #include <net/if.h>
+#include <assert.h>
 #include <errno.h>
+#include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #ifdef HAVE_IFADDRS_H
 # include <sys/ioctl.h>
 #endif
 
-static const char *iface = NULL;
-struct addr localip4, localip6;
-static struct addr last_localip4, last_localip6;
-
-void
-localip_init(const char *interface)
-{
-   iface = interface;
-
-   /* defaults */
-   localip4.family = IPv4;
-   localip4.ip.v4 = 0;
-
-   localip6.family = IPv6;
-   memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
+void localip_init(struct local_ips *ips) {
+   ips->is_valid = 0;
+   ips->last_update_mono = 0;
+   ips->num_addrs = 0;
+   ips->addrs = NULL;
+}
 
-   last_localip4 = localip4;
-   last_localip6 = localip6;
+void localip_free(struct local_ips *ips) {
+   if (ips->addrs != NULL)
+      free(ips->addrs);
+}
 
-   /* initial update */
-   localip_update();
+static void add_ip(const char *iface,
+                   struct local_ips *ips,
+                   int *idx,
+                   struct addr *a) {
+   if (ips->num_addrs <= *idx) {
+      /* Grow. */
+      ips->addrs = xrealloc(ips->addrs, sizeof(*(ips->addrs)) * (*idx + 1));
+      ips->num_addrs++;
+      assert(ips->num_addrs > *idx);
+      verbosef("interface '%s' gained new address %s", iface, addr_to_str(a));
+   } else {
+      /* Warn about changed address. */
+      if (!addr_equal(ips->addrs + *idx, a)) {
+         static char before[INET6_ADDRSTRLEN];
+         strncpy(before, addr_to_str(ips->addrs + *idx), INET6_ADDRSTRLEN);
+         verbosef("interface '%s' address %d/%d changed from %s to %s",
+            iface, *idx+1, ips->num_addrs, before, addr_to_str(a));
+      }
+   }
+   ips->addrs[*idx] = *a;
+   (*idx)++;
 }
 
-static void
-localip_update_helper(void)
-{
-   /* defaults */
-   localip4.family = IPv4;
-   localip4.ip.v4 = 0;
+/* Returns 0 on failure. */
+void localip_update(const char *iface, struct local_ips *ips) {
+   struct addr a;
+   int new_addrs = 0;
 
-   localip6.family = IPv6;
-   memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
+   if (iface == NULL) {
+      /* reading from capfile */
+      ips->is_valid = 0;
+      return;
+   }
 
-   if (iface == NULL)
-      return; /* reading from capfile */
+   if (ips->last_update_mono == now_mono()) {
+      /* Too soon, bail out. */
+      return;
+   }
+   ips->last_update_mono = now_mono();
 
 #ifdef HAVE_IFADDRS_H
    {
-      int got_v4 = 0, got_v6 = 0;
       struct ifaddrs *ifas, *ifa;
 
-      if (getifaddrs(&ifas) < 0) {
-         warn("can't getifaddrs() on interface \"%s\"", iface);
-         return;
-      }
-
-      for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
-         if (got_v4 && got_v6)
-            break;   /* Task is already complete. */
+      if (getifaddrs(&ifas) < 0)
+         err(1, "getifaddrs() failed");
 
+      for (ifa=ifas; ifa; ifa=ifa->ifa_next) {
          if (strncmp(ifa->ifa_name, iface, IFNAMSIZ))
             continue;   /* Wrong interface. */
 
          if (!ifa->ifa_addr)
             continue;   /* This can be NULL, e.g. for ppp0. */
 
-         /* The first IPv4 name is always functional. */
-         if ((ifa->ifa_addr->sa_family == AF_INET) && !got_v4)
-         {
-            /* Good IPv4 address. */
-            localip4.ip.v4 =
-               ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
-            got_v4 = 1;
-            continue;
+         if (ifa->ifa_addr->sa_family == AF_INET) {
+            a.family = IPv4;
+            a.ip.v4 = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
+            add_ip(iface, ips, &new_addrs, &a);
          }
-
-         /* IPv6 needs some obvious exceptions. */
-         if ( ifa->ifa_addr->sa_family == AF_INET6 ) {
-            struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
-
+         if (ifa->ifa_addr->sa_family == AF_INET6) {
+            struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+# if 0
             if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
                  || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
                continue;
-
-            /* Only standard IPv6 can reach this point. */
-            memcpy(&(localip6.ip.v6), &sa6->sin6_addr, sizeof(localip6.ip.v6));
-            got_v6 = 1;
+# endif
+            a.family = IPv6;
+            memcpy(&(a.ip.v6), &sa6->sin6_addr, sizeof(a.ip.v6));
+            add_ip(iface, ips, &new_addrs, &a);
          }
       }
-
       freeifaddrs(ifas);
-
-      if (!got_v4)
-          warnx("can't get own IPv4 address on interface \"%s\"", iface);
    }
 #else /* don't HAVE_IFADDRS_H */
    {
@@ -121,34 +126,39 @@ localip_update_helper(void)
 
       strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
       ifr.ifr_addr.sa_family = AF_INET;
-      if (ioctl(tmp, SIOCGIFADDR, &ifr) == -1) {
-         if (errno == EADDRNOTAVAIL) {
-            verbosef("lost local IP");
-         } else
-            warn("can't get own IP address on interface \"%s\"", iface);
-      } else {
-         /* success! */
+      if (ioctl(tmp, SIOCGIFADDR, &ifr) != -1) {
          sa = ifr.ifr_addr;
-         localip4.ip.v4 = ((struct sockaddr_in*)&sa)->sin_addr.s_addr;
+         a.family = IPv4;
+         a.ip.v4 = ((struct sockaddr_in*)(&ifr.ifr_addr))->sin_addr.s_addr;
+         add_ip(iface, ips, &new_addrs, &a);
       }
       close(tmp);
    }
 #endif
+   if (new_addrs == 0) {
+      if (ips->is_valid)
+         verbosef("interface '%s' no longer has any addresses", iface);
+      ips->is_valid = 0;
+   } else {
+      if (!ips->is_valid)
+         verbosef("interface '%s' now has addresses", iface);
+      ips->is_valid = 1;
+      if (ips->num_addrs != new_addrs)
+         verbosef("interface '%s' number of addresses decreased from %d to %d",
+            iface, ips->num_addrs, new_addrs);
+      ips->num_addrs = new_addrs;
+   }
 }
 
-void
-localip_update(void)
-{
-   localip_update_helper();
+int is_localip(const struct addr * const a,
+               const struct local_ips * const ips) {
+   int i;
 
-   if (!addr_equal(&last_localip4, &localip4)) {
-      verbosef("%s ip4 update: %s", iface, addr_to_str(&localip4));
-      last_localip4 = localip4;
-   }
-   if (!addr_equal(&last_localip6, &localip6)) {
-      verbosef("%s ip6 update: %s", iface, addr_to_str(&localip6));
-      last_localip6 = localip6;
+   for (i=0; i<ips->num_addrs; i++) {
+      if (addr_equal(a, ips->addrs+i))
+         return 1;
    }
+   return 0;
 }
 
-/* vim:set ts=3 sw=3 tw=78 expandtab: */
+/* vim:set ts=3 sw=3 tw=80 et: */
index 21231b6..a5b48ea 100644 (file)
--- a/localip.h
+++ b/localip.h
@@ -1,15 +1,29 @@
 /* darkstat 3
  * copyright (c) 2001-2011 Emil Mikulic.
  *
- * localip.h: determine local IP of our capture interface
+ * localip.h: determine the local IPs of an interface
  *
  * You may use, modify and redistribute this file under the terms of the
  * GNU General Public License version 2. (see COPYING.GPL)
  */
+#ifndef __DARKSTAT_LOCALIP_H
+#define __DARKSTAT_LOCALIP_H
 
-extern struct addr localip4, localip6;
+#include <time.h>
 
-void localip_init(const char *interface);
-void localip_update(void);
+struct local_ips {
+   int is_valid;
+   long last_update_mono;
+   int num_addrs;
+   struct addr *addrs;
+};
 
-/* vim:set ts=3 sw=3 tw=78 expandtab: */
+void localip_init(struct local_ips *ips);
+void localip_free(struct local_ips *ips);
+
+void localip_update(const char *iface, struct local_ips *ips);
+int is_localip(const struct addr * const a,
+               const struct local_ips * const ips);
+
+#endif
+/* vim:set ts=3 sw=3 tw=80 et: */
diff --git a/now.c b/now.c
new file mode 100644 (file)
index 0000000..f0751a8
--- /dev/null
+++ b/now.c
@@ -0,0 +1,152 @@
+/* darkstat 3
+ * copyright (c) 2012 Emil Mikulic.
+ *
+ * now.c: a cache of the current time.
+ *
+ * Permission to use, copy, modify, and distribute this file for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "err.h"
+#include "now.h"
+
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef __MACH__
+/* Fake up clock_gettime() on OS X. */
+# include <sys/time.h>
+# include <inttypes.h>
+# include <mach/mach.h>
+# include <mach/mach_time.h>
+
+typedef int clockid_t;
+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 1
+
+static uint64_t mono_first = 0;
+
+int clock_gettime(clockid_t clk_id, struct timespec *tp) {
+   if (clk_id == CLOCK_REALTIME) {
+      struct timeval tv;
+      gettimeofday(&tv, NULL);
+      tp->tv_sec = tv.tv_sec;
+      tp->tv_nsec = tv.tv_usec * 1000;
+      return 0;
+   }
+   if (clk_id == CLOCK_MONOTONIC) {
+      uint64_t t = mach_absolute_time();
+      mach_timebase_info_data_t timebase;
+      mach_timebase_info(&timebase);
+      if (!mono_first) {
+         mono_first = t;
+      }
+      uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom;
+      tp->tv_sec = tdiff / 1000000000;
+      tp->tv_nsec = tdiff % 1000000000;
+      return 0;
+   }
+   return -1;
+}
+#endif  /* __MACH__ */
+
+static struct timespec clock_real, clock_mono;
+static int now_initialized = 0;
+
+long now_real(void) {
+   assert(now_initialized);
+   return clock_real.tv_sec;
+}
+
+long now_mono(void) {
+   assert(now_initialized);
+   return clock_mono.tv_sec;
+}
+
+static int before(const struct timespec *a, const struct timespec *b) {
+   if (a->tv_sec < b->tv_sec)
+      return 1;
+   if (a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec)
+      return 1;
+   return 0;
+}
+
+static void clock_update(const clockid_t clk_id,
+                         struct timespec *dest,
+                         const char *name) {
+   struct timespec t;
+
+   clock_gettime(clk_id, &t);
+   if (now_initialized && before(&t, dest)) {
+      verbosef("%s clock went backwards from %ld.%09ld to %ld.%09ld",
+               name,
+               (long)dest->tv_sec,
+               (long)dest->tv_nsec,
+               (long)t.tv_sec,
+               (long)t.tv_nsec);
+   }
+   memcpy(dest, &t, sizeof(t));
+}
+
+static void all_clocks_update(void) {
+   clock_update(CLOCK_REALTIME,  &clock_real, "realtime");
+   clock_update(CLOCK_MONOTONIC, &clock_mono, "monotonic");
+}
+
+void now_init(void) {
+   assert(!now_initialized);
+   all_clocks_update();
+   now_initialized = 1;
+}
+
+void now_update(void) {
+   assert(now_initialized);
+   all_clocks_update();
+}
+
+long mono_to_real(const long t) {
+   assert(now_initialized);
+   return t - clock_mono.tv_sec + clock_real.tv_sec;
+}
+
+long real_to_mono(const long t) {
+   assert(now_initialized);
+   return t - clock_real.tv_sec + clock_mono.tv_sec;
+}
+
+void timer_start(struct timespec *t) {
+   clock_gettime(CLOCK_MONOTONIC, t);
+}
+
+static int64_t ts_diff(const struct timespec * const a,
+                       const struct timespec * const b) {
+   return (int64_t)(a->tv_sec - b->tv_sec) * 1000000000 +
+          a->tv_nsec - b->tv_nsec;
+}
+
+void timer_stop(const struct timespec * const t,
+                const int64_t nsec,
+                const char *warning) {
+   struct timespec t2;
+   int64_t diff;
+
+   clock_gettime(CLOCK_MONOTONIC, &t2);
+   diff = ts_diff(&t2, t);
+   assert(diff > 0);
+   if (diff > nsec)
+      warnx("%s (took %lld nsec, over threshold of %lld nsec)",
+            warning,
+            (long long)diff,
+            (long long)nsec);
+}
+
+/* vim:set ts=3 sw=3 tw=80 et: */
diff --git a/now.h b/now.h
index 2dd8e4a..9c61f45 100644 (file)
--- a/now.h
+++ b/now.h
@@ -1,9 +1,36 @@
 /* darkstat 3
  * copyright (c) 2001-2006 Emil Mikulic.
  *
- * now.h: a cache of the current time
- * This lets us avoid superfluous gettimeofday() syscalls.
+ * now.h: a cache of the current time.
+ *
+ * Permission to use, copy, modify, and distribute this file for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#include <time.h>
+#include <sys/types.h>
+
+void now_init(void);
+void now_update(void); /* once per event loop (in darkstat.c) */
+
+long now_real(void);
+long now_mono(void);
+
+long mono_to_real(const long t);
+long real_to_mono(const long t);
+
+/* Emits warnings if a call is too slow. */
+struct timespec;
+void timer_start(struct timespec *t);
+void timer_stop(const struct timespec * const t,
+                const int64_t nsec,
+                const char *warning);
 
-extern time_t now; /* updated in the event loop in darkstat.c */
+/* vim:set ts=3 sw=3 tw=80 et: */
diff --git a/opt.h b/opt.h
index 5d75cf3..34afc36 100644 (file)
--- a/opt.h
+++ b/opt.h
@@ -4,29 +4,22 @@
  * opt.h: global options
  */
 
-/*
- * Capture options.
- */
+/* Capture options. */
 extern int opt_want_pppoe;
 extern int opt_want_macs;
 extern int opt_want_hexdump;
 extern int opt_want_snaplen;
 extern int opt_wait_secs;
 
-/*
- * Error/logging options.
- */
+/* Error/logging options. */
 extern int opt_want_verbose;
 extern int opt_want_syslog;
 
-/*
- * Accounting options.
- */
+/* Accounting options. */
 extern unsigned int opt_highest_port;
 extern int opt_want_local_only;
 
-/*
- * Hosts table reduction - when the number of entries is about to exceed
+/* Hosts table reduction - when the number of entries is about to exceed
  * <max>, we reduce the table to the top <keep> entries.
  */
 extern unsigned int opt_hosts_max;
@@ -34,10 +27,10 @@ extern unsigned int opt_hosts_keep;
 extern unsigned int opt_ports_max;
 extern unsigned int opt_ports_keep;
 
-/*
- * Hosts output options.
- */
+/* Hosts output options. */
 extern int opt_want_lastseen;
-extern const char *opt_interface;
+
+/* Initialized in cap.c, added to <title> */
+extern char *title_interfaces;
 
 /* vim:set ts=3 sw=3 tw=78 expandtab: */
diff --git a/queue.h b/queue.h
index 95a1276..5773a91 100644 (file)
--- a/queue.h
+++ b/queue.h
@@ -49,6 +49,11 @@ struct {                                                             \
 
 #define        STAILQ_FIRST(head)      ((head)->stqh_first)
 
+#define STAILQ_FOREACH(var, head, field)                                \
+        for((var) = STAILQ_FIRST((head));                               \
+           (var);                                                       \
+           (var) = STAILQ_NEXT((var), field))
+
 #define        STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
 
 #ifdef STAILQ_INSERT_TAIL
diff --git a/str.c b/str.c
index 8092666..852a24d 100644 (file)
--- a/str.c
+++ b/str.c
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "conv.h"
+#include "err.h"
+#include "str.h"
+
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdint.h> /* for uint32_t on Linux and OS X */
-
-#include "conv.h"
-#include "err.h"
-#include "str.h"
+#include <unistd.h>
 
 #define INITIAL_LEN 1024
 
@@ -105,7 +106,8 @@ static void
 str_append_u32(struct str *s, const uint32_t i, const int mod_sep)
 {
    char out[I32_MAXLEN];
-   int pos, len;
+   int pos;
+   unsigned int len;
    uint32_t rem, next;
 
    if (i == 0) {
@@ -148,7 +150,8 @@ static void
 str_append_u64(struct str *s, const uint64_t i, const int mod_sep)
 {
    char out[I64_MAXLEN];
-   int pos, len;
+   int pos;
+   unsigned int len;
    uint64_t rem, next;
    uint32_t rem32, next32;
 
@@ -224,9 +227,7 @@ str_append_hex8(struct str *s, const uint8_t b)
  *
  * %x is equivalent to %02x and expects a uint8_t
  */
-static void
-str_vappendf(struct str *s, const char *format, va_list va)
-{
+void str_vappendf(struct str *s, const char *format, va_list va) {
    size_t pos, len;
    len = strlen(format);
 
@@ -354,4 +355,12 @@ length_of_time(const time_t t)
    return buf;
 }
 
+ssize_t str_write(const struct str * const buf, const int fd) {
+   return write(fd, buf->buf, buf->len);
+}
+
+size_t str_len(const struct str * const buf) {
+   return buf->len;
+}
+
 /* vim:set ts=3 sw=3 tw=78 expandtab: */
diff --git a/str.h b/str.h
index da8a2dc..92cb78f 100644 (file)
--- a/str.h
+++ b/str.h
@@ -31,9 +31,10 @@ void str_appendstr(struct str *buf, const struct str *s);
 
 #ifdef __GNUC__
 /* amusing efficiency hack */
-#include <string.h>
-#define str_append(buf, s) str_appendn(buf, s, \
-    (__builtin_constant_p(s) ? sizeof(s)-1 : strlen(s)) )
+# include <string.h>
+# define str_append(buf, s) \
+   str_appendn(buf, s, \
+               (__builtin_constant_p(s) ? sizeof(s)-1 : strlen(s)) )
 #else
 void str_append(struct str *buf, const char *s);
 #endif
@@ -41,7 +42,10 @@ void str_append(struct str *buf, const char *s);
 size_t xvasprintf(char **result, const char *format, va_list va);
 size_t xasprintf(char **result, const char *format, ...);
 void str_appendf(struct str *buf, const char *format, ...);
+void str_vappendf(struct str *s, const char *format, va_list va);
 
 struct str *length_of_time(const time_t t);
+ssize_t str_write(const struct str * const buf, const int fd);
+size_t str_len(const struct str * const buf);
 
 /* vim:set ts=3 sw=3 tw=78 expandtab: */