+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.
- 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.
v2.6 (Nov 2003)
End of the line for darkstat 2
+
+vim:set noet ts=8 sw=8 tw=72:
http.c \
localip.c \
ncache.c \
+now.c \
pidfile.c \
str.c
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
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
-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.
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;
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);
}
/* 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;
}
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);
}
}
#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: */
/* 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))
}
/* 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)
}
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.
*
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 */
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
*/
*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);
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");
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: */
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: */
/* 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
#! /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
# 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
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'\" &&
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 :
} # 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
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).
# ... 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
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'"
# 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/'
with_privdrop_user
enable_silent_rules
enable_debug
-enable_mad_warnings
+enable_warnings
with_pcap
'
ac_precious_vars='build_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
# 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]...
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
--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]
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
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
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 $@
# 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.
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
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
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
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
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
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
/* 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);
$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; }
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
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
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 :
done
+fi
+
+
+
# Some OSes (Solaris) need sys/sockio.h for SIOCGIFADDR
for ac_header in sys/sockio.h
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
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
# ... 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
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'"
# 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
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."
_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'
-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])
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"
# 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)
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
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
#include "hosts_db.h"
#include "localip.h"
#include "ncache.h"
+#include "now.h"
#include "pidfile.h"
#include <assert.h>
#include <unistd.h>
#include <pcap.h>
-#include "now.h"
-time_t now;
-
#ifndef INADDR_NONE
# define INADDR_NONE (-1) /* Solaris */
#endif
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;
}
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;
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; }
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)
{
};
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},
{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;
"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;
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();
}
/* 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)) {
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 --- */
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;
}
/* 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");
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");
#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. */
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$
*/
}
#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
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 */
*/
#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[] = {
{ -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;
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);
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;
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: {
}
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);
}
}
#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: */
#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 */
#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);
#include <assert.h>
#include <stdlib.h>
#include <string.h> /* for memcpy() */
+#include <time.h>
#define GRAPH_WIDTH "320"
#define GRAPH_HEIGHT "200"
};
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 {
}
/* 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;
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
* 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 */
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;
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++)
* 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;
* 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;
/* ---------------------------------------------------------------------------
* 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"
/* ---------------------------------------------------------------------------
* 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");
return (buf);
}
-/* vim:set ts=3 sw=3 tw=78 expandtab: */
+/* vim:set ts=3 sw=3 tw=80 et: */
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* memset(), strcmp() */
+#include <time.h>
#include <unistd.h>
int hosts_db_show_macs = 0;
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;
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)");
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)
/* ---------------------------------------------------------------------------
* 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)
"<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);
format_table(buf, h->u.host.ip_protos, 0,TOTAL,0);
html_close(buf);
- return (buf);
+ return buf;
}
/* ---------------------------------------------------------------------------
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);
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)))
#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"
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;
};
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);
"<!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,
"</html>\n");
}
-/* vim:set ts=4 sw=4 tw=78 expandtab: */
+/* vim:set ts=4 sw=4 tw=80 et: */
/* 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.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#include <zlib.h>
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 */
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;
* 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,
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"
"\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;
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);
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) {
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() */
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,
/* 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;
/* 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/",
free(bindaddr);
}
- if (insocks == 0)
+ if (insocks == NULL)
errx(1, "was not able to bind any ports for http interface");
/* ignore SIGPIPE */
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) {
/* 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 */
{
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: */
/* 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: */
--- /dev/null
+/* 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: */
/* 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: */
* 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;
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: */
#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
* 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
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) {
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;
*
* %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);
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: */
#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
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: */