ulogd: bump to version 2.0.8
authorAlexandru Ardelean <ardeleanalex@gmail.com>
Tue, 8 Nov 2022 09:20:37 +0000 (11:20 +0200)
committerRosen Penev <rosenp@gmail.com>
Fri, 11 Nov 2022 23:10:45 +0000 (15:10 -0800)
Removed upstream patches.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
net/ulogd/Makefile
net/ulogd/patches/010-json-remote.patch [deleted file]
net/ulogd/patches/020-fix-musl.patch [deleted file]
net/ulogd/patches/030-ipfix-add.patch [deleted file]
net/ulogd/patches/040-ipfix-template.patch [deleted file]

index 188091a1e69c5a37424b0b43d6b82e7697f68247..26a6b9297b7dd7f5461aba639be28c75dede0869 100644 (file)
@@ -8,13 +8,13 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=ulogd
-PKG_VERSION:=2.0.7
-PKG_RELEASE:=6
+PKG_VERSION:=2.0.8
+PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
 PKG_SOURCE_URL:=https://netfilter.org/projects/ulogd/files/ \
        ftp://ftp.netfilter.org/pub/ulogd/
-PKG_HASH:=990a05494d9c16029ba0a83f3b7294fc05c756546b8d60d1c1572dc25249a92b
+PKG_HASH:=4ead6c3970c3f57fa1e89fe2d7cc483ba6fe2bd1b08701521e0b3afd667df291
 
 PKG_MAINTAINER:=Alexandru Ardelean <ardeleanalex@gmail.com>
 PKG_LICENSE:=GPL-2.0-only
@@ -41,7 +41,7 @@ endef
 
 define Package/ulogd
   $(call Package/ulogd/Default)
-  DEPENDS:=+libmnl +libnfnetlink +libpthread
+  DEPENDS:=+libmnl +libnfnetlink +libpthread +libnetfilter-conntrack
   TITLE:=Netfilter userspace logging daemon
   MENU:=1
 endef
diff --git a/net/ulogd/patches/010-json-remote.patch b/net/ulogd/patches/010-json-remote.patch
deleted file mode 100644 (file)
index e217a11..0000000
+++ /dev/null
@@ -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
diff --git a/net/ulogd/patches/020-fix-musl.patch b/net/ulogd/patches/020-fix-musl.patch
deleted file mode 100644 (file)
index afbc64c..0000000
+++ /dev/null
@@ -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
diff --git a/net/ulogd/patches/030-ipfix-add.patch b/net/ulogd/patches/030-ipfix-add.patch
deleted file mode 100644 (file)
index 13d1472..0000000
+++ /dev/null
@@ -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);
-+}
diff --git a/net/ulogd/patches/040-ipfix-template.patch b/net/ulogd/patches/040-ipfix-template.patch
deleted file mode 100644 (file)
index c7b9d8e..0000000
+++ /dev/null
@@ -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;