|
|
- From 9d9ea2cd70a369a7f665a322e6c53631e01a2570 Mon Sep 17 00:00:00 2001
- From: Andreas Jaggi <andreas.jaggi@waterwave.ch>
- Date: Wed, 30 May 2018 22:15:36 +0200
- Subject: ulogd: json: send messages to a remote host / unix socket
-
- Extend the JSON output plugin so that the generated JSON stream can be
- sent to a remote host via TCP/UDP or to a local unix socket.
-
- Signed-off-by: Andreas Jaggi <andreas.jaggi@waterwave.ch>
- Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
- ---
- output/ulogd_output_JSON.c | 291 +++++++++++++++++++++++++++++++++++++++++----
- ulogd.conf.in | 11 ++
- 2 files changed, 281 insertions(+), 21 deletions(-)
-
- --- a/output/ulogd_output_JSON.c
- +++ b/output/ulogd_output_JSON.c
- @@ -20,10 +20,15 @@
-
- #include <stdio.h>
- #include <stdlib.h>
- +#include <unistd.h>
- #include <string.h>
- #include <time.h>
- #include <errno.h>
- #include <inttypes.h>
- +#include <sys/types.h>
- +#include <sys/socket.h>
- +#include <sys/un.h>
- +#include <netdb.h>
- #include <ulogd/ulogd.h>
- #include <ulogd/conffile.h>
- #include <jansson.h>
- @@ -36,6 +41,10 @@
- #define ULOGD_JSON_DEFAULT_DEVICE "Netfilter"
- #endif
-
- +#define host_ce(x) (x->ces[JSON_CONF_HOST])
- +#define port_ce(x) (x->ces[JSON_CONF_PORT])
- +#define mode_ce(x) (x->ces[JSON_CONF_MODE])
- +#define file_ce(x) (x->ces[JSON_CONF_FILENAME])
- #define unlikely(x) __builtin_expect((x),0)
-
- struct json_priv {
- @@ -44,6 +53,15 @@ struct json_priv {
- int usec_idx;
- long cached_gmtoff;
- char cached_tz[6]; /* eg +0200 */
- + int mode;
- + int sock;
- +};
- +
- +enum json_mode {
- + JSON_MODE_FILE = 0,
- + JSON_MODE_TCP,
- + JSON_MODE_UDP,
- + JSON_MODE_UNIX
- };
-
- enum json_conf {
- @@ -53,6 +71,9 @@ enum json_conf {
- JSON_CONF_EVENTV1,
- JSON_CONF_DEVICE,
- JSON_CONF_BOOLEAN_LABEL,
- + JSON_CONF_MODE,
- + JSON_CONF_HOST,
- + JSON_CONF_PORT,
- JSON_CONF_MAX
- };
-
- @@ -95,15 +116,167 @@ static struct config_keyset json_kset =
- .options = CONFIG_OPT_NONE,
- .u = { .value = 0 },
- },
- + [JSON_CONF_MODE] = {
- + .key = "mode",
- + .type = CONFIG_TYPE_STRING,
- + .options = CONFIG_OPT_NONE,
- + .u = { .string = "file" },
- + },
- + [JSON_CONF_HOST] = {
- + .key = "host",
- + .type = CONFIG_TYPE_STRING,
- + .options = CONFIG_OPT_NONE,
- + .u = { .string = "127.0.0.1" },
- + },
- + [JSON_CONF_PORT] = {
- + .key = "port",
- + .type = CONFIG_TYPE_STRING,
- + .options = CONFIG_OPT_NONE,
- + .u = { .string = "12345" },
- + },
- },
- };
-
- +static void close_socket(struct json_priv *op) {
- + if (op->sock != -1) {
- + close(op->sock);
- + op->sock = -1;
- + }
- +}
- +
- +static int _connect_socket_unix(struct ulogd_pluginstance *pi)
- +{
- + struct json_priv *op = (struct json_priv *) &pi->private;
- + struct sockaddr_un u_addr;
- + int sfd;
- +
- + close_socket(op);
- +
- + ulogd_log(ULOGD_DEBUG, "connecting to unix:%s\n",
- + file_ce(pi->config_kset).u.string);
- +
- + sfd = socket(AF_UNIX, SOCK_STREAM, 0);
- + if (sfd == -1) {
- + return -1;
- + }
- + u_addr.sun_family = AF_UNIX;
- + strncpy(u_addr.sun_path, file_ce(pi->config_kset).u.string,
- + sizeof(u_addr.sun_path) - 1);
- + if (connect(sfd, (struct sockaddr *) &u_addr, sizeof(struct sockaddr_un)) == -1) {
- + close(sfd);
- + return -1;
- + }
- +
- + op->sock = sfd;
- +
- + return 0;
- +}
- +
- +static int _connect_socket_net(struct ulogd_pluginstance *pi)
- +{
- + struct json_priv *op = (struct json_priv *) &pi->private;
- + struct addrinfo hints;
- + struct addrinfo *result, *rp;
- + int sfd, s;
- +
- + close_socket(op);
- +
- + ulogd_log(ULOGD_DEBUG, "connecting to %s:%s\n",
- + host_ce(pi->config_kset).u.string,
- + port_ce(pi->config_kset).u.string);
- +
- + memset(&hints, 0, sizeof(struct addrinfo));
- + hints.ai_family = AF_UNSPEC;
- + hints.ai_socktype = op->mode == JSON_MODE_UDP ? SOCK_DGRAM : SOCK_STREAM;
- + hints.ai_protocol = 0;
- + hints.ai_flags = 0;
- +
- + s = getaddrinfo(host_ce(pi->config_kset).u.string,
- + port_ce(pi->config_kset).u.string, &hints, &result);
- + if (s != 0) {
- + ulogd_log(ULOGD_ERROR, "getaddrinfo: %s\n", gai_strerror(s));
- + return -1;
- + }
- +
- + for (rp = result; rp != NULL; rp = rp->ai_next) {
- + int on = 1;
- +
- + sfd = socket(rp->ai_family, rp->ai_socktype,
- + rp->ai_protocol);
- + if (sfd == -1)
- + continue;
- +
- + setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
- + (char *) &on, sizeof(on));
- +
- + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
- + break;
- +
- + close(sfd);
- + }
- +
- + freeaddrinfo(result);
- +
- + if (rp == NULL) {
- + return -1;
- + }
- +
- + op->sock = sfd;
- +
- + return 0;
- +}
- +
- +static int _connect_socket(struct ulogd_pluginstance *pi)
- +{
- + struct json_priv *op = (struct json_priv *) &pi->private;
- +
- + if (op->mode == JSON_MODE_UNIX)
- + return _connect_socket_unix(pi);
- + else
- + return _connect_socket_net(pi);
- +}
- +
- +static int json_interp_socket(struct ulogd_pluginstance *upi, char *buf, int buflen)
- +{
- + struct json_priv *opi = (struct json_priv *) &upi->private;
- + int ret = 0;
- +
- + if (opi->sock != -1)
- + ret = send(opi->sock, buf, buflen, MSG_NOSIGNAL);
- + free(buf);
- + if (ret != buflen) {
- + ulogd_log(ULOGD_ERROR, "Failure sending message: %s\n",
- + strerror(errno));
- + if (ret == -1 || opi->sock == -1)
- + return _connect_socket(upi);
- + else
- + return ULOGD_IRET_ERR;
- + }
- +
- + return ULOGD_IRET_OK;
- +}
- +
- +static int json_interp_file(struct ulogd_pluginstance *upi, char *buf)
- +{
- + struct json_priv *opi = (struct json_priv *) &upi->private;
- +
- + fprintf(opi->of, "%s", buf);
- + free(buf);
- +
- + if (upi->config_kset->ces[JSON_CONF_SYNC].u.value != 0)
- + fflush(opi->of);
- +
- + return ULOGD_IRET_OK;
- +}
- +
- #define MAX_LOCAL_TIME_STRING 38
-
- static int json_interp(struct ulogd_pluginstance *upi)
- {
- struct json_priv *opi = (struct json_priv *) &upi->private;
- unsigned int i;
- + char *buf;
- + int buflen;
- json_t *msg;
-
- msg = json_object();
- @@ -218,34 +391,65 @@ static int json_interp(struct ulogd_plug
- }
- }
-
- - json_dumpf(msg, opi->of, 0);
- - fprintf(opi->of, "\n");
-
- + buf = json_dumps(msg, 0);
- json_decref(msg);
- + if (buf == NULL) {
- + ulogd_log(ULOGD_ERROR, "Could not create message\n");
- + return ULOGD_IRET_ERR;
- + }
- + buflen = strlen(buf);
- + buf = realloc(buf, sizeof(char)*(buflen+2));
- + if (buf == NULL) {
- + ulogd_log(ULOGD_ERROR, "Could not create message\n");
- + return ULOGD_IRET_ERR;
- + }
- + strncat(buf, "\n", 1);
- + buflen++;
-
- - if (upi->config_kset->ces[JSON_CONF_SYNC].u.value != 0)
- - fflush(opi->of);
- + if (opi->mode == JSON_MODE_FILE)
- + return json_interp_file(upi, buf);
- + else
- + return json_interp_socket(upi, buf, buflen);
- +}
-
- - return ULOGD_IRET_OK;
- +static void reopen_file(struct ulogd_pluginstance *upi)
- +{
- + struct json_priv *oi = (struct json_priv *) &upi->private;
- + FILE *old = oi->of;
- +
- + 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);
- + }
- +}
- +
- +static void reopen_socket(struct ulogd_pluginstance *upi)
- +{
- + ulogd_log(ULOGD_NOTICE, "JSON: reopening socket\n");
- + if (_connect_socket(upi) < 0) {
- + ulogd_log(ULOGD_ERROR, "can't open JSON "
- + "socket: %s\n",
- + strerror(errno));
- + }
- }
-
- 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);
- - }
- + if (oi->mode == JSON_MODE_FILE)
- + reopen_file(upi);
- + else
- + reopen_socket(upi);
- break;
- default:
- break;
- @@ -255,6 +459,8 @@ static void sighup_handler_print(struct
- static int json_configure(struct ulogd_pluginstance *upi,
- struct ulogd_pluginstance_stack *stack)
- {
- + struct json_priv *op = (struct json_priv *) &upi->private;
- + char *mode_str = mode_ce(upi->config_kset).u.string;
- int ret;
-
- ret = ulogd_wildcard_inputkeys(upi);
- @@ -265,13 +471,25 @@ static int json_configure(struct ulogd_p
- if (ret < 0)
- return ret;
-
- + if (!strcasecmp(mode_str, "udp")) {
- + op->mode = JSON_MODE_UDP;
- + } else if (!strcasecmp(mode_str, "tcp")) {
- + op->mode = JSON_MODE_TCP;
- + } else if (!strcasecmp(mode_str, "unix")) {
- + op->mode = JSON_MODE_UNIX;
- + } else if (!strcasecmp(mode_str, "file")) {
- + op->mode = JSON_MODE_FILE;
- + } else {
- + ulogd_log(ULOGD_ERROR, "unknown mode '%s'\n", mode_str);
- + return -EINVAL;
- + }
- +
- return 0;
- }
-
- -static int json_init(struct ulogd_pluginstance *upi)
- +static int json_init_file(struct ulogd_pluginstance *upi)
- {
- struct json_priv *op = (struct json_priv *) &upi->private;
- - unsigned int i;
-
- op->of = fopen(upi->config_kset->ces[0].u.string, "a");
- if (!op->of) {
- @@ -280,6 +498,27 @@ static int json_init(struct ulogd_plugin
- return -1;
- }
-
- + return 0;
- +}
- +
- +static int json_init_socket(struct ulogd_pluginstance *upi)
- +{
- + struct json_priv *op = (struct json_priv *) &upi->private;
- +
- + if (host_ce(upi->config_kset).u.string == NULL)
- + return -1;
- + if (port_ce(upi->config_kset).u.string == NULL)
- + return -1;
- +
- + op->sock = -1;
- + return _connect_socket(upi);
- +}
- +
- +static int json_init(struct ulogd_pluginstance *upi)
- +{
- + struct json_priv *op = (struct json_priv *) &upi->private;
- + unsigned int i;
- +
- /* search for time */
- op->sec_idx = -1;
- op->usec_idx = -1;
- @@ -293,15 +532,25 @@ static int json_init(struct ulogd_plugin
-
- *op->cached_tz = '\0';
-
- - return 0;
- + if (op->mode == JSON_MODE_FILE)
- + return json_init_file(upi);
- + else
- + return json_init_socket(upi);
- +}
- +
- +static void close_file(FILE *of) {
- + if (of != stdout)
- + fclose(of);
- }
-
- static int json_fini(struct ulogd_pluginstance *pi)
- {
- struct json_priv *op = (struct json_priv *) &pi->private;
-
- - if (op->of != stdout)
- - fclose(op->of);
- + if (op->mode == JSON_MODE_FILE)
- + close_file(op->of);
- + else
- + close_socket(op);
-
- return 0;
- }
- --- a/ulogd.conf.in
- +++ b/ulogd.conf.in
- @@ -213,6 +213,17 @@ sync=1
- # Uncomment the following line to use JSON v1 event format that
- # can provide better compatility with some JSON file reader.
- #eventv1=1
- +# Uncomment the following lines to send the JSON logs to a remote host via UDP
- +#mode="udp"
- +#host="192.0.2.10"
- +#port="10210"
- +# Uncomment the following lines to send the JSON logs to a remote host via TCP
- +#mode="tcp"
- +#host="192.0.2.10"
- +#port="10210"
- +# Uncomment the following lines to send the JSON logs to a local unix socket
- +#mode="unix"
- +#file="/var/run/ulogd.socket"
-
- [pcap1]
- #default file is /var/log/ulogd.pcap
|