Darkstat patch: throughput log per second
authorDario Fiumicello <fiumicello@antek.it>
Wed, 6 Apr 2011 14:13:41 +0000 (16:13 +0200)
committerEmil Mikulic <emikulic@gmail.com>
Thu, 7 Apr 2011 13:39:34 +0000 (23:39 +1000)
Hi Emil, I recently added darkstat 3.0.713 a new "--seclog" option,
based on the original --daylog functionality.

--seclog works quite the same way as daylog, but instead of producing a
report of daily geenrated traffic it emit a record every second. It also
rotate such records once per day, so you will have for example:

seclog.2011-04-05
seclog.2011-04-06
...

I needed to have this functionality for gathering detailed statistics
informations about traffic generated from a gateway installed at a
customer network.

I used as a base the debian source code present in Ubuntu 10.10 but I
think this patch will apply without problems also on original 3.0.713
source code.

It needs some minor adjustments but actually it works without problems
for me.

If you are interested in it and you think it can be added in the
darkstat main trunk (maybe in the next 3.0.714 release) please let me
know.

Cheers

Dario

Makefile.in
acct.c
darkstat.c
daylog.c
daylog.h
graph_db.h
hosts_db.c
http.c
seclog.c [new file with mode: 0644]
seclog.h [new file with mode: 0644]

index fb16631..cbe2201 100644 (file)
@@ -1,5 +1,5 @@
 # darkstat 3
-# copyright (c) 2001-2009 Emil Mikulic.
+# copyright (c) 2001-2007 Emil Mikulic.
 #
 # You may use, modify and redistribute this file under the terms of the
 # GNU General Public License version 2. (see COPYING.GPL)
@@ -30,6 +30,7 @@ cap.c         \
 conv.c         \
 darkstat.c     \
 daylog.c       \
+seclog.c       \
 db.c           \
 decode.c       \
 dns.c          \
@@ -87,15 +88,16 @@ install: darkstat
 
 # Automatically generated dependencies
 acct.o: acct.c darkstat.h config.h acct.h decode.h conv.h daylog.h \
-  graph_db.h err.h hosts_db.h str.h localip.h now.h
+  seclog.h graph_db.h err.h hosts_db.h str.h localip.h now.h
 cap.o: cap.c darkstat.h config.h cap.h conv.h decode.h hosts_db.h str.h \
   localip.h err.h
 conv.o: conv.c darkstat.h config.h conv.h err.h
 darkstat.o: darkstat.c darkstat.h config.h acct.h decode.h cap.h conv.h \
-  daylog.h graph_db.h db.h dns.h http.h hosts_db.h str.h localip.h \
-  ncache.h pidfile.h err.h now.h
+  daylog.h seclog.h graph_db.h db.h dns.h http.h hosts_db.h str.h \
+  localip.h ncache.h pidfile.h err.h now.h
 daylog.o: daylog.c darkstat.h config.h err.h daylog.h graph_db.h str.h \
   now.h
+seclog.o: daylog.c daylog.h seclog.c seclog.h
 db.o: db.c darkstat.h config.h err.h hosts_db.h str.h graph_db.h db.h
 decode.o: decode.c darkstat.h config.h acct.h decode.h cap.h err.h
 dns.o: dns.c darkstat.h config.h conv.h decode.h dns.h err.h hosts_db.h \
diff --git a/acct.c b/acct.c
index fb56034..670fa63 100644 (file)
--- a/acct.c
+++ b/acct.c
@@ -20,6 +20,7 @@
 #include "acct.h"
 #include "conv.h"
 #include "daylog.h"
+#include "seclog.h"
 #include "err.h"
 #include "hosts_db.h"
 #include "localip.h"
@@ -121,10 +122,12 @@ acct_for(const pktsummary *sm)
 
    if (dir_out) {
       daylog_acct((uint64_t)sm->len, GRAPH_OUT);
+      seclog_acct((uint64_t)sm->len, GRAPH_OUT);
       graph_acct((uint64_t)sm->len, GRAPH_OUT);
    }
    if (dir_in) {
       daylog_acct((uint64_t)sm->len, GRAPH_IN);
+      seclog_acct((uint64_t)sm->len, GRAPH_IN);
       graph_acct((uint64_t)sm->len, GRAPH_IN);
    }
 
index 6b393c7..3fd3caa 100644 (file)
@@ -12,6 +12,7 @@
 #include "cap.h"
 #include "conv.h"
 #include "daylog.h"
+#include "seclog.h"
 #include "db.h"
 #include "dns.h"
 #include "http.h"
@@ -127,6 +128,16 @@ static void cb_daylog(const char *arg)
       daylog_fn = arg;
 }
 
+const char *seclog_fn_base = NULL;
+static void cb_seclog(const char *arg)
+{
+   if (chroot_dir == NULL)
+      errx(1, "the seclog file is relative to the chroot.\n"
+      "You must specify a --chroot dir before you can use --seclog.");
+   else
+      seclog_fn_base = arg;
+}
+
 const char *import_fn = NULL;
 static void cb_import(const char *arg)
 {
@@ -211,6 +222,7 @@ static struct cmdline_arg cmdline_args[] = {
    {"--chroot",       "dir",             cb_chroot,       0},
    {"--user",         "username",        cb_user,         0},
    {"--daylog",       "filename",        cb_daylog,       0},
+   {"--seclog",       "filename",        cb_seclog,       0},
    {"--import",       "filename",        cb_import,       0},
    {"--export",       "filename",        cb_export,       0},
    {"--pidfile",      "filename",        cb_pidfile,      0},
@@ -399,6 +411,7 @@ main(int argc, char **argv)
    /* Don't need root privs for these: */
    now = time(NULL);
    if (daylog_fn != NULL) daylog_init(daylog_fn);
+   if (seclog_fn_base != NULL) seclog_init(seclog_fn_base);
    graph_init();
    hosts_db_init();
    if (import_fn != NULL) db_import(import_fn);
@@ -463,6 +476,7 @@ main(int argc, char **argv)
    hosts_db_free();
    graph_free();
    if (daylog_fn != NULL) daylog_free();
+   if (seclog_fn_base != NULL) seclog_free();
    ncache_free();
    if (pid_fn) pidfile_unlink();
    verbosef("shut down");
index c9799b4..eae9090 100644 (file)
--- a/daylog.c
+++ b/daylog.c
@@ -29,7 +29,7 @@ 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 *
+char *
 fmt_date(const time_t when)
 {
     time_t tmp = when;
@@ -40,7 +40,7 @@ fmt_date(const time_t when)
 }
 
 /* Given some time today, find the first second of tomorrow. */
-static time_t
+time_t
 tomorrow(const time_t today)
 {
    time_t tmp = today;
index 5f67a49..949cc7f 100644 (file)
--- a/daylog.h
+++ b/daylog.h
@@ -4,10 +4,17 @@
  * daylog.h: daily usage log
  */
 
+#ifndef __DAYLOG_H__
+#define __DAYLOG_H__
+
 #include "graph_db.h" /* for graph_dir */
 
 void daylog_init(const char *filename);
 void daylog_free(void);
 void daylog_acct(uint64_t amount, enum graph_dir dir);
+time_t tomorrow(const time_t today);
+char * fmt_date(const time_t when);
+
+#endif /* __DAYLOG_H__ */
 
 /* vim:set ts=3 sw=3 tw=78 et: */
index 0315680..b511d36 100644 (file)
@@ -4,6 +4,8 @@
  * graph_db.h: round robin database for graph data
  */
 
+#ifndef __GRAPH_DB_H__
+#define __GRAPH_DB_H__
 #include <stdint.h> /* for uint64_t on Linux and OS X */
 
 enum graph_dir {
@@ -24,4 +26,6 @@ int graph_export(const int fd);
 struct str *html_front_page(void);
 struct str *xml_graphs(void);
 
+#endif /* __GRAPH_DB_H__ */
+
 /* vim:set ts=3 sw=3 tw=78 expandtab: */
index cf90b6f..c66a075 100644 (file)
@@ -1,5 +1,5 @@
 /* darkstat 3
- * copyright (c) 2001-2009 Emil Mikulic.
+ * copyright (c) 2001-2008 Emil Mikulic.
  *
  * hosts_db.c: database of hosts, ports, protocols.
  *
@@ -26,7 +26,6 @@
 #include <string.h> /* memset(), strcmp() */
 #include <unistd.h>
 
-extern int want_lastseen;
 int show_mac_addrs = 0;
 extern const char *interface;
 
@@ -274,10 +273,8 @@ format_cols_host(struct str *buf)
    str_append(buf,
       " <th><a href=\"?sort=in\">In</a></th>\n"
       " <th><a href=\"?sort=out\">Out</a></th>\n"
-      " <th><a href=\"?sort=total\">Total</a></th>\n");
-   if (want_lastseen) str_append(buf,
-      " <th>Last seen</th>\n");
-   str_append(buf,
+      " <th><a href=\"?sort=total\">Total</a></th>\n"
+      " <th>Last seen</th>\n"
       "</tr>\n");
 }
 
@@ -286,6 +283,8 @@ format_row_host(struct str *buf, const struct bucket *b,
    const char *css_class)
 {
    const char *ip = ip_to_str( b->u.host.ip );
+   struct str *lastseen = NULL;
+   time_t last_t;
 
    str_appendf(buf,
       "<tr class=\"%s\">\n"
@@ -305,32 +304,26 @@ format_row_host(struct str *buf, const struct bucket *b,
          b->u.host.mac_addr[4],
          b->u.host.mac_addr[5]);
 
+   last_t = b->u.host.last_seen;
+   if (now >= last_t)
+      lastseen = length_of_time(now - last_t);
+
    str_appendf(buf,
       " <td class=\"num\">%'qu</td>\n"
       " <td class=\"num\">%'qu</td>\n"
-      " <td class=\"num\">%'qu</td>\n",
+      " <td class=\"num\">%'qu</td>\n"
+      " <td class=\"num\">",
       b->in, b->out, b->total);
 
-   if (want_lastseen) {
-      time_t last_t = b->u.host.last_seen;
-      struct str *lastseen = NULL;
-
-      if (now >= last_t)
-         lastseen = length_of_time(now - last_t);
-
-      str_append(buf,
-         " <td class=\"num\">");
-      if (lastseen == NULL)
-         str_append(buf, "(clock error)");
-      else {
-         str_appendstr(buf, lastseen);
-         str_free(lastseen);
-      }
-      str_append(buf,
-         "</td>");
+   if (lastseen == NULL)
+      str_append(buf, "(clock error)");
+   else {
+      str_appendstr(buf, lastseen);
+      str_free(lastseen);
    }
 
    str_appendf(buf,
+       "</td>\n"
       "</tr>\n");
 
    /* Only resolve hosts "on demand" */
@@ -778,10 +771,9 @@ struct bucket *
 host_get_ip_proto(struct bucket *host, const uint8_t proto)
 {
    struct host *h = &host->u.host;
-   static const unsigned int PROTOS_MAX = 512, PROTOS_KEEP = 256;
    assert(h != NULL);
    if (h->ip_protos == NULL)
-      h->ip_protos = hashtable_make(PROTO_BITS, PROTOS_MAX, PROTOS_KEEP,
+      h->ip_protos = hashtable_make(PROTO_BITS, ports_max, ports_keep,
          hash_func_byte, free_func_simple, key_func_ip_proto,
          find_func_ip_proto, make_func_ip_proto,
          format_cols_ip_proto, format_row_ip_proto);
diff --git a/http.c b/http.c
index 1c40898..3ec7848 100644 (file)
--- a/http.c
+++ b/http.c
@@ -918,11 +918,7 @@ http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd,
         /* Time out dead connections. */
         if (idlefor >= idletime)
         {
-            struct sockaddr_in addrin;
-            addrin.sin_addr.s_addr = conn->client;
-            verbosef("http socket timeout from %s (fd %d)",
-                inet_ntoa(addrin.sin_addr),
-                conn->socket);
+            verbosef("timeout on %d", conn->socket);
             conn->state = DONE;
         }
 
diff --git a/seclog.c b/seclog.c
new file mode 100644 (file)
index 0000000..94fa22c
--- /dev/null
+++ b/seclog.c
@@ -0,0 +1,209 @@
+/* darkstat 3
+ * copyright (c) 2011 Dario Fiumicello.
+ *
+ * seclog.c: reports usage every second
+ *
+ * You may use, modify and redistribute this file under the terms of the
+ * GNU General Public License version 2. (see COPYING.GPL)
+ */
+
+#define _GNU_SOURCE 1 /* for O_NOFOLLOW on Linux */
+
+#include <sys/types.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "darkstat.h"
+#include "err.h"
+#include "daylog.h"
+#include "seclog.h"
+#include "str.h"
+#include "now.h"
+
+static const char *seclog_fn_base = NULL, *seclog_fn = NULL;
+static time_t today_time, nextsec_time, tomorrow_time;
+static uint64_t bytes_in, bytes_out, pkts_in, pkts_out;
+static size_t seclog_fn_len;
+
+#define SECLOG_DATE_YYYYMMDD_LEN 10 /* strlen(".19000101") + 1 */
+static char date_yyyymmdd_buf[SECLOG_DATE_YYYYMMDD_LEN];
+
+/* Given a timestamp returns the date formatted as YYYYMMDD */
+char *
+date_to_yyyymmdd(const time_t when)
+{
+  time_t tmp = when;
+  if (strftime(date_yyyymmdd_buf, SECLOG_DATE_YYYYMMDD_LEN,
+        ".%Y%m%d", localtime(&tmp) ) == 0)
+    errx(1, "strftime() failed in date_to_yyyymmdd()");
+  return (date_yyyymmdd_buf);
+}
+
+/*Given a certain time it returnse time+1sec */
+  static time_t
+nextsec(const time_t today)
+{
+  time_t tmp = today;
+  struct tm tm, *lt;
+
+  lt = localtime(&tmp);
+  memcpy(&tm, lt, sizeof(tm));
+  tm.tm_sec = lt->tm_sec+1;
+  return mktime(&tm);
+}
+
+static int
+seclog_open(void)
+{
+  return open(seclog_fn, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600);
+}
+
+static void
+seclog_emit(void)
+{
+  int fd = seclog_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 */
+    free(s);
+
+    if (now >= tomorrow_time) {
+      /* If a new day is born close old file writing the closure comment, update
+       * the file name with the new day date and reopen it, writing an opening
+       * comment */
+      tomorrow_time = tomorrow(now);
+
+      buf = str_make();
+      str_appendf(buf, "# logging stopped at %s (%u)\n",
+          fmt_date(now), (unsigned int)now);
+      str_extract(buf, &len, &s);
+      (void)write(fd, s, len); /* ignore write errors */
+      close(fd);
+      free(s);
+
+      strncpy(seclog_fn, seclog_fn_base, seclog_fn_len);
+      strncat(seclog_fn, date_to_yyyymmdd(now), seclog_fn_len);
+
+      seclog_open();
+      if (fd == -1)
+        err(1, "couldn't open(\"%s\") for append", seclog_fn);
+
+      buf = str_make();
+      str_appendf(buf, "# logging started at %s (%u)\n",
+          fmt_date(now), (unsigned int)now);
+      str_extract(buf, &len, &s);
+      (void)write(fd, s, len); /* ignore write errors */
+      free(s);
+
+    }
+    close(fd);
+  }
+
+}
+
+void
+seclog_init(const char *filename)
+{
+   int fd;
+   struct str *buf;
+   char *s;
+   size_t len;
+
+   seclog_fn_base = filename;
+   seclog_fn_len = strlen(filename) + SECLOG_DATE_YYYYMMDD_LEN + 1;
+   
+   seclog_fn = (char*) malloc(seclog_fn_len*sizeof(char));
+   if (seclog_fn == NULL)
+     err(1, "Memory allocation error! Run out of RAM?");
+   memset(seclog_fn, 0, seclog_fn_len*sizeof(char));
+
+   today_time = time(NULL);
+   nextsec_time = nextsec(today_time);
+   tomorrow_time = tomorrow(today_time);
+   verbosef("today is %u, today+1sec is %u tomorrow is %u",
+      (unsigned int)today_time, (unsigned int)nextsec_time, 
+      (unsigned int)tomorrow);
+   bytes_in = bytes_out = pkts_in = pkts_out = 0;
+
+   strncpy(seclog_fn, seclog_fn_base, seclog_fn_len);
+   strncat(seclog_fn, date_to_yyyymmdd(now), seclog_fn_len);
+
+   fd = seclog_open();
+   if (fd == -1)
+      err(1, "couldn't open(\"%s\") for append", seclog_fn);
+
+   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 */
+   close(fd);
+   free(s);
+}
+
+void 
+seclog_free(void)
+{
+   int fd;
+   struct str *buf;
+   char *s;
+   size_t len;
+
+   today_time = time(NULL);
+
+   /* Emit what's currently accumulated. */
+   seclog_emit();
+
+   fd = seclog_open();
+   if (fd == -1) return;
+
+   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);
+   free(seclog_fn);
+}
+
+void
+seclog_acct(uint64_t amount, enum graph_dir dir)
+{
+   if (seclog_fn_base == NULL) return; /* disabled */
+
+   /* Check if we need to rotate. */
+   if (now >= nextsec_time) {
+      seclog_emit();
+
+      today_time = now;
+      nextsec_time = nextsec(today_time);
+      bytes_in = bytes_out = pkts_in = pkts_out = 0;
+      verbosef("rotated seclog, nextsec = %u",
+         (unsigned int)nextsec_time);
+   }
+
+   /* Accounting. */
+   if (dir == GRAPH_IN) {
+      bytes_in += amount;
+      pkts_in++;
+   } else {
+      assert(dir == GRAPH_OUT);
+      bytes_out += amount;
+      pkts_out++;
+   }
+}
+
+/* vim:set ts=3 sw=3 tw=78 et: */
+
diff --git a/seclog.h b/seclog.h
new file mode 100644 (file)
index 0000000..4dc80a4
--- /dev/null
+++ b/seclog.h
@@ -0,0 +1,16 @@
+/* darkstat 3
+ * copyright (c) 2011 Dario Fiumicello.
+ *
+ * seclog.h: reports usage every second
+ *
+ * You may use, modify and redistribute this file under the terms of the
+ * GNU General Public License version 2. (see COPYING.GPL)
+ */
+
+#include "graph_db.h" /* for graph_dir */
+
+void seclog_init(const char *filename);
+void seclog_free(void);
+void seclog_acct(uint64_t amount, enum graph_dir dir);
+
+/* vim:set ts=3 sw=3 tw=78 et: */