- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
- Preamble
+ Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
+the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
The precise terms and conditions for copying, distribution and
modification follow.
-\f
- GNU GENERAL PUBLIC LICENSE
+
+ GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
-\f
+
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
-\f
+
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
-\f
+
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
- NO WARRANTY
+ NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
- END OF TERMS AND CONDITIONS
-\f
- How to Apply These Terms to Your New Programs
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
- Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
+library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+v3.0.719 (24 May 2015)
+ - Implement tracking of remote ports: shows which ports the host
+ is making outgoing connections to. Long time feature request.
+ - Bugfix: when the capture interface goes down, exit instead of
+ busy-looping forever.
+ - Fix "clock error" due to machine reboot.
+ - SIGUSR1 now resets the time and bytes reported on the graphs
+ page.
+ - Account for all IP protocols.
+ - Change the default ports_max to only twice the default
+ ports_keep.
+
v3.0.718 (25 January 2014)
- (SECURITY!) Don't chroot() by default. The user must specify
a --chroot dir for this to happen now.
End of the line for darkstat 2.
-vim:set noet ts=8 sw=8 tw=72:
+vim:set noet ts=8 sts=8 sw=8 tw=72:
Portability
-----------
-Builds out of the box on:
+I, the darkstat maintainer, mostly develop darkstat on Debian GNU/Linux, but
+mostly run darkstat on FreeBSD.
- - FreeBSD 7.0
+darkstat usually builds out-of-the-box on FreeBSD, although you should probably
+install it from ports.
-Reported to also work on:
+In the past, darkstat has also been reported to work on:
- Solaris (with Sun C 5.8, and libpcap installed)
- Fedora Core (with libpcap-devel installed)
- Ubuntu (you need build-essential, zlib1g-dev, libpcap-dev)
- Mandrake
- OpenSUSE
+
+Sadly, darkstat doesn't run on GNU/Hurd 0.3 because the BPF there doesn't
+support non-blocking operation (FIONBIO).
+# vim:set ts=8 sw=8 sts=8 noet:
+#
# darkstat 3
# copyright (c) 2001-2014 Emil Mikulic.
#
datarootdir = @datarootdir@
mandir = @mandir@
-# Optimizations FIXME: dead code. push into autoconf?
-#CPPFLAGS += -D__OPTIMIZE__
-
SRCS = \
acct.c \
addr.c \
./config.status
rm -f Makefile.in.old
-show-dep:
- @echo $(CPP) $(CPPFLAGS) -MM $(SRCS)
-
graphjs.h: static/graph.js
$(AM_V_CIFY)
$(AM_V_at)./c-ify graph_js <static/graph.js >$@
$(INSTALL) -d $(DESTDIR)$(mandir)/man8
$(INSTALL) -m 444 darkstat.8 $(DESTDIR)$(mandir)/man8
-.PHONY: all install clean depend show-dep
+.PHONY: all install clean depend
# silent-rules
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
graph_db.h db.h dns.h err.h hosts_db.h addr.h http.h localip.h ncache.h \
now.h pidfile.h str.h
daylog.o: daylog.c cdefs.h err.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
+db.o: db.c err.h cdefs.h hosts_db.h addr.h graph_db.h db.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 \
+dns.o: dns.c cdefs.h cap.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
graph_db.o: graph_db.c cap.h conv.h db.h acct.h err.h cdefs.h str.h \
/* Account for the given packet summary. */
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;
+ struct bucket *hs = NULL; // Source host.
+ struct bucket *hd = NULL; // Dest host.
int dir_in, dir_out;
#if 0 /* WANT_CHATTY? */
/* Protocols. */
if (sm->proto != IPPROTO_INVALID) {
if (hs) {
- ps = host_get_ip_proto(hs, sm->proto);
+ struct bucket *ps = host_get_ip_proto(hs, sm->proto);
ps->out += sm->len;
ps->total += sm->len;
}
if (hd) {
- pd = host_get_ip_proto(hd, sm->proto);
+ struct bucket *pd = host_get_ip_proto(hd, sm->proto);
pd->in += sm->len;
pd->total += sm->len;
}
/* Ports. */
switch (sm->proto) {
case IPPROTO_TCP:
+ // Local ports on host.
if ((sm->src_port <= opt_highest_port) && hs) {
- ps = host_get_port_tcp(hs, sm->src_port);
+ struct bucket *ps = host_get_port_tcp(hs, sm->src_port);
ps->out += sm->len;
ps->total += sm->len;
}
if ((sm->dst_port <= opt_highest_port) && hd) {
- pd = host_get_port_tcp(hd, sm->dst_port);
+ struct bucket *pd = host_get_port_tcp(hd, sm->dst_port);
pd->in += sm->len;
pd->total += sm->len;
if (sm->tcp_flags == TH_SYN)
pd->u.port_tcp.syn++;
}
+
+ // Remote ports.
+ if ((sm->src_port <= opt_highest_port) && hd) {
+ struct bucket *pdr = host_get_port_tcp_remote(hd, sm->src_port);
+ pdr->out += sm->len;
+ pdr->total += sm->len;
+ }
+ if ((sm->dst_port <= opt_highest_port) && hs) {
+ struct bucket *psr = host_get_port_tcp_remote(hs, sm->dst_port);
+ psr->in += sm->len;
+ psr->total += sm->len;
+ if (sm->tcp_flags == TH_SYN)
+ psr->u.port_tcp.syn++;
+ }
break;
case IPPROTO_UDP:
+ // Local ports on host.
if ((sm->src_port <= opt_highest_port) && hs) {
- ps = host_get_port_udp(hs, sm->src_port);
+ struct bucket *ps = host_get_port_udp(hs, sm->src_port);
ps->out += sm->len;
ps->total += sm->len;
}
if ((sm->dst_port <= opt_highest_port) && hd) {
- pd = host_get_port_udp(hd, sm->dst_port);
+ struct bucket *pd = host_get_port_udp(hd, sm->dst_port);
pd->in += sm->len;
pd->total += sm->len;
}
+
+ // Remote ports.
+ if ((sm->src_port <= opt_highest_port) && hd) {
+ struct bucket *pdr = host_get_port_udp_remote(hd, sm->src_port);
+ pdr->out += sm->len;
+ pdr->total += sm->len;
+ }
+ if ((sm->dst_port <= opt_highest_port) && hs) {
+ struct bucket *psr = host_get_port_udp_remote(hs, sm->dst_port);
+ psr->in += sm->len;
+ psr->total += sm->len;
+ }
break;
case IPPROTO_INVALID:
/* proto decoding failed, don't complain in accounting */
break;
-
- default:
- verbosef("acct_for: unknown IP protocol 0x%02x", sm->proto);
}
}
free(tmp_filter);
}
+/* Start capturing on just one interface. Called from cap_start(). */
static void cap_start_one(struct cap_iface *iface, const int promisc) {
char errbuf[PCAP_ERRBUF_SIZE], *tmp_device;
int linktype, snaplen, waited;
acct_for(&sm, &iface->local_ips);
}
-/* Process any packets currently in the capture buffer. */
-void cap_poll(fd_set *read_set _unused_on_linux_) {
+/* Process any packets currently in the capture buffer.
+ * Returns 0 on error (usually means the interface went down).
+ */
+int cap_poll(fd_set *read_set _unused_on_linux_) {
struct cap_iface *iface;
static int told = 0;
if (ret < 0) {
warnx("pcap_dispatch('%s'): %s",
iface->name, pcap_geterr(iface->pcap));
- continue;
+ return 0;
}
#if 0 /* debugging */
}
}
cap_stats_update();
+ return 1;
}
void cap_stop(void) {
title_interfaces = NULL;
}
+/* This is only needed by the DNS child. In the main process, the deallocation
+ * happens in cap_start().
+ */
+void cap_free_args(void) {
+ while (!STAILQ_EMPTY(&cli_ifnames)) {
+ struct strnode *ifname = STAILQ_FIRST(&cli_ifnames);
+ STAILQ_REMOVE_HEAD(&cli_ifnames, entries);
+ free(ifname);
+ }
+
+ while (!STAILQ_EMPTY(&cli_filters)) {
+ struct strnode *filter = STAILQ_FIRST(&cli_filters);
+ STAILQ_REMOVE_HEAD(&cli_filters, entries);
+ free(filter);
+ }
+}
+
/* Run through entire capfile. */
void cap_from_file(const char *capfile) {
char errbuf[PCAP_ERRBUF_SIZE];
/* darkstat 3
- * copyright (c) 2001-2012 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
*
* cap.h: interface to libpcap.
*/
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);
+int cap_poll(fd_set *read_set);
void cap_stop(void);
+void cap_free_args(void);
void cap_from_file(const char *capfile);
# define _printflike_(fmtarg, firstvararg)
#endif
-#if __GNUC__ == 2
-# define inline __inline__
-#else
-# ifdef __TenDRA__
-# define inline __inline
-# endif
-#endif
-
-#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901
-# define restrict __restrict
-#endif
-
#ifndef MAX
# define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
+
+/* Define to the equivalent of the C99 'restrict' keyword, or to
+ nothing if this is not supported. Do not define if restrict is
+ supported directly. */
+#undef restrict
+/* Work around a bug in Sun C++: it does not support _Restrict or
+ __restrict__, even though the corresponding Sun C compiler ends up with
+ "#define restrict _Restrict" or "#define restrict __restrict__" in the
+ previous line. Perhaps some future version of Sun C++ will work with
+ restrict; if so, hopefully it defines __RESTRICT like Sun C does. */
+#if defined __SUNPRO_CC && !defined __RESTRICT
+# define _Restrict
+# define __restrict__
+#endif
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for darkstat 3.0.718.
+# Generated by GNU Autoconf 2.69 for darkstat 3.0.719.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
# Identity of this package.
PACKAGE_NAME='darkstat'
PACKAGE_TARNAME='darkstat'
-PACKAGE_VERSION='3.0.718'
-PACKAGE_STRING='darkstat 3.0.718'
+PACKAGE_VERSION='3.0.719'
+PACKAGE_STRING='darkstat 3.0.719'
PACKAGE_BUGREPORT=''
-PACKAGE_URL='http://unix4lyfe.org/darkstat/'
+PACKAGE_URL='https://unix4lyfe.org/darkstat/'
ac_unique_file="darkstat.c"
# Factoring default headers for most tests.
# 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.718 to adapt to many kinds of systems.
+\`configure' configures darkstat 3.0.719 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of darkstat 3.0.718:";;
+ short | recursive ) echo "Configuration of darkstat 3.0.719:";;
esac
cat <<\_ACEOF
it to find libraries and programs with nonstandard names/locations.
Report bugs to the package provider.
-darkstat home page: <http://unix4lyfe.org/darkstat/>.
+darkstat home page: <https://unix4lyfe.org/darkstat/>.
_ACEOF
ac_status=$?
fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-darkstat configure 3.0.718
+darkstat configure 3.0.719
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
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.718, which was
+It was created by darkstat $as_me 3.0.719, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
ac_compiler_gnu=$ac_cv_c_compiler_gnu
+# Compiler's language features.
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5
+$as_echo_n "checking for C/C++ restrict keyword... " >&6; }
+if ${ac_cv_c_restrict+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_restrict=no
+ # The order here caters to the fact that C++ does not require restrict.
+ for ac_kw in __restrict __restrict__ _Restrict restrict; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+typedef int * int_ptr;
+ int foo (int_ptr $ac_kw ip) {
+ return ip[0];
+ }
+int
+main ()
+{
+int s[1];
+ int * $ac_kw t = s;
+ t[0] = 0;
+ return foo(t)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_c_restrict=$ac_kw
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_restrict" != no && break
+ done
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5
+$as_echo "$ac_cv_c_restrict" >&6; }
+
+ case $ac_cv_c_restrict in
+ restrict) ;;
+ no) $as_echo "#define restrict /**/" >>confdefs.h
+ ;;
+ *) cat >>confdefs.h <<_ACEOF
+#define restrict $ac_cv_c_restrict
+_ACEOF
+ ;;
+ esac
+
+
# Check whether --enable-silent-rules was given.
if test "${enable_silent_rules+set}" = set; then :
# Check for zlib.
-
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for deflate in -lz" >&5
$as_echo_n "checking for deflate in -lz... " >&6; }
if ${ac_cv_lib_z_deflate+:} false; then :
I can't link to zlib. You really can't have a modern
operating system without zlib.
-If you are using a package-based operating system (like
-something with RPMs), see if there is a zlib-devel package
-that you can install, to provide the zlib headers and
-libraries.
+On Debian or a derivative, try: sudo apt-get install zlib1g-dev
+
+On an RPM system, see if there is a zlib-devel package.
$RULE
END
# 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.718, which was
+This file was extended by darkstat $as_me 3.0.719, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
$config_headers
Report bugs to the package provider.
-darkstat home page: <http://unix4lyfe.org/darkstat/>."
+darkstat home page: <https://unix4lyfe.org/darkstat/>."
_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.718
+darkstat config.status 3.0.719
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
# Need at least 2.64 for PACKAGE_URL
AC_PREREQ([2.64])
-AC_INIT(darkstat, 3.0.718, , , http://unix4lyfe.org/darkstat/)
+AC_INIT(darkstat, 3.0.719, , , https://unix4lyfe.org/darkstat/)
AC_CONFIG_SRCDIR([darkstat.c])
AC_CONFIG_HEADER([config.h])
AC_PROG_INSTALL
AC_PROG_CC
+# Compiler's language features.
+AC_C_RESTRICT
+
m4_pattern_allow([^AM_DEFAULT_VERBOSITY$])
AC_ARG_ENABLE([silent-rules],
[ --enable-silent-rules less verbose build output (undo: 'make V=1')
I can't link to zlib. You really can't have a modern
operating system without zlib.
-If you are using a package-based operating system (like
-something with RPMs), see if there is a zlib-devel package
-that you can install, to provide the zlib headers and
-libraries.
+On Debian or a derivative, try: sudo apt-get install zlib1g-dev
+
+On an RPM system, see if there is a zlib-devel package.
$RULE
END
#include "err.h"
#include <errno.h>
#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
-#include <limits.h>
#define PATH_DEVNULL "/dev/null"
err(1, "chroot(\"%s\") failed", chroot_dir);
verbosef("chrooted into: %s", chroot_dir);
}
+ {
+ gid_t list[1];
+ list[0] = pw->pw_gid;
+ if (setgroups(1, list) == -1)
+ err(1, "setgroups");
+ }
if (setgid(pw->pw_gid) == -1)
err(1, "setgid");
if (setuid(pw->pw_uid) == -1)
Use the specified filter expression when capturing traffic.
The filter syntax is beyond the scope of this manual page;
please refer to the
-.BR tcpdump (8)
+.BR tcpdump (1)
documentation.
.\"
.TP
.PP
.\"
(For a full reference on filter syntax, refer to the
-.BR tcpdump (8)
+.BR tcpdump (1)
manpage)
.PP
.\"
an appropriate netmask.
.\"
.SH SEE ALSO
-.BR tcpdump (8)
+.BR tcpdump (1)
.\"
.SH HISTORY
.I darkstat
static void cb_hosts_keep(const char *arg)
{ opt_hosts_keep = parsenum(arg, 0); }
-unsigned int opt_ports_max = 200;
+unsigned int opt_ports_max = 60;
static void cb_ports_max(const char *arg)
{ opt_ports_max = parsenum(arg, 65536); }
daemonize_finish();
while (running) {
- int select_ret, max_fd = -1, use_timeout = 0;
+ int select_ret;
+ int max_fd = -1;
+ int use_timeout = 0;
+ int cap_ret;
struct timeval timeout;
struct timespec t;
fd_set rs, ws;
}
graph_rotate();
- cap_poll(&rs);
+ cap_ret = cap_poll(&rs);
dns_poll();
http_poll(&rs, &ws);
timer_stop(&t, 1000000000, "event processing took longer than a second");
+
+ if (!cap_ret) {
+ running = 0;
+ }
}
verbosef("shutting down");
#include <string.h>
#include <unistd.h>
-#include "cdefs.h"
#include "err.h"
#include "hosts_db.h"
#include "graph_db.h"
#endif
#define ntoh64 hton64
-static inline uint64_t hton64(const uint64_t ho) {
+static uint64_t hton64(const uint64_t ho) {
if (ntohs(0x1234) == 0x1234)
return ho;
else
+darkstat (3.0.719-1) UNRELEASED; urgency=medium
+
+ * New upstream release
+
+ -- emil <emikulic@gmail.com> Thu, 15 Oct 2015 23:41:16 +1100
+
darkstat (3.0.718-2) unstable; urgency=low
* Fix FTBFS on Hurd
sm->dst.family = IPv6;
memcpy(&sm->dst.ip.v6, &hdr->ip6_dst, sizeof(sm->dst.ip.v6));
- /* 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;
- }
+ helper_ip_deeper(pdata + IPV6_HDR_LEN, len - IPV6_HDR_LEN, sm);
+ return 1;
}
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.
+ *
+ * If proto decode fails, we set IPPROTO_INVALID to skip accounting of port
+ * numbers.
+ *
* We don't need to "return 0" like other helpers.
*/
switch (sm->proto) {
sm->dst_port = ntohs(uhdr->uh_dport);
return;
}
-
- case IPPROTO_ICMP:
- case IPPROTO_IGMP:
- case IPPROTO_ICMPV6:
- case IPPROTO_OSPF:
- /* known protocol, don't complain about it */
- sm->proto = IPPROTO_INVALID; /* also don't do accounting */
- break;
-
- default:
- verbosef("ip_deeper: unknown protocol 0x%02x", sm->proto);
}
}
*/
#include "cdefs.h"
+#include "cap.h"
#include "conv.h"
#include "decode.h"
#include "dns.h"
daemonize_finish(); /* drop our copy of the lifeline! */
if (signal(SIGUSR1, SIG_IGN) == SIG_ERR)
errx(1, "signal(SIGUSR1, ignore) failed");
+ cap_free_args();
dns_main();
- exit(0);
+ errx(1, "DNS child fell out of dns_main()");
} else {
/* We are the parent. */
close(dns_sock[CHILD]);
host, sizeof(host), NULL, 0, flags);
break;
default:
- ret = EAI_FAMILY;
+ errx(1, "unexpected ip.family = %d", ip.family);
}
if (ret != 0) {
SECTION HEADER 0xDA 'H' 'S' 0x01 hosts_db ver1
HOST COUNT 0x00000001 1 host follows
For each host:
- HOST HEADER 'H' 'S' 'T' 0x03 host ver3
+ HOST HEADER 'H' 'S' 'T' 0x04 host ver4
ADDRESS FAMILY 0x04 Either 4 or 6.
IPv4 ADDR 0x0A010101 IPv4 10.1.1.1
- or for 0x06:
+ or for 0x06:
IPv6 ADDR 0x0000 0000 0000 0000 0000 0000 0000 0001
meaning IPv6 ::1
+ LASTSEEN 0x0000 0000 4800 0123 64-bit time_t meaning:
+ 2008-04-12 00:24:03 UTC
MACADDR 0x001122334455 00:11:22:33:44:55
- LASTSEEN 0x0000000048000123 (time_t) 2008-04-12 00:24:03 UTC
HOSTNAME 0x09 "localhost" 9 is the string length
IN 0x0000000000123456 Bytes in: 1193046
OUT 0x0000000000789ABC Bytes out: 7903932
PORT 0x0045 tftp (port 69)
IN 0x0000000000000001 Bytes in: 1
OUT 0x0000000000000002 Bytes out: 2
+ REMOTE TCP DATA 't' (as above)
+ REMOTE UDP DATA 'u' (as above)
SECTION HEADER 0xDA 'G' 'R' 0x01 graph_db ver1
LAST_TIME (time_t as 64-bit uint)
For each of 4 graphs: (60 seconds, 60 minutes, 24 hours, 31 days)
Host header version 2 is just version 3 without the address family
byte (or the possibility of an IPv6 address).
+
+Host header version 3 is just version 4 without the remote TCP and UDP ports.
graph_db[i]->in = xmalloc(sizeof(uint64_t) * graph_db[i]->num_bars);
graph_db[i]->out = xmalloc(sizeof(uint64_t) * graph_db[i]->num_bars);
}
- start_mono = now_mono();
- start_real = now_real();
- last_real = 0;
graph_reset();
}
for (i=0; i<graph_db_size; i++)
zero_graph(graph_db[i]);
+
+ /* Reset starting time. */
+ start_mono = now_mono();
+ start_real = now_real();
last_real = 0;
+
+ /* Clear counters. */
+ acct_total_bytes = 0;
+ acct_total_packets = 0;
}
void graph_free(void) {
d_mono = now_mono() - start_mono;
d_real = now_real() - start_real;
str_append(buf, "<p>\n");
- str_append(buf, "<b>Running for</b> <span id=\"rf\">");
+ str_append(buf, "<b>Measuring for</b> <span id=\"rf\">");
rf = length_of_time(d_mono);
str_appendstr(buf, rf);
str_free(rf);
str_append(buf, "</span>");
- if (abs(d_real - d_mono) > 1)
+ if (labs((long)(d_real - d_mono)) > 1)
str_appendf(buf, " (real time is off by %qd sec)",
- (qd)d_real - (qd)d_mono);
+ (qd)(d_real - d_mono));
if (strftime(start_when, sizeof(start_when),
"%Y-%m-%d %H:%M:%S %Z%z", localtime(&start_real)) != 0)
str_appendf(buf, "<b>, since</b> %s", start_when);
str_appendf(buf,"<b>.</b><br>\n"
- "<b>Total</b> <span id=\"tb\">%'qu</span> <b>bytes, "
+ "<b>Seen</b> <span id=\"tb\">%'qu</span> <b>bytes, "
"in</b> <span id=\"tp\">%'qu</span> <b>packets.</b> "
"(<span id=\"pc\">%'u</span> <b>captured,</b> "
"<span id=\"pd\">%'u</span> <b>dropped)</b><br>\n"
0.61803398874989490252573887119069695472717285156250;
/* Co-prime of u, using phi^-1 */
-inline static uint32_t
-coprime(const uint32_t u)
-{
+static uint32_t coprime(const uint32_t u) {
return ( (uint32_t)( (double)(u) * phi_1 ) | 1U );
}
* This is the "recommended" IPv4 hash function, as seen in FreeBSD's
* src/sys/netinet/tcp_hostcache.c 1.1
*/
-inline static uint32_t
-ipv4_hash(const struct addr *const a)
-{
+static uint32_t ipv4_hash(const struct addr *const a) {
uint32_t ip = a->ip.v4;
return ( (ip) ^ ((ip) >> 7) ^ ((ip) >> 17) );
}
* This is the IPv6 hash function used by FreeBSD in the same file as above,
* svn rev 122922.
*/
-inline static uint32_t
-ipv6_hash(const struct addr *const a)
-{
+static uint32_t ipv6_hash(const struct addr *const a) {
const struct in6_addr *const ip6 = &(a->ip.v6);
return ( ip6->s6_addr32[0] ^ ip6->s6_addr32[1] ^
ip6->s6_addr32[2] ^ ip6->s6_addr32[3] );
h->last_seen_mono = 0;
memset(&h->mac_addr, 0, sizeof(h->mac_addr));
h->ports_tcp = NULL;
+ h->ports_tcp_remote = NULL;
h->ports_udp = NULL;
+ h->ports_udp_remote = NULL;
h->ip_protos = NULL;
return (b);
}
struct host *h = &(b->u.host);
if (h->dns != NULL) free(h->dns);
hashtable_free(h->ports_tcp);
+ hashtable_free(h->ports_tcp_remote);
hashtable_free(h->ports_udp);
+ hashtable_free(h->ports_udp_remote);
hashtable_free(h->ip_protos);
}
(qu)b->total);
if (opt_want_lastseen) {
- time_t last = b->u.host.last_seen_mono;
+ int64_t last = b->u.host.last_seen_mono;
+ int64_t now = (int64_t)now_mono();
struct str *last_str = NULL;
- if ((now_mono() >= last) && (last > 0))
- last_str = length_of_time(now_mono() - last);
+ if ((now >= last) && (last != 0))
+ last_str = length_of_time(now - last);
str_append(buf, " <td class=\"num\">");
if (last_str == NULL) {
if (last == 0)
str_append(buf, "(never)");
else
- str_appendf(buf, "(clock error: now = %qu, last = %qu)",
- (qu)now_mono(),
- (qu)last);
+ str_appendf(buf, "(clock error: last = %qd, now = %qu)",
+ (qd)last,
+ (qu)now);
} else {
str_appendstr(buf, last_str);
str_free(last_str);
host_get_port_tcp(struct bucket *host, const uint16_t port)
{
struct host *h = &host->u.host;
- assert(h != NULL);
if (h->ports_tcp == NULL)
h->ports_tcp = hashtable_make(PORT_BITS, opt_ports_max, opt_ports_keep,
hash_func_short, free_func_simple, key_func_port_tcp,
return (hashtable_find_or_insert(h->ports_tcp, &port, ALLOW_REDUCE));
}
+struct bucket *
+host_get_port_tcp_remote(struct bucket *host, const uint16_t port)
+{
+ struct host *h = &host->u.host;
+ if (h->ports_tcp_remote == NULL)
+ h->ports_tcp_remote = hashtable_make(
+ PORT_BITS, opt_ports_max, opt_ports_keep, hash_func_short,
+ free_func_simple, key_func_port_tcp, find_func_port_tcp,
+ make_func_port_tcp, format_cols_port_tcp, format_row_port_tcp);
+ return (hashtable_find_or_insert(h->ports_tcp_remote, &port, ALLOW_REDUCE));
+}
+
/* ---------------------------------------------------------------------------
* Find or create a port_udp inside a host.
*/
host_get_port_udp(struct bucket *host, const uint16_t port)
{
struct host *h = &host->u.host;
- assert(h != NULL);
if (h->ports_udp == NULL)
h->ports_udp = hashtable_make(PORT_BITS, opt_ports_max, opt_ports_keep,
hash_func_short, free_func_simple, key_func_port_udp,
return (hashtable_find_or_insert(h->ports_udp, &port, ALLOW_REDUCE));
}
+struct bucket *
+host_get_port_udp_remote(struct bucket *host, const uint16_t port)
+{
+ struct host *h = &host->u.host;
+ if (h->ports_udp_remote == NULL)
+ h->ports_udp_remote = hashtable_make(
+ PORT_BITS, opt_ports_max, opt_ports_keep, hash_func_short,
+ free_func_simple, key_func_port_udp, find_func_port_udp,
+ make_func_port_udp, format_cols_port_udp, format_row_port_udp);
+ return (hashtable_find_or_insert(h->ports_udp_remote, &port, ALLOW_REDUCE));
+}
+
/* ---------------------------------------------------------------------------
* Find or create an ip_proto inside a host.
*/
"<p>\n"
"<b>Last seen:</b> ");
- last_seen_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(&last_seen_real)) != 0)
- str_append(buf, ls_when);
-
- 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);
- str_append(buf, " ago)");
+ if (h->u.host.last_seen_mono == 0) {
+ str_append(buf, "(never)");
} else {
- str_append(buf, " (in the future, possible clock problem)");
- }
+ last_seen_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(&last_seen_real)) != 0)
+ str_append(buf, ls_when);
+
+ if (h->u.host.last_seen_mono <= now_mono()) {
+ ls_len =
+ length_of_time((int64_t)now_mono() - h->u.host.last_seen_mono);
+ str_append(buf, " (");
+ str_appendstr(buf, ls_len);
+ str_free(ls_len);
+ str_append(buf, " ago)");
+ } else {
+ str_appendf(buf, " (in the future, possible clock problem, "
+ "last = %qd, now = %qu)",
+ (qd)h->u.host.last_seen_mono,
+ (qu)now_mono());
+ }
+ }
str_appendf(buf,
"</p>\n"
(qu)h->out,
(qu)h->total);
- str_append(buf, "<h3>TCP ports</h3>\n");
+ str_append(buf, "<h3>TCP ports on this host</h3>\n");
format_table(buf, h->u.host.ports_tcp, 0,TOTAL,0);
- str_append(buf, "<h3>UDP ports</h3>\n");
+ str_append(buf, "<h3>TCP ports on remote hosts</h3>\n");
+ format_table(buf, h->u.host.ports_tcp_remote, 0,TOTAL,0);
+
+ str_append(buf, "<h3>UDP ports on this host</h3>\n");
format_table(buf, h->u.host.ports_udp, 0,TOTAL,0);
+ str_append(buf, "<h3>UDP ports on remote hosts</h3>\n");
+ format_table(buf, h->u.host.ports_udp_remote, 0,TOTAL,0);
+
str_append(buf, "<h3>IP protocols</h3>\n");
format_table(buf, h->u.host.ip_protos, 0,TOTAL,0);
+ str_append(buf, "<br>\n");
html_close(buf);
return buf;
}
/* ---------------------------------------------------------------------------
* Database import and export code:
* Initially written and contributed by Ben Stewart.
- * copyright (c) 2007-2011 Ben Stewart, Emil Mikulic.
+ * copyright (c) 2007-2014 Ben Stewart, Emil Mikulic.
*/
static int hosts_db_export_ip(const struct hashtable *h, const int fd);
-static int hosts_db_export_tcp(const struct hashtable *h, const int fd);
-static int hosts_db_export_udp(const struct hashtable *h, const int fd);
+static int hosts_db_export_tcp(const char magic, const struct hashtable *h,
+ const int fd);
+static int hosts_db_export_udp(const char magic, const struct hashtable *h,
+ const int fd);
static const char
- export_proto_ip = 'P',
- export_proto_tcp = 'T',
- export_proto_udp = 'U';
+ export_proto_ip = 'P',
+ export_proto_tcp = 'T',
+ export_proto_tcp_remote = 't',
+ export_proto_udp = 'U',
+ export_proto_udp_remote = 'u';
static const unsigned char
export_tag_host_ver1[] = {'H', 'S', 'T', 0x01},
export_tag_host_ver2[] = {'H', 'S', 'T', 0x02},
- export_tag_host_ver3[] = {'H', 'S', 'T', 0x03};
+ export_tag_host_ver3[] = {'H', 'S', 'T', 0x03},
+ export_tag_host_ver4[] = {'H', 'S', 'T', 0x04};
/* ---------------------------------------------------------------------------
* Load a host's ip_proto table from a file.
}
/* ---------------------------------------------------------------------------
- * Load a host's port_tcp table from a file.
+ * Load a host's port_tcp{,_remote} table from a file.
* Returns 0 on failure, 1 on success.
*/
-static int
-hosts_db_import_tcp(const int fd, struct bucket *host)
-{
+static int hosts_db_import_tcp(const int fd, const char magic,
+ struct bucket *host,
+ struct bucket *(get_port_fn)(struct bucket *host,
+ uint16_t port)) {
uint16_t count, i;
- if (!expect8(fd, export_proto_tcp)) return 0;
+ if (!expect8(fd, magic)) return 0;
if (!read16(fd, &count)) return 0;
for (i=0; i<count; i++) {
if (!read64(fd, &out)) return 0;
/* Store data */
- b = host_get_port_tcp(host, port);
+ b = get_port_fn(host, port);
b->in = in;
b->out = out;
b->total = in + out;
* Load a host's port_tcp table from a file.
* Returns 0 on failure, 1 on success.
*/
-static int
-hosts_db_import_udp(const int fd, struct bucket *host)
-{
+static int hosts_db_import_udp(const int fd, const char magic,
+ struct bucket *host,
+ struct bucket *(get_port_fn)(struct bucket *host,
+ uint16_t port)) {
uint16_t count, i;
- if (!expect8(fd, export_proto_udp)) return 0;
+ if (!expect8(fd, magic)) return 0;
if (!read16(fd, &count)) return 0;
for (i=0; i<count; i++) {
if (!read64(fd, &out)) return 0;
/* Store data */
- b = host_get_port_udp(host, port);
+ b = get_port_fn(host, port);
b->in = in;
b->out = out;
b->total = in + out;
int ver = 0;
if (!readn(fd, hdr, sizeof(hdr))) return 0;
- if (memcmp(hdr, export_tag_host_ver3, sizeof(hdr)) == 0)
+ if (memcmp(hdr, export_tag_host_ver4, sizeof(hdr)) == 0)
+ ver = 4;
+ else if (memcmp(hdr, export_tag_host_ver3, sizeof(hdr)) == 0)
ver = 3;
else if (memcmp(hdr, export_tag_host_ver2, sizeof(hdr)) == 0)
ver = 2;
return 0;
}
- if (ver == 3) {
+ if (ver >= 3) {
if (!readaddr(fd, &a))
return 0;
} else {
/* Host's port and proto subtables: */
if (!hosts_db_import_ip(fd, host)) return 0;
- if (!hosts_db_import_tcp(fd, host)) return 0;
- if (!hosts_db_import_udp(fd, host)) return 0;
+ if (!hosts_db_import_tcp(fd, export_proto_tcp, host, host_get_port_tcp))
+ return 0;
+ if (!hosts_db_import_udp(fd, export_proto_udp, host, host_get_port_udp))
+ return 0;
+
+ if (ver == 4) {
+ if (!hosts_db_import_tcp(fd, export_proto_tcp_remote, host,
+ host_get_port_tcp_remote))
+ return 0;
+ if (!hosts_db_import_udp(fd, export_proto_udp_remote, host,
+ host_get_port_udp_remote))
+ return 0;
+ }
return 1;
}
for (i = 0; i<hosts_db->size; i++)
for (b = hosts_db->table[i]; b != NULL; b = b->next) {
/* For each host: */
- if (!writen(fd, export_tag_host_ver3, sizeof(export_tag_host_ver3)))
+ if (!writen(fd, export_tag_host_ver4, sizeof(export_tag_host_ver4)))
return 0;
if (!writeaddr(fd, &(b->u.host.addr)))
if (!write64(fd, b->out)) return 0;
if (!hosts_db_export_ip(b->u.host.ip_protos, fd)) return 0;
- if (!hosts_db_export_tcp(b->u.host.ports_tcp, fd)) return 0;
- if (!hosts_db_export_udp(b->u.host.ports_udp, fd)) return 0;
+ if (!hosts_db_export_tcp(export_proto_tcp, b->u.host.ports_tcp, fd))
+ return 0;
+ if (!hosts_db_export_udp(export_proto_udp, b->u.host.ports_udp, fd))
+ return 0;
+ if (!hosts_db_export_tcp(export_proto_tcp_remote,
+ b->u.host.ports_tcp_remote, fd))
+ return 0;
+ if (!hosts_db_export_udp(export_proto_udp_remote,
+ b->u.host.ports_udp_remote, fd))
+ return 0;
}
return 1;
}
* Dump the port_tcp table of a host.
*/
static int
-hosts_db_export_tcp(const struct hashtable *h, const int fd)
+hosts_db_export_tcp(const char magic, const struct hashtable *h, const int fd)
{
struct bucket *b;
uint32_t i, written = 0;
/* TCP DATA */
- if (!write8(fd, export_proto_tcp)) return 0;
+ if (!write8(fd, magic)) return 0;
/* If no data, write a count of 0 and we're done. */
if (h == NULL) {
* Dump the port_udp table of a host.
*/
static int
-hosts_db_export_udp(const struct hashtable *h, const int fd)
+hosts_db_export_udp(const char magic, const struct hashtable *h, const int fd)
{
struct bucket *b;
uint32_t i, written = 0;
/* UDP DATA */
- if (!write8(fd, export_proto_udp)) return 0;
+ if (!write8(fd, magic)) return 0;
/* If no data, write a count of 0 and we're done. */
if (h == NULL) {
return 1;
}
-/* vim:set ts=3 sw=3 tw=78 expandtab: */
+/* vim:set ts=3 sw=3 tw=80 expandtab: */
struct addr addr;
char *dns;
uint8_t mac_addr[6];
- time_t last_seen_mono;
- struct hashtable *ports_tcp, *ports_udp, *ip_protos;
+ /* last_seen_mono is converted to/from time_t in export/import.
+ * It can be negative (due to machine reboots).
+ */
+ int64_t last_seen_mono;
+ struct hashtable *ports_tcp;
+ struct hashtable *ports_tcp_remote;
+ struct hashtable *ports_udp;
+ struct hashtable *ports_udp_remote;
+ struct hashtable *ip_protos;
};
struct port_tcp {
struct bucket *host_find(const struct addr *const a); /* can return NULL */
struct bucket *host_get(const struct addr *const a);
struct bucket *host_get_port_tcp(struct bucket *host, const uint16_t port);
+struct bucket *host_get_port_tcp_remote(struct bucket *host,
+ const uint16_t port);
struct bucket *host_get_port_udp(struct bucket *host, const uint16_t port);
+struct bucket *host_get_port_udp_remote(struct bucket *host,
+ const uint16_t port);
struct bucket *host_get_ip_proto(struct bucket *host, const uint8_t proto);
/* Web pages. */
#include "err.h"
#include "hosts_db.h"
-/* ---------------------------------------------------------------------------
- * comparator for sorting (biggest first)
- */
-static int
-cmp(const struct bucket * const *x, const struct bucket * const *y,
- const enum sort_dir dir)
-{
- uint64_t a, b;
+static int cmp_u64(const uint64_t a, const uint64_t b) {
+ if (a < b) return (1);
+ if (a > b) return (-1);
+ return (0);
+}
+static int cmp_i64(const int64_t a, const int64_t b) {
+ if (a < b) return (1);
+ if (a > b) return (-1);
+ return (0);
+}
+
+/* Comparator for sorting 'struct bucket' */
+static int cmp(const struct bucket * const *x, const struct bucket * const *y,
+ const enum sort_dir dir) {
switch (dir) {
- case IN:
- a = (*x)->in;
- b = (*y)->in;
- break;
- case OUT:
- a = (*x)->out;
- b = (*y)->out;
- break;
- case TOTAL:
- a = (*x)->total;
- b = (*y)->total;
- break;
- case LASTSEEN:
- a = (*x)->u.host.last_seen_mono;
- b = (*y)->u.host.last_seen_mono;
- break;
- default:
- errx(1, "cmp: unknown direction: %d", dir);
+ case IN:
+ return cmp_u64((*x)->in, (*y)->in);
+ case OUT:
+ return cmp_u64((*x)->out, (*y)->out);
+ case TOTAL:
+ return cmp_u64((*x)->total, (*y)->total);
+ case LASTSEEN:
+ return cmp_i64((*x)->u.host.last_seen_mono,
+ (*y)->u.host.last_seen_mono);
+ default:
+ errx(1, "cmp: unknown direction: %d", dir);
}
-
- if (a < b) return (1);
- else if (a > b) return (-1);
- else return (0);
}
/*
void http_stop(void) {
struct connection *conn;
+ struct connection *next;
unsigned int i;
free(http_base_url);
insocks = NULL;
/* Close in-flight connections. */
- LIST_FOREACH(conn, &connlist, entries) {
+ LIST_FOREACH_SAFE(conn, &connlist, entries, next) {
LIST_REMOVE(conn, entries);
free_connection(conn);
free(conn);
#include <string.h>
#include <time.h>
-#ifdef __APPLE__
+#if defined(__MACH__) && !defined(__gnu_hurd__)
/* Fake up clock_gettime() on OS X. */
-# include <sys/time.h>
-# include <inttypes.h>
-# include <mach/mach.h>
-# include <mach/mach_time.h>
-
-typedef int clockid_t;
-#define CLOCK_REALTIME 0
-#define CLOCK_MONOTONIC 1
-
-static uint64_t mono_first = 0;
-
-int clock_gettime(clockid_t clk_id, struct timespec *tp) {
- if (clk_id == CLOCK_REALTIME) {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- tp->tv_sec = tv.tv_sec;
- tp->tv_nsec = tv.tv_usec * 1000;
- return 0;
- }
- if (clk_id == CLOCK_MONOTONIC) {
- uint64_t t = mach_absolute_time();
- mach_timebase_info_data_t timebase;
- mach_timebase_info(&timebase);
- if (!mono_first) {
- mono_first = t;
+# include <sys/time.h>
+# include <inttypes.h>
+# include <mach/mach.h>
+# include <mach/mach_time.h>
+
+ typedef int clockid_t;
+# define CLOCK_REALTIME 0
+# define CLOCK_MONOTONIC 1
+
+ static uint64_t mono_first = 0;
+
+ int clock_gettime(clockid_t clk_id, struct timespec *tp) {
+ if (clk_id == CLOCK_REALTIME) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ tp->tv_sec = tv.tv_sec;
+ tp->tv_nsec = tv.tv_usec * 1000;
+ return 0;
+ }
+ if (clk_id == CLOCK_MONOTONIC) {
+ uint64_t t = mach_absolute_time();
+ mach_timebase_info_data_t timebase;
+ mach_timebase_info(&timebase);
+ if (!mono_first) {
+ mono_first = t;
+ }
+ uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom;
+ tp->tv_sec = tdiff / 1000000000;
+ tp->tv_nsec = tdiff % 1000000000;
+ return 0;
}
- uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom;
- tp->tv_sec = tdiff / 1000000000;
- tp->tv_nsec = tdiff % 1000000000;
- return 0;
+ return -1;
}
- return -1;
-}
#endif /* __MACH__ */
static struct timespec clock_real, clock_mono;
all_clocks_update();
}
-time_t mono_to_real(const time_t t) {
+time_t mono_to_real(const int64_t t) {
assert(now_initialized);
- return t - clock_mono.tv_sec + clock_real.tv_sec;
+ return (time_t)(t - (int64_t)clock_mono.tv_sec + (int64_t)clock_real.tv_sec);
}
-time_t real_to_mono(const time_t t) {
+int64_t real_to_mono(const time_t t) {
assert(now_initialized);
- return t - clock_real.tv_sec + clock_mono.tv_sec;
+ return (int64_t)(t - clock_real.tv_sec + clock_mono.tv_sec);
}
void timer_start(struct timespec *t) {
time_t now_real(void);
time_t now_mono(void);
-time_t mono_to_real(const time_t t);
-time_t real_to_mono(const time_t t);
+/* Monotonic times can be negative (a time from before the machine booted) so
+ * treat them as signed. */
+time_t mono_to_real(const int64_t t);
+int64_t real_to_mono(const time_t t);
/* Emits warnings if a call is too slow. */
struct timespec;