From: Ken-ichirou MATSUZAWA <chamaken@gmail.com>
To: Eric Leblond <eric@regit.org>
Cc: Netfilter Devel <netfilter-devel@vger.kernel.org>
Subject: [PATCH ulogd 7/7] nflow9: introduce new NetFlow v9 output plugin
Date: Wed, 10 Feb 2016 11:05:40 +0900 [thread overview]
Message-ID: <20160210020540.GH17470@gmail.com> (raw)
In-Reply-To: <20160210015358.GA17470@gmail.com>
This patch introduces a NFLOW9 output plugin which sends Netflow
v9 encoded NFCT destroy events.
Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
---
output/Makefile.am | 10 +
output/ulogd_output_NFLOW9.c | 1696 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1706 insertions(+)
create mode 100644 output/ulogd_output_NFLOW9.c
diff --git a/output/Makefile.am b/output/Makefile.am
index ff851ad..6f62b48 100644
--- a/output/Makefile.am
+++ b/output/Makefile.am
@@ -13,6 +13,10 @@ if HAVE_JANSSON
pkglib_LTLIBRARIES += ulogd_output_JSON.la
endif
+if BUILD_NFCT
+pkglib_LTLIBRARIES += ulogd_output_NFLOW9.la
+endif
+
ulogd_output_GPRINT_la_SOURCES = ulogd_output_GPRINT.c
ulogd_output_GPRINT_la_LDFLAGS = -avoid-version -module
@@ -42,3 +46,9 @@ ulogd_output_JSON_la_SOURCES = ulogd_output_JSON.c
ulogd_output_JSON_la_LIBADD = ${libjansson_LIBS}
ulogd_output_JSON_la_LDFLAGS = -avoid-version -module
endif
+
+if BUILD_NFCT
+ulogd_output_NFLOW9_la_SOURCES = ulogd_output_NFLOW9.c
+ulogd_output_NFLOW9_la_LIBADD = $(LIBNETFILTER_CONNTRACK_LIBS)
+ulogd_output_NFLOW9_la_LDFLAGS = -avoid-version -module
+endif
diff --git a/output/ulogd_output_NFLOW9.c b/output/ulogd_output_NFLOW9.c
new file mode 100644
index 0000000..7af2422
--- /dev/null
+++ b/output/ulogd_output_NFLOW9.c
@@ -0,0 +1,1696 @@
+/* ulogd_output_NFLOW9.c
+ *
+ * ulogd output plugin for NetFlow version9
+ *
+ * This target produces a NetFlow v9 data and send it.
+ *
+ * (C) 2014 Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/uio.h>
+#include <asm/byteorder.h> /* __be64_to_cpu */
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+#include <ulogd/linuxlist.h>
+#include <ulogd/ulogd.h>
+#include <ulogd/conffile.h>
+#include <ulogd/ipfix_protocol.h>
+
+/* #define DEBUG_TMMAP */
+#ifdef DEBUG_TMMAP
+#include <sys/mman.h>
+int mmfd;
+void *mmaddr;
+static int nflow9_fprintf_header(FILE *fd, const struct netflow9_instance *ii);
+#endif
+
+/*
+ * This implementation sends NetFlow v9 entry only if ORIG or REPLY counter is
+ * greater than 0. Single NFCT entry contains duplex data, orig and reply but
+ * NetFlow v9 can represents simplex entry only, so that sigle NFCT entry may
+ * create two NetFlow v9 data entries. for example:
+ *
+ * 192.168.1.1 -> 172.16.1.1 will nat 1.1.1.1 -> 2.2.2.2
+ *
+ * NFCT:
+ * orig.ip.saddr 192.168.1.1
+ * orig.ip.daddr 172.16.1.1
+ * reply.ip.saddr 2.2.2.2
+ * reply.ip.daddr 1.1.1.1
+ * orig.raw.pktcount 111
+ * reply.raw.pktcount 222
+ *
+ * NFLOW9 entry1 entry2
+ * SRC_ADDR 192.168.1.1 172.16.1.1
+ * DST_ADDR 172.16.1.1 192.168.1.1
+ * XLATE_SRC_ADDR 1.1.1.1 2.2.2.2
+ * XLATE_DST_ADDR 2.2.2.2 1.1.1.1
+ * IN_PKTS 111 222
+ *
+ * then:
+ * orig.raw.pktcount.delta > 0: swap reply.*
+ * reply.raw.pktcount.delta > 0: swap orig.* and ifindex.
+ * invert flowDirection
+ *
+ * This means a NetFlow v9 entry has only one conter and same can be said to
+ * ip.protocol. Because of this, corksets_max should be greater than 3 since
+ * bidirectional handling above, and a template may be added.
+ *
+ * Note about NFCT:
+ * - To use same template, assume the number of keys starting with "orig." and
+ * "reply." is the same.
+ * - not handle both Count and DeltaCount, only either of them.
+ * NFCT plugin currently propagate Count even on polling mode. it's not
+ * suitable for this plugin, only Destroy NFCT event is acceptable I think.
+ *
+ * Example configuration and usage:
+ * minimum configuration - ulogd.conf
+ * [global]
+ * logfile="ulogd.log"
+ * loglevel=1
+ * plugin="/usr/local/lib/ulogd/ulogd_inpflow_NFCT.so"
+ * plugin="/usr/local/lib/ulogd/ulogd_filter_TIMECONV.so"
+ * plugin="/usr/local/lib/ulogd/ulogd_filter_PACKICMP.so"
+ * plugin="/usr/local/lib/ulogd/ulogd_output_NFLOW9.so"
+ * stack=ct:NFCT,timeconv:TIMECONV,packicmp:PACKICMP,nflow9:NFLOW9
+ *
+ * [ct]
+ * hash_enable=0
+ * event_mask=0x00000004
+ * [timeconv]
+ * [packicmp]
+ * [nflow9]
+ * dest=udp://127.0.0.1:9995
+ *
+ * run nfcapd (http://nfdump.sourceforge.net/)
+ * $ mkdir nfdata
+ * $ nfcapd -b 127.0.0.1 -l nfdata -T all
+ *
+ * run ulogd
+ * # ulogd -v -c ulogd.conf
+ *
+ * show flow data
+ * (wait for more than 5min or stop nfcapd by ^C)
+ * $ nfdump -o line -R nfdata | less -S
+ */
+
+/* index for ikey which needs special handling */
+enum {
+ CII_ORIG_RAW_PKTLEN_DELTA,
+ CII_ORIG_RAW_PKTCOUNT_DELTA,
+ CII_REPLY_RAW_PKTLEN_DELTA,
+ CII_REPLY_RAW_PKTCOUNT_DELTA,
+ CII_REPLY_IP_PROTOCOL, /* use only orig ip.protocol */
+ CII_FAMILY, /* illigal dirty hack */
+
+ /* XXX: will be removed if NFCT propagate delta counter */
+ CII_ORIG_RAW_PKTLEN,
+ CII_ORIG_RAW_PKTCOUNT,
+ CII_REPLY_RAW_PKTLEN,
+ CII_REPLY_RAW_PKTCOUNT,
+ CII_MAX,
+};
+
+static char *count_keys[] = {
+ [CII_ORIG_RAW_PKTLEN_DELTA] = "orig.raw.pktlen.delta",
+ [CII_ORIG_RAW_PKTCOUNT_DELTA] = "orig.raw.pktcount.delta",
+ [CII_REPLY_RAW_PKTLEN_DELTA] = "reply.raw.pktlen.delta",
+ [CII_REPLY_RAW_PKTCOUNT_DELTA] = "reply.raw.pktcount.delta",
+ [CII_REPLY_IP_PROTOCOL] = "reply.ip.protocol",
+ [CII_FAMILY] = "oob.family",
+
+ /* XXX: will be removed if NFCT propagate delta counter */
+ [CII_ORIG_RAW_PKTLEN] = "orig.raw.pktlen",
+ [CII_ORIG_RAW_PKTCOUNT] = "orig.raw.pktcount",
+ [CII_REPLY_RAW_PKTLEN] = "reply.raw.pktlen",
+ [CII_REPLY_RAW_PKTCOUNT] = "reply.raw.pktcount",
+};
+
+/* index for data field offset to swap by direction */
+enum {
+ FOI_ORIG_IP_SADDR = 0,
+ FOI_ORIG_IP_DADDR,
+ FOI_ORIG_IP6_SADDR,
+ FOI_ORIG_IP6_DADDR,
+ FOI_ORIG_L4_SPORT,
+ FOI_ORIG_L4_DPORT,
+ FOI_REPLY_IP_SADDR,
+ FOI_REPLY_IP_DADDR,
+ FOI_REPLY_IP6_SADDR,
+ FOI_REPLY_IP6_DADDR,
+ FOI_REPLY_L4_SPORT,
+ FOI_REPLY_L4_DPORT,
+ FOI_IF_INPUT,
+ FOI_IF_OUTPUT,
+ FOI_FLOW_DIR,
+ FOI_IN_BYTES,
+ FOI_IN_PKTS,
+ FOI_XXX_IN_BYTES,
+ FOI_XXX_IN_PKTS,
+ FOI_MAX,
+};
+
+static char *dir_keys[] = {
+ [FOI_ORIG_IP_SADDR] = "orig.ip.saddr",
+ [FOI_ORIG_IP_DADDR] = "orig.ip.daddr",
+ [FOI_ORIG_IP6_SADDR] = "orig.ip6.saddr",
+ [FOI_ORIG_IP6_DADDR] = "orig.ip6.daddr",
+ [FOI_ORIG_L4_SPORT] = "orig.l4.sport",
+ [FOI_ORIG_L4_DPORT] = "orig.l4.dport",
+ [FOI_REPLY_IP_SADDR] = "reply.ip.saddr",
+ [FOI_REPLY_IP_DADDR] = "reply.ip.daddr",
+ [FOI_REPLY_IP6_SADDR] = "reply.ip6.saddr",
+ [FOI_REPLY_IP6_DADDR] = "reply.ip6.daddr",
+ [FOI_REPLY_L4_SPORT] = "reply.l4.sport",
+ [FOI_REPLY_L4_DPORT] = "reply.l4.dport",
+ [FOI_IF_INPUT] = "oob.ifindex_in",
+ [FOI_IF_OUTPUT] = "oob.ifindex_out",
+ [FOI_FLOW_DIR] = "flow.direction",
+ [FOI_IN_BYTES] = "orig.raw.pktlen.delta",
+ [FOI_IN_PKTS] = "orig.raw.pktcount.delta",
+ /* XXX: will be removed if NFCT propagate delta counter */
+ [FOI_XXX_IN_BYTES] = "orig.raw.pktlen",
+ [FOI_XXX_IN_PKTS] = "orig.raw.pktcount",
+};
+
+enum {
+ NFLOW9_DIR_NONE = 0,
+ NFLOW9_DIR_ORIG = 1,
+ NFLOW9_DIR_REPLY = 2,
+ NFLOW9_DIR_BOTH = NFLOW9_DIR_ORIG | NFLOW9_DIR_REPLY,
+};
+
+enum {
+ NFLOW9_CONF_DEST,
+ NFLOW9_CONF_DOMAIN_ID,
+ NFLOW9_CONF_NTH_TEMPLATE,
+ NFLOW9_CONF_CORKSETS_MAX,
+ NFLOW9_CONF_MAX,
+};
+
+static struct config_keyset netflow9_kset = {
+ .num_ces = NFLOW9_CONF_MAX,
+ .ces = {
+ [NFLOW9_CONF_DEST] = {
+ .key = "dest",
+ .type = CONFIG_TYPE_STRING,
+ .options = CONFIG_OPT_NONE,
+ .u = { .string = "udp://localhost:9996" },
+ },
+ [NFLOW9_CONF_DOMAIN_ID] = {
+ .key = "domain_id",
+ .type = CONFIG_TYPE_INT,
+ .options = CONFIG_OPT_NONE,
+ .u.value = 0,
+ },
+ [NFLOW9_CONF_NTH_TEMPLATE] = {
+ .key = "nth_template",
+ .type = CONFIG_TYPE_INT,
+ .options = CONFIG_OPT_NONE,
+ .u.value = 16,
+ },
+ [NFLOW9_CONF_CORKSETS_MAX] = {
+ .key = "corksets_max",
+ .type = CONFIG_TYPE_INT,
+ .options = CONFIG_OPT_NONE,
+ .u.value = 20,
+ },
+ },
+};
+
+#define dest_ce(x) ((x)->config_kset->ces[NFLOW9_CONF_DEST].u.string)
+#define domain_ce(x) ((x)->config_kset->ces[NFLOW9_CONF_DOMAIN_ID].u.value)
+#define nth_template_ce(x) ((x)->config_kset->ces[NFLOW9_CONF_NTH_TEMPLATE].u.value)
+#define corksets_max_ce(x) ((x)->config_kset->ces[NFLOW9_CONF_CORKSETS_MAX].u.value)
+
+/* Section 5.1 */
+struct netflow9_msg_hdr {
+ uint16_t version;
+ uint16_t count;
+ uint32_t sys_uptime;
+ uint32_t unix_secs;
+ uint32_t sequence_number;
+ uint32_t source_id;
+};
+
+/* Section 5.2, 5.3 */
+struct netflow9_set_hdr {
+ uint16_t set_id;
+ uint16_t length;
+};
+
+/* Section 5.2 */
+struct netflow9_templ_hdr {
+ uint16_t template_id;
+ uint16_t field_count;
+};
+
+/* Section 5.2 */
+struct netflow9_templ_rec {
+ uint16_t type;
+ uint16_t length;
+};
+
+/* 8. Field Type Definitions octet (or default)*/
+enum {
+ NETFLOW9_IN_BYTES = 1, /* (4) octetDeltaCount */
+ NETFLOW9_IN_PKTS = 2, /* (4) packetDeltaCount */
+ NETFLOW9_FLOWS = 3, /* (4) */
+ NETFLOW9_PROTOCOL = 4, /* 1 protocolIdentifier */
+ NETFLOW9_TOS = 5, /* 1 classOfServiceIPv4 */
+ NETFLOW9_TCP_FLAGS = 6, /* 1 tcpControlBits */
+ NETFLOW9_L4_SRC_PORT = 7, /* 2 sourceTransportPort */
+ NETFLOW9_IPV4_SRC_ADDR = 8, /* 4 sourceIPv4Address */
+ NETFLOW9_SRC_MASK = 9, /* 1 sourceIPv4Mask */
+ NETFLOW9_INPUT_SNMP = 10, /* (2) ingressInterface */
+ NETFLOW9_L4_DST_PORT = 11, /* 2 destinationTransportPort */
+ NETFLOW9_IPV4_DST_ADDR = 12, /* 4 destinationIPv4Address */
+ NETFLOW9_DST_MASK = 13, /* 1 destinationIPv4Mask */
+ NETFLOW9_OUTPUT_SNMP = 14, /* (2) egressInterface */
+ NETFLOW9_IPV4_NEXT_HOP = 15, /* 4 ipNextHopIPv4Address */
+ NETFLOW9_SRC_AS = 16, /* (2) bgpSourceAsNumber */
+ NETFLOW9_DST_AS = 17, /* (2) bgpDestinationAsNumber */
+ NETFLOW9_BGP_IPV4_NEXT_HOP = 18, /* 4 bgpNextHopIPv4Address */
+ NETFLOW9_MUL_DST_PKTS = 19, /* (4) postMCastPacketDeltaCount */
+ NETFLOW9_MUL_DST_BYTES = 20, /* (4) postMCastOctetDeltaCount */
+ NETFLOW9_LAST_SWITCHED = 21, /* 4 flowEndSysUpTime */
+ NETFLOW9_FIRST_SWITCHED = 22, /* 4 flowStartSysUpTime */
+ NETFLOW9_OUT_BYTES = 23, /* (4) postOctetDeltaCount */
+ NETFLOW9_OUT_PKTS = 24, /* (4) postPacketDeltaCount */
+ /* reserved */
+ /* reserved */
+ NETFLOW9_IPV6_SRC_ADDR = 27, /* 16 sourceIPv6Address */
+ NETFLOW9_IPV6_DST_ADDR = 28, /* 16 destinationIPv6Address */
+ NETFLOW9_IPV6_SRC_MASK = 29, /* 1 sourceIPv6Mask */
+ NETFLOW9_IPV6_DST_MASK = 30, /* 1 destinationIPv6Mask */
+ NETFLOW9_FLOW_LABEL = 31, /* 3 flowLabelIPv6 */
+ NETFLOW9_ICMP_TYPE = 32, /* 2 icmpTypeCodeIPv4 */
+ NETFLOW9_MUL_IGMP_TYPE = 33, /* 1 igmpType */
+ NETFLOW9_SAMPLING_INTERVAL = 34, /* 4 */
+ /* reserved */
+ NETFLOW9_SAMPLING_ALGORITHM = 35, /* 1 */
+ NETFLOW9_FLOW_ACTIVE_TIMEOUT = 36, /* 2 flowActiveTimeOut */
+ NETFLOW9_FLOW_INAVTIVE_TIMEOUT = 37, /* 2 flowInactiveTimeout */
+ NETFLOW9_ENGINE_TYPE = 38, /* 1 */
+ NETFLOW9_ENGINE_ID = 39, /* 1 */
+ NETFLOW9_TOTAL_BYTES_EXP = 40, /* (4) exportedOctetTotalCount */
+ NETFLOW9_TOTAL_PKTS_EXP = 41, /* (4) exportedMessageTotalCount */
+ NETFLOW9_TOTAL_FLOWS_EXP = 42, /* (4) exportedFlowTotalCount */
+ /* reserved */
+ /* reserved */
+ /* reserved */
+ NETFLOW9_MPLS_TOP_LABEL_TYPE = 46, /* 1 mplsTopLabelType */
+ NETFLOW9_MPLS_TOP_LABEL_IP_ADDR = 47, /* 4 mplsTopLabelIPv4Address */
+ NETFLOW9_FLOW_SAMPLER_ID = 48, /* 1 */
+ NETFLOW9_FLOW_SAMPLER_MODE = 49, /* 1 */
+ NETFLOW9_FLOW_SAMPLER_RANDOM_INTERVAL = 50, /* 4 */
+ /* reserved */
+ /* reserved */
+ /* reserved */
+ /* reserved */
+ NETFLOW9_DST_TOS = 55, /* 1 postClassOfServiceIPv4 */
+ NETFLOW9_SRC_MAC = 56, /* 6 sourceMacAddress */
+ NETFLOW9_DST_MAC = 57, /* 6 postDestinationMacAddr */
+ NETFLOW9_SRC_VLAN = 58, /* 2 vlanId */
+ NETFLOW9_DST_VLAN = 59, /* 2 postVlanId */
+ NETFLOW9_IP_PROTOCOL_VERSION = 60, /* 1 ipVersion */
+ NETFLOW9_DIRECTION = 61, /* 1 flowDirection */
+ NETFLOW9_IPV6_NEXT_HOP = 62, /* 16 ipNextHopIPv6Address */
+ NETFLOW9_BGP_IPV6_NEXT_HOP = 63, /* 16 bgpNexthopIPv6Address */
+ NETFLOW9_IPV6_OPTION_HEADERS = 64, /* 4 ipv6ExtensionHeaders */
+ /* reserved */
+ /* reserved */
+ /* reserved */
+ /* reserved */
+ /* reserved */
+ NETFLOW9_MPLS_LABEL_1 = 70, /* 3 mplsTopLabelStackEntry */
+ NETFLOW9_MPLS_LABEL_2 = 71, /* 3 mplsLabelStackEntry2 */
+ NETFLOW9_MPLS_LABEL_3 = 72, /* 3 mplsLabelStackEntry3 */
+ NETFLOW9_MPLS_LABEL_4 = 73, /* 3 mplsLabelStackEntry4 */
+ NETFLOW9_MPLS_LABEL_5 = 74, /* 3 mplsLabelStackEntry5 */
+ NETFLOW9_MPLS_LABEL_6 = 75, /* 3 mplsLabelStackEntry6 */
+ NETFLOW9_MPLS_LABEL_7 = 76, /* 3 mplsLabelStackEntry7 */
+ NETFLOW9_MPLS_LABEL_8 = 77, /* 3 mplsLabelStackEntry8 */
+ NETFLOW9_MPLS_LABEL_9 = 78, /* 3 mplsLabelStackEntry9 */
+ NETFLOW9_MPLS_LABEL_10 = 79, /* 3 mplsLabelStackEntry10 */
+
+ /* pick up usefuls from:
+ * http://www.cisco.com/c/en/us/td/docs/security/asa/special/netflow/guide/asa_netflow.html */
+ NETFLOW9_IPV4_XLATE_SRC_ADDR = 225, /* 4 NF_F_XLATE_SRC_ADDR_IPV4 */
+ NETFLOW9_IPV4_XLATE_DST_ADDR = 226, /* 4 NF_F_XLATE_DST_ADDR_IPV4 */
+ NETFLOW9_L4_XLATE_SRC_PORT = 227, /* 2 NF_F_XLATE_SRC_PORT */
+ NETFLOW9_L4_XLATE_DST_PORT = 228, /* 2 NF_F_XLATE_DST_PORT */
+ NETFLOW9_IPV6_XLATE_SRC_ADDR = 281, /* 16 NF_F_XLATE_SRC_ADDR_IPV6 */
+ NETFLOW9_IPV6_XLATE_DST_ADDR = 282, /* 16 NF_F_XLATE_DST_ADDR_IPV6 */
+
+ NETFLOW9_FIELD_MAX = NETFLOW9_IPV6_XLATE_DST_ADDR,
+};
+
+static int ipfix_map[] = {
+ /* XXX: current NFCT does not propagate delta count
+ * [IPFIX_octetDeltaCount] = NETFLOW9_IN_BYTES,
+ * [IPFIX_packetDeltaCount] = NETFLOW9_IN_PKTS,
+ */
+ [IPFIX_octetTotalCount] = NETFLOW9_IN_BYTES,
+ [IPFIX_packetTotalCount] = NETFLOW9_IN_PKTS,
+ /* [3] = NETFLOW9_FLOWS, */
+ [IPFIX_protocolIdentifier] = NETFLOW9_PROTOCOL,
+ [IPFIX_classOfServiceIPv4] = NETFLOW9_TOS,
+ [IPFIX_tcpControlBits] = NETFLOW9_TCP_FLAGS,
+ [IPFIX_sourceTransportPort] = NETFLOW9_L4_SRC_PORT,
+ [IPFIX_sourceIPv4Address] = NETFLOW9_IPV4_SRC_ADDR,
+ [IPFIX_sourceIPv4Mask] = NETFLOW9_SRC_MASK,
+ [IPFIX_ingressInterface] = NETFLOW9_INPUT_SNMP,
+ [IPFIX_destinationTransportPort] = NETFLOW9_L4_DST_PORT,
+ [IPFIX_destinationIPv4Address] = NETFLOW9_IPV4_DST_ADDR,
+ [IPFIX_destinationIPv4Mask] = NETFLOW9_DST_MASK,
+ [IPFIX_egressInterface] = NETFLOW9_OUTPUT_SNMP,
+ [IPFIX_ipNextHopIPv4Address] = NETFLOW9_IPV4_NEXT_HOP,
+ [IPFIX_bgpSourceAsNumber] = NETFLOW9_SRC_AS,
+ [IPFIX_bgpDestinationAsNumber] = NETFLOW9_DST_AS,
+ [IPFIX_bgpNextHopIPv4Address] = NETFLOW9_BGP_IPV4_NEXT_HOP,
+ [IPFIX_postMCastPacketDeltaCount] = NETFLOW9_MUL_DST_PKTS,
+ [IPFIX_postMCastOctetDeltaCount] = NETFLOW9_MUL_DST_BYTES,
+ [IPFIX_flowEndSysUpTime] = NETFLOW9_LAST_SWITCHED,
+ [IPFIX_flowStartSysUpTime] = NETFLOW9_FIRST_SWITCHED,
+ [IPFIX_postOctetDeltaCount] = NETFLOW9_OUT_BYTES,
+ [IPFIX_postPacketDeltaCount] = NETFLOW9_OUT_PKTS,
+ [IPFIX_minimumPacketLength] = 0,
+ [IPFIX_maximumPacketLength] = 0,
+ [IPFIX_sourceIPv6Address] = NETFLOW9_IPV6_SRC_ADDR,
+ [IPFIX_destinationIPv6Address] = NETFLOW9_IPV6_DST_ADDR,
+ [IPFIX_sourceIPv6Mask] = NETFLOW9_IPV6_SRC_MASK,
+ [IPFIX_destinationIPv6Mask] = NETFLOW9_IPV6_DST_MASK,
+ [IPFIX_flowLabelIPv6] = NETFLOW9_FLOW_LABEL,
+ [IPFIX_icmpTypeCodeIPv4] = NETFLOW9_ICMP_TYPE,
+ [IPFIX_igmpType] = NETFLOW9_MUL_IGMP_TYPE,
+ /* [34] = [NETFLOW9_SAMPLING_INTERVAL], */
+ /* [35] = [NETFLOW9_SAMPLING_ALGORITHM],*/
+ [IPFIX_flowActiveTimeOut] = NETFLOW9_FLOW_ACTIVE_TIMEOUT,
+ [IPFIX_flowInactiveTimeout] = NETFLOW9_FLOW_INAVTIVE_TIMEOUT,
+ /* [38] = NETFLOW9_ENGINE_TYPE, */
+ /* [39] = NETFLOW9_ENGINE_ID, */
+ [IPFIX_exportedOctetTotalCount] = NETFLOW9_TOTAL_BYTES_EXP,
+ [IPFIX_exportedMessageTotalCount] = NETFLOW9_TOTAL_PKTS_EXP,
+ [IPFIX_exportedFlowTotalCount] = NETFLOW9_TOTAL_FLOWS_EXP,
+ /* [43] = , */
+ [IPFIX_sourceIPv4Prefix] = 0,
+ [IPFIX_destinationIPv4Prefix] = 0,
+ [IPFIX_mplsTopLabelType] = NETFLOW9_MPLS_TOP_LABEL_TYPE,
+ [IPFIX_mplsTopLabelIPv4Address] = NETFLOW9_MPLS_TOP_LABEL_IP_ADDR,
+ /* [48] = NETFLOW9_FLOW_SAMPLER_ID, */
+ /* [49] = NETFLOW9_FLOW_SAMPLER_MODE, */
+ /* [50] = NETFLOW9_FLOW_SAMPLER_RANDOM_INTERVAL, */
+ /* [51] = , */
+ [IPFIX_minimumTtl] = 0,
+ [IPFIX_maximumTtl] = 0,
+ [IPFIX_identificationIPv4] = 0,
+ [IPFIX_postClassOfServiceIPv4] = NETFLOW9_DST_TOS,
+ [IPFIX_sourceMacAddress] = NETFLOW9_SRC_MAC,
+ [IPFIX_postDestinationMacAddr] = NETFLOW9_DST_MAC,
+ [IPFIX_vlanId] = NETFLOW9_SRC_VLAN,
+ [IPFIX_postVlanId] = NETFLOW9_DST_VLAN,
+ [IPFIX_ipVersion] = NETFLOW9_IP_PROTOCOL_VERSION,
+ [IPFIX_flowDirection] = NETFLOW9_DIRECTION,
+ [IPFIX_ipNextHopIPv6Address] = NETFLOW9_IPV6_NEXT_HOP,
+ [IPFIX_bgpNexthopIPv6Address] = NETFLOW9_BGP_IPV6_NEXT_HOP,
+ [IPFIX_ipv6ExtensionHeaders] = NETFLOW9_IPV6_OPTION_HEADERS,
+ /* [65] = , */
+ /* [66] = , */
+ /* [67] = , */
+ /* [68] = , */
+ /* [69] = , */
+ [IPFIX_mplsTopLabelStackEntry] = NETFLOW9_MPLS_LABEL_1,
+ [IPFIX_mplsLabelStackEntry2] = NETFLOW9_MPLS_LABEL_2,
+ [IPFIX_mplsLabelStackEntry3] = NETFLOW9_MPLS_LABEL_3,
+ [IPFIX_mplsLabelStackEntry4] = NETFLOW9_MPLS_LABEL_4,
+ [IPFIX_mplsLabelStackEntry5] = NETFLOW9_MPLS_LABEL_5,
+ [IPFIX_mplsLabelStackEntry6] = NETFLOW9_MPLS_LABEL_6,
+ [IPFIX_mplsLabelStackEntry7] = NETFLOW9_MPLS_LABEL_7,
+ [IPFIX_mplsLabelStackEntry8] = NETFLOW9_MPLS_LABEL_8,
+ [IPFIX_mplsLabelStackEntry9] = NETFLOW9_MPLS_LABEL_9,
+ [IPFIX_mplsLabelStackEntry10] = NETFLOW9_MPLS_LABEL_10,
+ /* [80 - 224] = , */
+ [IPFIX_postNATSourceIPv4Address] = NETFLOW9_IPV4_XLATE_SRC_ADDR,
+ [IPFIX_postNATDestinationIPv4Address] = NETFLOW9_IPV4_XLATE_DST_ADDR,
+ [IPFIX_postNAPTSourceTransportPort] = NETFLOW9_L4_XLATE_SRC_PORT,
+ [IPFIX_postNAPTDestinationTransportPort]= NETFLOW9_L4_XLATE_DST_PORT,
+ [IPFIX_postNATSourceIPv6Address] = NETFLOW9_IPV6_XLATE_SRC_ADDR,
+ [IPFIX_postNATDestinationIPv6Address] = NETFLOW9_IPV6_XLATE_DST_ADDR,
+};
+
+struct ulogd_netflow9_template {
+ struct llist_head list;
+ struct nfct_bitmask *bitmask;
+ int until_template; /* decide if it's time to retransmit template */
+ int offset[FOI_MAX]; /* direction related field offset from data head */
+ int tmplset_len, dataset_len;
+ struct netflow9_set_hdr *template;
+ struct netflow9_set_hdr *databuf;
+ int datapos;
+};
+
+struct netflow9_instance {
+ int fd; /* socket that we use for sending NetFlow v9 data */
+ int uptime_fd; /* /proc/uptime to set sysUpTime */
+ uint16_t next_template_id;
+ struct llist_head template_list; /* ulogd_netflow9_template */
+ struct nfct_bitmask *valid_bitmask; /* bitmask of valid keys */
+ uint32_t seq;
+ unsigned int ikey_count[CII_MAX]; /* ikey indexes to counter fields */
+ struct netflow9_msg_hdr nflow9_msghdr;
+ struct iovec *iovecs; /* index 0 is reserved for nflow9_msghdr */
+ unsigned int iovcnt;
+ unsigned int corksets_max; /* cork limit include template */
+ unsigned int msglen;
+};
+
+#define UPTIME_FILE "/proc/uptime" /* for uptime_fd */
+#define ULOGD_NETFLOW9_TEMPL_BASE 256 /* 5.2 Template FlowSet Format
+ * for next_template_id */
+/*
+ * This function returns file or connected socket descriptor
+ * specified by URL like dest:
+ * <proto>://<filename or address>[:port]
+ * proto is either one of tcp, udp, sctp and file. port is required
+ * in case of socket. file will be stdout if proto is file and
+ * no filename specified.
+ */
+static int open_connect_descriptor(const char *dest)
+{
+ char *proto = NULL, *host, *port;
+ struct addrinfo hint, *result = NULL, *rp = NULL;
+ int ret, fd = -1;
+
+ proto = strdup(dest);
+ if (proto == NULL) {
+ ulogd_log(ULOGD_ERROR, "strdup: %s\n", strerror(errno));
+ return -1;
+ }
+ host = strchr(proto, ':');
+ if (host == NULL) {
+ ulogd_log(ULOGD_ERROR, "invalid dest\n");
+ goto error;
+ }
+ *host++ = '\0';
+ if (*host++ != '/') {
+ ulogd_log(ULOGD_ERROR, "invalid dest\n");
+ goto error;
+ }
+ if (*host++ != '/') {
+ ulogd_log(ULOGD_ERROR, "invalid dest\n");
+ goto error;
+ }
+
+ /* file */
+ if (!strcasecmp(proto, "file")) {
+ if (strlen(host) == 0)
+ fd = STDOUT_FILENO;
+ else
+ fd = open(host, O_CREAT|O_WRONLY|O_APPEND, 0600);
+ free(proto);
+ return fd;
+ }
+
+ /* socket */
+ port = strrchr(host, ':');
+ if (port == NULL) {
+ ulogd_log(ULOGD_ERROR, "no destination port\n");
+ goto error;
+ }
+ *port++ = '\0';
+
+ memset(&hint, 0, sizeof(struct addrinfo));
+ hint.ai_family = AF_UNSPEC;
+ if (!strcasecmp(proto, "udp")) {
+ hint.ai_socktype = SOCK_DGRAM;
+ hint.ai_protocol = IPPROTO_UDP;
+ } else if (!strcasecmp(proto, "tcp")) {
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_protocol = IPPROTO_TCP;
+ } else {
+ ulogd_log(ULOGD_ERROR, "unknown protocol `%s'\n",
+ proto);
+ goto error;
+ }
+
+ ret = getaddrinfo(host, port, &hint, &result);
+ if (ret != 0) {
+ ulogd_log(ULOGD_ERROR, "can't resolve host/service: %s\n",
+ gai_strerror(ret));
+ if (ret != EAI_SYSTEM)
+ errno = EINVAL;
+ goto error;
+ }
+
+ /* rp == NULL indicates could not get valid sockfd */
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ int on = 1;
+
+ fd = socket(rp->ai_family, rp->ai_socktype,
+ rp->ai_protocol);
+ if (fd == -1) {
+ switch (errno) {
+ case EACCES:
+ case EAFNOSUPPORT:
+ case EINVAL:
+ case EPROTONOSUPPORT:
+ /* try next result */
+ continue;
+ default:
+ ulogd_log(ULOGD_ERROR, "socket error: %s\n",
+ strerror(errno));
+ rp = NULL;
+ goto error;
+ }
+ }
+ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&on, sizeof(on));
+ if (ret < 0) {
+ ulogd_log(ULOGD_ERROR, "error on set SO_REUSEADDR: %s",
+ strerror(errno));
+ close(fd);
+ rp = NULL;
+ break;
+ }
+
+ if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0)
+ break;
+ close(fd);
+ }
+
+error:
+ if (proto)
+ free(proto);
+ if (result)
+ freeaddrinfo(result);
+
+ if (rp == NULL) {
+ ulogd_log(ULOGD_ERROR, "could not connect\n");
+ fd = -1;
+ }
+
+ return fd;
+}
+
+/*
+ * This functions stores ulogd key value, specifued by key into
+ * buf. buflen means buf len and is checked exceeds. This function
+ * returns the copied length or -1 on error.
+ */
+static int ulogd_key_putn(struct ulogd_key *key, void *buf, int buflen)
+{
+ int ret = -1;
+
+ switch (key->type) {
+ case ULOGD_RET_INT8:
+ case ULOGD_RET_UINT8:
+ case ULOGD_RET_BOOL:
+ ret = sizeof(uint8_t);
+ if (buflen - ret >= 0)
+ *(uint8_t *)buf = ikey_get_u8(key);
+ break;
+ case ULOGD_RET_INT16:
+ case ULOGD_RET_UINT16:
+ ret = sizeof(uint16_t);
+ if (buflen - ret >= 0)
+ *(uint16_t *)buf = htons(ikey_get_u16(key));
+ break;
+ case ULOGD_RET_INT32:
+ case ULOGD_RET_UINT32:
+ ret = sizeof(uint32_t);
+ if (buflen - ret >= 0)
+ *(uint32_t *)buf = htonl(ikey_get_u32(key));
+ break;
+ case ULOGD_RET_IPADDR:
+ ret = sizeof(uint32_t);
+ if (buflen - ret >= 0)
+ *(uint32_t *)buf = ikey_get_u32(key);
+ break;
+ case ULOGD_RET_INT64:
+ case ULOGD_RET_UINT64:
+ ret = sizeof(uint64_t);
+ if (buflen - ret >= 0)
+ *(uint64_t *)buf = __be64_to_cpu(ikey_get_u64(key));
+ break;
+ case ULOGD_RET_IP6ADDR:
+ ret = 16;
+ if (buflen - ret >= 0)
+ memcpy(buf, ikey_get_u128(key), 16);
+ break;
+ case ULOGD_RET_STRING:
+ ret = strlen(key->u.value.ptr);
+ if (buflen - ret >= 0)
+ memcpy(buf, key->u.value.ptr, ret);
+ break;
+ case ULOGD_RET_RAW:
+ ulogd_log(ULOGD_NOTICE, "put raw data in network byte order "
+ "`%s' type 0x%x\n", key->name, key->type);
+ ret = key->len;
+ if (buflen - ret >= 0)
+ memcpy(buf, key->u.value.ptr, ret);
+ break;
+ default:
+ ulogd_log(ULOGD_ERROR, "unknown size - key "
+ "`%s' type 0x%x\n", key->name, key->type);
+ return -1;
+ break;
+ }
+
+ if (buflen < 0)
+ ulogd_log(ULOGD_ERROR, "excess buflen, do nothing.\n");
+
+ return ret;
+}
+
+static struct ulogd_netflow9_template *
+alloc_ulogd_netflow9_template(struct ulogd_pluginstance *upi,
+ struct nfct_bitmask *bm)
+{
+ struct netflow9_instance *ii = (struct netflow9_instance *)&upi->private;
+ struct ulogd_netflow9_template *tmpl;
+ unsigned int i;
+ int tmpl_len = 0, data_len = 0;
+
+ for (i = 0; i < upi->input.num_keys; i++) {
+ if (!nfct_bitmask_test_bit(bm, i))
+ continue;
+
+ /* ignore reply for unidirection */
+ if (i == ii->ikey_count[CII_REPLY_RAW_PKTLEN_DELTA]
+ || i == ii->ikey_count[CII_REPLY_RAW_PKTCOUNT_DELTA]
+ || i == ii->ikey_count[CII_REPLY_IP_PROTOCOL]
+ /* XXX: will be removed if NFCT propagate delta counter */
+ || i == ii->ikey_count[CII_REPLY_RAW_PKTLEN]
+ || i == ii->ikey_count[CII_REPLY_RAW_PKTCOUNT])
+ continue;
+
+ tmpl_len += sizeof(struct netflow9_templ_rec);
+ data_len += ulogd_key_size(&upi->input.keys[i]);
+ }
+
+ tmpl = calloc(1, sizeof(struct ulogd_netflow9_template));
+ if (tmpl == NULL)
+ return NULL;
+
+ for (i = 0; i < FOI_MAX; i++)
+ tmpl->offset[i] = -1;
+
+ tmpl->bitmask = nfct_bitmask_clone(bm);
+ if (!tmpl->bitmask)
+ goto free_tmpl;
+
+ tmpl->dataset_len = sizeof(struct netflow9_set_hdr) + data_len;
+ tmpl->tmplset_len = sizeof(struct netflow9_set_hdr)
+ + sizeof(struct netflow9_templ_hdr) + tmpl_len;
+ /* 5.3. Data FlowSet Format / Padding */
+ tmpl->dataset_len = (tmpl->dataset_len + 3U) & ~3U;
+ tmpl->tmplset_len = (tmpl->tmplset_len + 3U) & ~3U;
+
+ tmpl->template = calloc(1, tmpl->tmplset_len);
+ if (tmpl->template == NULL)
+ goto free_bitmask;
+ tmpl->databuf = calloc(ii->corksets_max, tmpl->dataset_len);
+ if (tmpl->databuf == NULL)
+ goto free_template;
+
+ return tmpl;
+
+free_template:
+ free(tmpl->template);
+free_bitmask:
+ free(tmpl->bitmask);
+free_tmpl:
+ free(tmpl);
+
+ return NULL;
+}
+
+/* Build the NetFlow v9 template from the input keys */
+static struct ulogd_netflow9_template *
+build_template_for_bitmask(struct ulogd_pluginstance *upi,
+ struct nfct_bitmask *bm)
+{
+ struct netflow9_instance *ii
+ = (struct netflow9_instance *)&upi->private;
+ struct ulogd_netflow9_template *tmpl;
+ struct netflow9_templ_hdr *tmpl_hdr;
+ struct netflow9_templ_rec *tmpl_rec;
+ struct netflow9_set_hdr *set_hdr;
+ uint16_t field_count = 0;
+ unsigned int i, j, offset = 0;
+
+ tmpl = alloc_ulogd_netflow9_template(upi, bm);
+ if (tmpl == NULL)
+ return NULL;
+
+ /* build template records */
+ tmpl_rec = (void *)tmpl->template
+ + sizeof(struct netflow9_set_hdr)
+ + sizeof(struct netflow9_templ_hdr);
+ for (i = 0; i < upi->input.num_keys; i++) {
+ struct ulogd_key *key = &upi->input.keys[i];
+ int length = ulogd_key_size(key);
+
+ if (!nfct_bitmask_test_bit(tmpl->bitmask, i))
+ continue;
+
+ /* XXX: search swap related field and set its offset */
+ for (j = 0; j < FOI_MAX; j++) {
+ if (!strncmp(key->name, dir_keys[j],
+ strlen(dir_keys[j]))) {
+ tmpl->offset[j] = offset;
+ break;
+ }
+ }
+
+ if (i == ii->ikey_count[CII_REPLY_RAW_PKTLEN_DELTA]
+ || i == ii->ikey_count[CII_REPLY_RAW_PKTCOUNT_DELTA]
+ || i == ii->ikey_count[CII_REPLY_IP_PROTOCOL]
+ /* XXX: will be removed if NFCT propagate delta counter */
+ || i == ii->ikey_count[CII_REPLY_RAW_PKTLEN]
+ || i == ii->ikey_count[CII_REPLY_RAW_PKTCOUNT])
+ continue;
+
+ tmpl_rec->type = htons(ipfix_map[key->ipfix.field_id]);
+ tmpl_rec->length = htons(length);
+ tmpl_rec++;
+ field_count++;
+ offset += length;
+ }
+
+ /* initialize template set header */
+ tmpl->template->set_id = htons(0); /* 5.2 Template FlowSet Format */
+ tmpl->template->length = htons(tmpl->tmplset_len);
+
+ /* initialize template record header */
+ tmpl_hdr = (void *)tmpl->template + sizeof(struct netflow9_set_hdr);
+ tmpl_hdr->template_id = htons(ii->next_template_id++);
+ tmpl_hdr->field_count = htons(field_count);
+
+ /* initialize data buffer */
+ for (i = 0; i < ii->corksets_max; i++) {
+ set_hdr = (void *)tmpl->databuf + i * tmpl->dataset_len;
+ set_hdr->set_id = tmpl_hdr->template_id;
+ set_hdr->length = htons(tmpl->dataset_len);
+ }
+
+ return tmpl;
+}
+
+static struct ulogd_netflow9_template *
+find_template_for_bitmask(struct ulogd_pluginstance *upi,
+ struct nfct_bitmask *bm)
+{
+ struct netflow9_instance *ii
+ = (struct netflow9_instance *)&upi->private;
+ struct ulogd_netflow9_template *tmpl;
+
+ /* FIXME: this can be done more efficient! */
+ llist_for_each_entry(tmpl, &ii->template_list, list) {
+ if (nfct_bitmask_equal(bm, tmpl->bitmask))
+ return tmpl;
+ }
+
+ return NULL;
+}
+
+static int put_data_records(struct ulogd_pluginstance *upi,
+ struct ulogd_netflow9_template *tmpl,
+ void *buf, int buflen)
+{
+ struct netflow9_instance *ii
+ = (struct netflow9_instance *)&upi->private;
+ struct ulogd_key *keys = upi->input.keys;
+ unsigned int i;
+ int ret, len = 0;
+
+ for (i = 0; i < upi->input.num_keys; i++) {
+ if (!nfct_bitmask_test_bit(tmpl->bitmask, i))
+ continue;
+
+ /* store orig temporarily to (unidirectional) counter */
+ if (i == ii->ikey_count[CII_REPLY_RAW_PKTLEN_DELTA]
+ || i == ii->ikey_count[CII_REPLY_RAW_PKTCOUNT_DELTA]
+ || i == ii->ikey_count[CII_REPLY_IP_PROTOCOL]
+ /* XXX: will be removed if NFCT propagate delta counter */
+ || i == ii->ikey_count[CII_REPLY_RAW_PKTLEN]
+ || i == ii->ikey_count[CII_REPLY_RAW_PKTCOUNT])
+ continue;
+
+ ret = ulogd_key_putn(&keys[i], buf + len, buflen);
+ if (ret < 0)
+ return ret;
+
+ len += ret;
+ buflen -= ret;
+ if (buflen < 0)
+ return buflen;
+ }
+
+ return len;
+}
+
+static void swap(void *data, ssize_t size, int pos1, int pos2)
+{
+ uint8_t tmp[16] = {}; /* 16: ip6 addr len */
+ memcpy(tmp, data + pos1, size);
+ memcpy(data + pos1, data +pos2, size);
+ memcpy(data + pos2, tmp, size);
+}
+
+#define TOF(i) tmpl->offset[(i)]
+
+static int orig_swap(struct ulogd_netflow9_template *tmpl,
+ uint8_t family, void *buf)
+{
+ switch (family) {
+ case AF_INET:
+ swap(buf, sizeof(uint32_t),
+ TOF(FOI_REPLY_IP_SADDR), TOF(FOI_REPLY_IP_DADDR));
+ break;
+ case AF_INET6:
+ swap(buf, sizeof(struct in6_addr),
+ TOF(FOI_REPLY_IP6_SADDR), TOF(FOI_REPLY_IP6_DADDR));
+ break;
+ default:
+ ulogd_log(ULOGD_ERROR, "unknown family: %d", family);
+ return -1;
+ }
+ if (TOF(FOI_REPLY_L4_SPORT) >= 0
+ && TOF(FOI_REPLY_L4_DPORT) >= 0)
+ swap(buf, sizeof(uint16_t),
+ TOF(FOI_REPLY_L4_SPORT), TOF(FOI_REPLY_L4_DPORT));
+
+ return 0;
+}
+
+static int reply_swap(struct ulogd_netflow9_template *tmpl,
+ uint8_t family, void *buf)
+{
+ switch (family) {
+ case AF_INET:
+ swap(buf, sizeof(uint32_t),
+ TOF(FOI_ORIG_IP_SADDR), TOF(FOI_ORIG_IP_DADDR));
+ break;
+ case AF_INET6:
+ swap(buf, sizeof(struct in6_addr),
+ TOF(FOI_ORIG_IP_SADDR), TOF(FOI_ORIG_IP_DADDR));
+ break;
+ default:
+ ulogd_log(ULOGD_ERROR, "unknown family: %d", family);
+ return -1;
+ }
+ if (TOF(FOI_ORIG_L4_SPORT) >= 0
+ && TOF(FOI_ORIG_L4_DPORT) >= 0)
+ swap(buf, sizeof(uint16_t),
+ TOF(FOI_ORIG_L4_SPORT), TOF(FOI_ORIG_L4_DPORT));
+ if (TOF(FOI_IF_INPUT) >= 0 && TOF(FOI_IF_OUTPUT) >= 0)
+ swap(buf, sizeof(uint16_t),
+ TOF(FOI_IF_INPUT), TOF(FOI_IF_OUTPUT));
+ if (TOF(FOI_FLOW_DIR) >= 0)
+ *(uint8_t *)(buf + TOF(FOI_FLOW_DIR))
+ = !*(uint8_t *)(buf + TOF(FOI_FLOW_DIR));
+
+ return 0;
+}
+
+static int swap_by_dir(struct ulogd_netflow9_template *tmpl,
+ void *buf, uint8_t family,
+ int direction,
+ uint64_t bytes, uint64_t packets)
+{
+ switch (direction) {
+ case NFLOW9_DIR_ORIG:
+ if (orig_swap(tmpl, family, buf) < 0)
+ return -1;
+ break;
+
+ case NFLOW9_DIR_REPLY:
+ if (reply_swap(tmpl, family, buf) < 0)
+ return -1;
+ break;
+ default:
+ ulogd_log(ULOGD_ERROR, "unknown dir: %d", direction);
+ return -1;
+ }
+
+ if (TOF(FOI_IN_BYTES) >= 0)
+ *(uint64_t *)(buf + TOF(FOI_IN_BYTES)) = __cpu_to_be64(bytes);
+ if (TOF(FOI_IN_PKTS) >= 0)
+ *(uint64_t *)(buf + TOF(FOI_IN_PKTS)) = __cpu_to_be64(packets);
+ /* XXX: will be removed if NFCT propagate delta counter */
+ if (TOF(FOI_XXX_IN_BYTES) >= 0)
+ *(uint64_t *)(buf + TOF(FOI_XXX_IN_BYTES)) = __cpu_to_be64(bytes);
+ if (TOF(FOI_XXX_IN_PKTS) >= 0)
+ *(uint64_t *)(buf + TOF(FOI_XXX_IN_PKTS)) = __cpu_to_be64(packets);
+
+ return 0;
+}
+#undef TOF
+
+static int nflow9_direction(struct ulogd_pluginstance *upi, uint8_t *family,
+ uint64_t *orig_bytes, uint64_t *orig_packets,
+ uint64_t *reply_bytes, uint64_t *reply_packets)
+{
+ struct netflow9_instance *ii
+ = (struct netflow9_instance *)&upi->private;
+ struct ulogd_key *keys = upi->input.keys;
+ unsigned int sentry = upi->input.num_keys;
+ int ret = 0;
+
+#define IKC(i) ii->ikey_count[(i)]
+ if (IKC(CII_ORIG_RAW_PKTLEN_DELTA) != sentry
+ && pp_is_valid(keys, IKC(CII_ORIG_RAW_PKTLEN_DELTA))) {
+ *orig_bytes
+ = ikey_get_u64(&keys[IKC(CII_ORIG_RAW_PKTLEN_DELTA)]);
+ if (*orig_bytes > 0) {
+ *orig_packets
+ = ikey_get_u64(&keys[IKC(CII_ORIG_RAW_PKTCOUNT_DELTA)]);
+ ret |= NFLOW9_DIR_ORIG;
+ }
+ }
+ /* XXX: will be removed if NFCT propagate delta counter */
+ else if (IKC(CII_ORIG_RAW_PKTLEN) != sentry
+ && pp_is_valid(keys, IKC(CII_ORIG_RAW_PKTLEN))) {
+ *orig_bytes
+ = ikey_get_u64(&keys[IKC(CII_ORIG_RAW_PKTLEN)]);
+ if (*orig_bytes > 0) {
+ *orig_packets
+ = ikey_get_u64(&keys[IKC(CII_ORIG_RAW_PKTCOUNT)]);
+ ret |= NFLOW9_DIR_ORIG;
+ }
+ }
+ if (IKC(CII_REPLY_RAW_PKTLEN_DELTA) != sentry
+ && pp_is_valid(keys, IKC(CII_REPLY_RAW_PKTLEN_DELTA))) {
+ *reply_bytes
+ = ikey_get_u64(&keys[IKC(CII_REPLY_RAW_PKTLEN_DELTA)]);
+ if (*reply_bytes > 0) {
+ *reply_packets
+ = ikey_get_u64(&keys[IKC(CII_REPLY_RAW_PKTCOUNT_DELTA)]);
+ ret |= NFLOW9_DIR_REPLY;
+ }
+ }
+ /* XXX: will be removed if NFCT propagate delta counter */
+ else if (IKC(CII_REPLY_RAW_PKTLEN) != sentry
+ && pp_is_valid(keys, IKC(CII_REPLY_RAW_PKTLEN))) {
+ *reply_bytes
+ = ikey_get_u64(&keys[IKC(CII_REPLY_RAW_PKTLEN)]);
+ if (*reply_bytes > 0) {
+ *reply_packets
+ = ikey_get_u64(&keys[IKC(CII_REPLY_RAW_PKTCOUNT)]);
+ ret |= NFLOW9_DIR_REPLY;
+ }
+ }
+ *family = ikey_get_u8(&keys[IKC(CII_FAMILY)]);
+#undef IKC
+ return ret;
+}
+
+static void *data_record(struct netflow9_instance *ii,
+ struct ulogd_netflow9_template *tmpl)
+{
+ void *records;
+
+ /* data flowset */
+ ii->iovecs[ii->iovcnt].iov_base = (void *)tmpl->databuf
+ + tmpl->datapos * tmpl->dataset_len;
+ ii->iovecs[ii->iovcnt].iov_len = tmpl->dataset_len;
+
+ /* clear data records */
+ records = ii->iovecs[ii->iovcnt].iov_base
+ + sizeof(struct netflow9_set_hdr);
+ memset(records, 0,
+ tmpl->dataset_len - sizeof(struct netflow9_set_hdr));
+
+ /* increment position */
+ ii->iovcnt++;
+ tmpl->datapos++;
+
+ return records;
+}
+
+static int insert_template(struct ulogd_pluginstance *upi,
+ struct ulogd_netflow9_template *tmpl)
+{
+ struct netflow9_instance *ii
+ = (struct netflow9_instance *)&upi->private;
+
+ if (tmpl->until_template != 0) {
+ tmpl->until_template--;
+ return 0;
+ }
+ tmpl->until_template = nth_template_ce(upi);
+
+ ii->iovecs[ii->iovcnt].iov_base = tmpl->template;
+ ii->iovecs[ii->iovcnt].iov_len = tmpl->tmplset_len;
+
+ ii->iovcnt++;
+ ii->msglen += tmpl->tmplset_len;
+
+ return 1;
+}
+
+static int build_netflow9_msg(struct ulogd_pluginstance *upi,
+ struct ulogd_netflow9_template *tmpl)
+{
+ struct netflow9_instance *ii = (struct netflow9_instance *)&upi->private;
+ uint8_t family = 0;
+ uint64_t obytes = 0, opackets = 0;
+ uint64_t rbytes = 0, rpackets = 0;
+ int dir;
+ void *buf;
+
+ insert_template(upi, tmpl);
+ buf = data_record(ii, tmpl);
+ if (put_data_records(upi, tmpl, buf, tmpl->dataset_len) < 0) {
+ ulogd_log(ULOGD_ERROR, "could not build netflow v9 dataset\n");
+ return -1;
+ }
+
+ dir = nflow9_direction(upi, &family,
+ &obytes, &opackets, &rbytes, &rpackets);
+ switch (dir) {
+ case NFLOW9_DIR_ORIG:
+ swap_by_dir(tmpl, buf, family, dir, obytes, opackets);
+ break;
+
+ case NFLOW9_DIR_REPLY:
+ swap_by_dir(tmpl, buf, family, dir, rbytes, rpackets);
+ break;
+
+ case NFLOW9_DIR_BOTH:
+ swap_by_dir(tmpl, buf, family, NFLOW9_DIR_ORIG,
+ obytes, opackets);
+ ii->msglen += tmpl->dataset_len;
+ buf = data_record(ii, tmpl);
+ if (put_data_records(upi, tmpl, buf, tmpl->dataset_len) < 0) {
+ ulogd_log(ULOGD_ERROR,
+ "could not build netflow v9 dataset");
+ return -1;
+ }
+ swap_by_dir(tmpl, buf, family, NFLOW9_DIR_REPLY,
+ rbytes, rpackets);
+ break;
+
+ case NFLOW9_DIR_NONE:
+ ulogd_log(ULOGD_DEBUG, "receive zero counter data\n");
+ return 0;
+ break;
+
+ default:
+ ulogd_log(ULOGD_ERROR, "nflow9_direction() returns invalid");
+ return -1;
+ break;
+ }
+
+ ii->msglen += tmpl->dataset_len;
+ return 1;
+}
+
+static uint32_t uptime_millis(int fd)
+{
+ char buf[1024] = {0};
+ double up;
+ int nread;
+
+ lseek(fd, 0, SEEK_SET);
+ nread = read(fd, buf, sizeof(buf) - 1);
+ if (nread == -1)
+ return 0;
+ if (sscanf(buf, "%lf", &up) != 1)
+ return 0;
+ return (uint32_t)(up * 1000);
+}
+
+static void reset_counters(struct netflow9_instance *ii)
+{
+ struct ulogd_netflow9_template *tmpl;
+
+ llist_for_each_entry(tmpl, &ii->template_list, list) {
+ tmpl->datapos = 0;
+ }
+ ii->msglen = 0;
+ /* pos 0 is reserved for netflow9_msg_hdr */
+ ii->iovcnt = 1;
+}
+
+static ssize_t send_netflow9(struct netflow9_instance *ii)
+{
+ ssize_t nsent;
+
+ ii->nflow9_msghdr.sys_uptime
+ = htonl((uint32_t)uptime_millis(ii->uptime_fd));
+ ii->nflow9_msghdr.unix_secs = htonl((uint32_t)(time(NULL)));
+ ii->nflow9_msghdr.count = htons(ii->iovcnt - 1);
+ ii->nflow9_msghdr.sequence_number = htonl(ii->seq++);
+ ii->msglen += sizeof(struct netflow9_msg_hdr);
+
+#ifdef DEBUG_TMMAP
+ nflow9_fprintf_header(stdout, ii);
+ fflush(stdout);
+#endif
+ nsent = writev(ii->fd, ii->iovecs, ii->iovcnt);
+ if (nsent != ii->msglen) {
+ if (nsent == -1) {
+ ulogd_log(ULOGD_ERROR, "failed to send: %s\n",
+ strerror(errno));
+ } else {
+ ulogd_log(ULOGD_ERROR, "send - arg: %d, ret: %d\n",
+ ii->msglen, nsent);
+ }
+ }
+
+ return nsent;
+}
+
+static int output_netflow9(struct ulogd_pluginstance *upi)
+{
+ struct netflow9_instance *ii
+ = (struct netflow9_instance *)&upi->private;
+ struct ulogd_netflow9_template *template;
+ unsigned int i;
+ int ret;
+
+ /* FIXME: it would be more cache efficient if the IS_VALID
+ * flags would be a separate bitmask outside of the array.
+ * ulogd core could very easily flush it after every packet,
+ * too. */
+ nfct_bitmask_clear(ii->valid_bitmask);
+
+ for (i = 0; i < upi->input.num_keys; i++) {
+ struct ulogd_key *key = &upi->input.keys[i];
+ int length = ulogd_key_size(key);
+
+ if (length < 0 || length > 0xfffe)
+ continue;
+ if (!(key->u.source->flags & ULOGD_RETF_VALID))
+ continue;
+ if (key->ipfix.vendor != IPFIX_VENDOR_IETF
+ && key->ipfix.vendor != IPFIX_VENDOR_REVERSE)
+ continue;
+ if (ipfix_map[key->ipfix.field_id] == 0)
+ continue;
+
+ /* include both orig. reply. */
+ nfct_bitmask_set_bit(ii->valid_bitmask, i);
+ }
+
+ /* lookup template ID for this bitmask */
+ template = find_template_for_bitmask(upi, ii->valid_bitmask);
+ if (!template) {
+ ulogd_log(ULOGD_INFO, "building new template\n");
+ template = build_template_for_bitmask(upi, ii->valid_bitmask);
+ if (!template) {
+ ulogd_log(ULOGD_ERROR, "can't build new template!\n");
+ return ULOGD_IRET_ERR;
+ }
+ llist_add(&template->list, &ii->template_list);
+ }
+
+ ret = build_netflow9_msg(upi, template);
+ if (ret == -1) {
+ ulogd_log(ULOGD_ERROR, "can't build message\n");
+ reset_counters(ii);
+ return ULOGD_IRET_ERR;
+ }
+
+ /* XXX: magic number. practical UDP max */
+ if (ii->msglen > 65507 - sizeof(struct netflow9_msg_hdr)) {
+ ulogd_log(ULOGD_NOTICE, "We may have lost data since message "
+ "length exceeds practical UDP max size, then reducing "
+ "corksets_max to %d\n", ii->iovcnt);
+ ii->corksets_max = ii->iovcnt;
+ } else if (ii->iovcnt - 1 + 3 < ii->corksets_max) {
+ /* - 1 reserved for header
+ * + 3 for sending template, orig and reply on next */
+ return ULOGD_IRET_OK;
+ }
+
+ ret = send_netflow9(ii);
+ reset_counters(ii);
+ if (ret < 0)
+ return ULOGD_IRET_ERR;
+
+ return ULOGD_IRET_OK;
+}
+
+static int start_netflow9(struct ulogd_pluginstance *upi)
+{
+ struct netflow9_instance *ii = (struct netflow9_instance *)&upi->private;
+ int ret = -ENOMEM;
+
+ ulogd_log(ULOGD_DEBUG, "starting netflow9\n");
+
+ /* +1 for nflow9_msghdr */
+ ii->iovecs = calloc(ii->corksets_max + 1, sizeof(struct iovec));
+ if (ii->iovecs == NULL)
+ return ret;
+
+ ii->valid_bitmask = nfct_bitmask_new(upi->input.num_keys);
+ if (!ii->valid_bitmask)
+ goto out_iovecs_free;
+
+ INIT_LLIST_HEAD(&ii->template_list);
+
+ ii->fd = open_connect_descriptor(dest_ce(upi));
+ if (ii->fd < 0) {
+ ulogd_log(ULOGD_ERROR, "could not connect: %s\n",
+ strerror(errno));
+ goto out_bm_free;
+ }
+
+ ii->uptime_fd = open(UPTIME_FILE, O_RDONLY);
+ if (ii->uptime_fd == -1) {
+ ulogd_log(ULOGD_ERROR, "cound not open file: %s\n",
+ UPTIME_FILE);
+ goto out_close_sock;
+ }
+
+ /* initialize netflow v9 message header */
+ ii->nflow9_msghdr.version = htons(9);
+ ii->nflow9_msghdr.source_id = htonl(domain_ce(upi));
+ ii->iovecs[0].iov_base = &ii->nflow9_msghdr;
+ ii->iovecs[0].iov_len = sizeof(ii->nflow9_msghdr);
+
+ ii->next_template_id = ULOGD_NETFLOW9_TEMPL_BASE;
+ reset_counters(ii);
+
+#ifdef DEBUG_TMMAP
+ mmfd = fileno(tmpfile());
+ if (mmfd == -1) {
+ perror("tmpfile");
+ exit(EXIT_FAILURE);
+ }
+ mmaddr = mmap(NULL, 65507, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, mmfd, 0);
+ if (mmaddr == MAP_FAILED) {
+ perror("mmap");
+ exit(EXIT_FAILURE);
+ }
+#endif
+ return 0;
+
+out_close_sock:
+ close(ii->fd);
+out_bm_free:
+ nfct_bitmask_destroy(ii->valid_bitmask);
+ ii->valid_bitmask = NULL;
+out_iovecs_free:
+ free(ii->iovecs);
+
+ return ret;
+}
+
+static int stop_netflow9(struct ulogd_pluginstance *upi)
+{
+ struct netflow9_instance *ii = (struct netflow9_instance *)&upi->private;
+ struct ulogd_netflow9_template *tmpl, *n;
+
+ if (ii->iovcnt > 1) {
+ ulogd_log(ULOGD_INFO, "send remainded: %d\n", ii->iovcnt);
+ send_netflow9(ii); /* ignore retval, log error only */
+ }
+ reset_counters(ii);
+
+ llist_for_each_entry_safe(tmpl, n, &ii->template_list, list) {
+ nfct_bitmask_destroy(tmpl->bitmask);
+ free(tmpl->template);
+ free(tmpl->databuf);
+ llist_del(&tmpl->list);
+ free(tmpl);
+ }
+ close(ii->uptime_fd);
+ close(ii->fd);
+ nfct_bitmask_destroy(ii->valid_bitmask);
+ ii->valid_bitmask = NULL;
+ free(ii->iovecs);
+
+ return 0;
+}
+
+static void signal_handler_netflow9(struct ulogd_pluginstance *upi, int signal)
+{
+ switch (signal) {
+ case SIGHUP:
+ ulogd_log(ULOGD_NOTICE, "netflow9: reopening connection\n");
+ stop_netflow9(upi);
+ start_netflow9(upi);
+ break;
+ default:
+ break;
+ }
+}
+
+static int configure_netflow9(struct ulogd_pluginstance *upi,
+ struct ulogd_pluginstance_stack *stack)
+{
+ struct netflow9_instance *ii = (struct netflow9_instance *)&upi->private;
+ unsigned int i, j;
+ int ret;
+
+ /* FIXME: error handling */
+ ulogd_log(ULOGD_DEBUG, "parsing config file section %s\n", upi->id);
+ ret = config_parse_file(upi->id, upi->config_kset);
+ if (ret < 0)
+ return ret;
+
+ if (corksets_max_ce(upi) < 3) {
+ ulogd_log(ULOGD_ERROR, "corksets_max is required "
+ "more than 3 from implementation perspective\n");
+ return -EINVAL;
+ }
+ ii->corksets_max = (unsigned int)corksets_max_ce(upi);
+
+ /* postpone address lookup to ->start() time, since we want to
+ * re-lookup an address on SIGHUP */
+ ret = ulogd_wildcard_inputkeys(upi);
+ if (ret < 0)
+ return ret;
+
+ /* search key index for direction conditions and converts */
+ for (i = 0; i < CII_MAX; i++)
+ ii->ikey_count[i] = upi->input.num_keys;
+ for (i = 0; i < upi->input.num_keys; i++) {
+ for (j = 0; j < CII_MAX; j++) {
+ if (!strncmp(upi->input.keys[i].name, count_keys[j],
+ strlen(count_keys[j]))) {
+ ii->ikey_count[j] = i;
+ break;
+ }
+ }
+ }
+ /* XXX: check ii->ikey_count validity */
+
+ return 0;
+}
+
+static struct ulogd_plugin netflow9_plugin = {
+ .name = "NFLOW9",
+ .input = {
+ .type = ULOGD_DTYPE_FLOW,
+ },
+ .output = {
+ .type = ULOGD_DTYPE_SINK,
+ },
+ .config_kset = &netflow9_kset,
+ .priv_size = sizeof(struct netflow9_instance),
+
+ .configure = &configure_netflow9,
+ .start = &start_netflow9,
+ .stop = &stop_netflow9,
+
+ .interp = &output_netflow9,
+ .signal = &signal_handler_netflow9,
+ .version = VERSION,
+};
+
+void __attribute__ ((constructor)) init(void);
+
+void init(void)
+{
+ ulogd_register_plugin(&netflow9_plugin);
+}
+
+#ifdef DEBUG_TMMAP
+static char *nflow9_field_name[] = {
+ [NETFLOW9_IN_BYTES] = "IN_BYTES",
+ [NETFLOW9_IN_PKTS] = "IN_PKTS",
+ [NETFLOW9_FLOWS] = "FLOWS",
+ [NETFLOW9_PROTOCOL] = "PROTOCOL",
+ [NETFLOW9_TOS] = "TOS",
+ [NETFLOW9_TCP_FLAGS] = "TCP_FLAGS",
+ [NETFLOW9_L4_SRC_PORT] = "L4_SRC_PORT",
+ [NETFLOW9_IPV4_SRC_ADDR] = "IPV4_SRC_ADDR",
+ [NETFLOW9_SRC_MASK] = "SRC_MASK",
+ [NETFLOW9_INPUT_SNMP] = "INPUT_SNMP",
+ [NETFLOW9_L4_DST_PORT] = "L4_DST_PORT",
+ [NETFLOW9_IPV4_DST_ADDR] = "IPV4_DST_ADDR",
+ [NETFLOW9_DST_MASK] = "DST_MASK",
+ [NETFLOW9_OUTPUT_SNMP] = "OUTPUT_SNMP",
+ [NETFLOW9_IPV4_NEXT_HOP] = "IPV4_NEXT_HOP",
+ [NETFLOW9_SRC_AS] = "SRC_AS",
+ [NETFLOW9_DST_AS] = "DST_AS",
+ [NETFLOW9_BGP_IPV4_NEXT_HOP] = "BGP_IPV4_NEXT_HOP",
+ [NETFLOW9_MUL_DST_PKTS] = "MUL_DST_PKTS",
+ [NETFLOW9_MUL_DST_BYTES] = "MUL_DST_BYTES",
+ [NETFLOW9_LAST_SWITCHED] = "LAST_SWITCHED",
+ [NETFLOW9_FIRST_SWITCHED] = "FIRST_SWITCHED",
+ [NETFLOW9_OUT_BYTES] = "OUT_BYTES",
+ [NETFLOW9_OUT_PKTS] = "OUT_PKTS",
+ [NETFLOW9_IPV6_SRC_ADDR] = "IPV6_SRC_ADDR",
+ [NETFLOW9_IPV6_DST_ADDR] = "IPV6_DST_ADDR",
+ [NETFLOW9_IPV6_SRC_MASK] = "IPV6_SRC_MASK",
+ [NETFLOW9_IPV6_DST_MASK] = "IPV6_DST_MASK",
+ [NETFLOW9_FLOW_LABEL] = "FLOW_LABEL",
+ [NETFLOW9_ICMP_TYPE] = "ICMP_TYPE",
+ [NETFLOW9_MUL_IGMP_TYPE] = "MUL_IGMP_TYPE",
+ [NETFLOW9_SAMPLING_INTERVAL] = "SAMPLING_INTERVAL",
+ [NETFLOW9_SAMPLING_ALGORITHM] = "SAMPLING_ALGORITHM",
+ [NETFLOW9_FLOW_ACTIVE_TIMEOUT] = "FLOW_ACTIVE_TIMEOUT",
+ [NETFLOW9_FLOW_INAVTIVE_TIMEOUT] = "FLOW_INAVTIVE_TIMEOUT",
+ [NETFLOW9_ENGINE_TYPE] = "ENGINE_TYPE",
+ [NETFLOW9_ENGINE_ID] = "ENGINE_ID",
+ [NETFLOW9_TOTAL_BYTES_EXP] = "TOTAL_BYTES_EXP",
+ [NETFLOW9_TOTAL_PKTS_EXP] = "TOTAL_PKTS_EXP",
+ [NETFLOW9_TOTAL_FLOWS_EXP] = "TOTAL_FLOWS_EXP",
+ [NETFLOW9_MPLS_TOP_LABEL_TYPE] = "MPLS_TOP_LABEL_TYPE",
+ [NETFLOW9_MPLS_TOP_LABEL_IP_ADDR] = "MPLS_TOP_LABEL_IP_ADDR",
+ [NETFLOW9_FLOW_SAMPLER_ID] = "FLOW_SAMPLER_ID",
+ [NETFLOW9_FLOW_SAMPLER_MODE] = "FLOW_SAMPLER_MODE",
+ [NETFLOW9_FLOW_SAMPLER_RANDOM_INTERVAL] = "FLOW_SAMPLER_RANDOM_INTERVAL",
+ [NETFLOW9_DST_TOS] = "DST_TOS",
+ [NETFLOW9_SRC_MAC] = "SRC_MAC",
+ [NETFLOW9_DST_MAC] = "DST_MAC",
+ [NETFLOW9_SRC_VLAN] = "SRC_VLAN",
+ [NETFLOW9_DST_VLAN] = "DST_VLAN",
+ [NETFLOW9_IP_PROTOCOL_VERSION] = "IP_PROTOCOL_VERSION",
+ [NETFLOW9_DIRECTION] = "DIRECTION",
+ [NETFLOW9_IPV6_NEXT_HOP] = "IPV6_NEXT_HOP",
+ [NETFLOW9_BGP_IPV6_NEXT_HOP] = "BGP_IPV6_NEXT_HOP",
+ [NETFLOW9_IPV6_OPTION_HEADERS] = "IPV6_OPTION_HEADERS",
+ [NETFLOW9_MPLS_LABEL_1] = "MPLS_LABEL_1",
+ [NETFLOW9_MPLS_LABEL_2] = "MPLS_LABEL_2",
+ [NETFLOW9_MPLS_LABEL_3] = "MPLS_LABEL_3",
+ [NETFLOW9_MPLS_LABEL_4] = "MPLS_LABEL_4",
+ [NETFLOW9_MPLS_LABEL_5] = "MPLS_LABEL_5",
+ [NETFLOW9_MPLS_LABEL_6] = "MPLS_LABEL_6",
+ [NETFLOW9_MPLS_LABEL_7] = "MPLS_LABEL_7",
+ [NETFLOW9_MPLS_LABEL_8] = "MPLS_LABEL_8",
+ [NETFLOW9_MPLS_LABEL_9] = "MPLS_LABEL_9",
+ [NETFLOW9_MPLS_LABEL_10] = "MPLS_LABEL_10",
+ [NETFLOW9_IPV4_XLATE_SRC_ADDR] = "IPV4_XLATE_SRC_ADDR",
+ [NETFLOW9_IPV4_XLATE_DST_ADDR] = "IPV4_XLATE_DST_ADDR",
+ [NETFLOW9_L4_XLATE_SRC_PORT] = "L4_XLATE_SRC_PORT",
+ [NETFLOW9_L4_XLATE_DST_PORT] = "L4_XLATE_DST_PORT",
+ [NETFLOW9_IPV6_XLATE_SRC_ADDR] = "IPV6_XLATE_SRC_ADDR",
+ [NETFLOW9_IPV6_XLATE_DST_ADDR] = "IPV6_XLATE_DST_ADDR",
+};
+
+static int nflow9_fprintf_field(FILE *fd, const struct netflow9_templ_rec *field, int len)
+{
+ int ret;
+ void *ptr;
+
+ if (len < (int)sizeof(*field)) {
+ fprintf(fd, "ERROR ietf field: too short buflen: %d\n", len);
+ return -1;
+ }
+
+ fprintf(fd, "+---------------------------------+---------------------------------+\n");
+ fprintf(fd, "| Field Type: %19s | Field Length: %5d |\n",
+ nflow9_field_name[ntohs(field->type)], ntohs(field->length));
+
+ len -= sizeof(*field);
+ if (len == 0)
+ return sizeof(*field);
+
+ ptr = (void *)field + sizeof(*field);
+ ret = nflow9_fprintf_field(fd, ptr, len);
+ if (ret == -1)
+ return -1;
+ return ret + sizeof(*field);
+}
+
+static int nflow9_fprintf_data_records(FILE *fd, const void *data, int len)
+{
+ int i;
+
+ fprintf(fd, "+-------------------------------------------------------------------+\n");
+ /* don't say messy...*/
+ for (i = 0; i < len; i += 4) {
+ switch (len - i - 4) {
+ case -3:
+ fprintf(fd, "| 0x%02x |\n",
+ *(uint8_t *)(data + i));
+ break;
+ case -2:
+ fprintf(fd, "| 0x%02x 0x%02x |\n",
+ *(uint8_t *)(data + i), *(uint8_t *)(data + i + 1));
+ break;
+ case -1:
+ fprintf(fd, "| 0x%02x 0x%02x 0x%02x |\n",
+ *(uint8_t *)(data + i), *(uint8_t *)(data + i + 1), *(uint8_t *)(data + i + 2));
+ break;
+ default:
+ fprintf(fd, "| 0x%02x 0x%02x 0x%02x 0x%02x |\n",
+ *(uint8_t *)(data + i), *(uint8_t *)(data + i + 1),
+ *(uint8_t *)(data + i + 2), *(uint8_t *)(data + i + 3));
+ break;
+ }
+ }
+ return len;
+}
+
+static int nflow9_fprintf_template_records(FILE *fd, const struct netflow9_templ_hdr *hdr,
+ int len)
+{
+ int ret;
+ void *field;
+
+ if (len < (int)sizeof(*hdr)) {
+ fprintf(fd, "ERROR template records: too short buflen for template record: %d\n", len);
+ return -1;
+ }
+
+ fprintf(fd, "+---------------------------------+---------------------------------+\n");
+ fprintf(fd, "| Template ID: %5d | Field Count: %5d |\n",
+ ntohs(hdr->template_id), ntohs(hdr->field_count));
+
+ len -= sizeof(*hdr);
+ if (len == 0)
+ return sizeof(*hdr);
+
+ field = (void *)hdr + sizeof(*hdr);
+ ret = nflow9_fprintf_field(fd, field, len);
+ if (ret == -1)
+ return -1;
+ return ret + sizeof(*hdr);
+}
+
+static int nflow9_fprintf_set_header(FILE *fd, const struct netflow9_set_hdr *hdr, int len)
+{
+ int ret, setlen, total_len;
+ void *ptr;
+
+ if (len < (int)sizeof(*hdr)) {
+ fprintf(fd, "ERROR set header: too short buflen for set header: %d\n", len);
+ return -1;
+ }
+ setlen = ntohs(hdr->length);
+ if (len < setlen) {
+ fprintf(fd, "ERROR set header: buflen: %d is smaller than set length field: %d\n", len, setlen);
+ /* return -1; */
+ }
+ if (setlen < (int)sizeof(*hdr)) {
+ fprintf(fd, "ERROR set header: too short set length field: %d\n", setlen);
+ return -1;
+ }
+
+ fprintf(fd, "+---------------------------------+---------------------------------+\n");
+ fprintf(fd, "| Set ID: %5d | Length: %5d |\n",
+ ntohs(hdr->set_id), setlen);
+
+ setlen -= sizeof(*hdr);
+ ptr = (void *)hdr + sizeof(*hdr);
+ total_len = sizeof(*hdr);
+
+ switch (ntohs(hdr->set_id)) {
+ case 0:
+ ret = nflow9_fprintf_template_records(fd, ptr, setlen);
+ break;
+ case 1:
+ /* XXX: ret = nflow9_fprintf_options_template_records(fd, ptr, setlen); */
+ fprintf(fd, "ERROR: options template is not implemented yet, sorry");
+ ret = setlen;
+ break;
+ default:
+ ret = nflow9_fprintf_data_records(fd, ptr, setlen);
+ break;
+ }
+
+ if (ret == -1 || ret != setlen)
+ return -1;
+
+ fprintf(fd, "+-------------------------------------------------------------------+\n");
+ return total_len + ret;
+}
+
+static int _nflow9_fprintf_header(FILE *fd, const struct netflow9_msg_hdr *hdr,
+ int msglen)
+{
+ int ret, len;
+ char outstr[20];
+ void *ptr;
+ time_t t = (time_t)(ntohl(hdr->unix_secs));
+ struct tm *tmp = localtime(&t);
+
+ /* XXX: tmp == NULL and strftime == 0 */
+ strftime(outstr, sizeof(outstr), "%F %T", tmp);
+
+ fprintf(fd, "+---------------------------------+---------------------------------+\n");
+ fprintf(fd, "| Version Number: %5d | Count: %5d | (Length: %d) \n",
+ ntohs(hdr->version), ntohs(hdr->count), msglen);
+ fprintf(fd, "+-------------------------------------------------------------------+\n");
+ fprintf(fd, "| sysUpTime: %10u |\n",
+ ntohl(hdr->sys_uptime));
+ fprintf(fd, "+---------------------------------+---------------------------------+\n");
+ fprintf(fd, "| UNIX Secs: %10u |\t%s\n",
+ ntohl(hdr->unix_secs), outstr);
+ fprintf(fd, "+-------------------------------------------------------------------+\n");
+ fprintf(fd, "| Sequence Number: %10d |\n",
+ ntohl(hdr->sequence_number));
+ fprintf(fd, "+-------------------------------------------------------------------+\n");
+ fprintf(fd, "| Source ID: %10d |\n",
+ ntohl(hdr->source_id));
+ fprintf(fd, "+-------------------------------------------------------------------+\n");
+
+ len = msglen - sizeof(*hdr);
+ ptr = (void *)hdr + sizeof(*hdr);
+
+ while (len > 0) {
+ ret = nflow9_fprintf_set_header(fd, ptr, len);
+ if (ret == -1)
+ return -1;
+ len -= ret;
+ ptr += ret;
+ }
+
+ return msglen - len;
+}
+
+static int nflow9_fprintf_header(FILE *fd, const struct netflow9_instance *ii)
+{
+ lseek(mmfd, 0, SEEK_SET);
+ writev(mmfd, ii->iovecs, ii->iovcnt);
+ return _nflow9_fprintf_header(fd, mmaddr, ii->msglen);
+}
+#endif
--
2.1.4
prev parent reply other threads:[~2016-02-10 2:05 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-02-06 10:42 [RFC] a software based on ulogd Ken-ichirou MATSUZAWA
2016-02-07 10:51 ` Eric Leblond
2016-02-10 1:53 ` Ken-ichirou MATSUZAWA
2016-02-10 1:56 ` [PATCH ulogd 1/7] ipfix: add flowDirection IE Ken-ichirou MATSUZAWA
2016-02-10 1:58 ` [PATCH ulogd 2/7] nfct/ipfix: introduce new vendor id Ken-ichirou MATSUZAWA
2016-02-10 2:00 ` [PATCH ulogd 3/7] nfct/ipfix: introduce NAT entries Ken-ichirou MATSUZAWA
2016-02-10 2:01 ` [PATCH ulogd 4/7] filter: add new filter for Netflow ICMP_TYPE Ken-ichirou MATSUZAWA
2016-02-10 2:03 ` [PATCH ulogd 5/7] filter: add new filter for IPFIX time Ken-ichirou MATSUZAWA
2016-02-10 2:04 ` [PATCH ulogd 6/7] ulogd: update calling stop callback condition Ken-ichirou MATSUZAWA
2016-02-10 2:05 ` Ken-ichirou MATSUZAWA [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20160210020540.GH17470@gmail.com \
--to=chamaken@gmail.com \
--cc=eric@regit.org \
--cc=netfilter-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.