[netfilter-cvslog] r6398 - in branches/ulog/ulogd2: include/ulogd input/flow

laforge at netfilter.org laforge at netfilter.org
Thu Jan 12 13:18:22 CET 2006


Author: laforge at netfilter.org
Date: 2006-01-12 13:18:21 +0100 (Thu, 12 Jan 2006)
New Revision: 6398

Modified:
   branches/ulog/ulogd2/include/ulogd/ipfix_protocol.h
   branches/ulog/ulogd2/input/flow/ulogd_inpflow_NFCT.c
Log:
introduce NFCT input plugin hash table for start/end timestamps. Based on an
original patch by Christian Hentschel, amended like:

1) the hash table has to be per-instance, since [at least in the future]
   we can have nfnetlink messages routed from other machines over the
   network, thus every NFCT instance has to have it's own hash table.
2) Whether or not to use a preallocated table is now a configuration
   value, as is the number of buckets and max_entries
3) configure_nfct was not used in the struct ulogd_plugin.configure
4) don't put the hashtable buckets in BSS, but rather allocate them
   dynamically
5) allocate all ct_timestamps (in the preallocated case) at once, rather
   than malloc()ing each on its own.
6) use official IPFIX fields for flow start and flow end instead of
   private numbers
7) use llist instead of list (linuxlist.h adds an additional 'l')
8) add lots of TODO items
9) add IPFIX_NF_conntrack_id to header file


Modified: branches/ulog/ulogd2/include/ulogd/ipfix_protocol.h
===================================================================
--- branches/ulog/ulogd2/include/ulogd/ipfix_protocol.h	2006-01-12 11:20:19 UTC (rev 6397)
+++ branches/ulog/ulogd2/include/ulogd/ipfix_protocol.h	2006-01-12 12:18:21 UTC (rev 6398)
@@ -221,6 +221,7 @@
 	IPFIX_NF_prefix			= 3,	/* string */
 	IPFIX_NF_mark			= 4,	/* u_int32_t */
 	IPFIX_NF_hook			= 5,	/* u_int8_t */
+	IPFIX_NF_conntrack_id		= 6,	/* u_int32_t */
 };
 
 #endif

Modified: branches/ulog/ulogd2/input/flow/ulogd_inpflow_NFCT.c
===================================================================
--- branches/ulog/ulogd2/input/flow/ulogd_inpflow_NFCT.c	2006-01-12 11:20:19 UTC (rev 6397)
+++ branches/ulog/ulogd2/input/flow/ulogd_inpflow_NFCT.c	2006-01-12 12:18:21 UTC (rev 6398)
@@ -2,30 +2,71 @@
  *
  * ulogd input plugin for ctnetlink
  *
- * (C) 2005 by Harald Welte <laforge at gnumonks.org>
+ * (C) 2005 by Harald Welte <laforge at netfilter.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
  * as published by the Free Software Foundation
+ *
+ * 10 Jan 2005, Christian Hentschel <chentschel at people.netfilter.org>
+ *      Added timestamp accounting support of the conntrack entries,
+ *      reworked by Harald Welte.
+ *
+ * TODO:
+ * 	- add nanosecond-accurate packet receive timestamp of event-changing
+ * 	  packets to {ip,nf}_conntrack_netlink, so we can have accurate IPFIX
+ *	  flowStart / flowEnd NanoSeconds.
+ *	- if using preallocated data structure, get rid of all list heads and
+ *	  use per-bucket arrays instead.
+ *	- SIGHUP for reconfiguration without loosing hash table contents, but
+ *	  re-read of config and reallocation / rehashing of table, if required
+ *	- Split hashtable code into separate [filter] plugin, so we can run 
+ * 	  small non-hashtable ulogd installations on the firewall boxes, send
+ * 	  the messages via IPFX to one aggregator who then runs ulogd with a 
+ * 	  network wide connection hash table.
  */
 
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 
+#include <sys/time.h>
+#include <time.h>
+#include <ulogd/linuxlist.h>
+
 #include <ulogd/ulogd.h>
 #include <ulogd/ipfix_protocol.h>
 
 #include <libnetfilter_conntrack/libnetfilter_conntrack.h>
 
+typedef enum TIMES_ { START, STOP, __TIME_MAX } TIMES;
+ 
+struct ct_timestamp {
+	struct llist_head list;
+	struct timeval time[__TIME_MAX];
+	int id;
+};
+
+struct ct_htable {
+	struct llist_head *buckets;
+	int num_buckets;
+	int prealloc;
+	struct llist_head idle;
+	struct ct_timestamp *ts;
+};
+
 struct nfct_pluginstance {
 	struct nfct_handle *cth;
 	struct ulogd_fd nfct_fd;
 	struct ulogd_timer timer;
+	struct ct_htable *ct_active;
 };
 
+#define HTABLE_SIZE	(8192)
+#define MAX_ENTRIES	(4 * HTABLE_SIZE)
+
 static struct config_keyset nfct_kset = {
-	.num_ces = 1,
+	.num_ces = 5,
 	.ces = {
 		{
 			.key	 = "pollinterval",
@@ -33,9 +74,37 @@
 			.options = CONFIG_OPT_NONE,
 			.u.value = 0,
 		},
+		{
+			.key	 = "hash_enable",
+			.type	 = CONFIG_TYPE_INT,
+			.options = CONFIG_OPT_NONE,
+			.u.value = 1,
+		},
+		{
+			.key	 = "hash_prealloc",
+			.type	 = CONFIG_TYPE_INT,
+			.options = CONFIG_OPT_NONE,
+			.u.value = 1,
+		},
+		{
+			.key	 = "hash_buckets",
+			.type	 = CONFIG_TYPE_INT,
+			.options = CONFIG_OPT_NONE,
+			.u.value = HTABLE_SIZE,
+		},
+		{
+			.key	 = "hash_max_entries",
+			.type	 = CONFIG_TYPE_INT,
+			.options = CONFIG_OPT_NONE,
+			.u.value = MAX_ENTRIES,
+		},
 	},
 };
 #define pollint_ce(x)	(x->ces[0])
+#define usehash_ce(x)	(x->ces[1])
+#define prealloc_ce(x)	(x->ces[2])
+#define buckets_ce(x)	(x->ces[3])
+#define maxentries_ce(x) (x->ces[4])
 
 static struct ulogd_key nfct_okeys[] = {
 	{
@@ -121,13 +190,179 @@
 			.field_id	= IPFIX_icmpTypeIPv4,
 		},
 	},
-
+        {
+                .type	= ULOGD_RET_UINT32,
+                .flags	= ULOGD_RETF_NONE,
+                .name	= "ct.mark",
+                .ipfix	= {
+                        .vendor		= IPFIX_VENDOR_NETFILTER,
+                        .field_id	= IPFIX_NF_mark,
+                },
+        },
+        {
+                .type	= ULOGD_RET_UINT32,
+                .flags	= ULOGD_RETF_NONE,
+                .name	= "ct.id",
+                .ipfix	= {
+                        .vendor		= IPFIX_VENDOR_NETFILTER,
+                        .field_id	= IPFIX_NF_conntrack_id,
+                },
+        },
+	{
+		.type 	= ULOGD_RET_UINT32,
+		.flags 	= ULOGD_RETF_NONE,
+		.name	= "flow.start.sec",
+		.ipfix	= {
+			.vendor		= IPFIX_VENDOR_IETF,
+			.field_id	= IPFIX_flowStartSeconds,
+		},
+	},
+	{
+		.type 	= ULOGD_RET_UINT32,
+		.flags 	= ULOGD_RETF_NONE,
+		.name	= "flow.start.usec",
+		.ipfix	= {
+			.vendor		= IPFIX_VENDOR_IETF,
+			.field_id	= IPFIX_flowStartMicroSeconds,
+		},
+	},
+	{
+		.type	= ULOGD_RET_UINT32,
+		.flags	= ULOGD_RETF_NONE,
+		.name	= "flow.end.sec",
+		.ipfix	= {
+			.vendor		= IPFIX_VENDOR_IETF,
+			.field_id	= IPFIX_flowEndSeconds,
+		},
+	},
+	{
+		.type	= ULOGD_RET_UINT32,
+		.flags	= ULOGD_RETF_NONE,
+		.name	= "flow.end.sec",
+		.ipfix	= {
+			.vendor		= IPFIX_VENDOR_IETF,
+			.field_id	= IPFIX_flowEndSeconds,
+		},
+	},
 };
 
+static struct ct_htable *htable_alloc(int htable_size, int prealloc)
+{
+	struct ct_htable *htable;
+	struct ct_timestamp *ct;
+	int i;
+
+	htable = malloc(sizeof(*htable)
+			+ sizeof(struct llist_head)*htable_size);
+	if (!htable)
+		return NULL;
+
+	htable->buckets = (void *)htable + sizeof(*htable);
+	htable->num_buckets = htable_size;
+	htable->prealloc = prealloc;
+
+	for (i = 0; i < htable->num_buckets; i++)
+                INIT_LLIST_HEAD(&htable->buckets[i]);
+	
+	if (!htable->prealloc)
+		return htable;
+
+	ct = malloc(sizeof(struct ct_timestamp)
+		    * htable->num_buckets * htable->prealloc);
+	if (!ct) {
+		free(htable);
+		return NULL;
+	}
+
+	/* save the pointer for later free()ing */
+	htable->ts = ct;
+
+	for (i = 0; i < htable->num_buckets * htable->prealloc; i++)
+		llist_add(&ct[i].list, &htable->idle);
+
+	return htable;
+}
+
+static void htable_free(struct ct_htable *htable)
+{
+	struct llist_head *ptr, *ptr2;
+	int i;
+
+	if (htable->prealloc) {
+		/* the easy case */
+		free(htable->ts);
+		free(htable);
+
+		return;
+	}
+
+	/* non-prealloc case */
+
+	for (i = 0; i < htable->num_buckets; i++) {
+		llist_for_each_safe(ptr, ptr2, &htable->buckets[i])
+			free(container_of(ptr, struct ct_timestamp, list));
+	}
+
+	/* don't need to check for 'idle' list, since it is only used in
+	 * the preallocated case */
+}
+
+static int ct_hash_add(struct ct_htable *htable, unsigned int id)
+{
+	struct ct_timestamp *ct;
+
+	if (htable->prealloc) {
+		if (llist_empty(&htable->idle)) {
+			ulogd_log(ULOGD_ERROR, "Not enough ct_timestamp entries\n");
+			return -1;
+		}
+
+		ct = container_of(htable->idle.next, struct ct_timestamp, list);
+
+		ct->id = id;
+		gettimeofday(&ct->time[START], NULL);
+
+		llist_move(&ct->list, &htable->buckets[id % htable->num_buckets]);
+	} else {
+		ct = malloc(sizeof *ct);
+		if (!ct) {
+			ulogd_log(ULOGD_ERROR, "Not enough memory\n");
+			return -1;
+		}
+
+		ct->id = id;
+		gettimeofday(&ct->time[START], NULL);
+
+		llist_add(&ct->list, &htable->buckets[id % htable->num_buckets]);
+	}
+
+	return 0;
+}
+
+static struct ct_timestamp *ct_hash_get(struct ct_htable *htable, uint32_t id)
+{
+	struct ct_timestamp *ct = NULL;
+	struct llist_head *ptr;
+
+	llist_for_each(ptr, &htable->buckets[id % htable->num_buckets]) {
+		ct = container_of(ptr, struct ct_timestamp, list);
+		if (ct->id == id) {
+			gettimeofday(&ct->time[STOP], NULL);
+			if (htable->prealloc)
+				llist_move(&ct->list, &htable->idle);
+			else
+				free(ct);
+			break;
+		}
+	}
+	return ct;
+}
+
 static int propagate_ct_flow(struct ulogd_pluginstance *upi, 
 		             struct nfct_conntrack *ct,
 			     unsigned int flags,
-			     int dir)
+			     int dir,
+			     struct ct_timestamp *ts)
 {
 	struct ulogd_key *ret = upi->output.keys;
 
@@ -166,7 +401,28 @@
 		ret[6].u.value.ui64 = ct->counters[dir].packets;
 		ret[6].flags |= ULOGD_RETF_VALID;
 	}
-	
+
+	if (flags & NFCT_MARK) {
+		ret[9].u.value.ui32 = ct->mark;
+		ret[9].flags |= ULOGD_RETF_VALID;
+	}
+
+	if (flags & NFCT_ID) {
+		ret[10].u.value.ui32 = ct->id;
+		ret[10].flags |= ULOGD_RETF_VALID;
+	}
+
+	if (ts) {
+		ret[11].u.value.ui32 = ts->time[START].tv_sec;
+		ret[11].flags |= ULOGD_RETF_VALID;
+		ret[12].u.value.ui32 = ts->time[START].tv_usec;
+		ret[12].flags |= ULOGD_RETF_VALID;
+		ret[13].u.value.ui32 = ts->time[STOP].tv_sec;
+		ret[13].flags |= ULOGD_RETF_VALID;
+		ret[14].u.value.ui32 = ts->time[STOP].tv_usec;
+		ret[14].flags |= ULOGD_RETF_VALID;
+	}
+
 	ulogd_propagate_results(upi);
 
 	return 0;
@@ -174,14 +430,16 @@
 
 static int propagate_ct(struct ulogd_pluginstance *upi,
 			struct nfct_conntrack *ct,
-			unsigned int flags)
+			unsigned int flags,
+			struct ct_timestamp *ctstamp)
 {
 	int rc;
 
-	rc = propagate_ct_flow(upi, ct, flags, NFCT_DIR_ORIGINAL);
+	rc = propagate_ct_flow(upi, ct, flags, NFCT_DIR_ORIGINAL, ctstamp);
 	if (rc < 0)
 		return rc;
-	return propagate_ct_flow(upi, ct, flags, NFCT_DIR_REPLY);
+
+	return propagate_ct_flow(upi, ct, flags, NFCT_DIR_REPLY, ctstamp);
 }
 
 static int event_handler(void *arg, unsigned int flags, int type,
@@ -189,22 +447,25 @@
 {
 	struct nfct_conntrack *ct = arg;
 	struct ulogd_pluginstance *upi = data;
+	struct nfct_pluginstance *cpi = (struct nfct_pluginstance *) data;
 
 	if (type == NFCT_MSG_NEW) {
-		/* FIXME: build hash table with timestamp of start of
-		 * connection */
+		if (usehash_ce(upi->config_kset).u.value != 0)
+			ct_hash_add(cpi->ct_active, ct->id);
 	} else if (type == NFCT_MSG_DESTROY) {
-		/* We have the final count of bytes for this connection */
-		return propagate_ct(upi, ct, flags);
+		struct ct_timestamp *ts = NULL;
+
+		if (usehash_ce(upi->config_kset).u.value != 0)
+			ts = ct_hash_get(cpi->ct_active, ct->id);
+
+		return propagate_ct(upi, ct, flags, ts);
 	}
-
 	return 0;
 }
 
 static int read_cb_nfct(int fd, unsigned int what, void *param)
 {
-	struct nfct_pluginstance *cpi = 
-				(struct nfct_pluginstance *) param;
+	struct nfct_pluginstance *cpi = (struct nfct_pluginstance *) param;
 
 	if (!(what & ULOGD_FD_READ))
 		return 0;
@@ -258,6 +519,7 @@
 {
 	struct nfct_pluginstance *cpi = 
 			(struct nfct_pluginstance *)upi->private;
+	int prealloc;
 
 	memset(cpi, 0, sizeof(*cpi));
 
@@ -277,6 +539,20 @@
 	cpi->nfct_fd.when = ULOGD_FD_READ;
 
 	ulogd_register_fd(&cpi->nfct_fd);
+
+	if (prealloc_ce(upi->config_kset).u.value != 0)
+		prealloc = maxentries_ce(upi->config_kset).u.value / 
+				buckets_ce(upi->config_kset).u.value;
+	else
+		prealloc = 0;
+
+	cpi->ct_active = htable_alloc(buckets_ce(upi->config_kset).u.value,
+				      prealloc);
+	if (!cpi->ct_active) {
+		ulogd_log(ULOGD_FATAL, "error allocating hash\n");
+		nfct_close(cpi->cth);
+		return -1;
+	}
 	
 	return 0;
 }
@@ -285,6 +561,8 @@
 {
 	struct nfct_pluginstance *cpi = (void *) pi;
 	int rc;
+	
+	htable_free(cpi->ct_active);
 
 	rc = nfct_close(cpi->cth);
 	if (rc < 0)
@@ -314,7 +592,7 @@
 	},
 	.config_kset 	= &nfct_kset,
 	.interp 	= NULL,
-	.configure	= NULL,
+	.configure	= &configure_nfct,
 	.start		= &constructor_nfct,
 	.stop		= &destructor_nfct,
 	.signal		= &signal_nfct,




More information about the netfilter-cvslog mailing list