ulogd: bump to version 2.0.8
Removed upstream patches. Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
This commit is contained in:
parent
91c2a71776
commit
13b7a26297
5 changed files with 4 additions and 1741 deletions
|
@ -8,13 +8,13 @@
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=ulogd
|
PKG_NAME:=ulogd
|
||||||
PKG_VERSION:=2.0.7
|
PKG_VERSION:=2.0.8
|
||||||
PKG_RELEASE:=6
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
|
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
|
||||||
PKG_SOURCE_URL:=https://netfilter.org/projects/ulogd/files/ \
|
PKG_SOURCE_URL:=https://netfilter.org/projects/ulogd/files/ \
|
||||||
ftp://ftp.netfilter.org/pub/ulogd/
|
ftp://ftp.netfilter.org/pub/ulogd/
|
||||||
PKG_HASH:=990a05494d9c16029ba0a83f3b7294fc05c756546b8d60d1c1572dc25249a92b
|
PKG_HASH:=4ead6c3970c3f57fa1e89fe2d7cc483ba6fe2bd1b08701521e0b3afd667df291
|
||||||
|
|
||||||
PKG_MAINTAINER:=Alexandru Ardelean <ardeleanalex@gmail.com>
|
PKG_MAINTAINER:=Alexandru Ardelean <ardeleanalex@gmail.com>
|
||||||
PKG_LICENSE:=GPL-2.0-only
|
PKG_LICENSE:=GPL-2.0-only
|
||||||
|
@ -41,7 +41,7 @@ endef
|
||||||
|
|
||||||
define Package/ulogd
|
define Package/ulogd
|
||||||
$(call Package/ulogd/Default)
|
$(call Package/ulogd/Default)
|
||||||
DEPENDS:=+libmnl +libnfnetlink +libpthread
|
DEPENDS:=+libmnl +libnfnetlink +libpthread +libnetfilter-conntrack
|
||||||
TITLE:=Netfilter userspace logging daemon
|
TITLE:=Netfilter userspace logging daemon
|
||||||
MENU:=1
|
MENU:=1
|
||||||
endef
|
endef
|
||||||
|
|
|
@ -1,434 +0,0 @@
|
||||||
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
|
|
|
@ -1,24 +0,0 @@
|
||||||
From 675e762091380590f78ff07a94a25caa459b786b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Cameron Norman <camerontnorman@gmail.com>
|
|
||||||
Date: Sat, 27 Oct 2018 13:05:45 -0700
|
|
||||||
Subject: ulogd: fix build with musl libc
|
|
||||||
|
|
||||||
The attached patch fixes building ulogd2 with musl libc. It is being
|
|
||||||
used on Void Linux right now.
|
|
||||||
|
|
||||||
Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1278
|
|
||||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
||||||
---
|
|
||||||
src/ulogd.c | 1 +
|
|
||||||
1 file changed, 1 insertion(+)
|
|
||||||
|
|
||||||
--- a/src/ulogd.c
|
|
||||||
+++ b/src/ulogd.c
|
|
||||||
@@ -65,6 +65,7 @@
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sched.h>
|
|
||||||
+#include <limits.h>
|
|
||||||
#include <ulogd/conffile.h>
|
|
||||||
#include <ulogd/ulogd.h>
|
|
||||||
#ifdef DEBUG
|
|
|
@ -1,858 +0,0 @@
|
||||||
From 4f639231c83b09ea004c03e95c702b7750bf9930 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ander Juaristi <a@juaristi.eus>
|
|
||||||
Date: Fri, 26 Apr 2019 09:58:06 +0200
|
|
||||||
Subject: IPFIX: Add IPFIX output plugin
|
|
||||||
|
|
||||||
This patch adds an IPFIX output plugin to ulogd2. It generates NetFlow/IPFIX
|
|
||||||
traces and sends them to a remote server (collector) via TCP or UDP.
|
|
||||||
|
|
||||||
Based on original work by Holger Eitzenberger <holger@eitzenberger.org>.
|
|
||||||
|
|
||||||
How to test this
|
|
||||||
----------------
|
|
||||||
|
|
||||||
I am currently testing this with the NFCT input and Wireshark.
|
|
||||||
|
|
||||||
Place the following in ulogd.conf:
|
|
||||||
|
|
||||||
# this will print all flows on screen
|
|
||||||
loglevel=1
|
|
||||||
|
|
||||||
# load NFCT and IPFIX plugins
|
|
||||||
plugin="/lib/ulogd/ulogd_inpflow_NFCT.so"
|
|
||||||
plugin="/lib/ulogd/ulogd_output_IPFIX.so"
|
|
||||||
|
|
||||||
stack=ct1:NFCT,ipfix1:IPFIX
|
|
||||||
|
|
||||||
[ct1]
|
|
||||||
netlink_socket_buffer_size=217088
|
|
||||||
netlink_socket_buffer_maxsize=1085440
|
|
||||||
accept_proto_filter=tcp,sctp
|
|
||||||
|
|
||||||
[ipfix1]
|
|
||||||
oid=1
|
|
||||||
host="127.0.0.1"
|
|
||||||
#port=4739
|
|
||||||
#send_template="once"
|
|
||||||
|
|
||||||
I am currently testing it by launching a plain NetCat listener on port
|
|
||||||
4739 (the default for IPFIX) and then running Wireshark and see that it
|
|
||||||
dissects the IPFIX/NetFlow traffic correctly (obviously this relies on
|
|
||||||
the Wireshark NetFlow dissector being correct).
|
|
||||||
|
|
||||||
First:
|
|
||||||
|
|
||||||
nc -vvvv -l 127.0.0.1 4739
|
|
||||||
|
|
||||||
Then:
|
|
||||||
|
|
||||||
sudo ulogd -vc ulogd.conf
|
|
||||||
|
|
||||||
Signed-off-by: Ander Juaristi <a@juaristi.eus>
|
|
||||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
||||||
---
|
|
||||||
configure.ac | 2 +-
|
|
||||||
include/ulogd/ulogd.h | 5 +
|
|
||||||
input/flow/ulogd_inpflow_IPFIX.c | 2 -
|
|
||||||
output/Makefile.am | 2 +-
|
|
||||||
output/ipfix/Makefile.am | 7 +
|
|
||||||
output/ipfix/ipfix.c | 141 ++++++++++
|
|
||||||
output/ipfix/ipfix.h | 89 +++++++
|
|
||||||
output/ipfix/ulogd_output_IPFIX.c | 503 +++++++++++++++++++++++++++++++++++
|
|
||||||
output/ulogd_output_IPFIX.c | 546 --------------------------------------
|
|
||||||
9 files changed, 747 insertions(+), 550 deletions(-)
|
|
||||||
delete mode 100644 input/flow/ulogd_inpflow_IPFIX.c
|
|
||||||
create mode 100644 output/ipfix/Makefile.am
|
|
||||||
create mode 100644 output/ipfix/ipfix.c
|
|
||||||
create mode 100644 output/ipfix/ipfix.h
|
|
||||||
create mode 100644 output/ipfix/ulogd_output_IPFIX.c
|
|
||||||
delete mode 100644 output/ulogd_output_IPFIX.c
|
|
||||||
|
|
||||||
--- a/configure.ac
|
|
||||||
+++ b/configure.ac
|
|
||||||
@@ -179,7 +179,7 @@ AC_CONFIG_FILES(include/Makefile include
|
|
||||||
input/sum/Makefile \
|
|
||||||
filter/Makefile filter/raw2packet/Makefile filter/packet2flow/Makefile \
|
|
||||||
output/Makefile output/pcap/Makefile output/mysql/Makefile output/pgsql/Makefile output/sqlite3/Makefile \
|
|
||||||
- output/dbi/Makefile \
|
|
||||||
+ output/dbi/Makefile output/ipfix/Makefile \
|
|
||||||
src/Makefile Makefile Rules.make)
|
|
||||||
AC_OUTPUT
|
|
||||||
|
|
||||||
--- a/include/ulogd/ulogd.h
|
|
||||||
+++ b/include/ulogd/ulogd.h
|
|
||||||
@@ -28,6 +28,11 @@
|
|
||||||
|
|
||||||
/* types without length */
|
|
||||||
#define ULOGD_RET_NONE 0x0000
|
|
||||||
+#define __packed __attribute__((packed))
|
|
||||||
+#define __noreturn __attribute__((noreturn))
|
|
||||||
+#define __cold __attribute__((cold))
|
|
||||||
+
|
|
||||||
+#define __packed __attribute__((packed))
|
|
||||||
|
|
||||||
#define ULOGD_RET_INT8 0x0001
|
|
||||||
#define ULOGD_RET_INT16 0x0002
|
|
||||||
--- a/output/Makefile.am
|
|
||||||
+++ b/output/Makefile.am
|
|
||||||
@@ -2,7 +2,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include ${
|
|
||||||
${LIBNETFILTER_CONNTRACK_CFLAGS} ${LIBNETFILTER_LOG_CFLAGS}
|
|
||||||
AM_CFLAGS = ${regular_CFLAGS}
|
|
||||||
|
|
||||||
-SUBDIRS= pcap mysql pgsql sqlite3 dbi
|
|
||||||
+SUBDIRS= pcap mysql pgsql sqlite3 dbi ipfix
|
|
||||||
|
|
||||||
pkglib_LTLIBRARIES = ulogd_output_LOGEMU.la ulogd_output_SYSLOG.la \
|
|
||||||
ulogd_output_OPRINT.la ulogd_output_GPRINT.la \
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/output/ipfix/Makefile.am
|
|
||||||
@@ -0,0 +1,7 @@
|
|
||||||
+AM_CPPFLAGS = -I$(top_srcdir)/include
|
|
||||||
+AM_CFLAGS = $(regular_CFLAGS)
|
|
||||||
+
|
|
||||||
+pkglib_LTLIBRARIES = ulogd_output_IPFIX.la
|
|
||||||
+
|
|
||||||
+ulogd_output_IPFIX_la_SOURCES = ulogd_output_IPFIX.c ipfix.c
|
|
||||||
+ulogd_output_IPFIX_la_LDFLAGS = -avoid-version -module
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/output/ipfix/ipfix.c
|
|
||||||
@@ -0,0 +1,141 @@
|
|
||||||
+/*
|
|
||||||
+ * ipfix.c
|
|
||||||
+ *
|
|
||||||
+ * Holger Eitzenberger, 2009.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+/* These forward declarations are needed since ulogd.h doesn't like to be the first */
|
|
||||||
+#include <ulogd/linuxlist.h>
|
|
||||||
+
|
|
||||||
+#define __packed __attribute__((packed))
|
|
||||||
+
|
|
||||||
+#include "ipfix.h"
|
|
||||||
+
|
|
||||||
+#include <ulogd/ulogd.h>
|
|
||||||
+#include <ulogd/common.h>
|
|
||||||
+
|
|
||||||
+struct ipfix_msg *ipfix_msg_alloc(size_t len, uint32_t oid)
|
|
||||||
+{
|
|
||||||
+ struct ipfix_msg *msg;
|
|
||||||
+ struct ipfix_hdr *hdr;
|
|
||||||
+
|
|
||||||
+ if (len < IPFIX_HDRLEN + IPFIX_SET_HDRLEN)
|
|
||||||
+ return NULL;
|
|
||||||
+
|
|
||||||
+ msg = malloc(sizeof(struct ipfix_msg) + len);
|
|
||||||
+ memset(msg, 0, sizeof(struct ipfix_msg));
|
|
||||||
+ msg->tail = msg->data + IPFIX_HDRLEN;
|
|
||||||
+ msg->end = msg->data + len;
|
|
||||||
+
|
|
||||||
+ hdr = ipfix_msg_hdr(msg);
|
|
||||||
+ memset(hdr, 0, IPFIX_HDRLEN);
|
|
||||||
+ hdr->version = htons(IPFIX_VERSION);
|
|
||||||
+ hdr->oid = htonl(oid);
|
|
||||||
+
|
|
||||||
+ return msg;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void ipfix_msg_free(struct ipfix_msg *msg)
|
|
||||||
+{
|
|
||||||
+ if (!msg)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ if (msg->nrecs > 0)
|
|
||||||
+ ulogd_log(ULOGD_DEBUG, "%s: %d flows have been lost\n", __func__,
|
|
||||||
+ msg->nrecs);
|
|
||||||
+
|
|
||||||
+ free(msg);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+struct ipfix_hdr *ipfix_msg_hdr(const struct ipfix_msg *msg)
|
|
||||||
+{
|
|
||||||
+ return (struct ipfix_hdr *)msg->data;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void *ipfix_msg_data(struct ipfix_msg *msg)
|
|
||||||
+{
|
|
||||||
+ return msg->data;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+size_t ipfix_msg_len(const struct ipfix_msg *msg)
|
|
||||||
+{
|
|
||||||
+ return msg->tail - msg->data;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+struct ipfix_set_hdr *ipfix_msg_add_set(struct ipfix_msg *msg, uint16_t sid)
|
|
||||||
+{
|
|
||||||
+ struct ipfix_set_hdr *shdr;
|
|
||||||
+
|
|
||||||
+ if (msg->end - msg->tail < (int) IPFIX_SET_HDRLEN)
|
|
||||||
+ return NULL;
|
|
||||||
+
|
|
||||||
+ shdr = (struct ipfix_set_hdr *)msg->tail;
|
|
||||||
+ shdr->id = sid;
|
|
||||||
+ shdr->len = IPFIX_SET_HDRLEN;
|
|
||||||
+ msg->tail += IPFIX_SET_HDRLEN;
|
|
||||||
+ msg->last_set = shdr;
|
|
||||||
+ return shdr;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+struct ipfix_set_hdr *ipfix_msg_get_set(const struct ipfix_msg *msg)
|
|
||||||
+{
|
|
||||||
+ return msg->last_set;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * Add data record to an IPFIX message. The data is accounted properly.
|
|
||||||
+ *
|
|
||||||
+ * @return pointer to data or %NULL if not that much space left.
|
|
||||||
+ */
|
|
||||||
+void *ipfix_msg_add_data(struct ipfix_msg *msg, size_t len)
|
|
||||||
+{
|
|
||||||
+ void *data;
|
|
||||||
+
|
|
||||||
+ if (!msg->last_set) {
|
|
||||||
+ ulogd_log(ULOGD_FATAL, "msg->last_set is NULL\n");
|
|
||||||
+ return NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if ((ssize_t) len > msg->end - msg->tail)
|
|
||||||
+ return NULL;
|
|
||||||
+
|
|
||||||
+ data = msg->tail;
|
|
||||||
+ msg->tail += len;
|
|
||||||
+ msg->nrecs++;
|
|
||||||
+ msg->last_set->len += len;
|
|
||||||
+
|
|
||||||
+ return data;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* check and dump message */
|
|
||||||
+int ipfix_dump_msg(const struct ipfix_msg *msg)
|
|
||||||
+{
|
|
||||||
+ const struct ipfix_hdr *hdr = ipfix_msg_hdr(msg);
|
|
||||||
+ const struct ipfix_set_hdr *shdr = (struct ipfix_set_hdr *) hdr->data;
|
|
||||||
+
|
|
||||||
+ if (ntohs(hdr->len) < IPFIX_HDRLEN) {
|
|
||||||
+ ulogd_log(ULOGD_FATAL, "Invalid IPFIX message header length\n");
|
|
||||||
+ return -1;
|
|
||||||
+ }
|
|
||||||
+ if (ipfix_msg_len(msg) != IPFIX_HDRLEN + ntohs(shdr->len)) {
|
|
||||||
+ ulogd_log(ULOGD_FATAL, "Invalid IPFIX message length\n");
|
|
||||||
+ return -1;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ulogd_log(ULOGD_DEBUG, "msg: ver=%#x len=%#x t=%#x seq=%#x oid=%d\n",
|
|
||||||
+ ntohs(hdr->version), ntohs(hdr->len), htonl(hdr->time),
|
|
||||||
+ ntohl(hdr->seqno), ntohl(hdr->oid));
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* template management */
|
|
||||||
+size_t ipfix_rec_len(uint16_t sid)
|
|
||||||
+{
|
|
||||||
+ if (sid != htons(VY_IPFIX_SID)) {
|
|
||||||
+ ulogd_log(ULOGD_FATAL, "Invalid SID\n");
|
|
||||||
+ return 0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return sizeof(struct vy_ipfix_data);
|
|
||||||
+}
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/output/ipfix/ipfix.h
|
|
||||||
@@ -0,0 +1,89 @@
|
|
||||||
+/*
|
|
||||||
+ * ipfix.h
|
|
||||||
+ *
|
|
||||||
+ * Holger Eitzenberger <holger@eitzenberger.org>, 2009.
|
|
||||||
+ */
|
|
||||||
+#ifndef IPFIX_H
|
|
||||||
+#define IPFIX_H
|
|
||||||
+
|
|
||||||
+#include <stdint.h>
|
|
||||||
+#include <netinet/in.h>
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+struct ipfix_hdr {
|
|
||||||
+#define IPFIX_VERSION 0xa
|
|
||||||
+ uint16_t version;
|
|
||||||
+ uint16_t len;
|
|
||||||
+ uint32_t time;
|
|
||||||
+ uint32_t seqno;
|
|
||||||
+ uint32_t oid; /* Observation Domain ID */
|
|
||||||
+ uint8_t data[];
|
|
||||||
+} __packed;
|
|
||||||
+
|
|
||||||
+#define IPFIX_HDRLEN sizeof(struct ipfix_hdr)
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * IDs 0-255 are reserved for Template Sets. IDs of Data Sets are > 255.
|
|
||||||
+ */
|
|
||||||
+struct ipfix_templ_hdr {
|
|
||||||
+ uint16_t id;
|
|
||||||
+ uint16_t cnt;
|
|
||||||
+ uint8_t data[];
|
|
||||||
+} __packed;
|
|
||||||
+
|
|
||||||
+struct ipfix_set_hdr {
|
|
||||||
+#define IPFIX_SET_TEMPL 2
|
|
||||||
+#define IPFIX_SET_OPT_TEMPL 3
|
|
||||||
+ uint16_t id;
|
|
||||||
+ uint16_t len;
|
|
||||||
+ uint8_t data[];
|
|
||||||
+} __packed;
|
|
||||||
+
|
|
||||||
+#define IPFIX_SET_HDRLEN sizeof(struct ipfix_set_hdr)
|
|
||||||
+
|
|
||||||
+struct ipfix_msg {
|
|
||||||
+ struct llist_head link;
|
|
||||||
+ uint8_t *tail;
|
|
||||||
+ uint8_t *end;
|
|
||||||
+ unsigned nrecs;
|
|
||||||
+ struct ipfix_set_hdr *last_set;
|
|
||||||
+ uint8_t data[];
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct vy_ipfix_data {
|
|
||||||
+ struct in_addr saddr;
|
|
||||||
+ struct in_addr daddr;
|
|
||||||
+ uint16_t ifi_in;
|
|
||||||
+ uint16_t ifi_out;
|
|
||||||
+ uint32_t packets;
|
|
||||||
+ uint32_t bytes;
|
|
||||||
+ uint32_t start; /* Unix time */
|
|
||||||
+ uint32_t end; /* Unix time */
|
|
||||||
+ uint16_t sport;
|
|
||||||
+ uint16_t dport;
|
|
||||||
+ uint32_t aid; /* Application ID */
|
|
||||||
+ uint8_t l4_proto;
|
|
||||||
+ uint8_t dscp;
|
|
||||||
+ uint16_t __padding;
|
|
||||||
+} __packed;
|
|
||||||
+
|
|
||||||
+#define VY_IPFIX_SID 256
|
|
||||||
+
|
|
||||||
+#define VY_IPFIX_FLOWS 36
|
|
||||||
+#define VY_IPFIX_PKT_LEN (IPFIX_HDRLEN + IPFIX_SET_HDRLEN \
|
|
||||||
+ + VY_IPFIX_FLOWS * sizeof(struct vy_ipfix_data))
|
|
||||||
+
|
|
||||||
+/* template management */
|
|
||||||
+size_t ipfix_rec_len(uint16_t);
|
|
||||||
+
|
|
||||||
+/* message handling */
|
|
||||||
+struct ipfix_msg *ipfix_msg_alloc(size_t, uint32_t);
|
|
||||||
+void ipfix_msg_free(struct ipfix_msg *);
|
|
||||||
+struct ipfix_hdr *ipfix_msg_hdr(const struct ipfix_msg *);
|
|
||||||
+size_t ipfix_msg_len(const struct ipfix_msg *);
|
|
||||||
+void *ipfix_msg_data(struct ipfix_msg *);
|
|
||||||
+struct ipfix_set_hdr *ipfix_msg_add_set(struct ipfix_msg *, uint16_t);
|
|
||||||
+void *ipfix_msg_add_data(struct ipfix_msg *, size_t);
|
|
||||||
+int ipfix_dump_msg(const struct ipfix_msg *);
|
|
||||||
+
|
|
||||||
+#endif /* IPFIX_H */
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/output/ipfix/ulogd_output_IPFIX.c
|
|
||||||
@@ -0,0 +1,503 @@
|
|
||||||
+/*
|
|
||||||
+ * ulogd_output_IPFIX.c
|
|
||||||
+ *
|
|
||||||
+ * ulogd IPFIX Exporter plugin.
|
|
||||||
+ *
|
|
||||||
+ * 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
|
|
||||||
+ *
|
|
||||||
+ * Holger Eitzenberger <holger@eitzenberger.org> Astaro AG 2009
|
|
||||||
+ */
|
|
||||||
+#include <unistd.h>
|
|
||||||
+#include <time.h>
|
|
||||||
+#include <sys/types.h>
|
|
||||||
+#include <sys/socket.h>
|
|
||||||
+#include <arpa/inet.h>
|
|
||||||
+#include <netdb.h>
|
|
||||||
+#include <ulogd/ulogd.h>
|
|
||||||
+#include <ulogd/common.h>
|
|
||||||
+
|
|
||||||
+#include "ipfix.h"
|
|
||||||
+
|
|
||||||
+#define DEFAULT_MTU 512 /* RFC 5101, 10.3.3 */
|
|
||||||
+#define DEFAULT_PORT 4739 /* RFC 5101, 10.3.4 */
|
|
||||||
+#define DEFAULT_SPORT 4740
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ OID_CE = 0,
|
|
||||||
+ HOST_CE,
|
|
||||||
+ PORT_CE,
|
|
||||||
+ PROTO_CE,
|
|
||||||
+ MTU_CE,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define oid_ce(x) (x->ces[OID_CE])
|
|
||||||
+#define host_ce(x) (x->ces[HOST_CE])
|
|
||||||
+#define port_ce(x) (x->ces[PORT_CE])
|
|
||||||
+#define proto_ce(x) (x->ces[PROTO_CE])
|
|
||||||
+#define mtu_ce(x) (x->ces[MTU_CE])
|
|
||||||
+
|
|
||||||
+static const struct config_keyset ipfix_kset = {
|
|
||||||
+ .num_ces = 5,
|
|
||||||
+ .ces = {
|
|
||||||
+ {
|
|
||||||
+ .key = "oid",
|
|
||||||
+ .type = CONFIG_TYPE_INT,
|
|
||||||
+ .u.value = 0
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .key = "host",
|
|
||||||
+ .type = CONFIG_TYPE_STRING,
|
|
||||||
+ .u.string = ""
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .key = "port",
|
|
||||||
+ .type = CONFIG_TYPE_INT,
|
|
||||||
+ .u.value = DEFAULT_PORT
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .key = "proto",
|
|
||||||
+ .type = CONFIG_TYPE_STRING,
|
|
||||||
+ .u.string = "tcp"
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .key = "mtu",
|
|
||||||
+ .type = CONFIG_TYPE_INT,
|
|
||||||
+ .u.value = DEFAULT_MTU
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ipfix_templ {
|
|
||||||
+ struct ipfix_templ *next;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ipfix_priv {
|
|
||||||
+ struct ulogd_fd ufd;
|
|
||||||
+ uint32_t seqno;
|
|
||||||
+ struct ipfix_msg *msg; /* current message */
|
|
||||||
+ struct llist_head list;
|
|
||||||
+ struct ipfix_templ *templates;
|
|
||||||
+ int proto;
|
|
||||||
+ struct ulogd_timer timer;
|
|
||||||
+ struct sockaddr_in sa;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ InIpSaddr = 0,
|
|
||||||
+ InIpDaddr,
|
|
||||||
+ InRawInPktCount,
|
|
||||||
+ InRawInPktLen,
|
|
||||||
+ InRawOutPktCount,
|
|
||||||
+ InRawOutPktLen,
|
|
||||||
+ InFlowStartSec,
|
|
||||||
+ InFlowStartUsec,
|
|
||||||
+ InFlowEndSec,
|
|
||||||
+ InFlowEndUsec,
|
|
||||||
+ InL4SPort,
|
|
||||||
+ InL4DPort,
|
|
||||||
+ InIpProto,
|
|
||||||
+ InCtMark
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct ulogd_key ipfix_in_keys[] = {
|
|
||||||
+ [InIpSaddr] = {
|
|
||||||
+ .type = ULOGD_RET_IPADDR,
|
|
||||||
+ .name = "orig.ip.saddr"
|
|
||||||
+ },
|
|
||||||
+ [InIpDaddr] = {
|
|
||||||
+ .type = ULOGD_RET_IPADDR,
|
|
||||||
+ .name = "orig.ip.daddr"
|
|
||||||
+ },
|
|
||||||
+ [InRawInPktCount] = {
|
|
||||||
+ .type = ULOGD_RET_UINT64,
|
|
||||||
+ .name = "orig.raw.pktcount"
|
|
||||||
+ },
|
|
||||||
+ [InRawInPktLen] = {
|
|
||||||
+ .type = ULOGD_RET_UINT64,
|
|
||||||
+ .name = "orig.raw.pktlen"
|
|
||||||
+ },
|
|
||||||
+ [InRawOutPktCount] = {
|
|
||||||
+ .type = ULOGD_RET_UINT64,
|
|
||||||
+ .name = "reply.raw.pktcount"
|
|
||||||
+ },
|
|
||||||
+ [InRawOutPktLen] = {
|
|
||||||
+ .type = ULOGD_RET_UINT64,
|
|
||||||
+ .name = "reply.raw.pktlen"
|
|
||||||
+ },
|
|
||||||
+ [InFlowStartSec] = {
|
|
||||||
+ .type = ULOGD_RET_UINT32,
|
|
||||||
+ .name = "flow.start.sec"
|
|
||||||
+ },
|
|
||||||
+ [InFlowStartUsec] = {
|
|
||||||
+ .type = ULOGD_RET_UINT32,
|
|
||||||
+ .name = "flow.start.usec"
|
|
||||||
+ },
|
|
||||||
+ [InFlowEndSec] = {
|
|
||||||
+ .type = ULOGD_RET_UINT32,
|
|
||||||
+ .name = "flow.end.sec"
|
|
||||||
+ },
|
|
||||||
+ [InFlowEndUsec] = {
|
|
||||||
+ .type = ULOGD_RET_UINT32,
|
|
||||||
+ .name = "flow.end.usec"
|
|
||||||
+ },
|
|
||||||
+ [InL4SPort] = {
|
|
||||||
+ .type = ULOGD_RET_UINT16,
|
|
||||||
+ .name = "orig.l4.sport"
|
|
||||||
+ },
|
|
||||||
+ [InL4DPort] = {
|
|
||||||
+ .type = ULOGD_RET_UINT16,
|
|
||||||
+ .name = "orig.l4.dport"
|
|
||||||
+ },
|
|
||||||
+ [InIpProto] = {
|
|
||||||
+ .type = ULOGD_RET_UINT8,
|
|
||||||
+ .name = "orig.ip.protocol"
|
|
||||||
+ },
|
|
||||||
+ [InCtMark] = {
|
|
||||||
+ .type = ULOGD_RET_UINT32,
|
|
||||||
+ .name = "ct.mark"
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+/* do some polishing and enqueue it */
|
|
||||||
+static void enqueue_msg(struct ipfix_priv *priv, struct ipfix_msg *msg)
|
|
||||||
+{
|
|
||||||
+ struct ipfix_hdr *hdr = ipfix_msg_data(msg);
|
|
||||||
+
|
|
||||||
+ if (!msg)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ hdr->time = htonl(time(NULL));
|
|
||||||
+ hdr->seqno = htonl(priv->seqno += msg->nrecs);
|
|
||||||
+ if (msg->last_set) {
|
|
||||||
+ msg->last_set->id = htons(msg->last_set->id);
|
|
||||||
+ msg->last_set->len = htons(msg->last_set->len);
|
|
||||||
+ msg->last_set = NULL;
|
|
||||||
+ }
|
|
||||||
+ hdr->len = htons(ipfix_msg_len(msg));
|
|
||||||
+
|
|
||||||
+ llist_add(&msg->link, &priv->list);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * @return %ULOGD_IRET_OK or error value
|
|
||||||
+ */
|
|
||||||
+static int send_msgs(struct ulogd_pluginstance *pi)
|
|
||||||
+{
|
|
||||||
+ struct ipfix_priv *priv = (struct ipfix_priv *) &pi->private;
|
|
||||||
+ struct llist_head *curr, *tmp;
|
|
||||||
+ struct ipfix_msg *msg;
|
|
||||||
+ int ret = ULOGD_IRET_OK, sent;
|
|
||||||
+
|
|
||||||
+ llist_for_each_prev(curr, &priv->list) {
|
|
||||||
+ msg = llist_entry(curr, struct ipfix_msg, link);
|
|
||||||
+
|
|
||||||
+ sent = send(priv->ufd.fd, ipfix_msg_data(msg), ipfix_msg_len(msg), 0);
|
|
||||||
+ if (sent < 0) {
|
|
||||||
+ ulogd_log(ULOGD_ERROR, "send: %m\n");
|
|
||||||
+ ret = ULOGD_IRET_ERR;
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* TODO handle short send() for other protocols */
|
|
||||||
+ if ((size_t) sent < ipfix_msg_len(msg))
|
|
||||||
+ ulogd_log(ULOGD_ERROR, "short send: %d < %d\n",
|
|
||||||
+ sent, ipfix_msg_len(msg));
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ llist_for_each_safe(curr, tmp, &priv->list) {
|
|
||||||
+ msg = llist_entry(curr, struct ipfix_msg, link);
|
|
||||||
+ llist_del(curr);
|
|
||||||
+ msg->nrecs = 0;
|
|
||||||
+ ipfix_msg_free(msg);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+done:
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ipfix_ufd_cb(int fd, unsigned what, void *arg)
|
|
||||||
+{
|
|
||||||
+ struct ulogd_pluginstance *pi = arg;
|
|
||||||
+ struct ipfix_priv *priv = (struct ipfix_priv *) pi->private;
|
|
||||||
+ ssize_t nread;
|
|
||||||
+ char buf[16];
|
|
||||||
+
|
|
||||||
+ if (what & ULOGD_FD_READ) {
|
|
||||||
+ nread = recv(priv->ufd.fd, buf, sizeof(buf), MSG_DONTWAIT);
|
|
||||||
+ if (nread < 0) {
|
|
||||||
+ ulogd_log(ULOGD_ERROR, "recv: %m\n");
|
|
||||||
+ } else if (!nread) {
|
|
||||||
+ ulogd_log(ULOGD_INFO, "connection reset by peer\n");
|
|
||||||
+ ulogd_unregister_fd(&priv->ufd);
|
|
||||||
+ } else
|
|
||||||
+ ulogd_log(ULOGD_INFO, "unexpected data (%d bytes)\n", nread);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void ipfix_timer_cb(struct ulogd_timer *t, void *data)
|
|
||||||
+{
|
|
||||||
+ struct ulogd_pluginstance *pi = data;
|
|
||||||
+ struct ipfix_priv *priv = (struct ipfix_priv *) &pi->private;
|
|
||||||
+
|
|
||||||
+ if (priv->msg && priv->msg->nrecs > 0) {
|
|
||||||
+ enqueue_msg(priv, priv->msg);
|
|
||||||
+ priv->msg = NULL;
|
|
||||||
+ send_msgs(pi);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ipfix_configure(struct ulogd_pluginstance *pi, struct ulogd_pluginstance_stack *stack)
|
|
||||||
+{
|
|
||||||
+ struct ipfix_priv *priv = (struct ipfix_priv *) &pi->private;
|
|
||||||
+ int oid, port, mtu, ret;
|
|
||||||
+ char *host, *proto;
|
|
||||||
+ char addr[16];
|
|
||||||
+
|
|
||||||
+ ret = config_parse_file(pi->id, pi->config_kset);
|
|
||||||
+ if (ret < 0)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ oid = oid_ce(pi->config_kset).u.value;
|
|
||||||
+ host = host_ce(pi->config_kset).u.string;
|
|
||||||
+ port = port_ce(pi->config_kset).u.value;
|
|
||||||
+ proto = proto_ce(pi->config_kset).u.string;
|
|
||||||
+ mtu = mtu_ce(pi->config_kset).u.value;
|
|
||||||
+
|
|
||||||
+ if (!oid) {
|
|
||||||
+ ulogd_log(ULOGD_FATAL, "invalid Observation ID\n");
|
|
||||||
+ return ULOGD_IRET_ERR;
|
|
||||||
+ }
|
|
||||||
+ if (!host || !strcmp(host, "")) {
|
|
||||||
+ ulogd_log(ULOGD_FATAL, "no destination host specified\n");
|
|
||||||
+ return ULOGD_IRET_ERR;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (!strcmp(proto, "udp")) {
|
|
||||||
+ priv->proto = IPPROTO_UDP;
|
|
||||||
+ } else if (!strcmp(proto, "tcp")) {
|
|
||||||
+ priv->proto = IPPROTO_TCP;
|
|
||||||
+ } else {
|
|
||||||
+ ulogd_log(ULOGD_FATAL, "unsupported protocol '%s'\n", proto);
|
|
||||||
+ return ULOGD_IRET_ERR;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ memset(&priv->sa, 0, sizeof(priv->sa));
|
|
||||||
+ priv->sa.sin_family = AF_INET;
|
|
||||||
+ priv->sa.sin_port = htons(port);
|
|
||||||
+ ret = inet_pton(AF_INET, host, &priv->sa.sin_addr);
|
|
||||||
+ if (ret <= 0) {
|
|
||||||
+ ulogd_log(ULOGD_FATAL, "inet_pton: %m\n");
|
|
||||||
+ return ULOGD_IRET_ERR;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ INIT_LLIST_HEAD(&priv->list);
|
|
||||||
+
|
|
||||||
+ ulogd_init_timer(&priv->timer, pi, ipfix_timer_cb);
|
|
||||||
+
|
|
||||||
+ ulogd_log(ULOGD_INFO, "using IPFIX Collector at %s:%d (MTU %d)\n",
|
|
||||||
+ inet_ntop(AF_INET, &priv->sa.sin_addr, addr, sizeof(addr)),
|
|
||||||
+ port, mtu);
|
|
||||||
+
|
|
||||||
+ return ULOGD_IRET_OK;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int tcp_connect(struct ulogd_pluginstance *pi)
|
|
||||||
+{
|
|
||||||
+ struct ipfix_priv *priv = (struct ipfix_priv *) &pi->private;
|
|
||||||
+ int ret = ULOGD_IRET_ERR;
|
|
||||||
+
|
|
||||||
+ if ((priv->ufd.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
|
||||||
+ ulogd_log(ULOGD_FATAL, "socket: %m\n");
|
|
||||||
+ return ULOGD_IRET_ERR;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (connect(priv->ufd.fd, (struct sockaddr *) &priv->sa, sizeof(priv->sa)) < 0) {
|
|
||||||
+ ulogd_log(ULOGD_ERROR, "connect: %m\n");
|
|
||||||
+ ret = ULOGD_IRET_ERR;
|
|
||||||
+ goto err_close;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return ULOGD_IRET_OK;
|
|
||||||
+
|
|
||||||
+err_close:
|
|
||||||
+ close(priv->ufd.fd);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int udp_connect(struct ulogd_pluginstance *pi)
|
|
||||||
+{
|
|
||||||
+ struct ipfix_priv *priv = (struct ipfix_priv *) &pi->private;
|
|
||||||
+
|
|
||||||
+ if ((priv->ufd.fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
||||||
+ ulogd_log(ULOGD_FATAL, "socket: %m\n");
|
|
||||||
+ return ULOGD_IRET_ERR;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (connect(priv->ufd.fd, (struct sockaddr *) &priv->sa, sizeof(priv->sa)) < 0) {
|
|
||||||
+ ulogd_log(ULOGD_ERROR, "connect: %m\n");
|
|
||||||
+ return ULOGD_IRET_ERR;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ipfix_start(struct ulogd_pluginstance *pi)
|
|
||||||
+{
|
|
||||||
+ struct ipfix_priv *priv = (struct ipfix_priv *) &pi->private;
|
|
||||||
+ char addr[16];
|
|
||||||
+ int port, ret;
|
|
||||||
+
|
|
||||||
+ switch (priv->proto) {
|
|
||||||
+ case IPPROTO_UDP:
|
|
||||||
+ if ((ret = udp_connect(pi)) < 0)
|
|
||||||
+ return ret;
|
|
||||||
+ break;
|
|
||||||
+ case IPPROTO_TCP:
|
|
||||||
+ if ((ret = tcp_connect(pi)) < 0)
|
|
||||||
+ return ret;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ default:
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ priv->seqno = 0;
|
|
||||||
+
|
|
||||||
+ port = port_ce(pi->config_kset).u.value;
|
|
||||||
+ ulogd_log(ULOGD_INFO, "connected to %s:%d\n",
|
|
||||||
+ inet_ntop(AF_INET, &priv->sa.sin_addr, addr, sizeof(addr)),
|
|
||||||
+ port);
|
|
||||||
+
|
|
||||||
+ /* Register the socket FD */
|
|
||||||
+ priv->ufd.when = ULOGD_FD_READ;
|
|
||||||
+ priv->ufd.cb = ipfix_ufd_cb;
|
|
||||||
+ priv->ufd.data = pi;
|
|
||||||
+
|
|
||||||
+ if (ulogd_register_fd(&priv->ufd) < 0)
|
|
||||||
+ return ULOGD_IRET_ERR;
|
|
||||||
+
|
|
||||||
+ /* Add a 1 second timer */
|
|
||||||
+ ulogd_add_timer(&priv->timer, 1);
|
|
||||||
+
|
|
||||||
+ return ULOGD_IRET_OK;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ipfix_stop(struct ulogd_pluginstance *pi)
|
|
||||||
+{
|
|
||||||
+ struct ipfix_priv *priv = (struct ipfix_priv *) &pi->private;
|
|
||||||
+
|
|
||||||
+ ulogd_unregister_fd(&priv->ufd);
|
|
||||||
+ close(priv->ufd.fd);
|
|
||||||
+ priv->ufd.fd = -1;
|
|
||||||
+
|
|
||||||
+ ulogd_del_timer(&priv->timer);
|
|
||||||
+
|
|
||||||
+ ipfix_msg_free(priv->msg);
|
|
||||||
+ priv->msg = NULL;
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ipfix_interp(struct ulogd_pluginstance *pi)
|
|
||||||
+{
|
|
||||||
+ struct ipfix_priv *priv = (struct ipfix_priv *) &pi->private;
|
|
||||||
+ struct vy_ipfix_data *data;
|
|
||||||
+ int oid, mtu, ret;
|
|
||||||
+ char addr[16];
|
|
||||||
+
|
|
||||||
+ if (!(GET_FLAGS(pi->input.keys, InIpSaddr) & ULOGD_RETF_VALID))
|
|
||||||
+ return ULOGD_IRET_OK;
|
|
||||||
+
|
|
||||||
+ oid = oid_ce(pi->config_kset).u.value;
|
|
||||||
+ mtu = mtu_ce(pi->config_kset).u.value;
|
|
||||||
+
|
|
||||||
+again:
|
|
||||||
+ if (!priv->msg) {
|
|
||||||
+ priv->msg = ipfix_msg_alloc(mtu, oid);
|
|
||||||
+ if (!priv->msg) {
|
|
||||||
+ /* just drop this flow */
|
|
||||||
+ ulogd_log(ULOGD_ERROR, "out of memory, dropping flow\n");
|
|
||||||
+ return ULOGD_IRET_OK;
|
|
||||||
+ }
|
|
||||||
+ ipfix_msg_add_set(priv->msg, VY_IPFIX_SID);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ data = ipfix_msg_add_data(priv->msg, sizeof(struct vy_ipfix_data));
|
|
||||||
+ if (!data) {
|
|
||||||
+ enqueue_msg(priv, priv->msg);
|
|
||||||
+ priv->msg = NULL;
|
|
||||||
+ /* can't loop because the next will definitely succeed */
|
|
||||||
+ goto again;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ data->ifi_in = data->ifi_out = 0;
|
|
||||||
+
|
|
||||||
+ data->saddr.s_addr = ikey_get_u32(&pi->input.keys[InIpSaddr]);
|
|
||||||
+ data->daddr.s_addr = ikey_get_u32(&pi->input.keys[InIpDaddr]);
|
|
||||||
+
|
|
||||||
+ data->packets = htonl((uint32_t) (ikey_get_u64(&pi->input.keys[InRawInPktCount])
|
|
||||||
+ + ikey_get_u64(&pi->input.keys[InRawOutPktCount])));
|
|
||||||
+ data->bytes = htonl((uint32_t) (ikey_get_u64(&pi->input.keys[InRawInPktLen])
|
|
||||||
+ + ikey_get_u64(&pi->input.keys[InRawOutPktLen])));
|
|
||||||
+
|
|
||||||
+ data->start = htonl(ikey_get_u32(&pi->input.keys[InFlowStartSec]));
|
|
||||||
+ data->end = htonl(ikey_get_u32(&pi->input.keys[InFlowEndSec]));
|
|
||||||
+
|
|
||||||
+ if (GET_FLAGS(pi->input.keys, InL4SPort) & ULOGD_RETF_VALID) {
|
|
||||||
+ data->sport = htons(ikey_get_u16(&pi->input.keys[InL4SPort]));
|
|
||||||
+ data->dport = htons(ikey_get_u16(&pi->input.keys[InL4DPort]));
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ data->aid = 0;
|
|
||||||
+ if (GET_FLAGS(pi->input.keys, InCtMark) & ULOGD_RETF_VALID)
|
|
||||||
+ data->aid = htonl(ikey_get_u32(&pi->input.keys[InCtMark]));
|
|
||||||
+
|
|
||||||
+ data->l4_proto = ikey_get_u8(&pi->input.keys[InIpProto]);
|
|
||||||
+ data->__padding = 0;
|
|
||||||
+
|
|
||||||
+ ulogd_log(ULOGD_DEBUG, "Got new packet (packets = %u, bytes = %u, flow = (%u, %u), saddr = %s, daddr = %s, sport = %u, dport = %u)\n",
|
|
||||||
+ ntohl(data->packets), ntohl(data->bytes), ntohl(data->start), ntohl(data->end),
|
|
||||||
+ inet_ntop(AF_INET, &data->saddr.s_addr, addr, sizeof(addr)),
|
|
||||||
+ inet_ntop(AF_INET, &data->daddr.s_addr, addr, sizeof(addr)),
|
|
||||||
+ ntohs(data->sport), ntohs(data->dport));
|
|
||||||
+
|
|
||||||
+ if ((ret = send_msgs(pi)) < 0)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ return ULOGD_IRET_OK;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct ulogd_plugin ipfix_plugin = {
|
|
||||||
+ .name = "IPFIX",
|
|
||||||
+ .input = {
|
|
||||||
+ .keys = ipfix_in_keys,
|
|
||||||
+ .num_keys = ARRAY_SIZE(ipfix_in_keys),
|
|
||||||
+ .type = ULOGD_DTYPE_PACKET | ULOGD_DTYPE_FLOW | ULOGD_DTYPE_SUM
|
|
||||||
+ },
|
|
||||||
+ .output = {
|
|
||||||
+ .type = ULOGD_DTYPE_SINK
|
|
||||||
+ },
|
|
||||||
+ .config_kset = (struct config_keyset *) &ipfix_kset,
|
|
||||||
+ .priv_size = sizeof(struct ipfix_priv),
|
|
||||||
+ .configure = ipfix_configure,
|
|
||||||
+ .start = ipfix_start,
|
|
||||||
+ .stop = ipfix_stop,
|
|
||||||
+ .interp = ipfix_interp,
|
|
||||||
+ .version = VERSION,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+void __attribute__ ((constructor)) init(void);
|
|
||||||
+
|
|
||||||
+void init(void)
|
|
||||||
+{
|
|
||||||
+ ulogd_register_plugin(&ipfix_plugin);
|
|
||||||
+}
|
|
|
@ -1,421 +0,0 @@
|
||||||
From cc919f7013d2d76c8bef0b9c562c1faf98a095a3 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ander Juaristi <a@juaristi.eus>
|
|
||||||
Date: Fri, 26 Apr 2019 09:58:07 +0200
|
|
||||||
Subject: IPFIX: Introduce template record support
|
|
||||||
|
|
||||||
This commit adds the ability to send template records
|
|
||||||
to the remote collector.
|
|
||||||
|
|
||||||
In addition, it also introduces a new
|
|
||||||
configuration parameter 'send_template', which tells when template
|
|
||||||
records should be sent. It accepts the following string values:
|
|
||||||
|
|
||||||
- "once": Send the template record only the first time (might be coalesced
|
|
||||||
with data records).
|
|
||||||
- "always": Send the template record always, with every data record that is sent
|
|
||||||
to the collector (multiple data records might be sent together).
|
|
||||||
- "never": Assume the collector knows the schema already. Do not send template records.
|
|
||||||
|
|
||||||
If omitted, the default value for 'send_template' is "once".
|
|
||||||
|
|
||||||
Signed-off-by: Ander Juaristi <a@juaristi.eus>
|
|
||||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
||||||
---
|
|
||||||
include/ulogd/ipfix_protocol.h | 1 +
|
|
||||||
output/ipfix/ipfix.c | 97 +++++++++++++++++++++++++++++++++++++--
|
|
||||||
output/ipfix/ipfix.h | 22 ++++-----
|
|
||||||
output/ipfix/ulogd_output_IPFIX.c | 56 ++++++++++++----------
|
|
||||||
4 files changed, 139 insertions(+), 37 deletions(-)
|
|
||||||
|
|
||||||
--- a/include/ulogd/ipfix_protocol.h
|
|
||||||
+++ b/include/ulogd/ipfix_protocol.h
|
|
||||||
@@ -129,6 +129,7 @@ enum {
|
|
||||||
/* reserved */
|
|
||||||
IPFIX_fragmentOffsetIPv4 = 88,
|
|
||||||
/* reserved */
|
|
||||||
+ IPFIX_applicationId = 95,
|
|
||||||
IPFIX_bgpNextAdjacentAsNumber = 128,
|
|
||||||
IPFIX_bgpPrevAdjacentAsNumber = 129,
|
|
||||||
IPFIX_exporterIPv4Address = 130,
|
|
||||||
--- a/output/ipfix/ipfix.c
|
|
||||||
+++ b/output/ipfix/ipfix.c
|
|
||||||
@@ -2,6 +2,7 @@
|
|
||||||
* ipfix.c
|
|
||||||
*
|
|
||||||
* Holger Eitzenberger, 2009.
|
|
||||||
+ * Ander Juaristi, 2019
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* These forward declarations are needed since ulogd.h doesn't like to be the first */
|
|
||||||
@@ -13,25 +14,107 @@
|
|
||||||
|
|
||||||
#include <ulogd/ulogd.h>
|
|
||||||
#include <ulogd/common.h>
|
|
||||||
+#include <ulogd/ipfix_protocol.h>
|
|
||||||
|
|
||||||
-struct ipfix_msg *ipfix_msg_alloc(size_t len, uint32_t oid)
|
|
||||||
+struct ipfix_templ_elem {
|
|
||||||
+ uint16_t id;
|
|
||||||
+ uint16_t len;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ipfix_templ {
|
|
||||||
+ unsigned int num_templ_elements;
|
|
||||||
+ struct ipfix_templ_elem templ_elements[];
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+/* Template fields modeled after vy_ipfix_data */
|
|
||||||
+static const struct ipfix_templ template = {
|
|
||||||
+ .num_templ_elements = 10,
|
|
||||||
+ .templ_elements = {
|
|
||||||
+ {
|
|
||||||
+ .id = IPFIX_sourceIPv4Address,
|
|
||||||
+ .len = sizeof(uint32_t)
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .id = IPFIX_destinationIPv4Address,
|
|
||||||
+ .len = sizeof(uint32_t)
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .id = IPFIX_packetTotalCount,
|
|
||||||
+ .len = sizeof(uint32_t)
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .id = IPFIX_octetTotalCount,
|
|
||||||
+ .len = sizeof(uint32_t)
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .id = IPFIX_flowStartSeconds,
|
|
||||||
+ .len = sizeof(uint32_t)
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .id = IPFIX_flowEndSeconds,
|
|
||||||
+ .len = sizeof(uint32_t)
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .id = IPFIX_sourceTransportPort,
|
|
||||||
+ .len = sizeof(uint16_t)
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .id = IPFIX_destinationTransportPort,
|
|
||||||
+ .len = sizeof(uint16_t)
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .id = IPFIX_protocolIdentifier,
|
|
||||||
+ .len = sizeof(uint8_t)
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .id = IPFIX_applicationId,
|
|
||||||
+ .len = sizeof(uint32_t)
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ipfix_msg *ipfix_msg_alloc(size_t len, uint32_t oid, int tid)
|
|
||||||
{
|
|
||||||
struct ipfix_msg *msg;
|
|
||||||
struct ipfix_hdr *hdr;
|
|
||||||
+ struct ipfix_templ_hdr *templ_hdr;
|
|
||||||
+ struct ipfix_templ_elem *elem;
|
|
||||||
+ unsigned int i = 0;
|
|
||||||
|
|
||||||
- if (len < IPFIX_HDRLEN + IPFIX_SET_HDRLEN)
|
|
||||||
+ if ((tid > 0 && len < IPFIX_HDRLEN + IPFIX_TEMPL_HDRLEN(template.num_templ_elements) + IPFIX_SET_HDRLEN) ||
|
|
||||||
+ (len < IPFIX_HDRLEN + IPFIX_SET_HDRLEN))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
msg = malloc(sizeof(struct ipfix_msg) + len);
|
|
||||||
memset(msg, 0, sizeof(struct ipfix_msg));
|
|
||||||
- msg->tail = msg->data + IPFIX_HDRLEN;
|
|
||||||
+ msg->tid = tid;
|
|
||||||
msg->end = msg->data + len;
|
|
||||||
+ msg->tail = msg->data + IPFIX_HDRLEN;
|
|
||||||
+ if (tid > 0)
|
|
||||||
+ msg->tail += IPFIX_TEMPL_HDRLEN(template.num_templ_elements);
|
|
||||||
|
|
||||||
+ /* Initialize message header */
|
|
||||||
hdr = ipfix_msg_hdr(msg);
|
|
||||||
memset(hdr, 0, IPFIX_HDRLEN);
|
|
||||||
hdr->version = htons(IPFIX_VERSION);
|
|
||||||
hdr->oid = htonl(oid);
|
|
||||||
|
|
||||||
+ if (tid > 0) {
|
|
||||||
+ /* Initialize template record header */
|
|
||||||
+ templ_hdr = ipfix_msg_templ_hdr(msg);
|
|
||||||
+ templ_hdr->sid = htons(2);
|
|
||||||
+ templ_hdr->tid = htons(tid);
|
|
||||||
+ templ_hdr->len = htons(IPFIX_TEMPL_HDRLEN(template.num_templ_elements));
|
|
||||||
+ templ_hdr->cnt = htons(template.num_templ_elements);
|
|
||||||
+
|
|
||||||
+ while (i < template.num_templ_elements) {
|
|
||||||
+ elem = (struct ipfix_templ_elem *) &templ_hdr->data[i * 4];
|
|
||||||
+ elem->id = htons(template.templ_elements[i].id);
|
|
||||||
+ elem->len = htons(template.templ_elements[i].len);
|
|
||||||
+ i++;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -47,6 +130,14 @@ void ipfix_msg_free(struct ipfix_msg *ms
|
|
||||||
free(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
+struct ipfix_templ_hdr *ipfix_msg_templ_hdr(const struct ipfix_msg *msg)
|
|
||||||
+{
|
|
||||||
+ if (msg->tid > 0)
|
|
||||||
+ return (struct ipfix_templ_hdr *) (msg->data + IPFIX_HDRLEN);
|
|
||||||
+
|
|
||||||
+ return NULL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
struct ipfix_hdr *ipfix_msg_hdr(const struct ipfix_msg *msg)
|
|
||||||
{
|
|
||||||
return (struct ipfix_hdr *)msg->data;
|
|
||||||
--- a/output/ipfix/ipfix.h
|
|
||||||
+++ b/output/ipfix/ipfix.h
|
|
||||||
@@ -2,6 +2,7 @@
|
|
||||||
* ipfix.h
|
|
||||||
*
|
|
||||||
* Holger Eitzenberger <holger@eitzenberger.org>, 2009.
|
|
||||||
+ * Ander Juaristi <a@juaristi.eus>, 2019
|
|
||||||
*/
|
|
||||||
#ifndef IPFIX_H
|
|
||||||
#define IPFIX_H
|
|
||||||
@@ -20,17 +21,21 @@ struct ipfix_hdr {
|
|
||||||
uint8_t data[];
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
-#define IPFIX_HDRLEN sizeof(struct ipfix_hdr)
|
|
||||||
+#define IPFIX_HDRLEN sizeof(struct ipfix_hdr)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IDs 0-255 are reserved for Template Sets. IDs of Data Sets are > 255.
|
|
||||||
*/
|
|
||||||
struct ipfix_templ_hdr {
|
|
||||||
- uint16_t id;
|
|
||||||
+ uint16_t sid;
|
|
||||||
+ uint16_t len;
|
|
||||||
+ uint16_t tid;
|
|
||||||
uint16_t cnt;
|
|
||||||
uint8_t data[];
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
+#define IPFIX_TEMPL_HDRLEN(nfields) sizeof(struct ipfix_templ_hdr) + (sizeof(uint16_t) * 2 * nfields)
|
|
||||||
+
|
|
||||||
struct ipfix_set_hdr {
|
|
||||||
#define IPFIX_SET_TEMPL 2
|
|
||||||
#define IPFIX_SET_OPT_TEMPL 3
|
|
||||||
@@ -46,6 +51,7 @@ struct ipfix_msg {
|
|
||||||
uint8_t *tail;
|
|
||||||
uint8_t *end;
|
|
||||||
unsigned nrecs;
|
|
||||||
+ int tid;
|
|
||||||
struct ipfix_set_hdr *last_set;
|
|
||||||
uint8_t data[];
|
|
||||||
};
|
|
||||||
@@ -53,18 +59,14 @@ struct ipfix_msg {
|
|
||||||
struct vy_ipfix_data {
|
|
||||||
struct in_addr saddr;
|
|
||||||
struct in_addr daddr;
|
|
||||||
- uint16_t ifi_in;
|
|
||||||
- uint16_t ifi_out;
|
|
||||||
uint32_t packets;
|
|
||||||
uint32_t bytes;
|
|
||||||
uint32_t start; /* Unix time */
|
|
||||||
uint32_t end; /* Unix time */
|
|
||||||
uint16_t sport;
|
|
||||||
uint16_t dport;
|
|
||||||
- uint32_t aid; /* Application ID */
|
|
||||||
uint8_t l4_proto;
|
|
||||||
- uint8_t dscp;
|
|
||||||
- uint16_t __padding;
|
|
||||||
+ uint32_t aid; /* Application ID */
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
#define VY_IPFIX_SID 256
|
|
||||||
@@ -73,13 +75,11 @@ struct vy_ipfix_data {
|
|
||||||
#define VY_IPFIX_PKT_LEN (IPFIX_HDRLEN + IPFIX_SET_HDRLEN \
|
|
||||||
+ VY_IPFIX_FLOWS * sizeof(struct vy_ipfix_data))
|
|
||||||
|
|
||||||
-/* template management */
|
|
||||||
-size_t ipfix_rec_len(uint16_t);
|
|
||||||
-
|
|
||||||
/* message handling */
|
|
||||||
-struct ipfix_msg *ipfix_msg_alloc(size_t, uint32_t);
|
|
||||||
+struct ipfix_msg *ipfix_msg_alloc(size_t, uint32_t, int);
|
|
||||||
void ipfix_msg_free(struct ipfix_msg *);
|
|
||||||
struct ipfix_hdr *ipfix_msg_hdr(const struct ipfix_msg *);
|
|
||||||
+struct ipfix_templ_hdr *ipfix_msg_templ_hdr(const struct ipfix_msg *);
|
|
||||||
size_t ipfix_msg_len(const struct ipfix_msg *);
|
|
||||||
void *ipfix_msg_data(struct ipfix_msg *);
|
|
||||||
struct ipfix_set_hdr *ipfix_msg_add_set(struct ipfix_msg *, uint16_t);
|
|
||||||
--- a/output/ipfix/ulogd_output_IPFIX.c
|
|
||||||
+++ b/output/ipfix/ulogd_output_IPFIX.c
|
|
||||||
@@ -3,6 +3,9 @@
|
|
||||||
*
|
|
||||||
* ulogd IPFIX Exporter plugin.
|
|
||||||
*
|
|
||||||
+ * (C) 2009 by Holger Eitzenberger <holger@eitzenberger.org>, Astaro AG
|
|
||||||
+ * (C) 2019 by Ander Juaristi <a@juaristi.eus>
|
|
||||||
+ *
|
|
||||||
* 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
|
|
||||||
@@ -11,8 +14,6 @@
|
|
||||||
* 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
|
|
||||||
- *
|
|
||||||
- * Holger Eitzenberger <holger@eitzenberger.org> Astaro AG 2009
|
|
||||||
*/
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <time.h>
|
|
||||||
@@ -28,6 +29,7 @@
|
|
||||||
#define DEFAULT_MTU 512 /* RFC 5101, 10.3.3 */
|
|
||||||
#define DEFAULT_PORT 4739 /* RFC 5101, 10.3.4 */
|
|
||||||
#define DEFAULT_SPORT 4740
|
|
||||||
+#define DEFAULT_SEND_TEMPLATE "once"
|
|
||||||
|
|
||||||
enum {
|
|
||||||
OID_CE = 0,
|
|
||||||
@@ -35,16 +37,18 @@ enum {
|
|
||||||
PORT_CE,
|
|
||||||
PROTO_CE,
|
|
||||||
MTU_CE,
|
|
||||||
+ SEND_TEMPLATE_CE
|
|
||||||
};
|
|
||||||
|
|
||||||
-#define oid_ce(x) (x->ces[OID_CE])
|
|
||||||
-#define host_ce(x) (x->ces[HOST_CE])
|
|
||||||
-#define port_ce(x) (x->ces[PORT_CE])
|
|
||||||
-#define proto_ce(x) (x->ces[PROTO_CE])
|
|
||||||
-#define mtu_ce(x) (x->ces[MTU_CE])
|
|
||||||
+#define oid_ce(x) (x->ces[OID_CE])
|
|
||||||
+#define host_ce(x) (x->ces[HOST_CE])
|
|
||||||
+#define port_ce(x) (x->ces[PORT_CE])
|
|
||||||
+#define proto_ce(x) (x->ces[PROTO_CE])
|
|
||||||
+#define mtu_ce(x) (x->ces[MTU_CE])
|
|
||||||
+#define send_template_ce(x) (x->ces[SEND_TEMPLATE_CE])
|
|
||||||
|
|
||||||
static const struct config_keyset ipfix_kset = {
|
|
||||||
- .num_ces = 5,
|
|
||||||
+ .num_ces = 6,
|
|
||||||
.ces = {
|
|
||||||
{
|
|
||||||
.key = "oid",
|
|
||||||
@@ -70,20 +74,21 @@ static const struct config_keyset ipfix_
|
|
||||||
.key = "mtu",
|
|
||||||
.type = CONFIG_TYPE_INT,
|
|
||||||
.u.value = DEFAULT_MTU
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
+ .key = "send_template",
|
|
||||||
+ .type = CONFIG_TYPE_STRING,
|
|
||||||
+ .u.string = DEFAULT_SEND_TEMPLATE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
-struct ipfix_templ {
|
|
||||||
- struct ipfix_templ *next;
|
|
||||||
-};
|
|
||||||
-
|
|
||||||
struct ipfix_priv {
|
|
||||||
struct ulogd_fd ufd;
|
|
||||||
uint32_t seqno;
|
|
||||||
struct ipfix_msg *msg; /* current message */
|
|
||||||
struct llist_head list;
|
|
||||||
- struct ipfix_templ *templates;
|
|
||||||
+ int tid;
|
|
||||||
int proto;
|
|
||||||
struct ulogd_timer timer;
|
|
||||||
struct sockaddr_in sa;
|
|
||||||
@@ -258,8 +263,8 @@ static void ipfix_timer_cb(struct ulogd_
|
|
||||||
static int ipfix_configure(struct ulogd_pluginstance *pi, struct ulogd_pluginstance_stack *stack)
|
|
||||||
{
|
|
||||||
struct ipfix_priv *priv = (struct ipfix_priv *) &pi->private;
|
|
||||||
+ char *host, *proto, *send_template;
|
|
||||||
int oid, port, mtu, ret;
|
|
||||||
- char *host, *proto;
|
|
||||||
char addr[16];
|
|
||||||
|
|
||||||
ret = config_parse_file(pi->id, pi->config_kset);
|
|
||||||
@@ -271,6 +276,7 @@ static int ipfix_configure(struct ulogd_
|
|
||||||
port = port_ce(pi->config_kset).u.value;
|
|
||||||
proto = proto_ce(pi->config_kset).u.string;
|
|
||||||
mtu = mtu_ce(pi->config_kset).u.value;
|
|
||||||
+ send_template = send_template_ce(pi->config_kset).u.string;
|
|
||||||
|
|
||||||
if (!oid) {
|
|
||||||
ulogd_log(ULOGD_FATAL, "invalid Observation ID\n");
|
|
||||||
@@ -303,6 +309,8 @@ static int ipfix_configure(struct ulogd_
|
|
||||||
|
|
||||||
ulogd_init_timer(&priv->timer, pi, ipfix_timer_cb);
|
|
||||||
|
|
||||||
+ priv->tid = (strcmp(send_template, "never") ? VY_IPFIX_SID : -1);
|
|
||||||
+
|
|
||||||
ulogd_log(ULOGD_INFO, "using IPFIX Collector at %s:%d (MTU %d)\n",
|
|
||||||
inet_ntop(AF_INET, &priv->sa.sin_addr, addr, sizeof(addr)),
|
|
||||||
port, mtu);
|
|
||||||
@@ -410,25 +418,30 @@ static int ipfix_stop(struct ulogd_plugi
|
|
||||||
static int ipfix_interp(struct ulogd_pluginstance *pi)
|
|
||||||
{
|
|
||||||
struct ipfix_priv *priv = (struct ipfix_priv *) &pi->private;
|
|
||||||
+ char saddr[16], daddr[16], *send_template;
|
|
||||||
struct vy_ipfix_data *data;
|
|
||||||
int oid, mtu, ret;
|
|
||||||
- char addr[16];
|
|
||||||
|
|
||||||
if (!(GET_FLAGS(pi->input.keys, InIpSaddr) & ULOGD_RETF_VALID))
|
|
||||||
return ULOGD_IRET_OK;
|
|
||||||
|
|
||||||
oid = oid_ce(pi->config_kset).u.value;
|
|
||||||
mtu = mtu_ce(pi->config_kset).u.value;
|
|
||||||
+ send_template = send_template_ce(pi->config_kset).u.string;
|
|
||||||
|
|
||||||
again:
|
|
||||||
if (!priv->msg) {
|
|
||||||
- priv->msg = ipfix_msg_alloc(mtu, oid);
|
|
||||||
+ priv->msg = ipfix_msg_alloc(mtu, oid, priv->tid);
|
|
||||||
if (!priv->msg) {
|
|
||||||
/* just drop this flow */
|
|
||||||
ulogd_log(ULOGD_ERROR, "out of memory, dropping flow\n");
|
|
||||||
return ULOGD_IRET_OK;
|
|
||||||
}
|
|
||||||
ipfix_msg_add_set(priv->msg, VY_IPFIX_SID);
|
|
||||||
+
|
|
||||||
+ /* template sent - do not send it again the next time */
|
|
||||||
+ if (priv->tid == VY_IPFIX_SID && strcmp(send_template, "once") == 0)
|
|
||||||
+ priv->tid = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = ipfix_msg_add_data(priv->msg, sizeof(struct vy_ipfix_data));
|
|
||||||
@@ -439,8 +452,6 @@ again:
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
|
|
||||||
- data->ifi_in = data->ifi_out = 0;
|
|
||||||
-
|
|
||||||
data->saddr.s_addr = ikey_get_u32(&pi->input.keys[InIpSaddr]);
|
|
||||||
data->daddr.s_addr = ikey_get_u32(&pi->input.keys[InIpDaddr]);
|
|
||||||
|
|
||||||
@@ -462,13 +473,12 @@ again:
|
|
||||||
data->aid = htonl(ikey_get_u32(&pi->input.keys[InCtMark]));
|
|
||||||
|
|
||||||
data->l4_proto = ikey_get_u8(&pi->input.keys[InIpProto]);
|
|
||||||
- data->__padding = 0;
|
|
||||||
|
|
||||||
ulogd_log(ULOGD_DEBUG, "Got new packet (packets = %u, bytes = %u, flow = (%u, %u), saddr = %s, daddr = %s, sport = %u, dport = %u)\n",
|
|
||||||
- ntohl(data->packets), ntohl(data->bytes), ntohl(data->start), ntohl(data->end),
|
|
||||||
- inet_ntop(AF_INET, &data->saddr.s_addr, addr, sizeof(addr)),
|
|
||||||
- inet_ntop(AF_INET, &data->daddr.s_addr, addr, sizeof(addr)),
|
|
||||||
- ntohs(data->sport), ntohs(data->dport));
|
|
||||||
+ ntohl(data->packets), ntohl(data->bytes), ntohl(data->start), ntohl(data->end),
|
|
||||||
+ inet_ntop(AF_INET, &data->saddr.s_addr, saddr, sizeof(saddr)),
|
|
||||||
+ inet_ntop(AF_INET, &data->daddr.s_addr, daddr, sizeof(daddr)),
|
|
||||||
+ ntohs(data->sport), ntohs(data->dport));
|
|
||||||
|
|
||||||
if ((ret = send_msgs(pi)) < 0)
|
|
||||||
return ret;
|
|
Loading…
Reference in a new issue