From 4da209d9116b41ab59eeedc5823bc2cb64b5bc3e Mon Sep 17 00:00:00 2001 From: Emil Mikulic Date: Sun, 18 Aug 2013 16:34:48 +1000 Subject: [PATCH] Imported Upstream version 3.0.717 --- ChangeLog | 16 +- Makefile.in | 14 +- NEWS | 28 +-- acct.c | 62 +++--- acct.h | 6 +- cap.c | 540 ++++++++++++++++++++++++++++++--------------------- cap.h | 6 +- config.h.in | 3 - configure | 484 ++++++++++++++++++++++----------------------- configure.ac | 155 +++------------ darkstat.c | 134 ++++++------- daylog.c | 166 +++++++--------- db.c | 21 +- db.h | 4 - decode.c | 430 ++++++++++++++++++---------------------- decode.h | 58 +++--- dns.c | 5 + err.c | 1 - graph_db.c | 138 ++++++------- hosts_db.c | 44 ++--- hosts_db.h | 4 +- hosts_sort.c | 4 +- html.c | 8 +- http.c | 42 ++-- localip.c | 172 ++++++++-------- localip.h | 24 ++- now.c | 152 +++++++++++++++ now.h | 35 +++- opt.h | 23 +-- queue.h | 5 + str.c | 27 ++- str.h | 10 +- 32 files changed, 1459 insertions(+), 1362 deletions(-) create mode 100644 now.c diff --git a/ChangeLog b/ChangeLog index f14816c..561c567 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +v3.0.717 (14 August 2013) + - (OS X only) Work around lack of clock_gettime(). + - Fix crash due to str_appendf() not understanding %ld. + +v3.0.716 (8 August 2013) + - Implement support for multiple capture interfaces. + - Support multiple local IPs on an interface. + - Only error out if we fail to create all HTTP sockets. + In particular, this helps on IPv6-incapable platforms. + - Use monotonic time over wall time where appropriate. + - Portability fixes for NetBSD and OpenBSD. + v3.0.715 (January 2012) - Compatibility fixes for Hurd and Solaris. - Use link-time optimization and automake-like silent rules. @@ -5,7 +17,7 @@ v3.0.715 (January 2012) - Continuing fixes for IPv6 support. - Only update lastseen time for sender, not recipient. - Implement --local-only: accounting for hosts on the local net. - - Make failure to bind() a socket non-fatal. + - Make failure to bind() a socket non-fatal. - Make failure to get local IP non-fatal. - Fall back to gethostbyaddr() if getnameinfo() fails. - Fix detection of IPv4 multicast addresses. @@ -166,3 +178,5 @@ v3.0.471 (Jun 2006) v2.6 (Nov 2003) End of the line for darkstat 2 + +vim:set noet ts=8 sw=8 tw=72: diff --git a/Makefile.in b/Makefile.in index 27e971d..b0dbf2e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -43,6 +43,7 @@ html.c \ http.c \ localip.c \ ncache.c \ +now.c \ pidfile.c \ str.c @@ -124,16 +125,15 @@ acct.o: acct.c acct.h decode.h addr.h conv.h daylog.h graph_db.h err.h \ cdefs.h hosts_db.h localip.h now.h opt.h addr.o: addr.c addr.h bsd.o: bsd.c bsd.h config.h cdefs.h -cap.o: cap.c cdefs.h cap.h config.h conv.h decode.h addr.h hosts_db.h \ - localip.h opt.h err.h +cap.o: cap.c acct.h cdefs.h cap.h config.h conv.h decode.h addr.h err.h \ + hosts_db.h localip.h now.h opt.h queue.h str.h conv.o: conv.c conv.h err.h cdefs.h darkstat.o: darkstat.c acct.h cap.h cdefs.h config.h conv.h daylog.h \ graph_db.h db.h dns.h err.h http.h hosts_db.h addr.h localip.h ncache.h \ - pidfile.h now.h + now.h pidfile.h daylog.o: daylog.c err.h cdefs.h daylog.h graph_db.h str.h now.h db.o: db.c cdefs.h err.h hosts_db.h addr.h graph_db.h db.h -decode.o: decode.c cdefs.h acct.h cap.h config.h decode.h addr.h err.h \ - opt.h +decode.o: decode.c cdefs.h decode.h addr.h err.h opt.h dns.o: dns.c cdefs.h conv.h decode.h addr.h dns.h err.h hosts_db.h \ queue.h str.h tree.h bsd.h config.h err.o: err.c cdefs.h err.h opt.h pidfile.h bsd.h config.h @@ -145,7 +145,9 @@ hosts_sort.o: hosts_sort.c cdefs.h err.h hosts_db.h addr.h html.o: html.c config.h str.h html.h opt.h http.o: http.c cdefs.h config.h conv.h err.h graph_db.h hosts_db.h addr.h \ http.h now.h queue.h str.h stylecss.h graphjs.h -localip.o: localip.c addr.h config.h err.h cdefs.h localip.h bsd.h +localip.o: localip.c addr.h bsd.h config.h conv.h err.h cdefs.h localip.h \ + now.h ncache.o: ncache.c conv.h err.h cdefs.h ncache.h tree.h bsd.h config.h +now.o: now.c err.h cdefs.h now.h pidfile.o: pidfile.c err.h cdefs.h str.h pidfile.h str.o: str.c conv.h err.h cdefs.h str.h diff --git a/NEWS b/NEWS index 0ec2855..c40c9de 100644 --- a/NEWS +++ b/NEWS @@ -1,19 +1,19 @@ -darkstat v3.0.540 and earlier defaulted to running in the foreground -and had a "-d" commandline argument to get darkstat to daemonize, -detach from the controlling terminal, and run in the background. +Changes to defaults, most recent first: -Since r540, the default has been inverted. darkstat will daemonize by -default. +- After v3.0.708, --debug was split into --verbose and --no-daemon. -Since r626, this behaviour can be suppressed with the "--debug" -commandline argument, to force darkstat to stay in the foreground for -debugging purposes. +- Since v3.0.694, darkstat is able to save its internal database into a file and + reload it on startup. The file format is, by design, incompatible with the + format from darkstat v2. -The "-d" argument is no longer recognized, and will prevent darkstat -from starting, so make sure you adjust any startup scripts you may have. +- After v3.0.626, daemonizing can be suppressed with the "--debug" commandline + argument, to force darkstat to stay in the foreground for debugging purposes. -After 3.0.708, --debug was split into --verbose and --no-daemon. + The "-d" argument is no longer recognized, and will prevent darkstat + from starting, so make sure you adjust any startup scripts you may have. -Since v3.0.694, darkstat is able to save its internal database into a -file and reload it on startup. The file format is, by design, -incompatible with the format from darkstat v2. +- v3.0.540 and earlier defaulted to running in the foreground and had a "-d" + commandline argument to get darkstat to daemonize, detach from the controlling + terminal, and run in the background. + + After 540, the default has been inverted. darkstat will daemonize by default. diff --git a/acct.c b/acct.c index 62ad076..17aa4f0 100644 --- a/acct.c +++ b/acct.c @@ -133,34 +133,23 @@ acct_init_localnet(const char *spec) verbosef(" local network mask: %s", addr_to_str(&localmask)); } -static int -addr_is_local(const struct addr * const a) -{ - if (a->family == IPv4) { - if (using_localnet4) { - if (addr_inside(a, &localnet4, &localmask4)) - return 1; - } else { - if (addr_equal(a, &localip4)) - return 1; - } - } else { - assert(a->family == IPv6); - if (using_localnet6) { - if (addr_inside(a, &localnet6, &localmask6)) - return 1; - } else { - if (addr_equal(a, &localip6)) - return 1; - } +static int addr_is_local(const struct addr * const a, + const struct local_ips *local_ips) { + if (is_localip(a, local_ips)) + return 1; + if (a->family == IPv4 && using_localnet4) { + if (addr_inside(a, &localnet4, &localmask4)) + return 1; + } else if (a->family == IPv6 && using_localnet6) { + if (addr_inside(a, &localnet6, &localmask6)) + return 1; } return 0; } /* Account for the given packet summary. */ -void -acct_for(const struct pktsummary * const sm) -{ +void acct_for(const struct pktsummary * const sm, + const struct local_ips * const local_ips) { struct bucket *hs = NULL, *hd = NULL; struct bucket *ps, *pd; int dir_in, dir_out; @@ -189,18 +178,15 @@ acct_for(const struct pktsummary * const sm) acct_total_bytes += sm->len; /* Graphs. */ - dir_out = addr_is_local(&(sm->src)); - dir_in = addr_is_local(&(sm->dst)); + dir_out = addr_is_local(&sm->src, local_ips); + dir_in = addr_is_local(&sm->dst, local_ips); /* Traffic staying within the network isn't counted. */ - if (dir_in == 1 && dir_out == 1) - dir_in = dir_out = 0; - - if (dir_out) { + if (dir_out && !dir_in) { daylog_acct((uint64_t)sm->len, GRAPH_OUT); graph_acct((uint64_t)sm->len, GRAPH_OUT); } - if (dir_in) { + if (dir_in && !dir_out) { daylog_acct((uint64_t)sm->len, GRAPH_IN); graph_acct((uint64_t)sm->len, GRAPH_IN); } @@ -209,15 +195,15 @@ acct_for(const struct pktsummary * const sm) /* Hosts. */ hosts_db_reduce(); - if (!opt_want_local_only || addr_is_local(&sm->src)) { + if (!opt_want_local_only || dir_out) { hs = host_get(&(sm->src)); hs->out += sm->len; hs->total += sm->len; memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac)); - hs->u.host.lastseen = now; + hs->u.host.last_seen_mono = now_mono(); } - if (!opt_want_local_only || addr_is_local(&sm->dst)) { + if (!opt_want_local_only || dir_in) { hd = host_get(&(sm->dst)); hd->in += sm->len; hd->total += sm->len; @@ -274,16 +260,12 @@ acct_for(const struct pktsummary * const sm) } break; - case IPPROTO_ICMP: - case IPPROTO_ICMPV6: - case IPPROTO_AH: - case IPPROTO_ESP: - case IPPROTO_OSPF: - /* known protocol, don't complain about it */ + case IPPROTO_INVALID: + /* proto decoding failed, don't complain in accounting */ break; default: - verbosef("unknown IP proto (%04x)", sm->proto); + verbosef("acct_for: unknown IP protocol 0x%02x", sm->proto); } } diff --git a/acct.h b/acct.h index 3c2828b..b4edb7f 100644 --- a/acct.h +++ b/acct.h @@ -7,10 +7,12 @@ #include struct pktsummary; +struct local_ips; extern uint64_t acct_total_packets, acct_total_bytes; void acct_init_localnet(const char *spec); -void acct_for(const struct pktsummary * const sm); +void acct_for(const struct pktsummary * const sm, + const struct local_ips * const local_ips); -/* vim:set ts=3 sw=3 tw=78 expandtab: */ +/* vim:set ts=3 sw=3 tw=80 expandtab: */ diff --git a/cap.c b/cap.c index d07bf9c..7e1c41b 100644 --- a/cap.c +++ b/cap.c @@ -1,20 +1,25 @@ /* darkstat 3 * copyright (c) 2001-2011 Emil Mikulic. * - * cap.c: interface to libpcap. + * cap.c: capture packets, and hand them off to decode and acct. * * You may use, modify and redistribute this file under the terms of the * GNU General Public License version 2. (see COPYING.GPL) */ +#include "acct.h" #include "cdefs.h" #include "cap.h" #include "config.h" #include "conv.h" #include "decode.h" +#include "err.h" #include "hosts_db.h" #include "localip.h" +#include "now.h" #include "opt.h" +#include "queue.h" +#include "str.h" #include #include @@ -24,50 +29,113 @@ # include /* Solaris' FIONBIO hides here */ #endif #include -#include "err.h" +#include #include #include #include #include +char *title_interfaces = NULL; /* for html.c */ + /* The cap process life-cycle: - * - * Init - cap_init() - * Fill fd_set - cap_fd_set() - * Poll - cap_poll() - * Stop - cap_stop() + * - cap_add_ifname() one or more times + * - cap_add_filter() zero or more times + * - cap_start() once to start listening + * Once per main loop: + * - cap_fd_set() to update the select() set + * - cap_poll() to read from ready pcap fds + * Shutdown: + * - cap_stop() */ -/* Globals - only useful within this module. */ -static pcap_t *pcap = NULL; -static int pcap_fd = -1; -static const struct linkhdr *linkhdr = NULL; +struct strnode { + STAILQ_ENTRY(strnode) entries; + const char *str; +}; -#define CAP_TIMEOUT 500 /* granularity of capture buffer, in milliseconds */ +struct cap_iface { + STAILQ_ENTRY(cap_iface) entries; -/* --------------------------------------------------------------------------- - * Init pcap. Exits on failure. - */ -void -cap_init(const char *device, const char *filter, int promisc) -{ + const char *name; + const char *filter; + pcap_t *pcap; + int fd; + const struct linkhdr *linkhdr; + struct local_ips local_ips; +}; + +static STAILQ_HEAD(cli_ifnames_head, strnode) cli_ifnames = + STAILQ_HEAD_INITIALIZER(cli_ifnames); + +static STAILQ_HEAD(cli_filters_head, strnode) cli_filters = + STAILQ_HEAD_INITIALIZER(cli_filters); + +static STAILQ_HEAD(cap_ifs_head, cap_iface) cap_ifs = + STAILQ_HEAD_INITIALIZER(cap_ifs); + +/* The read timeout passed to pcap_open_live() */ +#define CAP_TIMEOUT_MSEC 500 + +void cap_add_ifname(const char *ifname) { + struct strnode *n = xmalloc(sizeof(*n)); + n->str = ifname; + STAILQ_INSERT_TAIL(&cli_ifnames, n, entries); +} + +void cap_add_filter(const char *filter) { + struct strnode *n = xmalloc(sizeof(*n)); + n->str = filter; + STAILQ_INSERT_TAIL(&cli_filters, n, entries); +} + +static void cap_set_filter(pcap_t *pcap, const char *filter) { + struct bpf_program prog; + char *tmp_filter; + + if (filter == NULL) + return; + + tmp_filter = xstrdup(filter); + if (pcap_compile( + pcap, + &prog, + tmp_filter, + 1, /* optimize */ + 0) /* netmask */ + == -1) + errx(1, "pcap_compile(): %s", pcap_geterr(pcap)); + + if (pcap_setfilter(pcap, &prog) == -1) + errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap)); + + pcap_freecode(&prog); + free(tmp_filter); +} + +static void cap_start_one(struct cap_iface *iface, const int promisc) { char errbuf[PCAP_ERRBUF_SIZE], *tmp_device; int linktype, snaplen, waited; - /* pcap doesn't like device being const */ - tmp_device = xstrdup(device); + /* pcap wants a non-const interface name string */ + tmp_device = xstrdup(iface->name); + if (iface->filter) + verbosef("capturing on interface '%s' with filter '%s'", + tmp_device, iface->filter); + else + verbosef("capturing on interface '%s' with no filter", tmp_device); /* Open packet capture descriptor. */ waited = 0; for (;;) { errbuf[0] = '\0'; /* zero length string */ - pcap = pcap_open_live( + iface->pcap = pcap_open_live( tmp_device, 1, /* snaplen, irrelevant at this point */ 0, /* promisc, also irrelevant */ - CAP_TIMEOUT, + CAP_TIMEOUT_MSEC, errbuf); - if (pcap != NULL) break; /* success! */ + if (iface->pcap != NULL) + break; /* success! */ if ((opt_wait_secs != -1) && strstr(errbuf, "device is not up")) { if ((opt_wait_secs > 0) && (waited >= opt_wait_secs)) @@ -82,16 +150,16 @@ cap_init(const char *device, const char *filter, int promisc) } /* Work out the linktype and what snaplen we need. */ - linktype = pcap_datalink(pcap); + linktype = pcap_datalink(iface->pcap); verbosef("linktype is %d", linktype); if ((linktype == DLT_EN10MB) && opt_want_macs) hosts_db_show_macs = 1; - linkhdr = getlinkhdr(linktype); - if (linkhdr == NULL) + iface->linkhdr = getlinkhdr(linktype); + if (iface->linkhdr == NULL) errx(1, "unknown linktype %d", linktype); - if (linkhdr->handler == NULL) - errx(1, "no handler for linktype %d", linktype); - snaplen = getsnaplen(linkhdr); + if (iface->linkhdr->decoder == NULL) + errx(1, "no decoder for linktype %d", linktype); + snaplen = getsnaplen(iface->linkhdr); if (opt_want_pppoe) { snaplen += PPPOE_HDR_LEN; if (linktype != DLT_EN10MB) @@ -99,6 +167,11 @@ cap_init(const char *device, const char *filter, int promisc) } verbosef("calculated snaplen minimum %d", snaplen); #ifdef linux + /* FIXME: actually due to libpcap moving to mmap (!!!) + * work out which version and fix the way we do capture + * on linux: + */ + /* Ubuntu 9.04 has a problem where requesting snaplen <= 60 will * give us 42 bytes, and we need at least 54 for TCP headers. * @@ -111,16 +184,16 @@ cap_init(const char *device, const char *filter, int promisc) verbosef("using snaplen %d", snaplen); /* Close and re-open pcap to use the new snaplen. */ - pcap_close(pcap); + pcap_close(iface->pcap); errbuf[0] = '\0'; /* zero length string */ - pcap = pcap_open_live( + iface->pcap = pcap_open_live( tmp_device, snaplen, promisc, - CAP_TIMEOUT, + CAP_TIMEOUT_MSEC, errbuf); - if (pcap == NULL) + if (iface->pcap == NULL) errx(1, "pcap_open_live(): %s", errbuf); if (errbuf[0] != '\0') /* not zero length anymore -> warning */ @@ -133,80 +206,117 @@ cap_init(const char *device, const char *filter, int promisc) else verbosef("capturing in non-promiscuous mode"); - /* Set filter expression, if any. */ - if (filter != NULL) - { - struct bpf_program prog; - char *tmp_filter = xstrdup(filter); - if (pcap_compile( - pcap, - &prog, - tmp_filter, - 1, /* optimize */ - 0) /* netmask */ - == -1) - errx(1, "pcap_compile(): %s", pcap_geterr(pcap)); - - if (pcap_setfilter(pcap, &prog) == -1) - errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap)); - - pcap_freecode(&prog); - free(tmp_filter); - } - - pcap_fd = pcap_fileno(pcap); + cap_set_filter(iface->pcap, iface->filter); + iface->fd = pcap_fileno(iface->pcap); /* set non-blocking */ #ifdef linux - if (pcap_setnonblock(pcap, 1, errbuf) == -1) + if (pcap_setnonblock(iface->pcap, 1, errbuf) == -1) errx(1, "pcap_setnonblock(): %s", errbuf); #else -{ int one = 1; - if (ioctl(pcap_fd, FIONBIO, &one) == -1) - err(1, "ioctl(pcap_fd, FIONBIO)"); } + { + int one = 1; + if (ioctl(iface->fd, FIONBIO, &one) == -1) + err(1, "ioctl(iface->fd, FIONBIO)"); + } #endif #ifdef BIOCSETWF -{ - /* Deny all writes to the socket */ - struct bpf_insn bpf_wfilter[] = { BPF_STMT(BPF_RET+BPF_K, 0) }; - int wf_len = sizeof(bpf_wfilter) / sizeof(struct bpf_insn); - struct bpf_program pr; - - pr.bf_len = wf_len; - pr.bf_insns = bpf_wfilter; - - if (ioctl(pcap_fd, BIOCSETWF, &pr) == -1) - err(1, "ioctl(pcap_fd, BIOCSETFW)"); - verbosef("filtered out BPF writes"); -} + { + /* Deny all writes to the socket */ + struct bpf_insn bpf_wfilter[] = { BPF_STMT(BPF_RET+BPF_K, 0) }; + int wf_len = sizeof(bpf_wfilter) / sizeof(struct bpf_insn); + struct bpf_program pr; + + pr.bf_len = wf_len; + pr.bf_insns = bpf_wfilter; + + if (ioctl(iface->fd, BIOCSETWF, &pr) == -1) + err(1, "ioctl(iface->fd, BIOCSETFW)"); + verbosef("filtered out BPF writes"); + } #endif #ifdef BIOCLOCK /* set "locked" flag (no reset) */ - if (ioctl(pcap_fd, BIOCLOCK) == -1) - err(1, "ioctl(pcap_fd, BIOCLOCK)"); + if (ioctl(iface->fd, BIOCLOCK) == -1) + err(1, "ioctl(iface->fd, BIOCLOCK)"); verbosef("locked down BPF for security"); #endif } -/* - * Set pcap_fd in the given fd_set. - */ -void -cap_fd_set( +void cap_start(const int promisc) { + struct str *ifs = str_make(); + + assert(STAILQ_EMPTY(&cap_ifs)); + if (STAILQ_EMPTY(&cli_ifnames)) + errx(1, "no interfaces specified"); + + /* For each ifname */ + while (!STAILQ_EMPTY(&cli_ifnames)) { + struct strnode *ifname, *filter = NULL; + struct cap_iface *iface = xmalloc(sizeof(*iface)); + + ifname = STAILQ_FIRST(&cli_ifnames); + STAILQ_REMOVE_HEAD(&cli_ifnames, entries); + + if (!STAILQ_EMPTY(&cli_filters)) { + filter = STAILQ_FIRST(&cli_filters); + STAILQ_REMOVE_HEAD(&cli_filters, entries); + } + + iface->name = ifname->str; + iface->filter = (filter == NULL) ? NULL : filter->str; + iface->pcap = NULL; + iface->fd = -1; + iface->linkhdr = NULL; + localip_init(&iface->local_ips); + STAILQ_INSERT_TAIL(&cap_ifs, iface, entries); + cap_start_one(iface, promisc); + + free(ifname); + if (filter) free(filter); + + if (str_len(ifs) == 0) + str_append(ifs, iface->name); + else + str_appendf(ifs, ", %s", iface->name); + } + verbosef("all capture interfaces prepared"); + + /* Deallocate extra filters, if any. */ + while (!STAILQ_EMPTY(&cli_filters)) { + struct strnode *filter = STAILQ_FIRST(&cli_filters); + + verbosef("ignoring extraneous filter '%s'", filter->str); + STAILQ_REMOVE_HEAD(&cli_filters, entries); + free(filter); + } + + str_appendn(ifs, "", 1); /* NUL terminate */ + { + size_t _; + str_extract(ifs, &_, &title_interfaces); + } +} + #ifdef linux - fd_set *read_set _unused_, - int *max_fd _unused_, - struct timeval *timeout, +# define _unused_on_linux_ _unused_ +# define _unused_otherwise_ #else - fd_set *read_set, - int *max_fd, - struct timeval *timeout _unused_, +# define _unused_on_linux_ +# define _unused_otherwise_ _unused_ #endif - int *need_timeout) -{ - assert(*need_timeout == 0); /* we're first to get a shot at this */ + +/* + * Set pcap_fd in the given fd_set. + */ +void cap_fd_set(fd_set *read_set _unused_on_linux_, + int *max_fd _unused_on_linux_, + struct timeval *timeout _unused_otherwise_, + int *need_timeout) { + assert(*need_timeout == 0); /* we're first to get a shot at the fd_set */ + #ifdef linux /* * Linux's BPF is immediate, so don't select() as it will lead to horrible @@ -214,36 +324,41 @@ cap_fd_set( */ *need_timeout = 1; timeout->tv_sec = 0; - timeout->tv_usec = CAP_TIMEOUT * 1000; /* msec->usec */ + timeout->tv_usec = CAP_TIMEOUT_MSEC * 1000; #else - /* We have a BSD-like BPF, we can select() on it. */ - FD_SET(pcap_fd, read_set); - *max_fd = MAX(*max_fd, pcap_fd); + { + struct cap_iface *iface; + STAILQ_FOREACH(iface, &cap_ifs, entries) { + /* We have a BSD-like BPF, we can select() on it. */ + FD_SET(iface->fd, read_set); + *max_fd = MAX(*max_fd, iface->fd); + } + } #endif } unsigned int cap_pkts_recv = 0, cap_pkts_drop = 0; -static void -cap_stats_update(void) -{ - struct pcap_stat ps; +static void cap_stats_update(void) { + struct cap_iface *iface; - if (pcap_stats(pcap, &ps) != 0) { - warnx("pcap_stats(): %s", pcap_geterr(pcap)); - return; + cap_pkts_recv = 0; + cap_pkts_drop = 0; + STAILQ_FOREACH(iface, &cap_ifs, entries) { + struct pcap_stat ps; + if (pcap_stats(iface->pcap, &ps) != 0) { + warnx("pcap_stats('%s'): %s", iface->name, pcap_geterr(iface->pcap)); + return; + } + cap_pkts_recv += ps.ps_recv; + cap_pkts_drop += ps.ps_drop; } - - cap_pkts_recv = ps.ps_recv; - cap_pkts_drop = ps.ps_drop; } -/* - * Print hexdump of received packet. - */ -static void -hexdump(const u_char *buf, const uint32_t len) -{ +/* Print hexdump of received packet to stdout, for debugging. */ +static void hexdump(const u_char *buf, + const uint32_t len, + const struct linkhdr *linkhdr) { uint32_t i, col; printf("packet of %u bytes:\n", len); @@ -251,10 +366,9 @@ hexdump(const u_char *buf, const uint32_t len) if (col == 0) printf(" "); printf("%02x", buf[i]); if (i+1 == linkhdr->hdrlen) - printf("["); - else if (i+1 == linkhdr->hdrlen + IP_HDR_LEN) - printf("]"); - else printf(" "); + printf("|"); /* marks end of link headers (e.g. ethernet) */ + else + printf(" "); col += 3; if (col >= 72) { printf("\n"); @@ -265,149 +379,135 @@ hexdump(const u_char *buf, const uint32_t len) printf("\n"); } -/* - * Callback function for pcap_dispatch() which chains to the decoder specified - * in linkhdr struct. +/* Callback function for pcap_dispatch() which chains to the decoder specified + * in the linkhdr struct. */ -static void -callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) -{ - if (opt_want_hexdump) hexdump(bytes, h->caplen); - linkhdr->handler(user, h, bytes); +static void callback(u_char *user, + const struct pcap_pkthdr *pheader, + const u_char *pdata) { + const struct cap_iface * const iface = (struct cap_iface *)user; + struct pktsummary sm; + + if (opt_want_hexdump) + hexdump(pdata, pheader->caplen, iface->linkhdr); + memset(&sm, 0, sizeof(sm)); + if (iface->linkhdr->decoder(pheader, pdata, &sm)) + acct_for(&sm, &iface->local_ips); } -/* - * Process any packets currently in the capture buffer. - */ -void -cap_poll(fd_set *read_set -#ifdef linux - _unused_ -#endif -) -{ - int total, ret; - -#ifndef linux /* We don't use select() on Linux. */ - if (!FD_ISSET(pcap_fd, read_set)) { - verbosef("cap_poll premature"); - return; - } -#endif - - /* - * Once per capture poll, check our IP address. It's used in accounting - * for traffic graphs. - */ - localip_update(); /* FIXME: this might even be too often */ - - total = 0; - for (;;) { -#ifndef NDEBUG - struct timeval t1; - gettimeofday(&t1, NULL); -#endif - ret = pcap_dispatch( - pcap, - -1, /* count, -1 = entire buffer */ - callback, - NULL); /* user */ - - if (ret < 0) { - warnx("pcap_dispatch(): %s", pcap_geterr(pcap)); - return; - } - -#ifndef NDEBUG - { - struct timeval t2; - int td; - - gettimeofday(&t2, NULL); - td = (t2.tv_sec - t1.tv_sec) * 1000000 + t2.tv_usec - t1.tv_usec; - if (td > CAP_TIMEOUT*1000) - warnx("pcap_dispatch blocked for %d usec! (expected <= %d usec)\n", - td, CAP_TIMEOUT*1000); - } -#endif - - /* Despite count = -1, Linux will only dispatch one packet at a time. */ - total += ret; +/* Process any packets currently in the capture buffer. */ +void cap_poll(fd_set *read_set _unused_on_linux_) { + struct cap_iface *iface; + + STAILQ_FOREACH(iface, &cap_ifs, entries) { + /* Once per capture poll, check our IP address. It's used in accounting + * for traffic graphs. + */ + localip_update(iface->name, &iface->local_ips); + + for (;;) { + struct timespec t; + int ret; + + timer_start(&t); + ret = pcap_dispatch( + iface->pcap, + -1, /* count = entire buffer */ + callback, + (u_char*)iface); /* user = struct to pass to callback */ + + if (ret < 0) { + warnx("pcap_dispatch('%s'): %s", + iface->name, pcap_geterr(iface->pcap)); + continue; + } + timer_stop(&t, + 2 * CAP_TIMEOUT_MSEC * 1000000, + "pcap_dispatch took too long"); + + if (0) /* debugging */ + verbosef("iface '%s' got %d pkts", iface->name, ret); #ifdef linux - /* keep looping until we've dispatched all the outstanding packets */ - if (ret == 0) break; + /* keep looping until we've dispatched all the outstanding packets */ + if (ret == 0) + break; #else - /* we get them all on the first shot */ - break; + /* we get them all on the first shot */ + break; #endif + } } cap_stats_update(); } -void -cap_stop(void) -{ - pcap_close(pcap); +void cap_stop(void) { + while (!STAILQ_EMPTY(&cap_ifs)) { + struct cap_iface *iface = STAILQ_FIRST(&cap_ifs); + + STAILQ_REMOVE_HEAD(&cap_ifs, entries); + pcap_close(iface->pcap); + localip_free(&iface->local_ips); + free(iface); + } + free(title_interfaces); + title_interfaces = NULL; } /* Run through entire capfile. */ -void -cap_from_file(const char *capfile, const char *filter) -{ +void cap_from_file(const char *capfile) { char errbuf[PCAP_ERRBUF_SIZE]; int linktype, ret; + struct cap_iface iface; + + iface.name = NULL; + iface.filter = NULL; + iface.pcap = NULL; + iface.fd = -1; + iface.linkhdr = NULL; + localip_init(&iface.local_ips); + + /* Process cmdline filters. */ + if (!STAILQ_EMPTY(&cli_filters)) + iface.filter = STAILQ_FIRST(&cli_filters)->str; + while (!STAILQ_EMPTY(&cli_filters)) { + struct strnode *n = STAILQ_FIRST(&cli_filters); + STAILQ_REMOVE_HEAD(&cli_filters, entries); + free(n); + } /* Open packet capture descriptor. */ errbuf[0] = '\0'; /* zero length string */ - pcap = pcap_open_offline(capfile, errbuf); + iface.pcap = pcap_open_offline(capfile, errbuf); - if (pcap == NULL) + if (iface.pcap == NULL) errx(1, "pcap_open_offline(): %s", errbuf); if (errbuf[0] != '\0') /* not zero length anymore -> warning */ warnx("pcap_open_offline() warning: %s", errbuf); /* Work out the linktype. */ - linktype = pcap_datalink(pcap); - linkhdr = getlinkhdr(linktype); - if (linkhdr == NULL) + linktype = pcap_datalink(iface.pcap); + iface.linkhdr = getlinkhdr(linktype); + if (iface.linkhdr == NULL) errx(1, "unknown linktype %d", linktype); - if (linkhdr->handler == NULL) - errx(1, "no handler for linktype %d", linktype); - if (linktype == DLT_EN10MB) /* FIXME: impossible with capfile? */ - hosts_db_show_macs = 1; + if (iface.linkhdr->decoder == NULL) + errx(1, "no decoder for linktype %d", linktype); - /* Set filter expression, if any. */ /* FIXME: factor! */ - if (filter != NULL) - { - struct bpf_program prog; - char *tmp_filter = xstrdup(filter); - if (pcap_compile( - pcap, - &prog, - tmp_filter, - 1, /* optimize */ - 0) /* netmask */ - == -1) - errx(1, "pcap_compile(): %s", pcap_geterr(pcap)); - - if (pcap_setfilter(pcap, &prog) == -1) - errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap)); - - pcap_freecode(&prog); - free(tmp_filter); - } + cap_set_filter(iface.pcap, iface.filter); /* Process file. */ ret = pcap_dispatch( - pcap, + iface.pcap, -1, /* count, -1 = entire buffer */ callback, - NULL); /* user */ + (u_char*)&iface); /* user */ if (ret < 0) - errx(1, "pcap_dispatch(): %s", pcap_geterr(pcap)); + errx(1, "pcap_dispatch(): %s", pcap_geterr(iface.pcap)); + + localip_free(&iface.local_ips); + pcap_close(iface.pcap); } /* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/cap.h b/cap.h index 18f2425..e1a77cf 100644 --- a/cap.h +++ b/cap.h @@ -10,12 +10,14 @@ 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: */ diff --git a/config.h.in b/config.h.in index 09fdf95..6a34856 100644 --- a/config.h.in +++ b/config.h.in @@ -24,9 +24,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H -/* Define to 1 if you have the header file. */ -#undef HAVE_NET_IF_ETHER_H - /* Define to 1 if you have the header file. */ #undef HAVE_PCAP_H diff --git a/configure b/configure index 5552f80..520dc65 100755 --- a/configure +++ b/configure @@ -1,11 +1,9 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for darkstat 3.0.715. +# Generated by GNU Autoconf 2.69 for darkstat 3.0.717. # # -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software -# Foundation, Inc. +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation @@ -134,6 +132,31 @@ export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh @@ -167,7 +190,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi -test x\$exitcode = x0 || exit 1" +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && @@ -212,21 +236,25 @@ IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : - # We cannot yet assume a decent shell, so we have to provide a - # neutralization value for shells without unset; and this also - # works around shells that cannot unset nonexistent variables. - # Preserve -v and -x to the replacement shell. - BASH_ENV=/dev/null - ENV=/dev/null - (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV - export CONFIG_SHELL - case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; - esac - exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 fi if test x$as_have_required = xno; then : @@ -328,6 +356,14 @@ $as_echo X"$as_dir" | } # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take @@ -449,6 +485,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). @@ -483,16 +523,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. + # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' + as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -504,28 +544,8 @@ else as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -557,8 +577,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='darkstat' PACKAGE_TARNAME='darkstat' -PACKAGE_VERSION='3.0.715' -PACKAGE_STRING='darkstat 3.0.715' +PACKAGE_VERSION='3.0.717' +PACKAGE_STRING='darkstat 3.0.717' PACKAGE_BUGREPORT='' PACKAGE_URL='http://unix4lyfe.org/darkstat/' @@ -660,7 +680,7 @@ with_chroot_dir with_privdrop_user enable_silent_rules enable_debug -enable_mad_warnings +enable_warnings with_pcap ' ac_precious_vars='build_alias @@ -1127,8 +1147,6 @@ target=$target_alias if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1214,7 +1232,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures darkstat 3.0.715 to adapt to many kinds of systems. +\`configure' configures darkstat 3.0.717 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1275,7 +1293,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of darkstat 3.0.715:";; + short | recursive ) echo "Configuration of darkstat 3.0.717:";; esac cat <<\_ACEOF @@ -1286,8 +1304,8 @@ Optional Features: --enable-silent-rules less verbose build output (undo: 'make V=1') --disable-silent-rules verbose build output (undo: 'make V=0') --disable-debug turn off debugging code and asserts - --enable-mad-warnings turn on lots of compile-time warnings, these are - GCC-specific and only really useful for development + --enable-warnings turn on lots of compile-time warnings, these are + only useful for development Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1374,10 +1392,10 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -darkstat configure 3.0.715 -generated by GNU Autoconf 2.68 +darkstat configure 3.0.717 +generated by GNU Autoconf 2.69 -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1453,7 +1471,7 @@ $as_echo "$ac_try_echo"; } >&5 test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || - $as_test_x conftest$ac_exeext + test -x conftest$ac_exeext }; then : ac_retval=0 else @@ -1672,8 +1690,8 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by darkstat $as_me 3.0.715, which was -generated by GNU Autoconf 2.68. Invocation command line was +It was created by darkstat $as_me 3.0.717, which was +generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2147,7 +2165,7 @@ case $as_dir/ in #(( # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. @@ -2225,7 +2243,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2265,7 +2283,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2318,7 +2336,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2359,7 +2377,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue @@ -2417,7 +2435,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2461,7 +2479,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2907,8 +2925,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include -#include -#include +struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); @@ -3063,125 +3080,29 @@ else $as_echo "skipped" >&6; } fi -# Check whether --enable-mad-warnings was given. -if test "${enable_mad_warnings+set}" = set; then : - enableval=$enable_mad_warnings; if test "x$enableval" = "xyes" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler can emit certain warnings" >&5 -$as_echo_n "checking if your C compiler can emit certain warnings... " >&6; } +# Check whether --enable-warnings was given. +if test "${enable_warnings+set}" = set; then : + enableval=$enable_warnings; if test "x$enableval" = "xyes" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler has gcc-like --extra-warnings" >&5 +$as_echo_n "checking if your C compiler has gcc-like --extra-warnings... " >&6; } save_cflags="$CFLAGS" - CFLAGS="$CFLAGS -fdiagnostics-show-option \ --Waddress \ --Waggregate-return \ --Wall \ --Wbad-function-cast \ --Wcast-align \ --Wcast-qual \ --Wchar-subscripts \ --Wcomment \ --Wdeclaration-after-statement \ --Wdisabled-optimization \ --Wextra \ --Wfloat-equal \ --Wformat \ --Wformat=2 \ --Wformat-nonliteral \ --Wformat-security \ --Wformat-y2k \ --Wimplicit \ --Wimplicit-function-declaration \ --Wimplicit-int \ --Winit-self \ --Winline \ --Winvalid-pch \ --Wmain \ --Wmissing-braces \ --Wmissing-declarations \ --Wmissing-field-initializers \ --Wmissing-format-attribute \ --Wmissing-include-dirs \ --Wmissing-noreturn \ --Wmissing-prototypes \ --Wnested-externs \ --Wnonnull \ --Wold-style-definition \ --Wpacked \ --Wparentheses \ --Wpointer-arith \ --Wpointer-sign \ --Wredundant-decls \ --Wreturn-type \ --Wsequence-point \ --Wshadow \ --Wsign-compare \ --Wstrict-aliasing -fstrict-aliasing \ --Wstrict-overflow=5 -fstrict-overflow \ --Wstrict-prototypes \ --Wswitch \ --Wswitch-default \ --Wswitch-enum \ --Wtrigraphs \ --Wundef \ --Wuninitialized \ --Wunknown-pragmas \ --Wunsafe-loop-optimizations \ --Wunused \ --Wunused-function \ --Wunused-label \ --Wunused-parameter \ --Wunused-value \ --Wunused-variable \ --Wvariadic-macros \ --Wvolatile-register-var \ --Wwrite-strings \ -" -# The above are valid for gcc version 4.2.1. - + CFLAGS="$CFLAGS -fdiagnostics-show-option --all-warnings --extra-warnings" if (eval $ac_link) 2>/dev/null; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler can emit certain other warnings" >&5 -$as_echo_n "checking if your C compiler can emit certain other warnings... " >&6; } - save_cflags="$CFLAGS" - CFLAGS="$CFLAGS \ --Warray-bounds \ --Wclobbered \ --Wcoverage-mismatch \ --Wempty-body \ --Wignored-qualifiers \ --Wlogical-op \ --Wmissing-parameter-type \ --Wold-style-declaration \ --Wpacked-bitfield-compat \ --Wsync-nand \ --Wtype-limits \ --Wvla \ -" -# The above are valid for gcc version 4.4.3. -# We skip the following entirely: -#-pedantic -#-Wabi -#-Wc++-compat -#-Wconversion -#-Wfatal-errors -#-Wlong-long -#-Wpadded -#-Wsign-conversion -#-Wstack-protector -#-Wsystem-headers -#-Wtraditional -#-Wtraditional-conversion -#-Wunreachable-code - - if (eval $ac_link) 2>/dev/null; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - CFLAGS="$save_cflags" - fi + CFLAGS="$save_cflags" + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler has clang-like -Weverything" >&5 +$as_echo_n "checking if your C compiler has clang-like -Weverything... " >&6; } + save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Weverything" + if (eval $ac_link) 2>/dev/null; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } @@ -3593,7 +3514,7 @@ do for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue + as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in @@ -3659,7 +3580,7 @@ do for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue + as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in @@ -3849,22 +3770,61 @@ fi done -# Debian GNU/kFreeBSD needs net/if_ether.h for ETH_P_IPV6 -for ac_header in net/if_ether.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "net/if_ether.h" "ac_cv_header_net_if_ether_h" "$ac_includes_default" -if test "x$ac_cv_header_net_if_ether_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_NET_IF_ETHER_H 1 -_ACEOF +# This is the modern way. Older systems use the ioctl method. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing getifaddrs" >&5 +$as_echo_n "checking for library containing getifaddrs... " >&6; } +if ${ac_cv_search_getifaddrs+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char getifaddrs (); +int +main () +{ +return getifaddrs (); + ; + return 0; +} +_ACEOF +for ac_lib in '' c; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_getifaddrs=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_getifaddrs+:} false; then : + break fi - done +if ${ac_cv_search_getifaddrs+:} false; then : - -# This is the modern way. Older systems use the ioctl method. -for ac_header in ifaddrs.h +else + ac_cv_search_getifaddrs=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_getifaddrs" >&5 +$as_echo "$ac_cv_search_getifaddrs" >&6; } +ac_res=$ac_cv_search_getifaddrs +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + for ac_header in ifaddrs.h do : ac_fn_c_check_header_mongrel "$LINENO" "ifaddrs.h" "ac_cv_header_ifaddrs_h" "$ac_includes_default" if test "x$ac_cv_header_ifaddrs_h" = xyes; then : @@ -3876,6 +3836,10 @@ fi done +fi + + + # Some OSes (Solaris) need sys/sockio.h for SIOCGIFADDR for ac_header in sys/sockio.h @@ -4000,19 +3964,16 @@ if test -n "$PCAP_BROKEN" ; then cat <&5 +$as_echo_n "checking for library containing clock_gettime... " >&6; } +if ${ac_cv_search_clock_gettime+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char clock_gettime (); +int +main () +{ +return clock_gettime (); + ; + return 0; +} +_ACEOF +for ac_lib in '' rt; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_clock_gettime=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_clock_gettime+:} false; then : + break +fi +done +if ${ac_cv_search_clock_gettime+:} false; then : + +else + ac_cv_search_clock_gettime=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 +$as_echo "$ac_cv_search_clock_gettime" >&6; } +ac_res=$ac_cv_search_clock_gettime +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + ac_config_files="$ac_config_files Makefile darkstat.8" cat >confcache <<\_ACEOF @@ -4629,16 +4647,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. + # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' + as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -4698,28 +4716,16 @@ else as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -4740,8 +4746,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by darkstat $as_me 3.0.715, which was -generated by GNU Autoconf 2.68. Invocation command line was +This file was extended by darkstat $as_me 3.0.717, which was +generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -4803,11 +4809,11 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -darkstat config.status 3.0.715 -configured by $0, generated by GNU Autoconf 2.68, +darkstat config.status 3.0.717 +configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -4896,7 +4902,7 @@ fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then - set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' diff --git a/configure.ac b/configure.ac index 15c356a..7af8316 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,6 @@ -AC_INIT(darkstat, 3.0.715, , , http://unix4lyfe.org/darkstat/) +# Need at least 2.64 for PACKAGE_URL +AC_PREREQ([2.64]) +AC_INIT(darkstat, 3.0.717, , , http://unix4lyfe.org/darkstat/) AC_CONFIG_SRCDIR([darkstat.c]) AC_CONFIG_HEADER([config.h]) @@ -92,121 +94,25 @@ else AC_MSG_RESULT(skipped) fi -AC_ARG_ENABLE(mad-warnings, AS_HELP_STRING([--enable-mad-warnings], - [turn on lots of compile-time warnings, these are GCC-specific and only - really useful for development]), +AC_ARG_ENABLE(warnings, AS_HELP_STRING([--enable-warnings], + [turn on lots of compile-time warnings, + these are only useful for development]), [if test "x$enableval" = "xyes" ; then - AC_MSG_CHECKING(if your C compiler can emit certain warnings) + AC_MSG_CHECKING(if your C compiler has gcc-like --extra-warnings) save_cflags="$CFLAGS" - CFLAGS="$CFLAGS -fdiagnostics-show-option \ --Waddress \ --Waggregate-return \ --Wall \ --Wbad-function-cast \ --Wcast-align \ --Wcast-qual \ --Wchar-subscripts \ --Wcomment \ --Wdeclaration-after-statement \ --Wdisabled-optimization \ --Wextra \ --Wfloat-equal \ --Wformat \ --Wformat=2 \ --Wformat-nonliteral \ --Wformat-security \ --Wformat-y2k \ --Wimplicit \ --Wimplicit-function-declaration \ --Wimplicit-int \ --Winit-self \ --Winline \ --Winvalid-pch \ --Wmain \ --Wmissing-braces \ --Wmissing-declarations \ --Wmissing-field-initializers \ --Wmissing-format-attribute \ --Wmissing-include-dirs \ --Wmissing-noreturn \ --Wmissing-prototypes \ --Wnested-externs \ --Wnonnull \ --Wold-style-definition \ --Wpacked \ --Wparentheses \ --Wpointer-arith \ --Wpointer-sign \ --Wredundant-decls \ --Wreturn-type \ --Wsequence-point \ --Wshadow \ --Wsign-compare \ --Wstrict-aliasing -fstrict-aliasing \ --Wstrict-overflow=5 -fstrict-overflow \ --Wstrict-prototypes \ --Wswitch \ --Wswitch-default \ --Wswitch-enum \ --Wtrigraphs \ --Wundef \ --Wuninitialized \ --Wunknown-pragmas \ --Wunsafe-loop-optimizations \ --Wunused \ --Wunused-function \ --Wunused-label \ --Wunused-parameter \ --Wunused-value \ --Wunused-variable \ --Wvariadic-macros \ --Wvolatile-register-var \ --Wwrite-strings \ -" -# The above are valid for gcc version 4.2.1. - + CFLAGS="$CFLAGS -fdiagnostics-show-option --all-warnings --extra-warnings" if (eval $ac_link) 2>/dev/null; then AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + CFLAGS="$save_cflags" + fi - AC_MSG_CHECKING(if your C compiler can emit certain other warnings) - save_cflags="$CFLAGS" - CFLAGS="$CFLAGS \ --Warray-bounds \ --Wclobbered \ --Wcoverage-mismatch \ --Wempty-body \ --Wignored-qualifiers \ --Wlogical-op \ --Wmissing-parameter-type \ --Wold-style-declaration \ --Wpacked-bitfield-compat \ --Wsync-nand \ --Wtype-limits \ --Wvla \ -" -# The above are valid for gcc version 4.4.3. -# We skip the following entirely: -#-pedantic -#-Wabi -#-Wc++-compat -#-Wconversion -#-Wfatal-errors -#-Wlong-long -#-Wpadded -#-Wsign-conversion -#-Wstack-protector -#-Wsystem-headers -#-Wtraditional -#-Wtraditional-conversion -#-Wunreachable-code - - if (eval $ac_link) 2>/dev/null; then - AC_MSG_RESULT(yes) - else - AC_MSG_RESULT(no) - CFLAGS="$save_cflags" - fi - + AC_MSG_CHECKING(if your C compiler has clang-like -Weverything) + save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Weverything" + if (eval $ac_link) 2>/dev/null; then + AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) CFLAGS="$save_cflags" @@ -248,11 +154,11 @@ AC_SEARCH_LIBS(hstrerror, [resolv], [], # Solaris need sys/filio.h for FIONBIO AC_CHECK_HEADERS(sys/filio.h) -# Debian GNU/kFreeBSD needs net/if_ether.h for ETH_P_IPV6 -AC_CHECK_HEADERS(net/if_ether.h) - # This is the modern way. Older systems use the ioctl method. -AC_CHECK_HEADERS(ifaddrs.h) +AC_SEARCH_LIBS(getifaddrs, [c], + [AC_CHECK_HEADERS(ifaddrs.h)]) + + # Some OSes (Solaris) need sys/sockio.h for SIOCGIFADDR AC_CHECK_HEADERS(sys/sockio.h) @@ -292,19 +198,16 @@ if test -n "$PCAP_BROKEN" ; then cat < @@ -33,9 +34,6 @@ #include #include -#include "now.h" -time_t now; - #ifndef INADDR_NONE # define INADDR_NONE (-1) /* Solaris */ #endif @@ -45,8 +43,7 @@ static volatile int running = 1; static void sig_shutdown(int signum _unused_) { running = 0; } static volatile int reset_pending = 0, export_pending = 0; -static void sig_reset(int signum _unused_) -{ +static void sig_reset(int signum _unused_) { reset_pending = 1; export_pending = 1; } @@ -54,9 +51,8 @@ static void sig_reset(int signum _unused_) static void sig_export(int signum _unused_) { export_pending = 1; } /* --- Commandline parsing --- */ -static unsigned long -parsenum(const char *str, unsigned long max /* 0 for no max */) -{ +static unsigned long parsenum(const char *str, + unsigned long max /* 0 for no max */) { unsigned long n; char *end; @@ -71,8 +67,13 @@ parsenum(const char *str, unsigned long max /* 0 for no max */) return n; } -const char *opt_interface = NULL; -static void cb_interface(const char *arg) { opt_interface = arg; } +static int opt_iface_seen = 0; +static void cb_interface(const char *arg) { + cap_add_ifname(arg); + opt_iface_seen = 1; +} + +static void cb_filter(const char *arg) { cap_add_filter(arg); } const char *opt_capfile = NULL; static void cb_capfile(const char *arg) { opt_capfile = arg; } @@ -111,9 +112,6 @@ static void cb_port(const char *arg) static void cb_bindaddr(const char *arg) { http_add_bindaddr(arg); } -const char *opt_filter = NULL; -static void cb_filter(const char *arg) { opt_filter = arg; } - static int is_localnet_specified = 0; static void cb_local(const char *arg) { @@ -214,11 +212,11 @@ struct cmdline_arg { }; static struct cmdline_arg cmdline_args[] = { - {"-i", "interface", cb_interface, 0}, - {"-r", "file", cb_capfile, 0}, + {"-i", "interface", cb_interface, -1}, + {"-f", "filter", cb_filter, -1}, + {"-r", "capfile", cb_capfile, 0}, {"-p", "port", cb_port, 0}, {"-b", "bindaddr", cb_bindaddr, -1}, - {"-f", "filter", cb_filter, 0}, {"-l", "network/netmask", cb_local, 0}, {"--local-only", NULL, cb_local_only, 0}, {"--snaplen", "bytes", cb_snaplen, 0}, @@ -248,12 +246,8 @@ static struct cmdline_arg cmdline_args[] = { {NULL, NULL, NULL, 0} }; -/* - * We autogenerate the usage statement from the cmdline_args data structure. - */ -static void -usage(void) -{ +/* We autogenerate the usage statement from the cmdline_args data structure. */ +static void usage(void) { static char intro[] = "usage: darkstat "; char indent[sizeof(intro)]; struct cmdline_arg *arg; @@ -278,9 +272,7 @@ usage(void) "documentation and usage examples.\n"); } -static void -parse_sub_cmdline(const int argc, char * const *argv) -{ +static void parse_sub_cmdline(const int argc, char * const *argv) { struct cmdline_arg *arg; if (argc == 0) return; @@ -319,9 +311,7 @@ parse_sub_cmdline(const int argc, char * const *argv) exit(EXIT_FAILURE); } -static void -parse_cmdline(const int argc, char * const *argv) -{ +static void parse_cmdline(const int argc, char * const *argv) { if (argc < 1) { /* Not enough args. */ usage(); @@ -336,17 +326,20 @@ parse_cmdline(const int argc, char * const *argv) } /* start syslogging as early as possible */ - if (opt_want_syslog) openlog("darkstat", LOG_NDELAY | LOG_PID, LOG_DAEMON); + if (opt_want_syslog) + openlog("darkstat", LOG_NDELAY | LOG_PID, LOG_DAEMON); /* some default values */ - if (opt_chroot_dir == NULL) opt_chroot_dir = CHROOT_DIR; - if (opt_privdrop_user == NULL) opt_privdrop_user = PRIVDROP_USER; + if (opt_chroot_dir == NULL) + opt_chroot_dir = CHROOT_DIR; + if (opt_privdrop_user == NULL) + opt_privdrop_user = PRIVDROP_USER; /* sanity check args */ - if ((opt_interface == NULL) && (opt_capfile == NULL)) + if (!opt_iface_seen && opt_capfile == NULL) errx(1, "must specify either interface (-i) or capture file (-r)"); - if ((opt_interface != NULL) && (opt_capfile != NULL)) + if (opt_iface_seen && opt_capfile != NULL) errx(1, "can't specify both interface (-i) and capture file (-r)"); if ((opt_hosts_max != 0) && (opt_hosts_keep >= opt_hosts_max)) { @@ -379,22 +372,17 @@ parse_cmdline(const int argc, char * const *argv) verbosef("WARNING: --local-only without -l only matches the local host"); } -static void -run_from_capfile(void) -{ +static void run_from_capfile(void) { + now_init(); graph_init(); hosts_db_init(); - cap_from_file(opt_capfile, opt_filter); - cap_stop(); + cap_from_file(opt_capfile); if (export_fn != NULL) db_export(export_fn); hosts_db_free(); graph_free(); -#ifndef PRIu64 -#warning "PRIu64 is not defined, using qu instead" -#define PRIu64 "qu" -#endif - verbosef("Total packets: %"PRIu64", bytes: %"PRIu64, - acct_total_packets, acct_total_bytes); + verbosef("Total packets: %llu, bytes: %llu", + (unsigned long long)acct_total_packets, + (unsigned long long)acct_total_bytes); } /* --- Program body --- */ @@ -405,10 +393,6 @@ main(int argc, char **argv) parse_cmdline(argc-1, argv+1); if (opt_capfile) { - /* - * This is very different from a regular run against a network - * interface. - */ run_from_capfile(); return 0; } @@ -426,19 +410,18 @@ main(int argc, char **argv) /* do this first as it forks - minimize memory use */ if (opt_want_dns) dns_init(opt_privdrop_user); - cap_init(opt_interface, opt_filter, opt_want_promisc); /* needs root */ + cap_start(opt_want_promisc); /* needs root */ http_listen(opt_bindport); ncache_init(); /* must do before chroot() */ privdrop(opt_chroot_dir, opt_privdrop_user); /* Don't need root privs for these: */ - now = time(NULL); + now_init(); if (daylog_fn != NULL) daylog_init(daylog_fn); graph_init(); hosts_db_init(); if (import_fn != NULL) db_import(import_fn); - localip_init(opt_interface); if (signal(SIGTERM, sig_shutdown) == SIG_ERR) errx(1, "signal(SIGTERM) failed"); @@ -455,48 +438,47 @@ main(int argc, char **argv) while (running) { int select_ret, max_fd = -1, use_timeout = 0; struct timeval timeout; + struct timespec t; fd_set rs, ws; - now = time(NULL); - - if (export_pending) { - if (export_fn != NULL) - db_export(export_fn); - export_pending = 0; - } - - if (reset_pending) { - if (export_pending) - continue; /* export before reset */ - hosts_db_reset(); - graph_reset(); - reset_pending = 0; - } - FD_ZERO(&rs); FD_ZERO(&ws); - cap_fd_set(&rs, &max_fd, &timeout, &use_timeout); http_fd_set(&rs, &ws, &max_fd, &timeout, &use_timeout); select_ret = select(max_fd+1, &rs, &ws, NULL, (use_timeout) ? &timeout : NULL); - - if ((select_ret == 0) && (!use_timeout)) + if (select_ret == 0 && !use_timeout) errx(1, "select() erroneously timed out"); - if (select_ret == -1) { if (errno == EINTR) continue; else err(1, "select()"); } - else { - graph_rotate(); - cap_poll(&rs); - dns_poll(); - http_poll(&rs, &ws); + + timer_start(&t); + now_update(); + + if (export_pending) { + if (export_fn != NULL) + db_export(export_fn); + export_pending = 0; + } + + if (reset_pending) { + if (export_pending) + continue; /* export before reset */ + hosts_db_reset(); + graph_reset(); + reset_pending = 0; } + + graph_rotate(); + cap_poll(&rs); + dns_poll(); + http_poll(&rs, &ws); + timer_stop(&t, 1000000000, "event processing took longer than a second"); } verbosef("shutting down"); diff --git a/daylog.c b/daylog.c index a0b517e..fe1928f 100644 --- a/daylog.c +++ b/daylog.c @@ -9,143 +9,119 @@ #define _GNU_SOURCE 1 /* for O_NOFOLLOW on Linux */ -#include +#include "err.h" +#include "daylog.h" +#include "str.h" +#include "now.h" + #include #include +#include #include #include +#include #include -#include "err.h" -#include "daylog.h" -#include "str.h" -#include "now.h" - static const char *daylog_fn = NULL; -static time_t today_time, tomorrow_time; +static long today_real, tomorrow_real; static uint64_t bytes_in, bytes_out, pkts_in, pkts_out; #define DAYLOG_DATE_LEN 26 /* strlen("1900-01-01 00:00:00 +1234") + 1 */ static char datebuf[DAYLOG_DATE_LEN]; -static char * -fmt_date(const time_t when) -{ - time_t tmp = when; - if (strftime(datebuf, DAYLOG_DATE_LEN, - "%Y-%m-%d %H:%M:%S %z", localtime(&tmp) ) == 0) +static char *fmt_date(time_t when) { + if (strftime(datebuf, + DAYLOG_DATE_LEN, + "%Y-%m-%d %H:%M:%S %z", + localtime(&when)) == 0) errx(1, "strftime() failed in fmt_date()"); - return (datebuf); + return datebuf; } /* Given some time today, find the first second of tomorrow. */ -static time_t -tomorrow(const time_t today) -{ - time_t tmp = today; +static time_t tomorrow(time_t t_before) { + time_t t_after; struct tm tm, *lt; - lt = localtime(&tmp); + lt = localtime(&t_before); memcpy(&tm, lt, sizeof(tm)); tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 0; tm.tm_mday = lt->tm_mday + 1; /* tomorrow */ - return mktime(&tm); -} - -static int -daylog_open(void) -{ - return open(daylog_fn, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600); -} - -static void -daylog_emit(void) -{ - int fd = daylog_open(); - - if (fd != -1) { - struct str *buf = str_make(); - char *s; - size_t len; - str_appendf(buf, "%s|%u|%qu|%qu|%qu|%qu\n", - fmt_date(today_time), (unsigned int)today_time, - bytes_in, bytes_out, pkts_in, pkts_out); - str_extract(buf, &len, &s); - - (void)write(fd, s, len); /* ignore write errors */ - close(fd); - free(s); - } + t_after = mktime(&tm); + assert(t_after > t_before); + return t_after; } -void -daylog_init(const char *filename) -{ +/* Warns on error. */ +static void daylog_write(const char *format, ...) { int fd; + ssize_t wr; + va_list va; struct str *buf; - char *s; - size_t len; - - daylog_fn = filename; - today_time = time(NULL); - tomorrow_time = tomorrow(today_time); - verbosef("today is %u, tomorrow is %u", - (unsigned int)today_time, (unsigned int)tomorrow_time); - bytes_in = bytes_out = pkts_in = pkts_out = 0; - fd = daylog_open(); - if (fd == -1) - err(1, "couldn't open(\"%s\") for append", filename); + assert(daylog_fn != NULL); + fd = open(daylog_fn, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600); + if (fd == -1) { + warn("daylog_write: couldn't open '%s' for append", daylog_fn); + return; + } buf = str_make(); - str_appendf(buf, "# logging started at %s (%u)\n", - fmt_date(today_time), (unsigned int)today_time); - str_extract(buf, &len, &s); - (void)write(fd, s, len); /* ignore write errors */ + va_start(va, format); + str_vappendf(buf, format, va); + va_end(va); + + wr = str_write(buf, fd); + if (wr == -1) + warn("daylog_write: couldn't write to '%s'", daylog_fn); + else if (wr != (ssize_t)str_len(buf)) + warnx("daylog_write: truncated write to '%s': wrote %d of %d bytes", + daylog_fn, + (int)wr, + (int)str_len(buf)); close(fd); - free(s); + str_free(buf); } -void daylog_free(void) -{ - int fd; - struct str *buf; - char *s; - size_t len; - - today_time = time(NULL); +static void daylog_emit(void) { + daylog_write("%s|%qu|%qu|%qu|%qu|%qu\n", + fmt_date(today_real), (uint64_t)today_real, + bytes_in, bytes_out, pkts_in, pkts_out); +} - /* Emit what's currently accumulated. */ - daylog_emit(); +void daylog_init(const char *filename) { + daylog_fn = filename; + today_real = now_real(); + tomorrow_real = tomorrow(today_real); + verbosef("today is %ld, tomorrow is %ld", + (long)today_real, (long)tomorrow_real); + bytes_in = bytes_out = pkts_in = pkts_out = 0; - fd = daylog_open(); - if (fd == -1) return; + daylog_write("# logging started at %s (%qu)\n", + fmt_date(today_real), (uint64_t)today_real); +} - buf = str_make(); - str_appendf(buf, "# logging stopped at %s (%u)\n", - fmt_date(today_time), (unsigned int)today_time); - str_extract(buf, &len, &s); - (void)write(fd, s, len); /* ignore write errors */ - close(fd); - free(s); +void daylog_free(void) { + today_real = now_real(); + daylog_emit(); /* Emit what's currently accumulated before we exit. */ + daylog_write("# logging stopped at %s (%qu)\n", + fmt_date(today_real), (uint64_t)today_real); } -void -daylog_acct(uint64_t amount, enum graph_dir dir) -{ - if (daylog_fn == NULL) return; /* disabled */ +void daylog_acct(uint64_t amount, enum graph_dir dir) { + if (daylog_fn == NULL) + return; /* daylogging disabled */ - /* Check if we need to rotate. */ - if (now >= tomorrow_time) { + /* Check if we need to update the log. */ + if (now_real() >= tomorrow_real) { daylog_emit(); - today_time = now; - tomorrow_time = tomorrow(today_time); + today_real = now_real(); + tomorrow_real = tomorrow(today_real); bytes_in = bytes_out = pkts_in = pkts_out = 0; - verbosef("rotated daylog, tomorrow = %u", - (unsigned int)tomorrow_time); + verbosef("updated daylog, tomorrow = %ld", (long)tomorrow_real); } /* Accounting. */ diff --git a/db.c b/db.c index 5a82d90..cf50da9 100644 --- a/db.c +++ b/db.c @@ -27,9 +27,7 @@ static const unsigned char export_tag_hosts_ver1[] = {0xDA, 'H', 'S', 0x01}; static const unsigned char export_tag_graph_ver1[] = {0xDA, 'G', 'R', 0x01}; #ifndef swap64 -static inline uint64_t -swap64(uint64_t _x) -{ +static uint64_t swap64(uint64_t _x) { /* this is __bswap64 from: * $FreeBSD: src/sys/i386/include/endian.h,v 1.41$ */ @@ -40,17 +38,12 @@ swap64(uint64_t _x) } #endif -uint64_t -hton64(const uint64_t ho) -{ - if (ntohs(0x1234) == 0x1234) return ho; - else return swap64(ho); -} - -uint64_t -ntoh64(const uint64_t no) -{ - return hton64(no); +#define ntoh64 hton64 +static inline uint64_t hton64(const uint64_t ho) { + if (ntohs(0x1234) == 0x1234) + return ho; + else + return swap64(ho); } void diff --git a/db.h b/db.h index 5f2eba6..6f9d28a 100644 --- a/db.h +++ b/db.h @@ -11,10 +11,6 @@ struct addr; void db_import(const char *filename); void db_export(const char *filename); - -/* byteswap */ -uint64_t hton64(const uint64_t ho); -uint64_t ntoh64(const uint64_t no); void test_64order(void); /* read helpers */ diff --git a/decode.c b/decode.c index def8c29..e8e5f35 100644 --- a/decode.c +++ b/decode.c @@ -11,9 +11,6 @@ */ #include "cdefs.h" -#include "acct.h" -#include "cap.h" -#include "config.h" #include "decode.h" #include "err.h" #include "opt.h" @@ -25,40 +22,38 @@ #include #include #include /* inet_ntoa() */ +#include /* struct ifreq */ /* need struct ether_header */ -#if defined(__NetBSD__) || defined(__OpenBSD__) -# include -# include -# include +#ifdef __NetBSD__ /* works for NetBSD 5.1.2 */ # include #else -# ifdef __sun -# include -# define ETHER_HDR_LEN 14 +# ifdef __OpenBSD__ +# include +# include +# include # else -# ifdef _AIX -# include +# ifdef __sun +# include # define ETHER_HDR_LEN 14 # else -# include +# ifdef _AIX +# include +# define ETHER_HDR_LEN 14 +# else +# include +# 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 /* ETH_P_IPV6 for GNU/kfreebsd */ -# endif -# ifdef ETH_P_IPV6 -# define ETHERTYPE_IPV6 ETH_P_IPV6 -# endif +# define ETHERTYPE_IPV6 0x86DD #endif -#include /* struct ifreq */ #include /* n_long */ #include /* struct ip */ #include /* struct ip6_hdr */ @@ -66,34 +61,42 @@ #include /* struct tcphdr */ #include /* struct udphdr */ +#define PPP_HDR_LEN 4 +#define FDDI_HDR_LEN 21 +#define IP_HDR_LEN sizeof(struct ip) +#define IPV6_HDR_LEN sizeof(struct ip6_hdr) +#define TCP_HDR_LEN sizeof(struct tcphdr) +#define UDP_HDR_LEN sizeof(struct udphdr) +#define NULL_HDR_LEN 4 +#define SLL_HDR_LEN 16 +#define RAW_HDR_LEN 0 + #ifndef IPV6_VERSION -#define IPV6_VERSION 0x60 +# define IPV6_VERSION 0x60 #endif #ifndef IPV6_VERSION_MASK -#define IPV6_VERSION_MASK 0xf0 +# define IPV6_VERSION_MASK 0xF0 #endif -static void decode_ether(u_char *, const struct pcap_pkthdr *, - const u_char *); -static void decode_loop(u_char *, const struct pcap_pkthdr *, - const u_char *); -static void decode_null(u_char *, const struct pcap_pkthdr *, - const u_char *); -static void decode_ppp(u_char *, const struct pcap_pkthdr *, - const u_char *); -static void decode_pppoe(u_char *, const struct pcap_pkthdr *, - const u_char *); -static void decode_pppoe_real(const u_char *pdata, const uint32_t len, - struct pktsummary *sm); -static void decode_linux_sll(u_char *, const struct pcap_pkthdr *, - const u_char *); -static void decode_raw(u_char *, const struct pcap_pkthdr *, - const u_char *); -static void decode_ip(const u_char *pdata, const uint32_t len, - struct pktsummary *sm); -static void decode_ipv6(const u_char *pdata, const uint32_t len, - struct pktsummary *sm); +static int decode_ether(DECODER_ARGS); +static int decode_loop(DECODER_ARGS); +static int decode_null(DECODER_ARGS); +static int decode_ppp(DECODER_ARGS); +static int decode_pppoe(DECODER_ARGS); +#ifdef DLT_LINUX_SLL +static int decode_linux_sll(DECODER_ARGS); +#endif +static int decode_raw(DECODER_ARGS); + +#define HELPER_ARGS const u_char *pdata, \ + const uint32_t len, \ + struct pktsummary *sm + +static int helper_pppoe(HELPER_ARGS); +static int helper_ip(HELPER_ARGS); +static int helper_ipv6(HELPER_ARGS); +static void helper_ip_deeper(HELPER_ARGS); /* protocols like TCP/UDP */ /* Link-type header information */ static const struct linkhdr linkhdrs[] = { @@ -114,208 +117,130 @@ static const struct linkhdr linkhdrs[] = { { -1, 0, NULL } }; -/* - * Returns a pointer to the linkhdr record matching the given linktype, or +/* Returns a pointer to the linkhdr record matching the given linktype, or * NULL if no matching entry found. */ -const struct linkhdr * -getlinkhdr(const int linktype) -{ +const struct linkhdr *getlinkhdr(const int linktype) { size_t i; for (i=0; linkhdrs[i].linktype != -1; i++) if (linkhdrs[i].linktype == linktype) return (&(linkhdrs[i])); - return (NULL); + return NULL; } -/* - * Returns the minimum snaplen needed to decode everything up to the TCP/UDP - * packet headers. The IPv6 header is normative. The argument lh is not - * allowed to be NULL. +/* Returns the minimum snaplen needed to decode everything up to and including + * the TCP/UDP packet headers. */ -int -getsnaplen(const struct linkhdr *lh) -{ +int getsnaplen(const struct linkhdr *lh) { return (int)(lh->hdrlen + IPV6_HDR_LEN + MAX(TCP_HDR_LEN, UDP_HDR_LEN)); } -/* Decoding functions. */ -static void -decode_ether(u_char *user _unused_, - const struct pcap_pkthdr *pheader, - const u_char *pdata) -{ +static int decode_ether(DECODER_ARGS) { u_short type; const struct ether_header *hdr = (const struct ether_header *)pdata; - struct pktsummary sm; - memset(&sm, 0, sizeof(sm)); - sm.time = pheader->ts.tv_sec; if (pheader->caplen < ETHER_HDR_LEN) { verbosef("ether: packet too short (%u bytes)", pheader->caplen); - return; + return 0; } - #ifdef __sun - memcpy(sm.src_mac, hdr->ether_shost.ether_addr_octet, sizeof(sm.src_mac)); - memcpy(sm.dst_mac, hdr->ether_dhost.ether_addr_octet, sizeof(sm.dst_mac)); + memcpy(sm->src_mac, hdr->ether_shost.ether_addr_octet, sizeof(sm->src_mac)); + memcpy(sm->dst_mac, hdr->ether_dhost.ether_addr_octet, sizeof(sm->dst_mac)); #else - memcpy(sm.src_mac, hdr->ether_shost, sizeof(sm.src_mac)); - memcpy(sm.dst_mac, hdr->ether_dhost, sizeof(sm.dst_mac)); + memcpy(sm->src_mac, hdr->ether_shost, sizeof(sm->src_mac)); + memcpy(sm->dst_mac, hdr->ether_dhost, sizeof(sm->dst_mac)); #endif - - type = ntohs( hdr->ether_type ); + type = ntohs(hdr->ether_type); switch (type) { - case ETHERTYPE_IP: - case ETHERTYPE_IPV6: - if (!opt_want_pppoe) { - decode_ip(pdata + ETHER_HDR_LEN, - pheader->caplen - ETHER_HDR_LEN, &sm); - acct_for(&sm); - } else + case ETHERTYPE_IP: + case ETHERTYPE_IPV6: + if (!opt_want_pppoe) + return helper_ip(pdata + ETHER_HDR_LEN, + pheader->caplen - ETHER_HDR_LEN, + sm); verbosef("ether: discarded IP packet, expecting PPPoE instead"); - break; - case ETHERTYPE_ARP: - /* known protocol, don't complain about it. */ - break; - case ETHERTYPE_PPPOE: - if (opt_want_pppoe) - decode_pppoe_real(pdata + ETHER_HDR_LEN, - pheader->caplen - ETHER_HDR_LEN, &sm); - else + return 0; + case ETHERTYPE_PPPOE: + if (opt_want_pppoe) + return helper_pppoe(pdata + ETHER_HDR_LEN, + pheader->caplen - ETHER_HDR_LEN, + sm); verbosef("ether: got PPPoE frame: maybe you want --pppoe"); - break; - default: - verbosef("ether: unknown protocol (0x%04x)", type); + return 0; + case ETHERTYPE_ARP: + /* known protocol, don't complain about it. */ + return 0; + default: + verbosef("ether: unknown protocol (0x%04x)", type); + return 0; } } /* Very similar to decode_null, except on OpenBSD we need to think * about family endianness. */ -static void -decode_loop(u_char *user _unused_, - const struct pcap_pkthdr *pheader, - const u_char *pdata) -{ +static int decode_loop(DECODER_ARGS) { uint32_t family; - struct pktsummary sm; - memset(&sm, 0, sizeof(sm)); if (pheader->caplen < NULL_HDR_LEN) { verbosef("loop: packet too short (%u bytes)", pheader->caplen); - return; + return 0; } family = *(const uint32_t *)pdata; #ifdef __OpenBSD__ family = ntohl(family); #endif - if (family == AF_INET) { - decode_ip(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm); - sm.time = pheader->ts.tv_sec; - acct_for(&sm); - } - else if (family == AF_INET6) { - decode_ipv6(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm); - sm.time = pheader->ts.tv_sec; - acct_for(&sm); - } - else - verbosef("loop: unknown family (%x)", family); + if (family == AF_INET) + return helper_ip(pdata + NULL_HDR_LEN, + pheader->caplen - NULL_HDR_LEN, sm); + if (family == AF_INET6) + return helper_ipv6(pdata + NULL_HDR_LEN, + pheader->caplen - NULL_HDR_LEN, sm); + verbosef("loop: unknown family (0x%04x)", family); + return 0; } -static void -decode_null(u_char *user _unused_, - const struct pcap_pkthdr *pheader, - const u_char *pdata) -{ +static int decode_null(DECODER_ARGS) { uint32_t family; - struct pktsummary sm; - memset(&sm, 0, sizeof(sm)); if (pheader->caplen < NULL_HDR_LEN) { verbosef("null: packet too short (%u bytes)", pheader->caplen); - return; + return 0; } family = *(const uint32_t *)pdata; - if (family == AF_INET) { - decode_ip(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm); - sm.time = pheader->ts.tv_sec; - acct_for(&sm); - } - else if (family == AF_INET6) { - decode_ipv6(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm); - sm.time = pheader->ts.tv_sec; - acct_for(&sm); - } - else - verbosef("null: unknown family (%x)", family); + if (family == AF_INET) + return helper_ip(pdata + NULL_HDR_LEN, + pheader->caplen - NULL_HDR_LEN, + sm); + if (family == AF_INET6) + return helper_ipv6(pdata + NULL_HDR_LEN, + pheader->caplen - NULL_HDR_LEN, + sm); + verbosef("null: unknown family (0x%04x)", family); + return 0; } -static void -decode_ppp(u_char *user _unused_, - const struct pcap_pkthdr *pheader, - const u_char *pdata) -{ - struct pktsummary sm; - memset(&sm, 0, sizeof(sm)); - +static int decode_ppp(DECODER_ARGS) { if (pheader->caplen < PPPOE_HDR_LEN) { verbosef("ppp: packet too short (%u bytes)", pheader->caplen); - return; + return 0; } - - if (pdata[2] == 0x00 && pdata[3] == 0x21) { - decode_ip(pdata + PPP_HDR_LEN, pheader->caplen - PPP_HDR_LEN, &sm); - sm.time = pheader->ts.tv_sec; - acct_for(&sm); - } else - verbosef("non-IP PPP packet; ignoring."); + if (pdata[2] == 0x00 && pdata[3] == 0x21) + return helper_ip(pdata + PPP_HDR_LEN, + pheader->caplen - PPP_HDR_LEN, + sm); + verbosef("ppp: non-IP PPP packet; ignoring."); + return 0; } -static void -decode_pppoe(u_char *user _unused_, - const struct pcap_pkthdr *pheader, - const u_char *pdata) -{ - struct pktsummary sm; - memset(&sm, 0, sizeof(sm)); - sm.time = pheader->ts.tv_sec; - decode_pppoe_real(pdata, pheader->caplen, &sm); -} - -static void -decode_pppoe_real(const u_char *pdata, const uint32_t len, - struct pktsummary *sm) -{ - if (len < PPPOE_HDR_LEN) { - verbosef("pppoe: packet too short (%u bytes)", len); - return; - } - - if (pdata[1] != 0x00) { - verbosef("pppoe: code = 0x%02x, expecting 0; ignoring.", pdata[1]); - return; - } - - if ((pdata[6] == 0xc0) && (pdata[7] == 0x21)) return; /* LCP */ - if ((pdata[6] == 0xc0) && (pdata[7] == 0x25)) return; /* LQR */ - - if ((pdata[6] == 0x00) && (pdata[7] == 0x21)) { - decode_ip(pdata + PPPOE_HDR_LEN, len - PPPOE_HDR_LEN, sm); - acct_for(sm); - } else - verbosef("pppoe: non-IP PPPoE packet (0x%02x%02x); ignoring.", - pdata[6], pdata[7]); +static int decode_pppoe(DECODER_ARGS) { + return helper_pppoe(pdata, pheader->caplen, sm); } +#ifdef DLT_LINUX_SLL /* very similar to decode_ether ... */ -static void -decode_linux_sll(u_char *user _unused_, - const struct pcap_pkthdr *pheader, - const u_char *pdata) -{ +static int decode_linux_sll(DECODER_ARGS) { const struct sll_header { uint16_t packet_type; uint16_t device_type; @@ -325,63 +250,67 @@ decode_linux_sll(u_char *user _unused_, uint16_t ether_type; } *hdr = (const struct sll_header *)pdata; u_short type; - struct pktsummary sm; - memset(&sm, 0, sizeof(sm)); if (pheader->caplen < SLL_HDR_LEN) { verbosef("linux_sll: packet too short (%u bytes)", pheader->caplen); - return; + return 0; } - - type = ntohs( hdr->ether_type ); + type = ntohs(hdr->ether_type); switch (type) { case ETHERTYPE_IP: case ETHERTYPE_IPV6: - decode_ip(pdata + SLL_HDR_LEN, pheader->caplen - SLL_HDR_LEN, &sm); - sm.time = pheader->ts.tv_sec; - acct_for(&sm); - break; + return helper_ip(pdata + SLL_HDR_LEN, + pheader->caplen - SLL_HDR_LEN, + sm); case ETHERTYPE_ARP: /* known protocol, don't complain about it. */ - break; + return 0; default: - verbosef("linux_sll: unknown protocol (%04x)", type); + verbosef("linux_sll: unknown protocol (0x%04x)", type); + return 0; } } +#endif /* DLT_LINUX_SLL */ -static void -decode_raw(u_char *user _unused_, - const struct pcap_pkthdr *pheader, - const u_char *pdata) -{ - struct pktsummary sm; - memset(&sm, 0, sizeof(sm)); - - decode_ip(pdata, pheader->caplen, &sm); - sm.time = pheader->ts.tv_sec; - acct_for(&sm); +static int decode_raw(DECODER_ARGS) { + return helper_ip(pdata, pheader->caplen, sm); } -static void decode_ip_payload(const u_char *pdata, const uint32_t len, - struct pktsummary *sm); +static int helper_pppoe(HELPER_ARGS) { + if (len < PPPOE_HDR_LEN) { + verbosef("pppoe: packet too short (%u bytes)", len); + return 0; + } -static void -decode_ip(const u_char *pdata, const uint32_t len, struct pktsummary *sm) -{ + if (pdata[1] != 0x00) { + verbosef("pppoe: code = 0x%02x, expecting 0; ignoring.", pdata[1]); + return 0; + } + + if ((pdata[6] == 0xc0) && (pdata[7] == 0x21)) return 0; /* LCP */ + if ((pdata[6] == 0xc0) && (pdata[7] == 0x25)) return 0; /* LQR */ + + if ((pdata[6] == 0x00) && (pdata[7] == 0x21)) + return helper_ip(pdata + PPPOE_HDR_LEN, len - PPPOE_HDR_LEN, sm); + + verbosef("pppoe: ignoring non-IP PPPoE packet (0x%02x%02x)", + pdata[6], pdata[7]); + return 0; +} + +static int helper_ip(HELPER_ARGS) { const struct ip *hdr = (const struct ip *)pdata; - if (hdr->ip_v == 6) { - /* Redirect parsing of IPv6 packets. */ - decode_ipv6(pdata, len, sm); - return; - } if (len < IP_HDR_LEN) { verbosef("ip: packet too short (%u bytes)", len); - return; + return 0; + } + if (hdr->ip_v == 6) { + return helper_ipv6(pdata, len, sm); } if (hdr->ip_v != 4) { verbosef("ip: version %d (expecting 4 or 6)", hdr->ip_v); - return; + return 0; } sm->len = ntohs(hdr->ip_len); @@ -393,42 +322,57 @@ decode_ip(const u_char *pdata, const uint32_t len, struct pktsummary *sm) sm->dst.family = IPv4; sm->dst.ip.v4 = hdr->ip_dst.s_addr; - decode_ip_payload(pdata + IP_HDR_LEN, len - IP_HDR_LEN, sm); + helper_ip_deeper(pdata + IP_HDR_LEN, len - IP_HDR_LEN, sm); + return 1; } -static void -decode_ipv6(const u_char *pdata, const uint32_t len, struct pktsummary *sm) -{ +static int helper_ipv6(HELPER_ARGS) { const struct ip6_hdr *hdr = (const struct ip6_hdr *)pdata; if (len < IPV6_HDR_LEN) { verbosef("ipv6: packet too short (%u bytes)", len); - return; + return 0; } - if ((hdr->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { verbosef("ipv6: bad version (%02x, expecting %02x)", - hdr->ip6_vfc & IPV6_VERSION_MASK, - IPV6_VERSION); - return; + hdr->ip6_vfc & IPV6_VERSION_MASK, IPV6_VERSION); + return 0; } + /* IPv4 has "total length," but IPv6 has "payload length" which doesn't + * count the header bytes. + */ sm->len = ntohs(hdr->ip6_plen) + IPV6_HDR_LEN; sm->proto = hdr->ip6_nxt; - sm->src.family = IPv6; memcpy(&sm->src.ip.v6, &hdr->ip6_src, sizeof(sm->src.ip.v6)); - sm->dst.family = IPv6; memcpy(&sm->dst.ip.v6, &hdr->ip6_dst, sizeof(sm->dst.ip.v6)); - decode_ip_payload(pdata + IPV6_HDR_LEN, len - IPV6_HDR_LEN, sm); + /* Don't do proto accounting if this packet uses extension headers. */ + switch (sm->proto) { + case 0: /* Hop-by-Hop Options */ + case IPPROTO_NONE: + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_FRAGMENT: + case IPPROTO_AH: + case IPPROTO_ESP: + case 135: /* Mobility */ + sm->proto = IPPROTO_INVALID; + return 1; /* but we have addresses, so host accounting is ok */ + + default: + helper_ip_deeper(pdata + IPV6_HDR_LEN, len - IPV6_HDR_LEN, sm); + return 1; + } } -static void -decode_ip_payload(const u_char *pdata, const uint32_t len, - struct pktsummary *sm) -{ +static void helper_ip_deeper(HELPER_ARGS) { + /* At this stage we have IP addresses so we can do host accounting. + * If proto decode fails, we set IPPROTO_INVALID to skip proto accounting. + * We don't need to "return 0" like other helpers. + */ switch (sm->proto) { case IPPROTO_TCP: { const struct tcphdr *thdr = (const struct tcphdr *)pdata; @@ -441,7 +385,7 @@ decode_ip_payload(const u_char *pdata, const uint32_t len, sm->dst_port = ntohs(thdr->th_dport); sm->tcp_flags = thdr->th_flags & (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG); - break; + return; } case IPPROTO_UDP: { @@ -453,19 +397,19 @@ decode_ip_payload(const u_char *pdata, const uint32_t len, } sm->src_port = ntohs(uhdr->uh_sport); sm->dst_port = ntohs(uhdr->uh_dport); - break; + return; } case IPPROTO_ICMP: + case IPPROTO_IGMP: case IPPROTO_ICMPV6: - case IPPROTO_AH: - case IPPROTO_ESP: case IPPROTO_OSPF: /* known protocol, don't complain about it */ + sm->proto = IPPROTO_INVALID; /* also don't do accounting */ break; default: - verbosef("ip: unknown protocol %d", sm->proto); + verbosef("ip_deeper: unknown protocol 0x%02x", sm->proto); } } diff --git a/decode.h b/decode.h index 35cb42a..cc4bd55 100644 --- a/decode.h +++ b/decode.h @@ -9,55 +9,49 @@ #ifndef __DARKSTAT_DECODE_H #define __DARKSTAT_DECODE_H -#include -#include /* n_time */ -#define __USE_GNU 1 -#include /* for */ -#include /* struct ip */ - #include "addr.h" -#define PPP_HDR_LEN 4 -#define FDDI_HDR_LEN 21 -#define IP_HDR_LEN sizeof(struct ip) -#define IPV6_HDR_LEN sizeof(struct ip6_hdr) -#define TCP_HDR_LEN sizeof(struct tcphdr) -#define UDP_HDR_LEN sizeof(struct udphdr) -#define NULL_HDR_LEN 4 -#define PPPOE_HDR_LEN 8 -#define SLL_HDR_LEN 16 -#define RAW_HDR_LEN 0 - #ifndef ETHER_ADDR_LEN -#define ETHER_ADDR_LEN 6 +# define ETHER_ADDR_LEN 6 #endif +#define IPPROTO_INVALID 254 /* special: means don't do proto accounting */ + #ifndef IPPROTO_OSPF -#define IPPROTO_OSPF 89 +# define IPPROTO_OSPF 89 #endif -#define IPPROTO_INVALID 254 /* don't do proto accounting */ - -struct linkhdr { - int linktype; - unsigned int hdrlen; - pcap_handler handler; -}; - -const struct linkhdr *getlinkhdr(const int linktype); -int getsnaplen(const struct linkhdr *lh); +#define PPPOE_HDR_LEN 8 +/* Decoding creates a summary which is passed to accounting. */ struct pktsummary { /* Fields are in host byte order (except IPs) */ struct addr src, dst; - time_t time; uint16_t len; - uint8_t proto; /* IPPROTO_{TCP, UDP, ICMP} */ + uint8_t proto; /* IPPROTO_INVALID means don't do proto accounting */ uint8_t tcp_flags; /* only for TCP */ uint16_t src_port, dst_port; /* only for TCP, UDP */ - uint8_t src_mac[ETHER_ADDR_LEN], + uint8_t src_mac[ETHER_ADDR_LEN], /* only for Ethernet */ dst_mac[ETHER_ADDR_LEN]; /* only for Ethernet */ }; +struct pcap_pkthdr; /* from pcap.h */ + +#define DECODER_ARGS const struct pcap_pkthdr *pheader, \ + const u_char *pdata, \ + struct pktsummary *sm + +/* Returns 0 on decode failure (meaning accounting should not be performed) */ +typedef int (decoder_fn)(DECODER_ARGS); + +struct linkhdr { + int linktype; + unsigned int hdrlen; + decoder_fn *decoder; +}; + +const struct linkhdr *getlinkhdr(const int linktype); +int getsnaplen(const struct linkhdr *lh); + #endif /* __DARKSTAT_DECODE_H */ /* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/dns.c b/dns.c index e8bdbb4..c89dc1b 100644 --- a/dns.c +++ b/dns.c @@ -29,6 +29,11 @@ #include #include +#ifdef __NetBSD__ +# define gethostbyaddr(addr, len, type) \ + gethostbyaddr((const char *)(addr), len, type) +#endif + static void dns_main(void) _noreturn_; /* the child process runs this */ #define CHILD 0 /* child process uses this socket */ diff --git a/err.c b/err.c index 358e564..3b42d18 100644 --- a/err.c +++ b/err.c @@ -29,7 +29,6 @@ #include #include #include -#include static void to_syslog(const char *type, const int want_err, const char *format, va_list va) _printflike_(3, 0); diff --git a/graph_db.c b/graph_db.c index 8c72102..c0875d4 100644 --- a/graph_db.c +++ b/graph_db.c @@ -23,6 +23,7 @@ #include #include #include /* for memcpy() */ +#include #define GRAPH_WIDTH "320" #define GRAPH_HEIGHT "200" @@ -46,63 +47,54 @@ static struct graph *graph_db[] = { }; static unsigned int graph_db_size = sizeof(graph_db)/sizeof(*graph_db); +static long start_mono, start_real, last_real; -static time_t start_time, last_time; - -void -graph_init(void) -{ +void graph_init(void) { unsigned int i; for (i=0; iin = 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; iin); 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; iin[ graph_db[i]->pos ] += amount; break; - case GRAPH_OUT: graph_db[i]->out[ graph_db[i]->pos ] += amount; break; - default: errx(1, "unknown graph_dir in graph_acct: %d", dir); - } + if (dir == GRAPH_IN) { + graph_db[i]->in[ graph_db[i]->pos ] += amount; + } else { + assert(dir == GRAPH_OUT); + graph_db[i]->out[ graph_db[i]->pos ] += amount; + } } /* Advance a graph: advance the pos, zeroing out bars as we move. */ -static void -advance(struct graph *g, const unsigned int pos) -{ +static void advance(struct graph *g, const unsigned int pos) { if (g->pos == pos) return; /* didn't need to advance */ do { @@ -112,10 +104,9 @@ advance(struct graph *g, const unsigned int pos) } /* Rotate a graph: rotate all bars so that the bar at the current pos is moved - * to the newly given pos. This is non-destructive. */ -static void -rotate(struct graph *g, const unsigned int pos) -{ + * to the newly given pos. + */ +static void rotate(struct graph *g, const unsigned int pos) { uint64_t *tmp; unsigned int i, ofs; size_t size; @@ -140,13 +131,11 @@ rotate(struct graph *g, const unsigned int pos) g->pos = pos; } -static void -graph_resync(const time_t new_time) -{ +static void graph_resync(const time_t new_real) { struct tm *tm; /* - * If time went backwards, we assume that real time is continuous and that - * the time adjustment should only affect display. i.e., if we have: + * If real time went backwards, we assume that the time adjustment should + * only affect display. i.e., if we have: * * second 15: 12 bytes * second 16: 345 bytes @@ -159,13 +148,15 @@ graph_resync(const time_t new_time) * second 7: 345 bytes * second 8: <-- current pos * - * Note that we don't make any corrections for time being stepped forward. + * We don't make any corrections for time being stepped forward, + * it's treated as though there was no traffic during that time. + * * We rely on graph advancement to happen at the correct real time to * account for, for example, bandwidth used per day. */ - assert(new_time < last_time); + assert(new_real < last_real); - tm = localtime(&new_time); + tm = localtime(&new_real); if (tm->tm_sec == 60) tm->tm_sec = 59; /* mis-handle leap seconds */ @@ -174,25 +165,21 @@ graph_resync(const time_t new_time) rotate(&graph_hrs, tm->tm_hour); rotate(&graph_days, tm->tm_mday - 1); - last_time = new_time; + last_real = new_real; } -void -graph_rotate(void) -{ - time_t t, td; +void graph_rotate(void) { + long t, td; struct tm *tm; unsigned int i; - t = now; + t = now_real(); + td = t - last_real; - if (last_time == 0) { + if (last_real == 0) { verbosef("first rotate"); - last_time = t; + last_real = t; tm = localtime(&t); - if (tm->tm_sec == 60) - tm->tm_sec = 59; /* mis-handle leap seconds */ - graph_secs.pos = tm->tm_sec; graph_mins.pos = tm->tm_min; graph_hrs.pos = tm->tm_hour; @@ -200,22 +187,20 @@ graph_rotate(void) return; } - if (t == last_time) - return; /* superfluous rotate */ + if (t == last_real) + return; /* time has not advanced a full second, don't rotate */ - if (t < last_time) { - verbosef("time went backwards! (from %u to %u, offset is %d)", - (unsigned int)last_time, (unsigned int)t, (int)(t - last_time)); + if (t < last_real) { + verbosef("graph_db: realtime went backwards! " + "(from %ld to %ld, offset is %ld)", + last_real, t, td); graph_resync(t); return; } /* else, normal rotation */ - td = t - last_time; - last_time = t; + last_real = t; tm = localtime(&t); - if (tm->tm_sec == 60) - tm->tm_sec = 59; /* mis-handle leap seconds */ /* zero out graphs which have been completely rotated through */ for (i=0; inum_bars)) return 0; if (!write8(fd, graph_db[i]->pos)) return 0; @@ -302,26 +283,29 @@ graph_export(const int fd) /* --------------------------------------------------------------------------- * Web interface: front page! */ -struct str * -html_front_page(void) -{ +struct str *html_front_page(void) { struct str *buf, *rf; unsigned int i; char start_when[100]; + long d_real, d_mono; buf = str_make(); html_open(buf, "Graphs", /*path_depth=*/0, /*want_graph_js=*/1); + d_mono = now_mono() - start_mono; + d_real = now_real() - start_real; str_append(buf, "

\n"); str_append(buf, "Running for "); - 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, ""); + 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, ", since %s", start_when); str_appendf(buf,".
\n" @@ -372,15 +356,13 @@ html_front_page(void) /* --------------------------------------------------------------------------- * Web interface: graphs.xml */ -struct str * -xml_graphs(void) -{ +struct str *xml_graphs(void) { unsigned int i, j; struct str *buf = str_make(), *rf; str_appendf(buf, "\n"); @@ -402,4 +384,4 @@ xml_graphs(void) return (buf); } -/* vim:set ts=3 sw=3 tw=78 expandtab: */ +/* vim:set ts=3 sw=3 tw=80 et: */ diff --git a/hosts_db.c b/hosts_db.c index 754dacc..c2d7ae4 100644 --- a/hosts_db.c +++ b/hosts_db.c @@ -26,6 +26,7 @@ #include #include #include /* memset(), strcmp() */ +#include #include int hosts_db_show_macs = 0; @@ -236,7 +237,7 @@ make_func_host(const void *key) MAKE_BUCKET(b, h, host); h->addr = CASTKEY(struct addr); h->dns = NULL; - h->lastseen = 0; + h->last_seen_mono = 0; memset(&h->mac_addr, 0, sizeof(h->mac_addr)); h->ports_tcp = NULL; h->ports_udp = NULL; @@ -344,16 +345,15 @@ format_row_host(struct str *buf, const struct bucket *b, b->in, b->out, b->total); if (opt_want_lastseen) { - time_t last_t = b->u.host.lastseen; + long last = b->u.host.last_seen_mono; struct str *last_str = NULL; - if ((now >= last_t) && (last_t > 0)) - last_str = length_of_time(now - last_t); + if ((now_mono() >= last) && (last > 0)) + last_str = length_of_time(now_mono() - last); - str_append(buf, - " "); + str_append(buf, " "); if (last_str == NULL) { - if (last_t == 0) + if (last == 0) str_append(buf, "(never)"); else str_append(buf, "(clock error)"); @@ -361,12 +361,10 @@ format_row_host(struct str *buf, const struct bucket *b, str_appendstr(buf, last_str); str_free(last_str); } - str_append(buf, - ""); + str_append(buf, ""); } - str_appendf(buf, - "\n"); + str_appendf(buf, "\n"); /* Only resolve hosts "on demand" */ if (b->u.host.dns == NULL) @@ -1024,14 +1022,12 @@ done: /* --------------------------------------------------------------------------- * Web interface: detailed view of a single host. */ -static struct str * -html_hosts_detail(const char *ip) -{ +static struct str *html_hosts_detail(const char *ip) { struct bucket *h; struct str *buf, *ls_len; char ls_when[100]; const char *canonical; - time_t ls; + time_t last_real; h = host_search(ip); if (h == NULL) @@ -1069,13 +1065,13 @@ html_hosts_detail(const char *ip) "

\n" "Last seen: "); - ls = h->u.host.lastseen; + last_real = mono_to_real(h->u.host.last_seen_mono); if (strftime(ls_when, sizeof(ls_when), - "%Y-%m-%d %H:%M:%S %Z%z", localtime(&ls)) != 0) + "%Y-%m-%d %H:%M:%S %Z%z", localtime(&last_real)) != 0) str_append(buf, ls_when); - if (h->u.host.lastseen <= now) { - ls_len = length_of_time(now - h->u.host.lastseen); + if (h->u.host.last_seen_mono <= now_mono()) { + ls_len = length_of_time(now_mono() - h->u.host.last_seen_mono); str_append(buf, " ("); str_appendstr(buf, ls_len); str_free(ls_len); @@ -1103,7 +1099,7 @@ html_hosts_detail(const char *ip) format_table(buf, h->u.host.ip_protos, 0,TOTAL,0); html_close(buf); - return (buf); + return buf; } /* --------------------------------------------------------------------------- @@ -1263,7 +1259,7 @@ hosts_db_import_host(const int fd) if (ver > 1) { uint64_t t; if (!read64(fd, &t)) return 0; - host->u.host.lastseen = (time_t)t; + host->u.host.last_seen_mono = real_to_mono(t); } assert(sizeof(host->u.host.mac_addr) == 6); @@ -1336,9 +1332,11 @@ int hosts_db_export(const int fd) if (!writen(fd, export_tag_host_ver3, sizeof(export_tag_host_ver3))) return 0; - if (!writeaddr(fd, &(b->u.host.addr))) return 0; + if (!writeaddr(fd, &(b->u.host.addr))) + return 0; - if (!write64(fd, (uint64_t)(b->u.host.lastseen))) return 0; + if (!write64(fd, (uint64_t)mono_to_real(b->u.host.last_seen_mono))) + return 0; assert(sizeof(b->u.host.mac_addr) == 6); if (!writen(fd, b->u.host.mac_addr, sizeof(b->u.host.mac_addr))) diff --git a/hosts_db.h b/hosts_db.h index f2f389a..3f869f1 100644 --- a/hosts_db.h +++ b/hosts_db.h @@ -9,7 +9,7 @@ #ifndef __DARKSTAT_HOSTS_DB_H #define __DARKSTAT_HOSTS_DB_H -#include /* for time_t and uint64_t (esp on FreeBSD) */ +#include /* for uint64_t */ #include "addr.h" @@ -19,7 +19,7 @@ struct host { struct addr addr; char *dns; uint8_t mac_addr[6]; - time_t lastseen; + long last_seen_mono; struct hashtable *ports_tcp, *ports_udp, *ip_protos; }; diff --git a/hosts_sort.c b/hosts_sort.c index d17a21f..b6f32dd 100644 --- a/hosts_sort.c +++ b/hosts_sort.c @@ -34,8 +34,8 @@ cmp(const struct bucket * const *x, const struct bucket * const *y, b = (*y)->total; break; case LASTSEEN: - a = (*x)->u.host.lastseen; - b = (*y)->u.host.lastseen; + a = (*x)->u.host.last_seen_mono; + b = (*y)->u.host.last_seen_mono; break; default: errx(1, "cmp: unknown direction: %d", dir); diff --git a/html.c b/html.c index 2d2b7f7..fe5bb68 100644 --- a/html.c +++ b/html.c @@ -32,11 +32,11 @@ void html_open(struct str *buf, const char *title, "\n" "\n" "\n" - "%s (darkstat3 %s)\n" + "%s (darkstat %s)\n" "\n" "\n" - "\n" - , title, opt_interface, root); + "\n", + title, title_interfaces, root); if (want_graph_js) str_appendf(buf, @@ -67,4 +67,4 @@ void html_close(struct str *buf) "\n"); } -/* vim:set ts=4 sw=4 tw=78 expandtab: */ +/* vim:set ts=4 sw=4 tw=80 et: */ diff --git a/http.c b/http.c index 2835e8e..e39f16b 100644 --- a/http.c +++ b/http.c @@ -1,5 +1,5 @@ /* darkstat 3 - * copyright (c) 2001-2011 Emil Mikulic. + * copyright (c) 2001-2012 Emil Mikulic. * * http.c: embedded webserver. * This borrows a lot of code from darkhttpd. @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -55,7 +56,7 @@ struct connection { int socket; struct sockaddr_storage client; - time_t last_active; + long last_active_mono; enum { RECV_REQUEST, /* receiving request */ SEND_HEADER_AND_REPLY, /* try to send header+reply together */ @@ -266,7 +267,7 @@ static struct connection *new_connection(void) conn->socket = -1; memset(&conn->client, 0, sizeof(conn->client)); - conn->last_active = now; + conn->last_active_mono = now_mono(); conn->request = NULL; conn->request_length = 0; conn->accept_gzip = 0; @@ -363,13 +364,11 @@ static void free_connection(struct connection *conn) * buffer is returned for convenience. */ #define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */ -static char *rfc1123_date(char *dest, const time_t when) -{ - time_t tmp = when; +static char *rfc1123_date(char *dest, time_t when) { if (strftime(dest, DATE_LEN, - "%a, %d %b %Y %H:%M:%S %Z", gmtime(&tmp) ) == 0) + "%a, %d %b %Y %H:%M:%S %Z", gmtime(&when) ) == 0) errx(1, "strftime() failed [%s]", dest); - return (dest); + return dest; } static void generate_header(struct connection *conn, @@ -382,7 +381,7 @@ static void generate_header(struct connection *conn, if (conn->encoding == NULL) conn->encoding = encoding_identity; - verbosef("http: %d %s (%s: %d bytes)", code, text, + verbosef("http: %d %s (%s: %zu bytes)", code, text, conn->encoding, conn->reply_length); conn->header_length = xasprintf(&(conn->header), "HTTP/1.1 %d %s\r\n" @@ -397,7 +396,7 @@ static void generate_header(struct connection *conn, "\r\n" , code, text, - rfc1123_date(date, now), server, + rfc1123_date(date, now_real()), server, conn->mime_type, conn->reply_length, conn->encoding, conn->header_extra); conn->http_code = code; @@ -719,7 +718,7 @@ static void poll_recv_request(struct connection *conn) conn->state = DONE; return; } - conn->last_active = now; + conn->last_active_mono = now_mono(); /* append to conn->request */ conn->request = xrealloc(conn->request, conn->request_length+recvd+1); @@ -772,7 +771,7 @@ static void poll_send_header_and_reply(struct connection *conn) iov[1].iov_len = conn->reply_length; sent = writev(conn->socket, iov, 2); - conn->last_active = now; + conn->last_active_mono = now_mono(); /* handle any errors (-1) or closure (0) in send() */ if (sent < 1) { @@ -814,7 +813,7 @@ static void poll_send_header(struct connection *conn) sent = send(conn->socket, conn->header + conn->header_sent, conn->header_length - conn->header_sent, 0); - conn->last_active = now; + conn->last_active_mono = now_mono(); dverbosef("poll_send_header(%d) sent %d bytes", conn->socket, (int)sent); /* handle any errors (-1) or closure (0) in send() */ @@ -850,7 +849,7 @@ static void poll_send_reply(struct connection *conn) sent = send(conn->socket, conn->reply + conn->reply_sent, conn->reply_length - conn->reply_sent, 0); - conn->last_active = now; + conn->last_active_mono = now_mono(); dverbosef("poll_send_reply(%d) sent %d: [%d-%d] of %d", conn->socket, (int)sent, (int)conn->reply_sent, @@ -921,14 +920,18 @@ static void http_listen_one(struct addrinfo *ai, /* create incoming socket */ if ((sockin = socket(ai->ai_family, ai->ai_socktype, - ai->ai_protocol)) == -1) - err(1, "http_listen_one(%s, %u): socket(%d (%s), %d, %d) failed", + ai->ai_protocol)) == -1) { + warn("http_listen_one(%s, %u): socket(%d (%s), %d, %d) failed", ipaddr, (unsigned int)bindport, ai->ai_family, (ai->ai_family == AF_INET6) ? "AF_INET6" : (ai->ai_family == AF_INET) ? "AF_INET" : "?", ai->ai_socktype, ai->ai_protocol); + return; + } + + fd_set_nonblock(sockin); /* reuse address */ sockopt = 1; @@ -951,11 +954,12 @@ static void http_listen_one(struct addrinfo *ai, /* bind socket */ if (bind(sockin, ai->ai_addr, ai->ai_addrlen) == -1) { warn("bind(\"%s\") failed", ipaddr); + close(sockin); return; } /* listen on socket */ - if (listen(sockin, -1) == -1) + if (listen(sockin, 128) == -1) err(1, "listen() failed"); verbosef("listening on http://%s%s%s:%u/", @@ -993,7 +997,7 @@ void http_listen(const unsigned short bindport) free(bindaddr); } - if (insocks == 0) + if (insocks == NULL) errx(1, "was not able to bind any ports for http interface"); /* ignore SIGPIPE */ @@ -1022,7 +1026,7 @@ http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd, LIST_FOREACH_SAFE(conn, &connlist, entries, next) { - int idlefor = now - conn->last_active; + int idlefor = now_mono() - conn->last_active_mono; /* Time out dead connections. */ if (idlefor >= idletime) { diff --git a/localip.c b/localip.c index 0c15409..f7b7f93 100644 --- a/localip.c +++ b/localip.c @@ -1,22 +1,27 @@ /* darkstat 3 * copyright (c) 2001-2011 Emil Mikulic. * - * localip.c: determine local IP of our capture interface + * localip.c: determine local IPs of an interface * * You may use, modify and redistribute this file under the terms of the * GNU General Public License version 2. (see COPYING.GPL) */ #include "addr.h" +#include "bsd.h" /* for strlcpy */ #include "config.h" /* for HAVE_IFADDRS_H */ +#include "conv.h" #include "err.h" #include "localip.h" -#include "bsd.h" /* for strlcpy */ +#include "now.h" #include #include +#include #include +#include #include +#include #include #ifdef HAVE_IFADDRS_H @@ -28,90 +33,90 @@ # include #endif -static const char *iface = NULL; -struct addr localip4, localip6; -static struct addr last_localip4, last_localip6; - -void -localip_init(const char *interface) -{ - iface = interface; - - /* defaults */ - localip4.family = IPv4; - localip4.ip.v4 = 0; - - localip6.family = IPv6; - memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6)); +void localip_init(struct local_ips *ips) { + ips->is_valid = 0; + ips->last_update_mono = 0; + ips->num_addrs = 0; + ips->addrs = NULL; +} - last_localip4 = localip4; - last_localip6 = localip6; +void localip_free(struct local_ips *ips) { + if (ips->addrs != NULL) + free(ips->addrs); +} - /* initial update */ - localip_update(); +static void add_ip(const char *iface, + struct local_ips *ips, + int *idx, + struct addr *a) { + if (ips->num_addrs <= *idx) { + /* Grow. */ + ips->addrs = xrealloc(ips->addrs, sizeof(*(ips->addrs)) * (*idx + 1)); + ips->num_addrs++; + assert(ips->num_addrs > *idx); + verbosef("interface '%s' gained new address %s", iface, addr_to_str(a)); + } else { + /* Warn about changed address. */ + if (!addr_equal(ips->addrs + *idx, a)) { + static char before[INET6_ADDRSTRLEN]; + strncpy(before, addr_to_str(ips->addrs + *idx), INET6_ADDRSTRLEN); + verbosef("interface '%s' address %d/%d changed from %s to %s", + iface, *idx+1, ips->num_addrs, before, addr_to_str(a)); + } + } + ips->addrs[*idx] = *a; + (*idx)++; } -static void -localip_update_helper(void) -{ - /* defaults */ - localip4.family = IPv4; - localip4.ip.v4 = 0; +/* Returns 0 on failure. */ +void localip_update(const char *iface, struct local_ips *ips) { + struct addr a; + int new_addrs = 0; - localip6.family = IPv6; - memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6)); + if (iface == NULL) { + /* reading from capfile */ + ips->is_valid = 0; + return; + } - if (iface == NULL) - return; /* reading from capfile */ + if (ips->last_update_mono == now_mono()) { + /* Too soon, bail out. */ + return; + } + ips->last_update_mono = now_mono(); #ifdef HAVE_IFADDRS_H { - int got_v4 = 0, got_v6 = 0; struct ifaddrs *ifas, *ifa; - if (getifaddrs(&ifas) < 0) { - warn("can't getifaddrs() on interface \"%s\"", iface); - return; - } - - for (ifa = ifas; ifa; ifa = ifa->ifa_next) { - if (got_v4 && got_v6) - break; /* Task is already complete. */ + if (getifaddrs(&ifas) < 0) + err(1, "getifaddrs() failed"); + for (ifa=ifas; ifa; ifa=ifa->ifa_next) { if (strncmp(ifa->ifa_name, iface, IFNAMSIZ)) continue; /* Wrong interface. */ if (!ifa->ifa_addr) continue; /* This can be NULL, e.g. for ppp0. */ - /* The first IPv4 name is always functional. */ - if ((ifa->ifa_addr->sa_family == AF_INET) && !got_v4) - { - /* Good IPv4 address. */ - localip4.ip.v4 = - ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; - got_v4 = 1; - continue; + if (ifa->ifa_addr->sa_family == AF_INET) { + a.family = IPv4; + a.ip.v4 = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; + add_ip(iface, ips, &new_addrs, &a); } - - /* IPv6 needs some obvious exceptions. */ - if ( ifa->ifa_addr->sa_family == AF_INET6 ) { - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr; - + if (ifa->ifa_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ifa->ifa_addr; +# if 0 if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr)) || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) ) continue; - - /* Only standard IPv6 can reach this point. */ - memcpy(&(localip6.ip.v6), &sa6->sin6_addr, sizeof(localip6.ip.v6)); - got_v6 = 1; +# endif + a.family = IPv6; + memcpy(&(a.ip.v6), &sa6->sin6_addr, sizeof(a.ip.v6)); + add_ip(iface, ips, &new_addrs, &a); } } - freeifaddrs(ifas); - - if (!got_v4) - warnx("can't get own IPv4 address on interface \"%s\"", iface); } #else /* don't HAVE_IFADDRS_H */ { @@ -121,34 +126,39 @@ localip_update_helper(void) strlcpy(ifr.ifr_name, iface, IFNAMSIZ); ifr.ifr_addr.sa_family = AF_INET; - if (ioctl(tmp, SIOCGIFADDR, &ifr) == -1) { - if (errno == EADDRNOTAVAIL) { - verbosef("lost local IP"); - } else - warn("can't get own IP address on interface \"%s\"", iface); - } else { - /* success! */ + if (ioctl(tmp, SIOCGIFADDR, &ifr) != -1) { sa = ifr.ifr_addr; - localip4.ip.v4 = ((struct sockaddr_in*)&sa)->sin_addr.s_addr; + a.family = IPv4; + a.ip.v4 = ((struct sockaddr_in*)(&ifr.ifr_addr))->sin_addr.s_addr; + add_ip(iface, ips, &new_addrs, &a); } close(tmp); } #endif + if (new_addrs == 0) { + if (ips->is_valid) + verbosef("interface '%s' no longer has any addresses", iface); + ips->is_valid = 0; + } else { + if (!ips->is_valid) + verbosef("interface '%s' now has addresses", iface); + ips->is_valid = 1; + if (ips->num_addrs != new_addrs) + verbosef("interface '%s' number of addresses decreased from %d to %d", + iface, ips->num_addrs, new_addrs); + ips->num_addrs = new_addrs; + } } -void -localip_update(void) -{ - localip_update_helper(); +int is_localip(const struct addr * const a, + const struct local_ips * const ips) { + int i; - if (!addr_equal(&last_localip4, &localip4)) { - verbosef("%s ip4 update: %s", iface, addr_to_str(&localip4)); - last_localip4 = localip4; - } - if (!addr_equal(&last_localip6, &localip6)) { - verbosef("%s ip6 update: %s", iface, addr_to_str(&localip6)); - last_localip6 = localip6; + for (i=0; inum_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: */ diff --git a/localip.h b/localip.h index 21231b6..a5b48ea 100644 --- a/localip.h +++ b/localip.h @@ -1,15 +1,29 @@ /* darkstat 3 * copyright (c) 2001-2011 Emil Mikulic. * - * localip.h: determine local IP of our capture interface + * localip.h: determine the local IPs of an interface * * You may use, modify and redistribute this file under the terms of the * GNU General Public License version 2. (see COPYING.GPL) */ +#ifndef __DARKSTAT_LOCALIP_H +#define __DARKSTAT_LOCALIP_H -extern struct addr localip4, localip6; +#include -void localip_init(const char *interface); -void localip_update(void); +struct local_ips { + int is_valid; + long last_update_mono; + int num_addrs; + struct addr *addrs; +}; -/* vim:set ts=3 sw=3 tw=78 expandtab: */ +void localip_init(struct local_ips *ips); +void localip_free(struct local_ips *ips); + +void localip_update(const char *iface, struct local_ips *ips); +int is_localip(const struct addr * const a, + const struct local_ips * const ips); + +#endif +/* vim:set ts=3 sw=3 tw=80 et: */ diff --git a/now.c b/now.c new file mode 100644 index 0000000..f0751a8 --- /dev/null +++ b/now.c @@ -0,0 +1,152 @@ +/* darkstat 3 + * copyright (c) 2012 Emil Mikulic. + * + * now.c: a cache of the current time. + * + * Permission to use, copy, modify, and distribute this file for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include "err.h" +#include "now.h" + +#include +#include +#include + +#ifdef __MACH__ +/* Fake up clock_gettime() on OS X. */ +# include +# include +# include +# include + +typedef int clockid_t; +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 + +static uint64_t mono_first = 0; + +int clock_gettime(clockid_t clk_id, struct timespec *tp) { + if (clk_id == CLOCK_REALTIME) { + struct timeval tv; + gettimeofday(&tv, NULL); + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * 1000; + return 0; + } + if (clk_id == CLOCK_MONOTONIC) { + uint64_t t = mach_absolute_time(); + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + if (!mono_first) { + mono_first = t; + } + uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom; + tp->tv_sec = tdiff / 1000000000; + tp->tv_nsec = tdiff % 1000000000; + return 0; + } + return -1; +} +#endif /* __MACH__ */ + +static struct timespec clock_real, clock_mono; +static int now_initialized = 0; + +long now_real(void) { + assert(now_initialized); + return clock_real.tv_sec; +} + +long now_mono(void) { + assert(now_initialized); + return clock_mono.tv_sec; +} + +static int before(const struct timespec *a, const struct timespec *b) { + if (a->tv_sec < b->tv_sec) + return 1; + if (a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec) + return 1; + return 0; +} + +static void clock_update(const clockid_t clk_id, + struct timespec *dest, + const char *name) { + struct timespec t; + + clock_gettime(clk_id, &t); + if (now_initialized && before(&t, dest)) { + verbosef("%s clock went backwards from %ld.%09ld to %ld.%09ld", + name, + (long)dest->tv_sec, + (long)dest->tv_nsec, + (long)t.tv_sec, + (long)t.tv_nsec); + } + memcpy(dest, &t, sizeof(t)); +} + +static void all_clocks_update(void) { + clock_update(CLOCK_REALTIME, &clock_real, "realtime"); + clock_update(CLOCK_MONOTONIC, &clock_mono, "monotonic"); +} + +void now_init(void) { + assert(!now_initialized); + all_clocks_update(); + now_initialized = 1; +} + +void now_update(void) { + assert(now_initialized); + all_clocks_update(); +} + +long mono_to_real(const long t) { + assert(now_initialized); + return t - clock_mono.tv_sec + clock_real.tv_sec; +} + +long real_to_mono(const long t) { + assert(now_initialized); + return t - clock_real.tv_sec + clock_mono.tv_sec; +} + +void timer_start(struct timespec *t) { + clock_gettime(CLOCK_MONOTONIC, t); +} + +static int64_t ts_diff(const struct timespec * const a, + const struct timespec * const b) { + return (int64_t)(a->tv_sec - b->tv_sec) * 1000000000 + + a->tv_nsec - b->tv_nsec; +} + +void timer_stop(const struct timespec * const t, + const int64_t nsec, + const char *warning) { + struct timespec t2; + int64_t diff; + + clock_gettime(CLOCK_MONOTONIC, &t2); + diff = ts_diff(&t2, t); + assert(diff > 0); + if (diff > nsec) + warnx("%s (took %lld nsec, over threshold of %lld nsec)", + warning, + (long long)diff, + (long long)nsec); +} + +/* vim:set ts=3 sw=3 tw=80 et: */ diff --git a/now.h b/now.h index 2dd8e4a..9c61f45 100644 --- a/now.h +++ b/now.h @@ -1,9 +1,36 @@ /* darkstat 3 * copyright (c) 2001-2006 Emil Mikulic. * - * now.h: a cache of the current time - * This lets us avoid superfluous gettimeofday() syscalls. + * now.h: a cache of the current time. + * + * Permission to use, copy, modify, and distribute this file for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include + +void now_init(void); +void now_update(void); /* once per event loop (in darkstat.c) */ + +long now_real(void); +long now_mono(void); + +long mono_to_real(const long t); +long real_to_mono(const long t); + +/* Emits warnings if a call is too slow. */ +struct timespec; +void timer_start(struct timespec *t); +void timer_stop(const struct timespec * const t, + const int64_t nsec, + const char *warning); -extern time_t now; /* updated in the event loop in darkstat.c */ +/* vim:set ts=3 sw=3 tw=80 et: */ diff --git a/opt.h b/opt.h index 5d75cf3..34afc36 100644 --- a/opt.h +++ b/opt.h @@ -4,29 +4,22 @@ * opt.h: global options */ -/* - * Capture options. - */ +/* Capture options. */ extern int opt_want_pppoe; extern int opt_want_macs; extern int opt_want_hexdump; extern int opt_want_snaplen; extern int opt_wait_secs; -/* - * Error/logging options. - */ +/* Error/logging options. */ extern int opt_want_verbose; extern int opt_want_syslog; -/* - * Accounting options. - */ +/* Accounting options. */ extern unsigned int opt_highest_port; extern int opt_want_local_only; -/* - * Hosts table reduction - when the number of entries is about to exceed +/* Hosts table reduction - when the number of entries is about to exceed * , we reduce the table to the top entries. */ extern unsigned int opt_hosts_max; @@ -34,10 +27,10 @@ extern unsigned int opt_hosts_keep; extern unsigned int opt_ports_max; extern unsigned int opt_ports_keep; -/* - * Hosts output options. - */ +/* Hosts output options. */ extern int opt_want_lastseen; -extern const char *opt_interface; + +/* Initialized in cap.c, added to */ +extern char *title_interfaces; /* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/queue.h b/queue.h index 95a1276..5773a91 100644 --- a/queue.h +++ b/queue.h @@ -49,6 +49,11 @@ struct { \ #define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) #ifdef STAILQ_INSERT_TAIL diff --git a/str.c b/str.c index 8092666..852a24d 100644 --- a/str.c +++ b/str.c @@ -16,14 +16,15 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "conv.h" +#include "err.h" +#include "str.h" + #include <assert.h> #include <stdlib.h> #include <string.h> #include <stdint.h> /* for uint32_t on Linux and OS X */ - -#include "conv.h" -#include "err.h" -#include "str.h" +#include <unistd.h> #define INITIAL_LEN 1024 @@ -105,7 +106,8 @@ static void str_append_u32(struct str *s, const uint32_t i, const int mod_sep) { char out[I32_MAXLEN]; - int pos, len; + int pos; + unsigned int len; uint32_t rem, next; if (i == 0) { @@ -148,7 +150,8 @@ static void str_append_u64(struct str *s, const uint64_t i, const int mod_sep) { char out[I64_MAXLEN]; - int pos, len; + int pos; + unsigned int len; uint64_t rem, next; uint32_t rem32, next32; @@ -224,9 +227,7 @@ str_append_hex8(struct str *s, const uint8_t b) * * %x is equivalent to %02x and expects a uint8_t */ -static void -str_vappendf(struct str *s, const char *format, va_list va) -{ +void str_vappendf(struct str *s, const char *format, va_list va) { size_t pos, len; len = strlen(format); @@ -354,4 +355,12 @@ length_of_time(const time_t t) return buf; } +ssize_t str_write(const struct str * const buf, const int fd) { + return write(fd, buf->buf, buf->len); +} + +size_t str_len(const struct str * const buf) { + return buf->len; +} + /* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/str.h b/str.h index da8a2dc..92cb78f 100644 --- a/str.h +++ b/str.h @@ -31,9 +31,10 @@ void str_appendstr(struct str *buf, const struct str *s); #ifdef __GNUC__ /* amusing efficiency hack */ -#include <string.h> -#define str_append(buf, s) str_appendn(buf, s, \ - (__builtin_constant_p(s) ? sizeof(s)-1 : strlen(s)) ) +# include <string.h> +# define str_append(buf, s) \ + str_appendn(buf, s, \ + (__builtin_constant_p(s) ? sizeof(s)-1 : strlen(s)) ) #else void str_append(struct str *buf, const char *s); #endif @@ -41,7 +42,10 @@ void str_append(struct str *buf, const char *s); size_t xvasprintf(char **result, const char *format, va_list va); size_t xasprintf(char **result, const char *format, ...); void str_appendf(struct str *buf, const char *format, ...); +void str_vappendf(struct str *s, const char *format, va_list va); struct str *length_of_time(const time_t t); +ssize_t str_write(const struct str * const buf, const int fd); +size_t str_len(const struct str * const buf); /* vim:set ts=3 sw=3 tw=78 expandtab: */ -- 2.17.1