summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Leblond <eric@regit.org>2014-01-26 16:05:29 (GMT)
committerEric Leblond <eric@regit.org>2014-01-28 22:12:14 (GMT)
commite0ae1870e5b15138c12071d9d96522a2720bf44a (patch)
treea77a48efc8cb9b069186736db9369931d19e429d
parent0d213c1f1de63a016bcd730106e3aea83439c388 (diff)
json: introduce new JSON output plugin
This patch introduces a new JSON output plugin. This patch displays CIM field name instead of ulogd key valu if this CIM field is available. The module does not display binary address but uses the string version of them. So a complete stack is for example: stack=log2:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,mac2str1:HWHDR,json1:JSON If boolean_label is set to 1, then the numeric_label put on packet by the input plugin is coding the decision on packet. If 0, then packet has been blocked and if non null it has been accepted.
-rw-r--r--configure.ac12
-rw-r--r--output/Makefile.am10
-rw-r--r--output/ulogd_output_JSON.c254
-rw-r--r--ulogd.conf.in15
4 files changed, 291 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 5e45aaa..544a256 100644
--- a/configure.ac
+++ b/configure.ac
@@ -117,6 +117,17 @@ else
enable_pcap="no"
fi
+AC_ARG_WITH([jansson], AS_HELP_STRING([--without-jansson], [Build without JSON output plugin [default=test]]))
+AS_IF([test "x$with_jansson" != "xno"], [
+ PKG_CHECK_MODULES([libjansson], [jansson], [], [:])
+])
+AM_CONDITIONAL([HAVE_JANSSON], [test -n "$libjansson_LIBS"])
+if test "x$libjansson_LIBS" != "x"; then
+ enable_jansson="yes"
+else
+ enable_jansson="no"
+fi
+
dnl AC_SUBST(DATABASE_DIR)
dnl AC_SUBST(DATABASE_LIB)
dnl AC_SUBST(DATABASE_LIB_DIR)
@@ -152,5 +163,6 @@ Ulogd configuration:
MySQL plugin: ${enable_mysql}
SQLITE3 plugin: ${enable_sqlite3}
DBI plugin: ${enable_dbi}
+ JSON plugin: ${enable_jansson}
"
echo "You can now run 'make' and 'make install'"
diff --git a/output/Makefile.am b/output/Makefile.am
index 17427d0..ff851ad 100644
--- a/output/Makefile.am
+++ b/output/Makefile.am
@@ -9,6 +9,10 @@ pkglib_LTLIBRARIES = ulogd_output_LOGEMU.la ulogd_output_SYSLOG.la \
ulogd_output_NACCT.la ulogd_output_XML.la \
ulogd_output_GRAPHITE.la
+if HAVE_JANSSON
+pkglib_LTLIBRARIES += ulogd_output_JSON.la
+endif
+
ulogd_output_GPRINT_la_SOURCES = ulogd_output_GPRINT.c
ulogd_output_GPRINT_la_LDFLAGS = -avoid-version -module
@@ -32,3 +36,9 @@ ulogd_output_XML_la_LDFLAGS = -avoid-version -module
ulogd_output_GRAPHITE_la_SOURCES = ulogd_output_GRAPHITE.c
ulogd_output_GRAPHITE_la_LDFLAGS = -avoid-version -module
+
+if HAVE_JANSSON
+ulogd_output_JSON_la_SOURCES = ulogd_output_JSON.c
+ulogd_output_JSON_la_LIBADD = ${libjansson_LIBS}
+ulogd_output_JSON_la_LDFLAGS = -avoid-version -module
+endif
diff --git a/output/ulogd_output_JSON.c b/output/ulogd_output_JSON.c
new file mode 100644
index 0000000..04158a8
--- /dev/null
+++ b/output/ulogd_output_JSON.c
@@ -0,0 +1,254 @@
+/* ulogd_output_JSON.c
+ *
+ * ulogd output target for logging to a file in JSON format.
+ *
+ * (C) 2014 by Eric Leblond <eric@regit.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <ulogd/ulogd.h>
+#include <ulogd/conffile.h>
+#include <jansson.h>
+
+#ifndef ULOGD_JSON_DEFAULT
+#define ULOGD_JSON_DEFAULT "/var/log/ulogd.json"
+#endif
+
+#ifndef ULOGD_JSON_DEFAULT_DEVICE
+#define ULOGD_JSON_DEFAULT_DEVICE "Netfilter"
+#endif
+
+struct json_priv {
+ FILE *of;
+};
+
+enum json_conf {
+ JSON_CONF_FILENAME = 0,
+ JSON_CONF_SYNC,
+ JSON_CONF_TIMESTAMP,
+ JSON_CONF_DEVICE,
+ JSON_CONF_BOOLEAN_LABEL,
+ JSON_CONF_MAX
+};
+
+static struct config_keyset json_kset = {
+ .num_ces = JSON_CONF_MAX,
+ .ces = {
+ [JSON_CONF_FILENAME] = {
+ .key = "file",
+ .type = CONFIG_TYPE_STRING,
+ .options = CONFIG_OPT_NONE,
+ .u = {.string = ULOGD_JSON_DEFAULT },
+ },
+ [JSON_CONF_SYNC] = {
+ .key = "sync",
+ .type = CONFIG_TYPE_INT,
+ .options = CONFIG_OPT_NONE,
+ .u = { .value = 0 },
+ },
+ [JSON_CONF_TIMESTAMP] = {
+ .key = "timestamp",
+ .type = CONFIG_TYPE_INT,
+ .options = CONFIG_OPT_NONE,
+ .u = { .value = 1 },
+ },
+ [JSON_CONF_DEVICE] = {
+ .key = "device",
+ .type = CONFIG_TYPE_STRING,
+ .options = CONFIG_OPT_NONE,
+ .u = { .string = ULOGD_JSON_DEFAULT_DEVICE },
+ },
+ [JSON_CONF_BOOLEAN_LABEL] = {
+ .key = "boolean_label",
+ .type = CONFIG_TYPE_INT,
+ .options = CONFIG_OPT_NONE,
+ .u = { .value = 0 },
+ },
+ },
+};
+
+static int json_interp(struct ulogd_pluginstance *upi)
+{
+ struct json_priv *opi = (struct json_priv *) &upi->private;
+ unsigned int i;
+ json_t *msg;
+
+ msg = json_object();
+ if (!msg) {
+ ulogd_log(ULOGD_ERROR, "Unable to create JSON object\n");
+ return ULOGD_IRET_ERR;
+ }
+
+ if (upi->config_kset->ces[JSON_CONF_TIMESTAMP].u.value != 0) {
+ time_t now;
+ char *timestr = NULL;
+ now = time(NULL);
+
+ timestr = ctime(&now);
+ timestr[strlen(timestr) - 1] = '\0';
+
+ json_object_set_new(msg, "timestamp", json_string(timestr));
+ }
+
+ if (upi->config_kset->ces[JSON_CONF_DEVICE].u.string) {
+ char *dvc = upi->config_kset->ces[JSON_CONF_DEVICE].u.string;
+ json_object_set_new(msg, "dvc", json_string(dvc));
+ }
+
+
+
+ for (i = 0; i < upi->input.num_keys; i++) {
+ struct ulogd_key *key = upi->input.keys[i].u.source;
+ char *field_name;
+
+ if (!key)
+ continue;
+
+ if (!IS_VALID(*key))
+ continue;
+
+ field_name = key->cim_name ? key->cim_name : key->name;
+
+ switch (key->type) {
+ case ULOGD_RET_STRING:
+ json_object_set_new(msg, field_name, json_string(key->u.value.ptr));
+ break;
+ case ULOGD_RET_BOOL:
+ case ULOGD_RET_INT8:
+ case ULOGD_RET_INT16:
+ case ULOGD_RET_INT32:
+ json_object_set_new(msg, field_name, json_integer(key->u.value.i32));
+ break;
+ case ULOGD_RET_UINT8:
+ if ((upi->config_kset->ces[JSON_CONF_BOOLEAN_LABEL].u.value != 0)
+ && (!strcmp(key->name, "raw.label"))) {
+ if (key->u.value.ui8)
+ json_object_set_new(msg, "action", json_string("allowed"));
+ else
+ json_object_set_new(msg, "action", json_string("blocked"));
+ break;
+ }
+ case ULOGD_RET_UINT16:
+ case ULOGD_RET_UINT32:
+ case ULOGD_RET_UINT64:
+ json_object_set_new(msg, field_name, json_integer(key->u.value.ui64));
+ default:
+ /* don't know how to interpret this key. */
+ break;
+ }
+ }
+
+ json_dumpf(msg, opi->of, 0);
+ fprintf(opi->of, "\n");
+
+ json_decref(msg);
+
+ if (upi->config_kset->ces[JSON_CONF_SYNC].u.value != 0)
+ fflush(opi->of);
+
+ return ULOGD_IRET_OK;
+}
+
+static void sighup_handler_print(struct ulogd_pluginstance *upi, int signal)
+{
+ struct json_priv *oi = (struct json_priv *) &upi->private;
+ FILE *old = oi->of;
+
+ switch (signal) {
+ case SIGHUP:
+ ulogd_log(ULOGD_NOTICE, "JSON: reopening logfile\n");
+ oi->of = fopen(upi->config_kset->ces[0].u.string, "a");
+ if (!oi->of) {
+ ulogd_log(ULOGD_ERROR, "can't open JSON "
+ "log file: %s\n",
+ strerror(errno));
+ oi->of = old;
+ } else {
+ fclose(old);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int json_configure(struct ulogd_pluginstance *upi,
+ struct ulogd_pluginstance_stack *stack)
+{
+ int ret;
+
+ ret = ulogd_wildcard_inputkeys(upi);
+ if (ret < 0)
+ return ret;
+
+ ret = config_parse_file(upi->id, upi->config_kset);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int json_init(struct ulogd_pluginstance *upi)
+{
+ struct json_priv *op = (struct json_priv *) &upi->private;
+
+ op->of = fopen(upi->config_kset->ces[0].u.string, "a");
+ if (!op->of) {
+ ulogd_log(ULOGD_FATAL, "can't open JSON log file: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int json_fini(struct ulogd_pluginstance *pi)
+{
+ struct json_priv *op = (struct json_priv *) &pi->private;
+
+ if (op->of != stdout)
+ fclose(op->of);
+
+ return 0;
+}
+
+static struct ulogd_plugin json_plugin = {
+ .name = "JSON",
+ .input = {
+ .type = ULOGD_DTYPE_PACKET | ULOGD_DTYPE_FLOW | ULOGD_DTYPE_SUM,
+ },
+ .output = {
+ .type = ULOGD_DTYPE_SINK,
+ },
+ .configure = &json_configure,
+ .interp = &json_interp,
+ .start = &json_init,
+ .stop = &json_fini,
+ .signal = &sighup_handler_print,
+ .config_kset = &json_kset,
+ .version = VERSION,
+};
+
+void __attribute__ ((constructor)) init(void);
+
+void init(void)
+{
+ ulogd_register_plugin(&json_plugin);
+}
diff --git a/ulogd.conf.in b/ulogd.conf.in
index 0f9df7b..8893175 100644
--- a/ulogd.conf.in
+++ b/ulogd.conf.in
@@ -49,6 +49,7 @@ plugin="@pkglibdir@/ulogd_output_GPRINT.so"
plugin="@pkglibdir@/ulogd_raw2packet_BASE.so"
plugin="@pkglibdir@/ulogd_inpflow_NFACCT.so"
plugin="@pkglibdir@/ulogd_output_GRAPHITE.so"
+#plugin="@pkglibdir@/ulogd_output_JSON.so"
# this is a stack for logging packet send by system via LOGEMU
#stack=log1:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,print1:PRINTPKT,emu1:LOGEMU
@@ -92,6 +93,9 @@ plugin="@pkglibdir@/ulogd_output_GRAPHITE.so"
# this is a stack for logging packet to PGsql after a collect via NFLOG
#stack=log2:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,mac2str1:HWHDR,pgsql1:PGSQL
+# this is a stack for logging packet to JSON formatted file after a collect via NFLOG
+#stack=log2:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,mac2str1:HWHDR,json1:JSON
+
# this is a stack for logging packets to syslog after a collect via NFLOG
#stack=log3:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,print1:PRINTPKT,sys1:SYSLOG
@@ -195,6 +199,17 @@ timestamp=1
directory="/var/log/"
sync=1
+[json1]
+sync=1
+#file="/var/log/ulogd.json"
+#timestamp=0
+# device name to be used in JSON message
+#device="My awesome Netfilter firewall"
+# If boolean_label is set to 1 then the numeric_label put on packet
+# by the input plugin is coding the action on packet: if 0, then
+# packet has been blocked and if non null it has been accepted.
+#boolean_label=1
+
[pcap1]
#default file is /var/log/ulogd.pcap
#file="/var/log/ulogd.pcap"