[netfilter-cvslog] r6715 - in trunk/libnetfilter_conntrack: . include include/libnetfilter_conntrack src src/conntrack utils

pablo at netfilter.org pablo at netfilter.org
Tue Dec 19 18:41:53 CET 2006


Author: pablo at netfilter.org
Date: 2006-12-19 18:41:53 +0100 (Tue, 19 Dec 2006)
New Revision: 6715

Added:
   trunk/libnetfilter_conntrack/include/internal.h
   trunk/libnetfilter_conntrack/src/conntrack/
   trunk/libnetfilter_conntrack/src/conntrack/Makefile.am
   trunk/libnetfilter_conntrack/src/conntrack/api.c
   trunk/libnetfilter_conntrack/src/conntrack/build.c
   trunk/libnetfilter_conntrack/src/conntrack/callback.c
   trunk/libnetfilter_conntrack/src/conntrack/getter.c
   trunk/libnetfilter_conntrack/src/conntrack/objopt.c
   trunk/libnetfilter_conntrack/src/conntrack/parse.c
   trunk/libnetfilter_conntrack/src/conntrack/setter.c
   trunk/libnetfilter_conntrack/src/conntrack/snprintf.c
   trunk/libnetfilter_conntrack/src/conntrack/snprintf_default.c
   trunk/libnetfilter_conntrack/src/conntrack/snprintf_xml.c
   trunk/libnetfilter_conntrack/utils/new_api_test.c
Modified:
   trunk/libnetfilter_conntrack/Make_global.am
   trunk/libnetfilter_conntrack/configure.in
   trunk/libnetfilter_conntrack/include/Makefile.am
   trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/libnetfilter_conntrack.h
   trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/libnetfilter_conntrack_tcp.h
   trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/linux_nfnetlink_conntrack.h
   trunk/libnetfilter_conntrack/src/Makefile.am
   trunk/libnetfilter_conntrack/src/libnetfilter_conntrack.c
   trunk/libnetfilter_conntrack/utils/Makefile.am
Log:
Introduce the new libnetfilter_conntrack API, features:

- object oriented infrastructure
- extensible and configurable output (XML)
- low level functions to interact with netlink details
- fairly documented

Still backward compatible.



Modified: trunk/libnetfilter_conntrack/Make_global.am
===================================================================
--- trunk/libnetfilter_conntrack/Make_global.am	2006-12-19 17:12:21 UTC (rev 6714)
+++ trunk/libnetfilter_conntrack/Make_global.am	2006-12-19 17:41:53 UTC (rev 6715)
@@ -1,6 +1,7 @@
 # This is _NOT_ the library release version, it's an API version.
 # Please read Chapter 6 "Library interface versions" of the libtool
 # documentation before making any modification
-LIBVERSION=2:0:1
+# http://sources.redhat.com/autobook/autobook/autobook_91.html
+LIBVERSION=3:0:2
 
 INCLUDES=$(all_includes) -I$(top_srcdir)/include

Modified: trunk/libnetfilter_conntrack/configure.in
===================================================================
--- trunk/libnetfilter_conntrack/configure.in	2006-12-19 17:12:21 UTC (rev 6714)
+++ trunk/libnetfilter_conntrack/configure.in	2006-12-19 17:41:53 UTC (rev 6715)
@@ -4,7 +4,7 @@
 
 AC_CANONICAL_SYSTEM
 
-AM_INIT_AUTOMAKE(libnetfilter_conntrack, 0.0.31)
+AM_INIT_AUTOMAKE(libnetfilter_conntrack, 0.0.50)
 
 AC_PROG_CC
 AM_PROG_LIBTOOL
@@ -70,5 +70,5 @@
 AC_SUBST(LIBNFCONNTRACK_LIBS)
 
 dnl Output the makefile
-AC_OUTPUT(Makefile src/Makefile include/Makefile utils/Makefile include/libnetfilter_conntrack/Makefile l3extensions/Makefile extensions/Makefile libnetfilter_conntrack.pc)
+AC_OUTPUT(Makefile src/Makefile include/Makefile utils/Makefile include/libnetfilter_conntrack/Makefile l3extensions/Makefile extensions/Makefile src/conntrack/Makefile libnetfilter_conntrack.pc)
 

Modified: trunk/libnetfilter_conntrack/include/Makefile.am
===================================================================
--- trunk/libnetfilter_conntrack/include/Makefile.am	2006-12-19 17:12:21 UTC (rev 6714)
+++ trunk/libnetfilter_conntrack/include/Makefile.am	2006-12-19 17:41:53 UTC (rev 6715)
@@ -1,4 +1,4 @@
 
 SUBDIRS = libnetfilter_conntrack
 
-noinst_HEADERS = linux_list.h
+noinst_HEADERS = linux_list.h internal.h

Added: trunk/libnetfilter_conntrack/include/internal.h
===================================================================
--- trunk/libnetfilter_conntrack/include/internal.h	                        (rev 0)
+++ trunk/libnetfilter_conntrack/include/internal.h	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,154 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ * 
+ * WARNING: Do *NOT* ever include this file, only for internal use!
+ * 	    Use the set/get API in order to set/get the conntrack attributes
+ */
+
+#ifndef __LIBNETFILTER_CONNTRACK_INTERNAL__
+#define __LIBNETFILTER_CONNTRACK_INTERNAL__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <libnfnetlink/libnfnetlink.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+struct nfct_handle;
+
+typedef void (*set_attr)(struct nf_conntrack *ct, const void *value);
+typedef const void *(*get_attr)(const struct nf_conntrack *ct);
+
+extern set_attr set_attr_array[];
+extern get_attr get_attr_array[];
+
+typedef int (*nfct_handler)(struct nfct_handle *cth, struct nlmsghdr *nlh,
+			    void *arg);
+
+struct nfct_handle {
+	struct nfnl_handle *nfnlh;
+	struct nfnl_subsys_handle *nfnlssh_ct;
+	struct nfnl_subsys_handle *nfnlssh_exp;
+	nfct_callback callback;		/* user callback */
+	void *callback_data;		/* user data for callback */
+	nfct_handler handler;		/* netlink handler */
+
+	/* callback handler for the new API */
+	struct nfnl_callback nfnl_cb;
+	int(*cb)(enum nf_conntrack_msg_type type, 
+		 struct nf_conntrack *ct,
+		 void *data);
+};
+
+union __nfct_l4 {
+	/* Add other protocols here. */
+	u_int16_t all;
+	struct {
+		u_int16_t port;
+	} tcp;
+	struct {
+		u_int16_t port;
+	} udp;
+	struct {
+		u_int8_t type, code;
+		u_int16_t id;
+	} icmp;
+	struct {
+		u_int16_t port;
+	} sctp;
+};
+
+union __nfct_address {
+	u_int32_t v4;
+	struct in6_addr v6;
+};
+
+struct __nfct_tuple {
+	union __nfct_address src;
+	union __nfct_address dst;
+
+	u_int8_t l3protonum;
+	u_int8_t protonum;
+	union __nfct_l4 l4src;
+	union __nfct_l4 l4dst;
+};
+
+union __nfct_protoinfo {
+	struct {
+		u_int8_t state;
+	} tcp;
+};
+
+struct __nfct_counters {
+	u_int64_t packets;
+	u_int64_t bytes;
+};
+
+struct __nfct_nat {
+	u_int32_t min_ip, max_ip;
+	union __nfct_l4 l4min, l4max;
+};
+
+#define __DIR_ORIG 0
+#define __DIR_REPL 1
+#define __DIR_MAX __DIR_REPL+1
+
+struct nf_conntrack {
+	struct __nfct_tuple tuple[__DIR_MAX];
+	
+	u_int32_t 	timeout;
+	u_int32_t	mark;
+	u_int32_t 	status;
+	u_int32_t	use;
+	u_int32_t	id;
+
+	union __nfct_protoinfo protoinfo;
+	struct __nfct_counters counters[__DIR_MAX];
+	struct __nfct_nat snat;
+	struct __nfct_nat dnat;
+
+	u_int32_t set[2];
+};
+
+/* container used to pass data to nfnl callbacks */
+struct __data_container {
+	struct nfct_handle *h;
+	enum nf_conntrack_msg_type type;
+	void *data;
+};
+
+static inline void set_bit(int nr, u_int32_t *addr)
+{
+	addr[nr >> 5] |= (1UL << (nr & 31));
+}
+
+static inline void unset_bit(int nr, u_int32_t *addr)
+{
+	addr[nr >> 5] &= ~(1UL << (nr & 31));
+}
+
+static inline int test_bit(int nr, const u_int32_t *addr)
+{
+	return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0;
+}
+
+int __build_conntrack(struct nfnl_subsys_handle *ssh, struct nfnlhdr *req, size_t size, u_int16_t type, u_int16_t flags, const struct nf_conntrack *ct);
+int __parse_message_type(const struct nlmsghdr *nlh);
+void __parse_conntrack(const struct nlmsghdr *nlh, const struct nfattr *cda[], struct nf_conntrack *ct);
+int __snprintf_conntrack(char *buf, unsigned int len, const struct nf_conntrack *ct, unsigned int type, unsigned int msg_output, unsigned int flags);
+
+
+int __callback(struct nlmsghdr *nlh, struct nfattr *nfa[], void *data);
+
+int __setobjopt(struct nf_conntrack *ct, unsigned int option);
+int __getobjopt(const struct nf_conntrack *ct, unsigned int option);
+
+#endif

Modified: trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/libnetfilter_conntrack.h
===================================================================
--- trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/libnetfilter_conntrack.h	2006-12-19 17:12:21 UTC (rev 6714)
+++ trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/libnetfilter_conntrack.h	2006-12-19 17:41:53 UTC (rev 6715)
@@ -332,6 +332,220 @@
 extern void nfct_build_tuple(struct nfnlhdr *req, int size, 
 			     struct nfct_tuple *t, int type);
 
+/* 
+ * NEW libnetfilter_conntrack API 
+ */
 
+/* high level API */
 
+#include <sys/types.h>
+
+/* conntrack object */
+struct nf_conntrack;
+
+/* conntrack attributes */
+enum nf_conntrack_attr {
+	ATTR_ORIG_IPV4_SRC = 0,		/* u32 bits */
+	ATTR_ORIG_IPV4_DST,		/* u32 bits */
+	ATTR_REPL_IPV4_SRC,		/* u32 bits */
+	ATTR_REPL_IPV4_DST,		/* u32 bits */
+	ATTR_ORIG_IPV6_SRC = 4,		/* u128 bits */
+	ATTR_ORIG_IPV6_DST,		/* u128 bits */
+	ATTR_REPL_IPV6_SRC,		/* u128 bits */
+	ATTR_REPL_IPV6_DST,		/* u128 bits */
+	ATTR_ORIG_PORT_SRC = 8,		/* u16 bits */
+	ATTR_ORIG_PORT_DST,		/* u16 bits */
+	ATTR_REPL_PORT_SRC,		/* u16 bits */
+	ATTR_REPL_PORT_DST,		/* u16 bits */
+	ATTR_ICMP_TYPE = 12,		/* u8 bits */
+	ATTR_ICMP_CODE,			/* u8 bits */
+	ATTR_ICMP_ID,			/* u8 bits */
+	ATTR_ORIG_L3PROTO,		/* u8 bits */
+	ATTR_REPL_L3PROTO = 16,		/* u8 bits */
+	ATTR_ORIG_L4PROTO,		/* u8 bits */
+	ATTR_REPL_L4PROTO,		/* u8 bits */
+	ATTR_TCP_STATE,			/* u8 bits */
+	ATTR_SNAT_IPV4 = 20,		/* u32 bits */
+	ATTR_DNAT_IPV4,			/* u32 bits */
+	ATTR_SNAT_PORT,			/* u16 bits */
+	ATTR_DNAT_PORT,			/* u16 bits */
+	ATTR_TIMEOUT = 24,		/* u32 bits */
+	ATTR_MARK,			/* u32 bits */
+	ATTR_ORIG_COUNTER_PACKETS,	/* u32 bits */
+	ATTR_REPL_COUNTER_PACKETS,	/* u32 bits */
+	ATTR_ORIG_COUNTER_BYTES = 28,	/* u32 bits */
+	ATTR_REPL_COUNTER_BYTES,	/* u32 bits */
+	ATTR_USE,			/* u32 bits */
+	ATTR_ID,			/* u32 bits */
+	ATTR_STATUS = 32,		/* u32 bits  */
+	ATTR_MAX
+};
+
+/* message type */
+enum nf_conntrack_msg_type {
+	NFCT_T_UNKNOWN = 0,
+
+	NFCT_T_NEW_BIT = 0,
+	NFCT_T_NEW = (1 << NFCT_T_NEW_BIT),
+
+	NFCT_T_UPDATE_BIT = 1,
+	NFCT_T_UPDATE = (1 << NFCT_T_UPDATE_BIT),
+
+	NFCT_T_DESTROY_BIT = 2,
+	NFCT_T_DESTROY = (1 << NFCT_T_DESTROY_BIT),
+
+	NFCT_T_ALL = NFCT_T_NEW | NFCT_T_UPDATE | NFCT_T_DESTROY,
+
+	NFCT_T_ERROR_BIT = 31,
+	NFCT_T_ERROR = (1 << NFCT_T_ERROR_BIT),
+};
+
+/* constructor / destructor */
+extern struct nf_conntrack *nfct_new(void);
+extern void nfct_destroy(struct nf_conntrack *ct);
+
+/* clone */
+struct nf_conntrack *nfct_clone(const struct nf_conntrack *ct);
+
+/* object size */
+extern size_t nfct_sizeof(const struct nf_conntrack *ct);
+
+/* set option */
+enum {
+	NFCT_SOPT_UNDO_SNAT,
+	NFCT_SOPT_UNDO_DNAT,
+	NFCT_SOPT_UNDO_SPAT,
+	NFCT_SOPT_UNDO_DPAT,
+	__NFCT_SOPT_MAX,
+};
+#define NFCT_SOPT_MAX (__NFCT_SOPT_MAX - 1)
+
+/* get option */
+enum {
+	NFCT_GOPT_IS_SNAT,
+	NFCT_GOPT_IS_DNAT,
+	NFCT_GOPT_IS_SPAT,
+	NFCT_GOPT_IS_DPAT,
+	__NFCT_GOPT_MAX,
+};
+#define NFCT_GOPT_MAX (__NFCT_GOPT_MAX - 1)
+
+extern int nfct_setobjopt(struct nf_conntrack *ct, unsigned int option);
+extern int nfct_getobjopt(const struct nf_conntrack *ct, unsigned int option);
+
+/* register / unregister callback */
+
+extern int nfct_callback_register(struct nfct_handle *h,
+				  enum nf_conntrack_msg_type type,
+				  int (*cb)(enum nf_conntrack_msg_type type,
+				  	    struct nf_conntrack *ct,
+					    void *data),
+				  void *data);
+
+extern void nfct_callback_unregister(struct nfct_handle *h);
+
+/* callback verdict */
+enum {
+	NFCT_CB_FAILURE = -1,   /* failure */
+	NFCT_CB_STOP = 0,       /* stop the query */
+	NFCT_CB_CONTINUE = 1,   /* keep iterating through data */
+	NFCT_CB_STOLEN = 2,     /* like continue, but ct is not freed */
+};
+
+/* setter */
+extern void nfct_set_attr(struct nf_conntrack *ct,
+			  const enum nf_conntrack_attr type,
+			  void *value);
+
+extern void nfct_set_attr_u8(struct nf_conntrack *ct,
+			     const enum nf_conntrack_attr type,
+			     u_int8_t value);
+
+extern void nfct_set_attr_u16(struct nf_conntrack *ct,
+			      const enum nf_conntrack_attr type,
+			      u_int16_t value);
+
+extern void nfct_set_attr_u32(struct nf_conntrack *ct,
+			      const enum nf_conntrack_attr type,
+			      u_int32_t value);
+
+/* getter */
+extern const void *nfct_get_attr(const struct nf_conntrack *ct,
+				 const enum nf_conntrack_attr type);
+
+extern u_int8_t nfct_get_attr_u8(const struct nf_conntrack *ct,
+				 const enum nf_conntrack_attr type);
+
+extern u_int16_t nfct_get_attr_u16(const struct nf_conntrack *ct,
+				   const enum nf_conntrack_attr type);
+
+extern u_int32_t nfct_get_attr_u32(const struct nf_conntrack *ct,
+				   const enum nf_conntrack_attr type);
+
+/* checker */
+extern int nfct_attr_is_set(const struct nf_conntrack *ct,
+			    const enum nf_conntrack_attr type);
+
+/* unsetter */
+extern int nfct_attr_unset(struct nf_conntrack *ct,
+			   const enum nf_conntrack_attr type);
+
+/* print */
+
+/* output type */
+enum {
+	NFCT_O_DEFAULT,
+	NFCT_O_XML,
+	NFCT_O_MAX
+};
+
+/* output flags */
+enum {
+	NFCT_OF_SHOW_LAYER3_BIT = 0,
+	NFCT_OF_SHOW_LAYER3 = (1 << NFCT_OF_SHOW_LAYER3_BIT),
+};
+
+extern int nfct_snprintf(char *buf, 
+			 unsigned int size,
+			 const struct nf_conntrack *ct,
+			 const unsigned int msg_type,
+			 const unsigned int out_type,
+			 const unsigned int out_flags);
+
+/* query */
+enum nf_conntrack_query {
+	NFCT_Q_CREATE,
+	NFCT_Q_UPDATE,
+	NFCT_Q_DESTROY,
+	NFCT_Q_GET,
+	NFCT_Q_FLUSH,
+	NFCT_Q_DUMP,
+	NFCT_Q_DUMP_RESET,
+};
+
+extern int nfct_query(struct nfct_handle *h,
+		      const enum nf_conntrack_query query,
+		      const void *data);
+
+extern int nfct_catch(struct nfct_handle *h);
+
+/* low level API: netlink functions */
+
+extern int nfct_build_conntrack(struct nfnl_subsys_handle *ssh,
+				void *req,
+				size_t size,
+				u_int16_t type,
+				u_int16_t flags,
+				const struct nf_conntrack *ct);
+
+extern int nfct_parse_conntrack(enum nf_conntrack_msg_type msg,
+				const struct nlmsghdr *nlh, 
+				struct nf_conntrack *ct);
+
+extern int nfct_build_query(struct nfnl_subsys_handle *ssh,
+			    const enum nf_conntrack_query query,
+			    const void *data,
+			    void *req,
+			    unsigned int size);
+
 #endif	/* _LIBNETFILTER_CONNTRACK_H_ */

Modified: trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/libnetfilter_conntrack_tcp.h
===================================================================
--- trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/libnetfilter_conntrack_tcp.h	2006-12-19 17:12:21 UTC (rev 6714)
+++ trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/libnetfilter_conntrack_tcp.h	2006-12-19 17:41:53 UTC (rev 6715)
@@ -8,6 +8,21 @@
 #ifndef _LIBNETFILTER_CONNTRACK_TCP_H_
 #define _LIBNETFILTER_CONNTRACK_TCP_H_
 
+enum tcp_state {
+	TCP_CONNTRACK_NONE,
+	TCP_CONNTRACK_SYN_SENT,
+	TCP_CONNTRACK_SYN_RECV,
+	TCP_CONNTRACK_ESTABLISHED,
+	TCP_CONNTRACK_FIN_WAIT,
+	TCP_CONNTRACK_CLOSE_WAIT,
+	TCP_CONNTRACK_LAST_ACK,
+	TCP_CONNTRACK_TIME_WAIT,
+	TCP_CONNTRACK_CLOSE,
+	TCP_CONNTRACK_LISTEN,
+	TCP_CONNTRACK_MAX,
+	TCP_CONNTRACK_IGNORE
+};
+
 enum tcp_flags {
 	TCP_ORIG_SPORT_BIT = 0,
 	TCP_ORIG_SPORT = (1 << TCP_ORIG_SPORT_BIT),

Modified: trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/linux_nfnetlink_conntrack.h
===================================================================
--- trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/linux_nfnetlink_conntrack.h	2006-12-19 17:12:21 UTC (rev 6714)
+++ trunk/libnetfilter_conntrack/include/libnetfilter_conntrack/linux_nfnetlink_conntrack.h	2006-12-19 17:41:53 UTC (rev 6715)
@@ -27,13 +27,15 @@
 	CTA_STATUS,
 	CTA_PROTOINFO,
 	CTA_HELP,
-	CTA_NAT,
+	CTA_NAT_SRC,
+#define CTA_NAT	CTA_NAT_SRC	/* backwards compatibility */
 	CTA_TIMEOUT,
 	CTA_MARK,
 	CTA_COUNTERS_ORIG,
 	CTA_COUNTERS_REPLY,
 	CTA_USE,
 	CTA_ID,
+	CTA_NAT_DST,
 	__CTA_MAX
 };
 #define CTA_MAX (__CTA_MAX - 1)

Modified: trunk/libnetfilter_conntrack/src/Makefile.am
===================================================================
--- trunk/libnetfilter_conntrack/src/Makefile.am	2006-12-19 17:12:21 UTC (rev 6714)
+++ trunk/libnetfilter_conntrack/src/Makefile.am	2006-12-19 17:41:53 UTC (rev 6715)
@@ -4,11 +4,14 @@
 
 #EXTRA_DIST = $(man_MANS) acinclude.m4
 
+SUBDIRS=conntrack
+
 AM_CFLAGS = -fPIC -Wall
 LIBS = @LIBNFCONNTRACK_LIBS@
 
 lib_LTLIBRARIES = libnetfilter_conntrack.la 
 
+libnetfilter_conntrack_la_LIBADD = conntrack/libnetfilter_conntrack_new_api.la
 libnetfilter_conntrack_la_LDFLAGS = -Wc,-nostartfiles -lnfnetlink -ldl 	\
 				    -version-info $(LIBVERSION)
 libnetfilter_conntrack_la_SOURCES = libnetfilter_conntrack.c 

Added: trunk/libnetfilter_conntrack/src/conntrack/Makefile.am
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/Makefile.am	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/Makefile.am	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,17 @@
+include $(top_srcdir)/Make_global.am
+
+#AUTOMAKE_OPTIONS = no-dependencies foreign
+
+#EXTRA_DIST = $(man_MANS) acinclude.m4
+
+AM_CFLAGS = -fPIC -Wall
+LIBS = @LIBNFCONNTRACK_LIBS@
+
+noinst_LTLIBRARIES = libnetfilter_conntrack_new_api.la 
+
+libnetfilter_conntrack_new_api_la_SOURCES = api.c callback.c \
+					    getter.c setter.c \
+					    parse.c build.c \
+					    snprintf.c \
+					    snprintf_default.c snprintf_xml.c \
+					    objopt.c

Added: trunk/libnetfilter_conntrack/src/conntrack/api.c
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/api.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/api.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,596 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <stdlib.h>
+#include <string.h> /* for memset */
+#include <errno.h>
+#include <assert.h>
+
+#include "internal.h"
+
+/**
+ * nfct_conntrack_new - allocate a new conntrack
+ *
+ * In case of success, this function returns a valid pointer to a memory blob,
+ * otherwise NULL is returned and errno is set appropiately.
+ */
+struct nf_conntrack *nfct_new()
+{
+	struct nf_conntrack *ct;
+
+	ct = malloc(sizeof(struct nf_conntrack));
+	if (!ct)
+		return NULL;
+
+	memset(ct, 0, sizeof(struct nf_conntrack));
+
+	/* always work with confirmed conntracks */
+//	ct->status |= IPS_CONFIRMED;
+
+	return ct;
+}
+
+/**
+ * nf_conntrack_destroy - release a conntrack object
+ * @ct: pointer to the conntrack object
+ */
+void nfct_destroy(struct nf_conntrack *ct)
+{
+	assert(ct != NULL);
+	free(ct);
+	ct = NULL; /* bugtrap */
+}
+
+/**
+ * nf_sizeof - return the size of a certain conntrack object
+ * @ct: pointer to the conntrack object
+ */
+size_t nfct_sizeof(const struct nf_conntrack *ct)
+{
+	assert(ct != NULL);
+	return sizeof(*ct);
+}
+
+/**
+ * nfct_clone - clone a conntrack object
+ * @ct: pointer to a valid conntrack object
+ *
+ * On error, NULL is returned and errno is appropiately set. Otherwise,
+ * a valid pointer to the clone conntrack is returned.
+ */
+struct nf_conntrack *nfct_clone(const struct nf_conntrack *ct)
+{
+	struct nf_conntrack *clone;
+
+	assert(ct != NULL);
+
+	if ((clone = nfct_new()) == NULL)
+		return NULL;
+	memcpy(clone, ct, sizeof(*ct));
+
+	return clone;
+}
+
+/**
+ * nfct_setobjopt - set a certain option for a conntrack object
+ * @ct: conntrack object
+ * @option: option parameter
+ *
+ * In case of error, -1 is returned and errno is appropiately set. On success,
+ * 0 is returned.
+ */
+int nfct_setobjopt(struct nf_conntrack *ct, unsigned int option)
+{
+	assert(ct != NULL);
+
+	if (option > NFCT_SOPT_MAX) {
+		errno = EOPNOTSUPP;
+		return -1;
+	}
+
+	return __setobjopt(ct, option);
+}
+
+/**
+ * nfct_getobjopt - get a certain option for a conntrack object
+ * @ct: conntrack object
+ * @option: option parameter
+ *
+ * In case of error, -1 is returned and errno is appropiately set. On success,
+ * 0 is returned.
+ */
+int nfct_getobjopt(const struct nf_conntrack *ct, unsigned int option)
+{
+	assert(ct != NULL);
+
+	if (option > NFCT_GOPT_MAX) {
+		errno = EOPNOTSUPP;
+		return -1;
+	}
+
+	return __getobjopt(ct, option);
+}
+
+/**
+ * nf_callback_register - register a callback
+ * @h: library handler
+ * @cb: callback used to process conntrack received
+ * @data: data used by the callback, if any.
+ *
+ * This function register a callback to handle the conntrack received, 
+ * in case of error -1 is returned and errno is set appropiately, otherwise
+ * 0 is returned.
+ *
+ * Note that the data parameter is optional, if you do not want to pass any
+ * data to your callback, then use NULL.
+ */
+int nfct_callback_register(struct nfct_handle *h,
+			   enum nf_conntrack_msg_type type,
+			   int (*cb)(enum nf_conntrack_msg_type type,
+			   	     struct nf_conntrack *ct, 
+				     void *data),
+			   void *data)
+{
+	struct __data_container *container;
+
+	assert(h != NULL);
+
+	container = malloc(sizeof(struct __data_container));
+	if (!container)
+		return -1;
+	memset(container, 0, sizeof(struct __data_container));
+
+	h->cb = cb;
+	container->h = h;
+	container->type = type;
+	container->data = data;
+
+	h->nfnl_cb.call = __callback;
+	h->nfnl_cb.data = container;
+	h->nfnl_cb.attr_count = CTA_MAX;
+
+	nfnl_callback_register(h->nfnlssh_ct, 
+			       IPCTNL_MSG_CT_NEW,
+			       &h->nfnl_cb);
+
+	nfnl_callback_register(h->nfnlssh_ct,
+			       IPCTNL_MSG_CT_DELETE,
+			       &h->nfnl_cb);
+
+	return 0;
+}
+
+/**
+ * nfct_callback_unregister - unregister a callback
+ * @h: library handler
+ */
+void nfct_callback_unregister(struct nfct_handle *h)
+{
+	assert(h != NULL);
+
+	nfnl_callback_unregister(h->nfnlssh_ct, IPCTNL_MSG_CT_NEW);
+	nfnl_callback_unregister(h->nfnlssh_ct, IPCTNL_MSG_CT_DELETE);
+
+	h->cb = NULL;
+	free(h->nfnl_cb.data);
+
+	h->nfnl_cb.call = NULL;
+	h->nfnl_cb.data = NULL;
+	h->nfnl_cb.attr_count = 0;
+}
+
+/**
+ * nfct_set_attr - set the value of a certain conntrack attribute
+ * @ct: pointer to a valid conntrack
+ * @type: attribute type
+ * @value: pointer to the attribute value
+ */
+void nfct_set_attr(struct nf_conntrack *ct,
+		   const enum nf_conntrack_attr type, 
+		   void *value)
+{
+	assert(ct != NULL);
+	assert(value != NULL);
+
+	if (type >= ATTR_MAX)
+		return;
+
+	set_attr_array[type](ct, value);
+	set_bit(type, ct->set);
+}
+
+/**
+ * nfct_set_attr_u8 - set the value of a certain conntrack attribute
+ * @ct: pointer to a valid conntrack
+ * @type: attribute type
+ * @value: unsigned 8 bits attribute value
+ */
+void nfct_set_attr_u8(struct nf_conntrack *ct,
+		      const enum nf_conntrack_attr type, 
+		      u_int8_t value)
+{
+	nfct_set_attr(ct, type, &value);
+}
+
+/**
+ * nfct_set_attr_u16 - set the value of a certain conntrack attribute
+ * @ct: pointer to a valid conntrack
+ * @type: attribute type
+ * @value: unsigned 16 bits attribute value
+ */
+void nfct_set_attr_u16(struct nf_conntrack *ct,
+		       const enum nf_conntrack_attr type, 
+		       u_int16_t value)
+{
+	nfct_set_attr(ct, type, &value);
+}
+
+/**
+ * nfct_set_attr_u32 - set the value of a certain conntrack attribute
+ * @ct: pointer to a valid conntrack
+ * @type: attribute type
+ * @value: unsigned 32 bits attribute value
+ */
+void nfct_set_attr_u32(struct nf_conntrack *ct,
+		       const enum nf_conntrack_attr type, 
+		       u_int32_t value)
+{
+	nfct_set_attr(ct, type, &value);
+}
+
+/**
+ * nfct_get_attr - get a conntrack attribute
+ * ct: pointer to a valid conntrack
+ * @type: attribute type
+ *
+ * In case of success a valid pointer to the attribute requested is returned,
+ * on error NULL is returned and errno is set appropiately.
+ */
+const void *nfct_get_attr(const struct nf_conntrack *ct,
+			  const enum nf_conntrack_attr type)
+{
+	assert(ct != NULL);
+
+	if (type >= ATTR_MAX) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (!test_bit(type, ct->set)) {
+		errno = ENODATA;
+		return NULL;
+	}
+
+	return get_attr_array[type](ct);
+}
+
+/**
+ * nfct_get_attr_u8 - get attribute of unsigned 8-bits long
+ * @ct: pointer to a valid conntrack
+ * @type: attribute type
+ *
+ * On error, -1 is returned and errno is set appropiately, otherwise
+ * the value of the attribute is returned.
+ */
+u_int8_t nfct_get_attr_u8(const struct nf_conntrack *ct,
+			  const enum nf_conntrack_attr type)
+{
+	const int *ret = nfct_get_attr(ct, type);
+	return ret == NULL ? 0 : *ret;
+}
+
+/**
+ * nfct_get_attr_u16 - get attribute of unsigned 16-bits long
+ * @ct: pointer to a valid conntrack
+ * @type: attribute type
+ *
+ * On error, -1 is returned and errno is set appropiately, otherwise
+ * the value of the attribute is returned.
+ */
+u_int16_t nfct_get_attr_u16(const struct nf_conntrack *ct,
+			    const enum nf_conntrack_attr type)
+{
+	const int *ret = nfct_get_attr(ct, type);
+	return ret == NULL ? 0 : *ret;
+}
+
+/**
+ * nfct_get_attr_u32 - get attribute of unsigned 32-bits long
+ * @ct: pointer to a valid conntrack
+ * @type: attribute type
+ *
+ * On error, -1 is returned and errno is set appropiately, otherwise
+ * the value of the attribute is returned.
+ */
+u_int32_t nfct_get_attr_u32(const struct nf_conntrack *ct,
+			    const enum nf_conntrack_attr type)
+{
+	const int *ret = nfct_get_attr(ct, type);
+	return ret == NULL ? 0 : *ret;
+}
+
+/**
+ * nfct_attr_is_set - check if a certain attribute is set
+ * @ct: pointer to a valid conntrack object
+ * @type: attribute type
+ *
+ * On error, -1 is returned and errno is set appropiately, otherwise
+ * the value of the attribute is returned.
+ */
+int nfct_attr_is_set(const struct nf_conntrack *ct,
+		     const enum nf_conntrack_attr type)
+{
+	assert(ct != NULL);
+
+	if (type >= ATTR_MAX) {
+		errno = EINVAL;
+		return -1;
+	}
+	return test_bit(type, ct->set);
+}
+
+/**
+ * nfct_attr_unset - unset a certain attribute
+ * @type: attribute type
+ * @ct: pointer to a valid conntrack object
+ * 
+ * On error, -1 is returned and errno is set appropiately, otherwise
+ * 0 is returned.
+ */
+int nfct_attr_unset(struct nf_conntrack *ct,
+		    const enum nf_conntrack_attr type)
+{
+	assert(ct != NULL);
+
+	if (type >= ATTR_MAX) {
+		errno = EINVAL;
+		return -1;
+	}
+	unset_bit(type, ct->set);
+
+	return 0;
+}
+
+/**
+ * nfct_build_conntrack - build a netlink message from a conntrack object
+ * @ssh: nfnetlink subsystem handler
+ * @req: buffer used to build the netlink message
+ * @size: size of the buffer passed
+ * @type: netlink message type
+ * @flags: netlink flags
+ * @ct: pointer to a conntrack object
+ *
+ * This is a low level function for those that require to be close to
+ * netlink details via libnfnetlink. If you do want to obviate the netlink
+ * details then we suggest you to use nfct_query.
+ *
+ * On error, -1 is returned and errno is appropiately set.
+ * On success, 0 is returned.
+ */
+int nfct_build_conntrack(struct nfnl_subsys_handle *ssh,
+			 void *req,
+			 size_t size,
+			 u_int16_t type,
+			 u_int16_t flags,
+			 const struct nf_conntrack *ct)
+{
+	assert(ssh != NULL);
+	assert(req != NULL);
+	assert(ct != NULL);
+
+	return __build_conntrack(ssh, req, size, type, flags, ct);
+}
+
+/**
+ * nfct_build_query - build a query in netlink message format for ctnetlink
+ * @ssh: nfnetlink subsystem handler
+ * @qt: query type
+ * @data: data required to build the query
+ * @req: buffer to build the netlink message
+ * @size: size of the buffer passed
+ *
+ * This is a low level function, use it if you want to require to work
+ * with netlink details via libnfnetlink, otherwise we suggest you to
+ * use nfct_query.
+ *
+ * The pointer to data can be a conntrack object or the protocol family
+ * depending on the request.
+ *
+ * For query types:
+ * 	NFCT_Q_CREATE
+ * 	NFCT_Q_UPDATE
+ * 	NFCT_Q_DESTROY
+ * 	NFCT_Q_GET
+ *
+ * Pass a valid pointer to a conntrack object.
+ *
+ * For query types:
+ * 	NFCT_Q_FLUSH
+ * 	NFCT_Q_DUMP
+ * 	NFCT_Q_DUMP_RESET
+ *
+ * Pass a valid pointer to the protocol family (u_int8_t)
+ *
+ * On success, 0 is returned. On error, -1 is returned and errno is set
+ * appropiately.
+ */
+int nfct_build_query(struct nfnl_subsys_handle *ssh,
+		     const enum nf_conntrack_query qt,
+		     const void *data,
+		     void *buffer,
+		     unsigned int size)
+{
+	struct nfnlhdr *req = buffer;
+	const u_int8_t *family = data;
+
+	assert(ssh != NULL);
+	assert(data != NULL);
+	assert(req != NULL);
+
+	memset(req, 0, size);
+
+	switch(qt) {
+	case NFCT_Q_CREATE:
+		nfct_build_conntrack(ssh, req, size, IPCTNL_MSG_CT_NEW, NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK|NLM_F_EXCL, data);
+		break;
+	case NFCT_Q_UPDATE:
+		nfct_build_conntrack(ssh, req, size, IPCTNL_MSG_CT_NEW, NLM_F_REQUEST|NLM_F_ACK, data);
+		break;
+	case NFCT_Q_DESTROY:
+		nfct_build_conntrack(ssh, req, size, IPCTNL_MSG_CT_DELETE, NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST|NLM_F_ACK, data);
+		break;
+	case NFCT_Q_GET:
+		nfct_build_conntrack(ssh, req, size, IPCTNL_MSG_CT_GET, NLM_F_REQUEST|NLM_F_ACK, data);
+		break;
+	case NFCT_Q_FLUSH:
+		nfnl_fill_hdr(ssh, &req->nlh, 0, *family, 0, IPCTNL_MSG_CT_DELETE, NLM_F_REQUEST|NLM_F_ACK);
+		break;
+	case NFCT_Q_DUMP:
+		nfnl_fill_hdr(ssh, &req->nlh, 0, *family, 0, IPCTNL_MSG_CT_GET, NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST|NLM_F_DUMP);
+		break;
+	case NFCT_Q_DUMP_RESET:
+		nfnl_fill_hdr(ssh, &req->nlh, 0, *family, 0, IPCTNL_MSG_CT_GET_CTRZERO, NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST|NLM_F_DUMP);
+		break;
+	default:
+		errno = ENOTSUP;
+		return -1;
+	}
+	return 1;
+}
+
+/**
+ * nfct_parse_conntrack - translate a netlink message to a conntrack object
+ * @type: do the translation iif the message type is of a certain type
+ * @nlh: pointer to the netlink message
+ * @ct: pointer to the conntrack object
+ *
+ * This is a low level function, use it in case that you require to work
+ * with netlink details via libnfnetlink. Otherwise, we suggest you to
+ * use the high level API.
+ *
+ * The message types are:
+ *
+ * NFCT_T_NEW: parse messages with new conntracks
+ * NFCT_T_UPDATE: parse messages with conntrack updates
+ * NFCT_T_DESTROY: parse messages with conntrack destroy 
+ * NFCT_T_ALL: all message types
+ *
+ * The message type is a flag, therefore the can be combined, ie.
+ * NFCT_T_NEW | NFCT_T_DESTROY to parse only new and destroy messages
+ *
+ * On error, NFCT_T_ERROR is returned and errno is set appropiately. If 
+ * the message received is not of the requested type then 0 is returned, 
+ * otherwise this function returns the message type parsed.
+ */
+int nfct_parse_conntrack(enum nf_conntrack_msg_type type,
+			 const struct nlmsghdr *nlh,
+			 struct nf_conntrack *ct)
+{
+	unsigned int flags;
+	int len = nlh->nlmsg_len;
+	struct nfgenmsg *nfhdr = NLMSG_DATA(nlh);
+	struct nfattr *cda[CTA_MAX];
+
+	assert(nlh != NULL);
+	assert(ct != NULL);
+
+	len -= NLMSG_LENGTH(sizeof(struct nfgenmsg));
+	if (len < 0) {
+		errno = EINVAL;
+		return NFCT_T_ERROR;
+	}
+
+	flags = __parse_message_type(nlh);
+	if (!(flags & type))
+		return 0;
+
+	nfnl_parse_attr(cda, CTA_MAX, NFA_DATA(nfhdr), len);
+
+	__parse_conntrack(nlh, cda, ct);
+
+	return flags;
+}
+
+/**
+ * nfct_query - send a query to ctnetlin
+ * @h: library handler
+ * @qt: query type
+ * @data: data required to send the query
+ *
+ * On error, -1 is returned and errno is explicitely set. On success, 0
+ * is returned.
+ */
+int nfct_query(struct nfct_handle *h,
+	       const enum nf_conntrack_query qt,
+	       const void *data)
+{
+	size_t size = 4096;	/* enough for now */
+	char buffer[4096];
+	struct nfnlhdr *req = (struct nfnlhdr *) buffer;
+
+	assert(h != NULL);
+	assert(data != NULL);
+
+	if (nfct_build_query(h->nfnlssh_ct, qt, data, req, size) == -1)
+		return -1;
+
+	return nfnl_query(h->nfnlh, &req->nlh);
+}
+
+/**
+ * nfct_catch - catch events
+ * @h: library handler
+ *
+ * On error, -1 is returned and errno is set appropiately. On success, 
+ * a value greater or equal to 0 is returned indicating the callback
+ * verdict: NFCT_CB_STOP, NFCT_CB_CONTINUE or NFCT_CB_STOLEN
+ */
+int nfct_catch(struct nfct_handle *h)
+{
+	assert(h != NULL);
+
+	return nfnl_catch(h->nfnlh);
+}
+
+/**
+ * nfct_snprintf - print a conntrack object to a buffer
+ * @buf: buffer used to build the printable conntrack
+ * @size: size of the buffer
+ * @ct: pointer to a valid conntrack object
+ * @message_type: print message type (NFCT_T_UNKNOWN, NFCT_T_NEW,...)
+ * @output_type: print type (NFCT_O_DEFAULT, NFCT_O_XML, ...)
+ * @flags: extra flags for the output type (NFCT_OF_LAYER3)
+ *
+ * If you are listening to events, probably you want to display the message 
+ * type as well. In that case, set the message type parameter to any of the
+ * known existing types, ie. NFCT_T_NEW, NFCT_T_UPDATE, NFCT_T_DESTROY.
+ * If you pass NFCT_T_UNKNOWN, the message type will not be output. 
+ *
+ * Currently, the output available are:
+ * 	- NFCT_O_DEFAULT: default /proc-like output
+ * 	- NFCT_O_XML: XML output
+ *
+ * The output flags are:
+ * 	- NFCT_O_LAYER: include layer 3 information in the output, this is
+ * 			*only* required by NFCT_O_DEFAULT.
+ *
+ * On error, -1 is returned and errno is set appropiately. Otherwise,
+ * 0 is returned.
+ */
+int nfct_snprintf(char *buf,
+		  unsigned int size,
+		  const struct nf_conntrack *ct,
+		  unsigned int msg_type,
+		  unsigned int out_type,
+		  unsigned int flags) 
+{
+	assert(buf != NULL);
+	assert(size > 0);
+	assert(ct != NULL);
+
+	return __snprintf_conntrack(buf, size, ct, msg_type, out_type, flags);
+}

Added: trunk/libnetfilter_conntrack/src/conntrack/build.c
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/build.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/build.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,284 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "internal.h"
+
+void __build_tuple_ip(struct nfnlhdr *req, 
+		      size_t size,
+		      const struct __nfct_tuple *t)
+{
+	struct nfattr *nest;
+
+	nest = nfnl_nest(&req->nlh, size, CTA_TUPLE_IP);
+
+	switch(t->l3protonum) {
+	case AF_INET:
+	        nfnl_addattr_l(&req->nlh, size, CTA_IP_V4_SRC, &t->src.v4,
+			       sizeof(u_int32_t));
+		nfnl_addattr_l(&req->nlh, size, CTA_IP_V4_DST, &t->dst.v4,
+			       sizeof(u_int32_t));
+		break;
+	case AF_INET6:
+		nfnl_addattr_l(&req->nlh, size, CTA_IP_V6_SRC, &t->src.v6,
+			       sizeof(struct in6_addr));
+		nfnl_addattr_l(&req->nlh, size, CTA_IP_V6_DST, &t->dst.v6,
+			       sizeof(struct in6_addr));
+		break;
+	default:
+		break;
+	}
+
+	nfnl_nest_end(&req->nlh, nest);
+}
+
+void __build_tuple_proto(struct nfnlhdr *req,
+			 size_t size,
+			 const struct __nfct_tuple *t)
+{
+	struct nfattr *nest;
+
+	nest = nfnl_nest(&req->nlh, size, CTA_TUPLE_PROTO);
+
+	nfnl_addattr_l(&req->nlh, size, CTA_PROTO_NUM, &t->protonum,
+		       sizeof(u_int8_t));
+
+	switch(t->protonum) {
+	case IPPROTO_UDP:
+	case IPPROTO_TCP:
+	case IPPROTO_SCTP:
+		nfnl_addattr_l(&req->nlh, size, CTA_PROTO_SRC_PORT,
+			       &t->l4src.tcp.port, sizeof(u_int16_t));
+		nfnl_addattr_l(&req->nlh, size, CTA_PROTO_DST_PORT,
+			       &t->l4dst.tcp.port, sizeof(u_int16_t));
+		break;
+	case IPPROTO_ICMP:
+		nfnl_addattr_l(&req->nlh, size, CTA_PROTO_ICMP_CODE,
+			       &t->l4dst.icmp.code, sizeof(u_int8_t));
+		nfnl_addattr_l(&req->nlh, size, CTA_PROTO_ICMP_TYPE,
+			       &t->l4dst.icmp.type, sizeof(u_int8_t));
+		nfnl_addattr_l(&req->nlh, size, CTA_PROTO_ICMP_ID,
+			       &t->l4src.icmp.id, sizeof(u_int16_t));
+		break;
+	default:
+		break;
+	}
+
+	nfnl_nest_end(&req->nlh, nest);
+}
+
+void __build_tuple(struct nfnlhdr *req, 
+		   size_t size, 
+		   const struct __nfct_tuple *t, 
+		   const int type)
+{
+	struct nfattr *nest;
+
+	nest = nfnl_nest(&req->nlh, size, type);
+
+	__build_tuple_ip(req, size, t);
+	__build_tuple_proto(req, size, t);
+
+	nfnl_nest_end(&req->nlh, nest);
+}
+
+void __build_protoinfo(struct nfnlhdr *req,
+		       size_t size,
+		       const struct nf_conntrack *ct)
+{
+	struct nfattr *nest, *nest_proto;
+
+	switch(ct->tuple[__DIR_ORIG].protonum) {
+	case IPPROTO_TCP:
+		nest = nfnl_nest(&req->nlh, size, CTA_PROTOINFO);
+		nest_proto = nfnl_nest(&req->nlh, size, CTA_PROTOINFO_TCP);
+		nfnl_addattr_l(&req->nlh, size, CTA_PROTOINFO_TCP_STATE,
+			       &ct->protoinfo.tcp.state, sizeof(u_int8_t));
+		nfnl_nest_end(&req->nlh, nest_proto);
+		nfnl_nest_end(&req->nlh, nest);
+		break;
+	default:
+		break;
+	}
+}
+
+void __build_protonat(struct nfnlhdr *req,
+		      size_t size,
+		      const struct nf_conntrack *ct,
+		      const struct __nfct_nat *nat)
+{
+	struct nfattr *nest;
+
+	nest = nfnl_nest(&req->nlh, size, CTA_NAT_PROTO);
+
+	switch (ct->tuple[NFCT_DIR_ORIGINAL].protonum) {
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+		nfnl_addattr_l(&req->nlh, size, CTA_PROTONAT_PORT_MIN,
+			       &nat->l4min.tcp.port, sizeof(u_int16_t));
+		nfnl_addattr_l(&req->nlh, size, CTA_PROTONAT_PORT_MAX,
+			       &nat->l4max.tcp.port, sizeof(u_int16_t));
+		break;
+	}
+	nfnl_nest_end(&req->nlh, nest);
+}
+
+void __build_nat(struct nfnlhdr *req,
+		 size_t size,
+		 const struct __nfct_nat *nat)
+{
+	nfnl_addattr_l(&req->nlh, size, CTA_NAT_MINIP,
+		       &nat->min_ip, sizeof(u_int32_t));
+}
+
+void __build_snat(struct nfnlhdr *req,
+		  size_t size,
+		  const struct nf_conntrack *ct)
+{
+	struct nfattr *nest;
+
+	nest = nfnl_nest(&req->nlh, size, CTA_NAT_SRC);
+	__build_nat(req, size, &ct->snat);
+	__build_protonat(req, size, ct, &ct->snat);
+	nfnl_nest_end(&req->nlh, nest);
+}
+
+void __build_snat_ipv4(struct nfnlhdr *req,
+		       size_t size,
+		       const struct nf_conntrack *ct)
+{
+	struct nfattr *nest;
+
+	nest = nfnl_nest(&req->nlh, size, CTA_NAT_SRC);
+	__build_nat(req, size, &ct->snat);
+	nfnl_nest_end(&req->nlh, nest);
+}
+
+void __build_snat_port(struct nfnlhdr *req,
+		       size_t size,
+		       const struct nf_conntrack *ct)
+{
+	struct nfattr *nest;
+
+	nest = nfnl_nest(&req->nlh, size, CTA_NAT_SRC);
+	__build_protonat(req, size, ct, &ct->snat);
+	nfnl_nest_end(&req->nlh, nest);
+}
+
+void __build_dnat(struct nfnlhdr *req,
+		  size_t size, 
+		  const struct nf_conntrack *ct)
+{
+	struct nfattr *nest;
+
+	nest = nfnl_nest(&req->nlh, size, CTA_NAT_DST);
+	__build_nat(req, size, &ct->dnat);
+	__build_protonat(req, size, ct, &ct->dnat);
+	nfnl_nest_end(&req->nlh, nest);
+}
+
+void __build_dnat_ipv4(struct nfnlhdr *req,
+		       size_t size, 
+		       const struct nf_conntrack *ct)
+{
+	struct nfattr *nest;
+
+	nest = nfnl_nest(&req->nlh, size, CTA_NAT_DST);
+	__build_nat(req, size, &ct->dnat);
+	nfnl_nest_end(&req->nlh, nest);
+}
+
+void __build_dnat_port(struct nfnlhdr *req,
+		       size_t size, 
+		       const struct nf_conntrack *ct)
+{
+	struct nfattr *nest;
+
+	nest = nfnl_nest(&req->nlh, size, CTA_NAT_DST);
+        __build_protonat(req, size, ct, &ct->dnat);
+	nfnl_nest_end(&req->nlh, nest);
+}
+
+void __build_status(struct nfnlhdr *req,
+		    size_t size,
+		    const struct nf_conntrack *ct)
+{
+	nfnl_addattr32(&req->nlh, size, CTA_STATUS,
+		       htonl(ct->status | IPS_CONFIRMED));
+}
+
+void __build_timeout(struct nfnlhdr *req,
+			size_t size,
+			const struct nf_conntrack *ct)
+{
+	nfnl_addattr32(&req->nlh, size, CTA_TIMEOUT, htonl(ct->timeout));
+}
+
+void __build_mark(struct nfnlhdr *req,
+		  size_t size,
+		  const struct nf_conntrack *ct)
+{
+	nfnl_addattr32(&req->nlh, size, CTA_MARK, htonl(ct->mark));
+}
+
+void __build_id(struct nfnlhdr *req,
+		size_t size,
+		const const struct nf_conntrack *ct)
+{
+	nfnl_addattr32(&req->nlh, size, CTA_ID, htonl(ct->id));
+}
+
+int __build_conntrack(struct nfnl_subsys_handle *ssh,
+		      struct nfnlhdr *req,
+		      size_t size,
+		      u_int16_t type,
+		      u_int16_t flags,
+		      const struct nf_conntrack *ct)
+{
+	u_int8_t l3num = ct->tuple[NFCT_DIR_ORIGINAL].l3protonum;
+
+	if (!test_bit(ATTR_ORIG_L3PROTO, ct->set)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	memset(req, 0, size);
+
+	nfnl_fill_hdr(ssh, &req->nlh, 0, l3num, 0, type, flags);
+
+	__build_tuple(req, size, &ct->tuple[__DIR_ORIG], CTA_TUPLE_ORIG);
+	__build_tuple(req, size, &ct->tuple[__DIR_REPL], CTA_TUPLE_REPLY);
+
+	/* always build IPS_CONFIRMED */
+	__build_status(req, size, ct);
+
+	if (test_bit(ATTR_TIMEOUT, ct->set))
+		__build_timeout(req, size, ct);
+
+	if (test_bit(ATTR_MARK, ct->set))
+		__build_mark(req, size, ct);
+
+	if (test_bit(ATTR_TCP_STATE, ct->set))
+		__build_protoinfo(req, size, ct);
+
+	if (test_bit(ATTR_SNAT_IPV4, ct->set) && 
+	    test_bit(ATTR_SNAT_PORT, ct->set))
+		__build_snat(req, size, ct);
+	else if (test_bit(ATTR_SNAT_IPV4, ct->set))
+		__build_snat_ipv4(req, size, ct);
+	else if (test_bit(ATTR_SNAT_PORT, ct->set))
+		__build_snat_port(req, size, ct);
+
+	if (test_bit(ATTR_DNAT_IPV4, ct->set) &&
+	    test_bit(ATTR_DNAT_PORT, ct->set))
+		__build_dnat(req, size, ct);
+	else if (test_bit(ATTR_DNAT_IPV4, ct->set))
+		__build_dnat_ipv4(req, size, ct);
+	else if (test_bit(ATTR_DNAT_PORT, ct->set))
+		__build_dnat_port(req, size, ct);
+
+	return 0;
+}

Added: trunk/libnetfilter_conntrack/src/conntrack/callback.c
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/callback.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/callback.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,53 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "internal.h"
+
+int __callback(struct nlmsghdr *nlh, struct nfattr *nfa[], void *data)
+{
+	int ret = NFNL_CB_STOP;
+	unsigned int type;
+	struct nf_conntrack *ct;
+	int len = nlh->nlmsg_len;
+	struct __data_container *container = data;
+
+	len -= NLMSG_LENGTH(sizeof(struct nfgenmsg));
+	if (len < 0)
+		return NFNL_CB_CONTINUE;
+
+	type = __parse_message_type(nlh);
+	if (!(type & container->type))
+		return NFNL_CB_CONTINUE;
+
+	ct = nfct_new();
+	if (!ct)
+		return NFNL_CB_CONTINUE;
+
+	__parse_conntrack(nlh, nfa, ct);
+
+	if (container->h->cb)
+		ret = container->h->cb(type, ct, container->data);
+
+	switch(ret) {
+	case NFCT_CB_FAILURE:
+		free(ct);
+		ret = NFNL_CB_FAILURE;
+		break;
+	case NFCT_CB_STOP:
+		free(ct);
+		ret = NFNL_CB_STOP;
+		break;
+	case NFCT_CB_CONTINUE:
+		free(ct);
+		ret = NFNL_CB_CONTINUE;
+		break;
+	case NFCT_CB_STOLEN:
+		ret = NFNL_CB_CONTINUE;
+		break;
+	}
+	return ret;
+}

Added: trunk/libnetfilter_conntrack/src/conntrack/getter.c
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/getter.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/getter.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,197 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "internal.h"
+
+static const void *get_attr_orig_ipv4_src(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].src.v4;
+}
+
+static const void *get_attr_orig_ipv4_dst(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].dst.v4;
+}
+
+static const void *get_attr_repl_ipv4_src(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_REPL].src.v4;
+}
+
+static const void *get_attr_repl_ipv4_dst(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_REPL].dst.v4;
+}
+
+static const void *get_attr_orig_ipv6_src(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].src.v6;
+}
+
+static const void *get_attr_orig_ipv6_dst(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].dst.v6;
+}
+
+static const void *get_attr_repl_ipv6_src(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_REPL].src.v6;
+}
+
+static const void *get_attr_repl_ipv6_dst(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_REPL].dst.v6;
+}
+
+static const void *get_attr_orig_port_src(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].l4src.all; 
+}
+
+static const void *get_attr_orig_port_dst(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].l4dst.all; 
+}
+
+static const void *get_attr_repl_port_src(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_REPL].l4src.all; 
+}
+
+static const void *get_attr_repl_port_dst(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_REPL].l4dst.all;
+}
+
+static const void *get_attr_icmp_type(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].l4dst.icmp.type;
+}
+
+static const void *get_attr_icmp_code(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].l4dst.icmp.code;
+}
+
+static const void *get_attr_icmp_id(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].l4src.icmp.id;
+}
+
+static const void *get_attr_orig_l3proto(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].l3protonum;
+}
+
+static const void *get_attr_repl_l3proto(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_REPL].l3protonum;
+}
+
+static const void *get_attr_orig_l4proto(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_ORIG].protonum;
+}
+
+static const void *get_attr_repl_l4proto(const struct nf_conntrack *ct)
+{
+	return &ct->tuple[__DIR_REPL].protonum;
+}
+
+static const void *get_attr_tcp_state(const struct nf_conntrack *ct)
+{
+	return &ct->protoinfo.tcp.state;
+}
+
+static const void *get_attr_snat_ipv4(const struct nf_conntrack *ct)
+{
+	return &ct->snat.min_ip;
+}
+
+static const void *get_attr_dnat_ipv4(const struct nf_conntrack *ct)
+{
+	return &ct->dnat.min_ip;
+}
+
+static const void *get_attr_snat_port(const struct nf_conntrack *ct)
+{
+	return &ct->snat.l4min.all;
+}
+
+static const void *get_attr_dnat_port(const struct nf_conntrack *ct)
+{
+	return &ct->dnat.l4min.all;
+}
+
+static const void *get_attr_timeout(const struct nf_conntrack *ct)
+{
+	return &ct->timeout;
+}
+
+static const void *get_attr_mark(const struct nf_conntrack *ct)
+{
+	return &ct->mark;
+}
+
+static const void *get_attr_orig_counter_packets(const struct nf_conntrack *ct)
+{
+	return &ct->counters[__DIR_ORIG].packets;
+}
+
+static const void *get_attr_orig_counter_bytes(const struct nf_conntrack *ct)
+{
+	return &ct->counters[__DIR_ORIG].bytes;
+}
+
+static const void *get_attr_repl_counter_packets(const struct nf_conntrack *ct)
+{
+	return &ct->counters[__DIR_REPL].packets;
+}
+
+static const void *get_attr_repl_counter_bytes(const struct nf_conntrack *ct)
+{
+	return &ct->counters[__DIR_REPL].bytes;
+}
+
+static const void *get_attr_status(const struct nf_conntrack *ct)
+{
+	return &ct->status;
+}
+
+get_attr get_attr_array[] = {
+	[ATTR_ORIG_IPV4_SRC]		= get_attr_orig_ipv4_src,
+	[ATTR_ORIG_IPV4_DST] 		= get_attr_orig_ipv4_dst,
+	[ATTR_REPL_IPV4_SRC]		= get_attr_repl_ipv4_src,
+	[ATTR_REPL_IPV4_DST]		= get_attr_repl_ipv4_dst,
+	[ATTR_ORIG_IPV6_SRC]		= get_attr_orig_ipv6_src,
+	[ATTR_ORIG_IPV6_DST]		= get_attr_orig_ipv6_dst,
+	[ATTR_REPL_IPV6_SRC]		= get_attr_repl_ipv6_src,
+	[ATTR_REPL_IPV6_DST]		= get_attr_repl_ipv6_dst,
+	[ATTR_ORIG_PORT_SRC]		= get_attr_orig_port_src,
+	[ATTR_ORIG_PORT_DST]		= get_attr_orig_port_dst,
+	[ATTR_REPL_PORT_SRC]		= get_attr_repl_port_src,
+	[ATTR_REPL_PORT_DST]		= get_attr_repl_port_dst,
+	[ATTR_ICMP_TYPE]		= get_attr_icmp_type,
+	[ATTR_ICMP_CODE]		= get_attr_icmp_code,
+	[ATTR_ICMP_ID]			= get_attr_icmp_id,
+	[ATTR_ORIG_L3PROTO]		= get_attr_orig_l3proto,
+	[ATTR_REPL_L3PROTO]		= get_attr_repl_l3proto,
+	[ATTR_ORIG_L4PROTO]		= get_attr_orig_l4proto,
+	[ATTR_REPL_L4PROTO]		= get_attr_repl_l4proto,
+	[ATTR_TCP_STATE]		= get_attr_tcp_state,
+	[ATTR_SNAT_IPV4]		= get_attr_snat_ipv4,
+	[ATTR_DNAT_IPV4]		= get_attr_dnat_ipv4,
+	[ATTR_SNAT_PORT]		= get_attr_snat_port,
+	[ATTR_DNAT_PORT]		= get_attr_dnat_port,
+	[ATTR_TIMEOUT]			= get_attr_timeout,
+	[ATTR_MARK]			= get_attr_mark,
+	[ATTR_ORIG_COUNTER_PACKETS] 	= get_attr_orig_counter_packets,
+	[ATTR_ORIG_COUNTER_BYTES]	= get_attr_orig_counter_bytes,
+	[ATTR_REPL_COUNTER_PACKETS]	= get_attr_repl_counter_packets,
+	[ATTR_REPL_COUNTER_BYTES]	= get_attr_repl_counter_bytes,
+	[ATTR_STATUS]			= get_attr_status,
+};

Added: trunk/libnetfilter_conntrack/src/conntrack/objopt.c
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/objopt.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/objopt.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,71 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "internal.h"
+
+int __setobjopt(struct nf_conntrack *ct, unsigned int option)
+{
+	switch(option) {
+	case NFCT_SOPT_UNDO_SNAT:
+		ct->snat.min_ip = ct->tuple[__DIR_REPL].dst.v4;
+		ct->snat.max_ip = ct->snat.min_ip;
+		ct->tuple[__DIR_REPL].dst.v4 = ct->tuple[__DIR_ORIG].src.v4;
+		set_bit(ATTR_SNAT_IPV4, ct->set);
+		break;
+	case NFCT_SOPT_UNDO_DNAT:
+		ct->dnat.min_ip = ct->tuple[__DIR_REPL].src.v4;
+		ct->dnat.max_ip = ct->dnat.min_ip;
+		ct->tuple[__DIR_REPL].src.v4 = ct->tuple[__DIR_ORIG].dst.v4;
+		set_bit(ATTR_DNAT_IPV4, ct->set);
+		break;
+	case NFCT_SOPT_UNDO_SPAT:
+		ct->snat.l4min.all = ct->tuple[__DIR_REPL].l4dst.tcp.port;
+		ct->snat.l4max.all = ct->snat.l4max.all;
+		ct->tuple[__DIR_REPL].l4dst.tcp.port = 
+			ct->tuple[__DIR_ORIG].l4src.tcp.port;
+		set_bit(ATTR_SNAT_PORT, ct->set);
+		break;
+	case NFCT_SOPT_UNDO_DPAT:
+		ct->dnat.l4min.all = ct->tuple[__DIR_REPL].l4src.tcp.port;
+		ct->dnat.l4max.all = ct->dnat.l4min.all;
+		ct->tuple[__DIR_REPL].l4src.tcp.port =
+			ct->tuple[__DIR_ORIG].l4dst.tcp.port;
+		set_bit(ATTR_DNAT_PORT, ct->set);
+		break;
+	}
+	return 0;
+}
+
+int __getobjopt(const struct nf_conntrack *ct, unsigned int option)
+{
+	int ret = -1;
+
+	switch(option) {
+	case NFCT_GOPT_IS_SNAT:
+		ret = (ct->status & IPS_SRC_NAT_DONE &&
+		       ct->tuple[__DIR_REPL].dst.v4 !=
+		       ct->tuple[__DIR_ORIG].src.v4);
+		break;
+	case NFCT_GOPT_IS_DNAT:
+		ret = (ct->status & IPS_DST_NAT_DONE &&
+		       ct->tuple[__DIR_REPL].src.v4 !=
+		       ct->tuple[__DIR_ORIG].dst.v4);
+		break;
+	case NFCT_GOPT_IS_SPAT:
+		ret = (ct->status & IPS_SRC_NAT_DONE &&
+		       ct->tuple[__DIR_REPL].l4dst.tcp.port !=
+		       ct->tuple[__DIR_ORIG].l4src.tcp.port);
+		break;
+	case NFCT_GOPT_IS_DPAT:
+		ret = (ct->status & IPS_DST_NAT_DONE &&
+		       ct->tuple[__DIR_REPL].l4src.tcp.port !=
+		       ct->tuple[__DIR_ORIG].l4dst.tcp.port);
+		break;
+	}
+
+	return ret;
+}

Added: trunk/libnetfilter_conntrack/src/conntrack/parse.c
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/parse.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/parse.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,295 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "internal.h"
+
+static void __parse_ip(const struct nfattr *attr,
+		       struct __nfct_tuple *tuple,
+		       const int dir,
+		       u_int32_t *set)
+{
+	struct nfattr *tb[CTA_IP_MAX];
+
+        nfnl_parse_nested(tb, CTA_IP_MAX, attr);
+
+	if (tb[CTA_IP_V4_SRC-1]) {
+		tuple->src.v4 = *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_SRC-1]);
+		switch(dir) {
+		case __DIR_ORIG:
+			set_bit(ATTR_ORIG_IPV4_SRC, set);
+			break;
+		case __DIR_REPL:
+			set_bit(ATTR_REPL_IPV4_SRC, set);
+			break;
+		}
+	}
+
+	if (tb[CTA_IP_V4_DST-1]) {
+		tuple->dst.v4 = *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_DST-1]);
+		switch(dir) {
+		case __DIR_ORIG:
+			set_bit(ATTR_ORIG_IPV4_DST, set);
+			break;
+		case __DIR_REPL:
+			set_bit(ATTR_REPL_IPV4_DST, set);
+			break;
+		}
+	}
+
+	if (tb[CTA_IP_V6_SRC-1]) {
+		memcpy(&tuple->src.v6, NFA_DATA(tb[CTA_IP_V6_SRC-1]), 
+		       sizeof(struct in6_addr));
+		switch(dir) {
+		case __DIR_ORIG:
+			set_bit(ATTR_ORIG_IPV6_SRC, set);
+			break;
+		case __DIR_REPL:
+			set_bit(ATTR_REPL_IPV6_SRC, set);
+			break;
+		}
+	}
+
+	if (tb[CTA_IP_V6_DST-1]) {
+		memcpy(&tuple->dst.v6, NFA_DATA(tb[CTA_IP_V6_DST-1]),
+		       sizeof(struct in6_addr));
+		switch(dir) {
+		case __DIR_ORIG:
+			set_bit(ATTR_ORIG_IPV6_DST, set);
+			break;
+		case __DIR_REPL:
+			set_bit(ATTR_REPL_IPV6_DST, set);
+			break;
+		}
+	}
+}
+
+static void __parse_proto(const struct nfattr *attr,
+			  struct __nfct_tuple *tuple,
+		   const int dir,
+		   u_int32_t *set)
+{
+	struct nfattr *tb[CTA_PROTO_MAX];
+
+	nfnl_parse_nested(tb, CTA_PROTO_MAX, attr);
+
+	if (tb[CTA_PROTO_NUM-1]) {
+		tuple->protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]);
+		switch(dir) {
+		case __DIR_ORIG:
+			set_bit(ATTR_ORIG_L4PROTO, set);
+			break;
+		case __DIR_REPL:
+			set_bit(ATTR_REPL_L4PROTO, set);
+			break;
+		}
+	}
+
+	if (tb[CTA_PROTO_SRC_PORT-1]) {
+		tuple->l4src.tcp.port =
+			*(u_int16_t *)NFA_DATA(tb[CTA_PROTO_SRC_PORT-1]);
+		switch(dir) {
+		case __DIR_ORIG:
+			set_bit(ATTR_ORIG_PORT_SRC, set);
+			break;
+		case __DIR_REPL:
+			set_bit(ATTR_REPL_PORT_SRC, set);
+			break;
+		}
+	}
+	
+	if (tb[CTA_PROTO_DST_PORT-1]) {
+		tuple->l4dst.tcp.port =
+			*(u_int16_t *)NFA_DATA(tb[CTA_PROTO_DST_PORT-1]);
+		switch(dir) {
+		case __DIR_ORIG:
+			set_bit(ATTR_ORIG_PORT_DST, set);
+			break;
+		case __DIR_REPL:
+			set_bit(ATTR_REPL_PORT_DST, set);
+			break;
+		}
+	}
+	
+	if (tb[CTA_PROTO_ICMP_TYPE-1]) {
+		tuple->l4dst.icmp.type =
+			*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]);
+		set_bit(ATTR_ICMP_TYPE, set);
+	}
+	
+	if (tb[CTA_PROTO_ICMP_CODE-1]) {
+		tuple->l4dst.icmp.code =
+			*(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]);
+		set_bit(ATTR_ICMP_CODE, set);
+	}
+	
+	if (tb[CTA_PROTO_ICMP_ID-1]) {
+		tuple->l4src.icmp.id =
+			*(u_int16_t *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]);
+		set_bit(ATTR_ICMP_ID, set);
+	}
+}
+
+static void __parse_tuple(const struct nfattr *attr,
+			  struct __nfct_tuple *tuple, 
+			  int dir,
+			  u_int32_t *set)
+{
+	struct nfattr *tb[CTA_TUPLE_MAX];
+
+	nfnl_parse_nested(tb, CTA_TUPLE_MAX, attr);
+
+	if (tb[CTA_TUPLE_IP-1])
+		__parse_ip(tb[CTA_TUPLE_IP-1], tuple, dir, set);
+	if (tb[CTA_TUPLE_PROTO-1])
+		__parse_proto(tb[CTA_TUPLE_PROTO-1], tuple, dir, set);
+}
+
+static void __parse_mask(const struct nfattr *attr,
+			 struct __nfct_tuple *tuple,
+			 const u_int8_t l3protonum, 
+			 const u_int16_t protonum,
+			 u_int32_t *set)
+{
+	__parse_tuple(attr, tuple, __DIR_ORIG, set);
+}
+
+static void __parse_protoinfo_tcp(const struct nfattr *attr, 
+				  struct nf_conntrack *ct)
+{
+	struct nfattr *tb[CTA_PROTOINFO_TCP_MAX];
+
+	nfnl_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr);
+
+	if (tb[CTA_PROTOINFO_TCP_STATE-1]) {
+                ct->protoinfo.tcp.state =
+                        *(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
+		set_bit(ATTR_TCP_STATE, ct->set);
+	}
+}
+
+static void __parse_protoinfo(const struct nfattr *attr,
+			      struct nf_conntrack *ct)
+{
+	struct nfattr *tb[CTA_PROTOINFO_MAX];
+
+	nfnl_parse_nested(tb, CTA_PROTOINFO_MAX, attr);
+
+	if (!tb[CTA_PROTOINFO_TCP-1])
+		return;
+
+	__parse_protoinfo_tcp(tb[CTA_PROTOINFO_TCP-1], ct);
+}
+
+static void __parse_counters(const struct nfattr *attr,
+			     struct nf_conntrack *ct,
+			     int dir)
+{
+	struct nfattr *tb[CTA_COUNTERS_MAX];
+
+	nfnl_parse_nested(tb, CTA_COUNTERS_MAX, attr);
+	if (tb[CTA_COUNTERS32_PACKETS-1]) {
+		ct->counters[dir].packets
+			= htonl(*(u_int32_t *)
+				NFA_DATA(tb[CTA_COUNTERS32_PACKETS-1]));
+		switch(dir) {
+		case __DIR_ORIG:
+			set_bit(ATTR_ORIG_COUNTER_PACKETS, ct->set);
+			break;
+		case __DIR_REPL:
+			set_bit(ATTR_REPL_COUNTER_PACKETS, ct->set);
+			break;
+		}
+	}
+	if (tb[CTA_COUNTERS32_BYTES-1]) {
+		ct->counters[dir].bytes
+			= htonl(*(u_int32_t *)
+				NFA_DATA(tb[CTA_COUNTERS32_BYTES-1]));
+		switch(dir) {
+		case __DIR_ORIG:
+			set_bit(ATTR_ORIG_COUNTER_BYTES, ct->set);
+			break;
+		case __DIR_REPL:
+			set_bit(ATTR_REPL_COUNTER_BYTES, ct->set);
+			break;
+		}
+	}
+}
+
+int __parse_message_type(const struct nlmsghdr *nlh)
+{
+	u_int16_t type = NFNL_MSG_TYPE(nlh->nlmsg_type);
+	u_int16_t flags = nlh->nlmsg_flags;
+	int ret = NFCT_T_UNKNOWN;
+
+	if (type == IPCTNL_MSG_CT_NEW) {
+		if (flags & (NLM_F_CREATE|NLM_F_EXCL))
+			ret = NFCT_T_NEW;
+		else
+			ret = NFCT_T_UPDATE;
+	} else if (type == IPCTNL_MSG_CT_DELETE)
+		ret = NFCT_T_DESTROY;
+
+	return ret;
+}
+
+void __parse_conntrack(const struct nlmsghdr *nlh,
+		       const struct nfattr *cda[],
+		       struct nf_conntrack *ct)
+{
+	struct nfgenmsg *nfhdr = NLMSG_DATA(nlh);
+
+	ct->tuple[__DIR_ORIG].l3protonum = nfhdr->nfgen_family;
+	set_bit(ATTR_ORIG_L3PROTO, ct->set);
+
+	ct->tuple[__DIR_REPL].l3protonum = nfhdr->nfgen_family;
+	set_bit(ATTR_REPL_L3PROTO, ct->set);
+
+	if (cda[CTA_TUPLE_ORIG-1])
+		__parse_tuple(cda[CTA_TUPLE_ORIG-1], 
+			      &ct->tuple[__DIR_ORIG], __DIR_ORIG, ct->set);
+
+	if (cda[CTA_TUPLE_REPLY-1])
+		__parse_tuple(cda[CTA_TUPLE_REPLY-1], 
+			      &ct->tuple[__DIR_REPL], __DIR_REPL, ct->set);
+
+	if (cda[CTA_STATUS-1]) {
+		ct->status = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_STATUS-1]));
+		if (ct->status & IPS_ASSURED)
+			set_bit(ATTR_STATUS, ct->set);
+		if (ct->status & IPS_SEEN_REPLY)
+			set_bit(ATTR_STATUS, ct->set);
+	}
+
+	if (cda[CTA_PROTOINFO-1])
+		__parse_protoinfo(cda[CTA_PROTOINFO-1], ct);
+
+	if (cda[CTA_TIMEOUT-1]) {
+		ct->timeout = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_TIMEOUT-1]));
+		set_bit(ATTR_TIMEOUT, ct->set);
+	}
+	
+	if (cda[CTA_MARK-1]) {
+		ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
+		set_bit(ATTR_MARK, ct->set);
+	}
+
+	if (cda[CTA_COUNTERS_ORIG-1])
+		__parse_counters(cda[CTA_COUNTERS_ORIG-1], ct, __DIR_ORIG);
+
+	if (cda[CTA_COUNTERS_REPLY-1])
+		__parse_counters(cda[CTA_COUNTERS_REPLY-1], ct, __DIR_REPL);
+
+	if (cda[CTA_USE-1]) {
+		ct->use = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_USE-1]));
+		set_bit(ATTR_USE, ct->set);
+	}
+
+	if (cda[CTA_ID-1]) {
+		ct->id = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_ID-1]));
+		set_bit(ATTR_ID, ct->set);
+	}
+}

Added: trunk/libnetfilter_conntrack/src/conntrack/setter.c
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/setter.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/setter.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,173 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "internal.h"
+
+static void set_attr_orig_ipv4_src(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_ORIG].src.v4 = *((u_int32_t *) value);
+}
+
+static void set_attr_orig_ipv4_dst(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_ORIG].dst.v4 = *((u_int32_t *) value);
+}
+
+static void set_attr_repl_ipv4_src(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_REPL].src.v4 = *((u_int32_t *) value);
+}
+
+static void set_attr_repl_ipv4_dst(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_REPL].dst.v4 = *((u_int32_t *) value);
+}
+
+static void set_attr_orig_ipv6_src(struct nf_conntrack *ct, const void *value)
+{
+	memcpy(&ct->tuple[__DIR_ORIG].src.v6, value, sizeof(u_int32_t)*4);
+}
+
+static void set_attr_orig_ipv6_dst(struct nf_conntrack *ct, const void *value)
+{
+	memcpy(&ct->tuple[__DIR_ORIG].dst.v6, value, sizeof(u_int32_t)*4);
+}
+
+static void set_attr_repl_ipv6_src(struct nf_conntrack *ct, const void *value)
+{
+	memcpy(&ct->tuple[__DIR_REPL].src.v6, value, sizeof(u_int32_t)*4);
+}
+
+static void set_attr_repl_ipv6_dst(struct nf_conntrack *ct, const void *value)
+{
+	memcpy(&ct->tuple[__DIR_REPL].dst.v6, value, sizeof(u_int32_t)*4);
+}
+
+static void set_attr_orig_port_src(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_ORIG].l4src.all = *((u_int16_t *) value);
+}
+
+static void set_attr_orig_port_dst(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_ORIG].l4dst.all = *((u_int16_t *) value);
+}
+
+static void set_attr_repl_port_src(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_REPL].l4src.all = *((u_int16_t *) value);
+}
+
+static void set_attr_repl_port_dst(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_REPL].l4dst.all = *((u_int16_t *) value);
+}
+
+static void set_attr_icmp_type(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_ORIG].l4dst.icmp.type = *((u_int8_t *) value);
+}
+
+static void set_attr_icmp_code(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_ORIG].l4dst.icmp.code = *((u_int8_t *) value);
+}
+
+static void set_attr_icmp_id(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_ORIG].l4src.icmp.id = *((u_int8_t *) value);
+}
+
+static void set_attr_orig_l3proto(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_ORIG].l3protonum = *((u_int8_t *) value);
+}
+
+static void set_attr_repl_l3proto(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_REPL].l3protonum = *((u_int8_t *) value);
+}
+
+static void set_attr_orig_l4proto(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_ORIG].protonum = *((u_int8_t *) value);
+}
+
+static void set_attr_repl_l4proto(struct nf_conntrack *ct, const void *value)
+{
+	ct->tuple[__DIR_REPL].protonum = *((u_int8_t *) value);
+}
+
+static void set_attr_tcp_state(struct nf_conntrack *ct, const void *value)
+{
+	ct->protoinfo.tcp.state = *((u_int8_t *) value);
+}
+
+static void set_attr_snat_ipv4(struct nf_conntrack *ct, const void *value)
+{
+	ct->snat.min_ip = ct->snat.max_ip = *((u_int32_t *) value);
+}
+
+static void set_attr_dnat_ipv4(struct nf_conntrack *ct, const void *value)
+{
+	ct->dnat.min_ip = ct->snat.max_ip = *((u_int32_t *) value);
+}
+
+static void set_attr_snat_port(struct nf_conntrack *ct, const void *value)
+{
+	ct->snat.l4min.all = ct->snat.l4max.all = *((u_int16_t *) value);
+}
+
+static void set_attr_dnat_port(struct nf_conntrack *ct, const void *value)
+{
+	ct->dnat.l4min.all = ct->dnat.l4max.all = *((u_int16_t *) value);
+}
+
+static void set_attr_timeout(struct nf_conntrack *ct, const void *value)
+{
+	ct->timeout = *((u_int32_t *) value);
+}
+
+static void set_attr_mark(struct nf_conntrack *ct, const void *value)
+{
+	ct->mark = *((u_int32_t *) value);
+}
+
+static void set_attr_status(struct nf_conntrack *ct, const void *value)
+{
+	ct->status |= *((u_int32_t *) value);
+}
+
+set_attr set_attr_array[] = {
+	[ATTR_ORIG_IPV4_SRC]	= set_attr_orig_ipv4_src,
+	[ATTR_ORIG_IPV4_DST] 	= set_attr_orig_ipv4_dst,
+	[ATTR_REPL_IPV4_SRC]	= set_attr_repl_ipv4_src,
+	[ATTR_REPL_IPV4_DST]	= set_attr_repl_ipv4_dst,
+	[ATTR_ORIG_IPV6_SRC]	= set_attr_orig_ipv6_src,
+	[ATTR_ORIG_IPV6_DST]	= set_attr_orig_ipv6_dst,
+	[ATTR_REPL_IPV6_SRC]	= set_attr_repl_ipv6_src,
+	[ATTR_REPL_IPV6_DST]	= set_attr_repl_ipv6_dst,
+	[ATTR_ORIG_PORT_SRC]	= set_attr_orig_port_src,
+	[ATTR_ORIG_PORT_DST]	= set_attr_orig_port_dst,
+	[ATTR_REPL_PORT_SRC]	= set_attr_repl_port_src,
+	[ATTR_REPL_PORT_DST]	= set_attr_repl_port_dst,
+	[ATTR_ICMP_TYPE]	= set_attr_icmp_type,
+	[ATTR_ICMP_CODE]	= set_attr_icmp_code,
+	[ATTR_ICMP_ID]		= set_attr_icmp_id,
+	[ATTR_ORIG_L3PROTO]	= set_attr_orig_l3proto,
+	[ATTR_REPL_L3PROTO]	= set_attr_repl_l3proto,
+	[ATTR_ORIG_L4PROTO]	= set_attr_orig_l4proto,
+	[ATTR_REPL_L4PROTO]	= set_attr_repl_l4proto,
+	[ATTR_TCP_STATE]	= set_attr_tcp_state,
+	[ATTR_SNAT_IPV4]	= set_attr_snat_ipv4,
+	[ATTR_DNAT_IPV4]	= set_attr_dnat_ipv4,
+	[ATTR_SNAT_PORT]	= set_attr_snat_port,
+	[ATTR_DNAT_PORT]	= set_attr_dnat_port,
+	[ATTR_TIMEOUT]		= set_attr_timeout,
+	[ATTR_MARK]		= set_attr_mark,
+	[ATTR_STATUS]		= set_attr_status,
+};

Added: trunk/libnetfilter_conntrack/src/conntrack/snprintf.c
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/snprintf.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/snprintf.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,36 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "internal.h"
+
+int __snprintf_conntrack(char *buf,
+			 unsigned int len,
+			 const struct nf_conntrack *ct,
+			 unsigned int type,
+			 unsigned int msg_output,
+			 unsigned int flags)
+{
+	int size;
+
+	switch(msg_output) {
+	case NFCT_O_DEFAULT:
+		size = __snprintf_conntrack_default(buf, len, ct, type, flags);
+		break;
+	case NFCT_O_XML:
+		size = __snprintf_conntrack_xml(buf, len, ct, type, flags);
+		break;
+	default:
+		errno = ENOENT;
+		return -1;
+	}
+
+	/* NULL terminated string */
+	if (snprintf(buf+size, len-size, "\0") == -1)
+		return -1;
+
+	return size;
+}

Added: trunk/libnetfilter_conntrack/src/conntrack/snprintf_default.c
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/snprintf_default.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/snprintf_default.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,344 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "internal.h"
+
+static char *proto2str[IPPROTO_MAX] = {
+	[IPPROTO_TCP] = "tcp",
+        [IPPROTO_UDP] = "udp",
+        [IPPROTO_ICMP] = "icmp",
+        [IPPROTO_SCTP] = "sctp"
+};
+
+static char *l3proto2str[AF_MAX] = {
+	[AF_INET] = "ipv4",
+	[AF_INET6] = "ipv6"
+};
+
+static const char *states[] = {
+	"NONE",
+	"SYN_SENT",
+	"SYN_RECV",
+	"ESTABLISHED",
+	"FIN_WAIT",
+	"CLOSE_WAIT",
+	"LAST_ACK",
+	"TIME_WAIT",
+	"CLOSE",
+	"LISTEN"
+};
+
+static int __snprintf_l3protocol(char *buf,
+				 unsigned int len,
+				 const struct nf_conntrack *ct)
+{
+	return (snprintf(buf, len, "%-8s %u ", 
+		l3proto2str[ct->tuple[__DIR_ORIG].l3protonum] == NULL ?
+		"unknown" : l3proto2str[ct->tuple[__DIR_ORIG].l3protonum], 
+		 ct->tuple[__DIR_ORIG].l3protonum));
+}
+
+static int __snprintf_protocol(char *buf,
+			       unsigned int len,
+			       const struct nf_conntrack *ct)
+{
+	return (snprintf(buf, len, "%-8s %u ", 
+		proto2str[ct->tuple[__DIR_ORIG].protonum] == NULL ?
+		"unknown" : proto2str[ct->tuple[__DIR_ORIG].protonum], 
+		 ct->tuple[__DIR_ORIG].protonum));
+}
+
+int __snprintf_timeout(char *buf,
+		       unsigned int len,
+		       const struct nf_conntrack *ct)
+{
+	return snprintf(buf, len, "%u ", ct->timeout);
+}
+
+int __snprintf_protoinfo(char *buf, 
+			 unsigned int len,
+			 const struct nf_conntrack *ct)
+{
+	return snprintf(buf, len, "%s ", states[ct->protoinfo.tcp.state]);
+}
+
+int __snprintf_address_ipv4(char *buf,
+			    unsigned int len,
+			    const struct __nfct_tuple *tuple)
+{
+	int ret, size;
+	struct in_addr src = { .s_addr = tuple->src.v4 };
+	struct in_addr dst = { .s_addr = tuple->dst.v4 };
+
+	ret = snprintf(buf, len, "src=%s ", inet_ntoa(src));
+	if (ret == -1)
+		return -1;
+	size = ret;
+
+	ret = snprintf(buf+size, len-size, "dst=%s ", inet_ntoa(dst));
+	if (ret == -1)
+		return -1;
+	size += ret;
+
+	return size;
+}
+
+int __snprintf_address_ipv6(char *buf, 
+			    unsigned int len,
+			    const struct __nfct_tuple *tuple)
+{
+	int size;
+	struct in6_addr src;
+	struct in6_addr dst;
+	char tmp[INET6_ADDRSTRLEN];
+
+	memcpy(&src.in6_u, &tuple->src.v6, sizeof(struct in6_addr));
+	memcpy(&dst.in6_u, &tuple->dst.v6, sizeof(struct in6_addr));
+
+	if (!inet_ntop(AF_INET6, &src, tmp, sizeof(tmp)))
+		return -1;
+
+	size = snprintf(buf, len, "src=%s ", tmp); 
+
+	if (!inet_ntop(AF_INET6, &dst, tmp, sizeof(tmp)))
+		return -1;
+
+	size += snprintf(buf+size, len-size, "dst=%s ", tmp);
+
+	return size;
+}
+
+int __snprintf_address(char *buf,
+		       unsigned int len,
+		       const struct __nfct_tuple *tuple)
+{
+	int size = 0;
+
+	switch (tuple->l3protonum) {
+	case AF_INET:
+		size = __snprintf_address_ipv4(buf, len, tuple);
+		break;
+	case AF_INET6:
+		size = __snprintf_address_ipv4(buf, len, tuple);
+		break;
+	}
+
+	return size;
+}
+
+int __snprintf_proto(char *buf, 
+		     unsigned int len,
+		     const struct __nfct_tuple *tuple)
+{
+	int size = 0;
+
+	switch(tuple->protonum) {
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+	case IPPROTO_SCTP:
+		return snprintf(buf, len, "sport=%u dport=%u ",
+			        htons(tuple->l4src.tcp.port),
+			        htons(tuple->l4dst.tcp.port));
+		break;
+	case IPPROTO_ICMP:
+		/* The ID only makes sense some ICMP messages but we want to
+		 * display the same output that /proc/net/ip_conntrack does */
+		return (snprintf(buf, len, "type=%d code=%d id=%d ",
+			tuple->l4dst.icmp.type,
+			tuple->l4dst.icmp.code,
+			ntohs(tuple->l4src.icmp.id)));
+		break;
+	}
+
+	return size;
+}
+
+int __snprintf_status_assured(char *buf,
+			      unsigned int len,
+			      const struct nf_conntrack *ct)
+{
+	int size = 0;
+	
+	if (ct->status & IPS_ASSURED)
+		size = snprintf(buf, len, "[ASSURED] ");
+
+	return size;
+}
+
+int __snprintf_status_not_seen_reply(char *buf,
+				     unsigned int len,
+				     const struct nf_conntrack *ct)
+{
+	int size = 0;
+	
+        if (!(ct->status & IPS_SEEN_REPLY))
+                size = snprintf(buf, len, "[UNREPLIED] ");
+
+	return size;
+}
+
+int __snprintf_counters(char *buf, 
+		        unsigned int len, 
+		        const struct nf_conntrack *ct,
+		        int dir)
+{
+	return (snprintf(buf, len, "packets=%llu bytes=%llu ",
+			 (unsigned long long) ct->counters[dir].packets,
+			 (unsigned long long) ct->counters[dir].bytes));
+}
+
+int __snprintf_mark(char *buf, unsigned int len, const struct nf_conntrack *ct)
+{
+	return (snprintf(buf, len, "mark=%u ", ct->mark));
+}
+
+int __snprintf_use(char *buf, unsigned int len, const struct nf_conntrack *ct)
+{
+	return (snprintf(buf, len, "use=%u ", ct->use));
+}
+
+int __snprintf_id(char *buf, unsigned int len, u_int32_t id)
+{
+	return (snprintf(buf, len, "id=%u ", id));
+}
+
+int __snprintf_conntrack_default(char *buf, 
+				 unsigned int remain,
+				 const struct nf_conntrack *ct,
+				 unsigned int msg_type,
+				 unsigned int flags) 
+{
+	int ret = 0, size = 0;
+
+	switch(msg_type) {
+		case NFCT_T_NEW:
+			ret = snprintf(buf, remain, "%9s ", "[NEW]");
+			break;
+		case NFCT_T_UPDATE:
+			ret = snprintf(buf, remain, "%9s ", "[UPDATE]");
+			break;
+		case NFCT_T_DESTROY:
+			ret = snprintf(buf, remain, "%9s ", "[DESTROY]");
+			break;
+		default:
+			break;
+	}
+
+	if (ret == -1)
+		return -1;
+	size += ret;
+	remain -= ret;
+
+	if (flags & NFCT_OF_SHOW_LAYER3) {
+		ret = __snprintf_l3protocol(buf+size, remain, ct);
+		if (ret == -1)
+			return -1;
+		size += ret;
+		remain -= ret;
+	}
+
+	ret = __snprintf_protocol(buf+size, remain, ct);
+	if (ret == -1)
+		return -1;
+	size += ret;
+	remain -= ret;
+
+	if (test_bit(ATTR_TIMEOUT, ct->set)) {
+		ret = __snprintf_timeout(buf+size, remain, ct);
+		if (ret == -1)
+			return -1;
+		size += ret;
+		remain -= ret;
+	}
+
+        if (test_bit(ATTR_TCP_STATE, ct->set)) {
+		ret = __snprintf_protoinfo(buf+size, remain, ct);
+		if (ret == -1)
+			return -1;
+		size += ret;
+		remain -= ret;
+	}
+
+	ret = __snprintf_address(buf+size, remain, &ct->tuple[__DIR_ORIG]);
+	if (ret == -1)
+		return -1;
+	size += ret;
+	remain -= ret;
+
+	ret = __snprintf_proto(buf+size, remain, &ct->tuple[__DIR_ORIG]);
+	if (ret == -1)
+		return -1;
+	size += ret;
+	remain -= ret;
+
+	if (test_bit(ATTR_ORIG_COUNTER_PACKETS, ct->set) &&
+	    test_bit(ATTR_ORIG_COUNTER_BYTES, ct->set)) {
+		ret = __snprintf_counters(buf+size, remain, ct, __DIR_ORIG);
+		if (ret == -1)
+			return -1;
+		size += ret;
+		remain -= ret;
+	}
+
+	if (test_bit(ATTR_STATUS, ct->set)) {
+		ret = __snprintf_status_not_seen_reply(buf+size, remain, ct);
+		if (ret == -1)
+			return -1;
+		size += ret;
+		remain -= ret;
+	}
+
+	ret = __snprintf_address(buf+size, remain, &ct->tuple[__DIR_REPL]);
+	if (ret == -1)
+		return -1;
+	size += ret;
+	remain -= ret;
+
+	ret = __snprintf_proto(buf+size, remain, &ct->tuple[__DIR_REPL]);
+	if (ret == -1)
+		return -1;
+	size += ret;
+	remain -= ret;
+
+	if (test_bit(ATTR_REPL_COUNTER_PACKETS, ct->set) &&
+	    test_bit(ATTR_REPL_COUNTER_BYTES, ct->set)) {
+		ret = __snprintf_counters(buf+size, remain, ct, __DIR_REPL);
+		if (ret == -1)
+			return -1;
+		size += ret;
+		remain -= ret;
+	}
+
+	if (test_bit(ATTR_STATUS, ct->set)) {
+		ret = __snprintf_status_assured(buf+size, remain, ct);
+		if (ret == -1)
+			return -1;
+		size += ret;
+		remain -= ret;
+	}
+
+	if (test_bit(ATTR_MARK, ct->set)) {
+		ret = __snprintf_mark(buf+size, remain, ct);
+		if (ret == -1)
+			return -1;
+		size += ret;
+		remain -= ret;
+	}
+
+	if (test_bit(ATTR_USE, ct->set)) {
+		ret = __snprintf_use(buf+size, remain, ct);
+		if (ret == -1)
+			return -1;
+		size += ret;
+		remain -= ret;
+	}
+
+	/* Delete the last blank space */
+	size--;
+
+	return size;
+}

Added: trunk/libnetfilter_conntrack/src/conntrack/snprintf_xml.c
===================================================================
--- trunk/libnetfilter_conntrack/src/conntrack/snprintf_xml.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/src/conntrack/snprintf_xml.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,413 @@
+/*
+ * (C) 2006 by Pablo Neira Ayuso <pablo at netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "internal.h"
+
+/*
+ * XML output sample:
+ *
+ * <flow>
+ * 	<meta direction="original">
+ * 		<layer3 protonum="2" protoname="IPv4">
+ * 			<src>192.168.0.1</src>
+ * 			<dst>192.168.0.2</dst>
+ * 		</layer3>
+ * 		<layer4 protonum="16" protoname"udp">
+ * 			<sport>80</sport>
+ * 			<dport>56665</dport>
+ * 		</layer4>
+ * 		<counters>
+ * 			<bytes>10</bytes>
+ * 			<packets>1</packets>
+ * 		</counters>
+ * 	</meta>
+ * 	<meta direction="reply">
+ * 		<layer3 protonum="2" protoname="IPv4">
+ * 			<src>192.168.0.2</src>
+ * 			<dst>192.168.0.1</dst>
+ * 		</layer3>
+ * 		<layer4 protonum="16" protoname="udp">
+ * 			<sport>80</sport>
+ * 			<dport>56665</dport>
+ * 		</layer4>
+ * 		<counters>
+ * 			<bytes>5029</bytes>
+ * 			<packets>12</packets>
+ *		</counters>
+ * 	</meta>
+ * 	<meta direction="independent">
+ *	 	<layer4>
+ * 			<state>ESTABLISHED</state>
+ * 		</layer4>
+ * 		<timeout>100</timeout>
+ * 		<mark>1</mark>
+ * 		<use>1</use>
+ * 		<assured/>
+ * 	</meta>
+ * </flow>
+ */
+
+static char *proto2str[IPPROTO_MAX] = {
+	[IPPROTO_TCP] = "tcp",
+        [IPPROTO_UDP] = "udp",
+        [IPPROTO_ICMP] = "icmp",
+        [IPPROTO_SCTP] = "sctp"
+};
+static char *l3proto2str[AF_MAX] = {
+	[AF_INET] = "ipv4",
+	[AF_INET6] = "ipv6"
+};
+
+enum {
+	__ADDR_SRC = 0,
+	__ADDR_DST,
+};
+
+static void buffer_size(int ret, unsigned int *size, unsigned int *len)
+{
+	*size += ret;
+	*len -= ret;
+}
+
+static char *__proto2str(u_int8_t protonum)
+{
+	return proto2str[protonum] ? proto2str[protonum] : "unknown";
+}
+
+static char *__l3proto2str(u_int8_t protonum)
+{
+	return l3proto2str[protonum] ? l3proto2str[protonum] : "unknown";
+}
+
+static int __snprintf_ipv4_xml(char *buf,
+			       unsigned int len,
+			       const struct __nfct_tuple *tuple,
+			       unsigned int type)
+{
+	struct in_addr addr = { 
+		.s_addr = (type == __ADDR_SRC) ? tuple->src.v4 : tuple->dst.v4,
+	};
+
+	return snprintf(buf, len, "%s", inet_ntoa(addr));
+}
+
+static int __snprintf_ipv6_xml(char *buf,
+			       unsigned int len,
+			       const struct __nfct_tuple *tuple,
+			       unsigned int type)
+{
+	struct in6_addr addr;
+	static char tmp[INET6_ADDRSTRLEN];
+	const void *p = (type == __ADDR_SRC) ? &tuple->src.v6 : &tuple->dst.v6;
+
+	memcpy(&addr.in6_u, p, sizeof(struct in6_addr));
+
+	if (!inet_ntop(AF_INET6, &addr, tmp, sizeof(tmp)))
+		return -1;
+
+	return snprintf(buf, len, "%s", tmp);
+}
+
+static int __snprintf_addr_xml(char *buf,
+			       unsigned int len,
+			       const struct __nfct_tuple *tuple, 
+			       unsigned int type)
+{
+	int ret;
+	unsigned int size = 0;
+
+	switch(type) {
+	case __ADDR_SRC:
+		ret = snprintf(buf, len, "<src>");
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+		break;
+	case __ADDR_DST:
+		ret = snprintf(buf+size, len, "<dst>");
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+		break;
+	}
+
+	switch (tuple->l3protonum) {
+	case AF_INET:
+		ret = __snprintf_ipv4_xml(buf+size, len, tuple, type);
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+		break;
+	case AF_INET6:
+		ret = __snprintf_ipv6_xml(buf+size, len, tuple, type);
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+		break;
+	}
+
+	switch(type) {
+	case __ADDR_SRC:
+		ret = snprintf(buf+size, len, "</src>");
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+		break;
+	case __ADDR_DST:
+		ret = snprintf(buf+size, len, "</dst>");
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+		break;
+	}
+
+	return size;
+}
+
+static int __snprintf_proto_xml(char *buf,
+				unsigned int len,
+				const struct __nfct_tuple *tuple, 
+				unsigned int type)
+{
+	int ret = 0;
+	unsigned int size = 0;
+
+	switch(tuple->protonum) {
+	case IPPROTO_TCP:
+	case IPPROTO_UDP:
+	case IPPROTO_SCTP:
+		if (type == __ADDR_SRC) {
+			ret = snprintf(buf, len, "<sport>%u</sport>", 
+				       tuple->l4src.tcp.port);
+			if (ret == -1)
+				return -1;
+			buffer_size(ret, &size, &len);
+		} else {
+			ret = snprintf(buf, len, "<dport>%u</dport>",
+				       tuple->l4dst.tcp.port);
+			if (ret == -1)
+				return -1;
+			buffer_size(ret, &size, &len);
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static int __snprintf_counters_xml(char *buf,
+				   unsigned int len,
+				   const struct nf_conntrack *ct,
+				   unsigned int type)
+{
+	int ret;
+	unsigned int size = 0;
+
+	ret = snprintf(buf, len, "<packets>%llu</packets>",
+		       ct->counters[type].packets);
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = snprintf(buf+size, len, "<bytes>%llu</bytes>",
+		       ct->counters[type].bytes);
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	return size;
+}
+
+static int __snprintf_tuple_xml(char *buf,
+				unsigned int len,
+				const struct nf_conntrack *ct,
+				unsigned int dir)
+{
+	int ret;
+	unsigned int size = 0;
+	const struct __nfct_tuple *tuple = &ct->tuple[dir];
+
+	ret = snprintf(buf, len, "<meta direction=\"%s\">",
+		       dir == __DIR_ORIG ? "original" : "reply");
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = snprintf(buf+size, len, 
+		       "<layer3 protonum=\"%d\" protoname=\"%s\">",
+		       tuple->l3protonum, __l3proto2str(tuple->l3protonum));
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = __snprintf_addr_xml(buf+size, len, tuple, __DIR_ORIG);
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = __snprintf_addr_xml(buf+size, len, tuple, __DIR_REPL);
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = snprintf(buf+size, len, "</layer3>");
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = snprintf(buf+size, len, 
+		       "<layer4 protonum=\"%d\" protoname=\"%s\">",
+		       tuple->protonum, __proto2str(tuple->protonum));
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = __snprintf_proto_xml(buf+size, len, tuple, __DIR_ORIG);
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = __snprintf_proto_xml(buf+size, len, tuple, __DIR_REPL);
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = snprintf(buf+size, len, "</layer4>");
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	if (test_bit(ATTR_ORIG_COUNTER_PACKETS, ct->set) &&
+	    test_bit(ATTR_ORIG_COUNTER_BYTES, ct->set)) {
+		ret = snprintf(buf+size, len, "<counters>");
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+
+		ret = __snprintf_counters_xml(buf+size, len, ct, dir);
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+
+		ret = snprintf(buf+size, len, "</counters>");
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+	}
+
+	ret = snprintf(buf+size, len, "</meta>");
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	return size;
+}
+
+int __snprintf_conntrack_xml(char *buf,
+			     unsigned int len,
+			     const struct nf_conntrack *ct,
+			     const unsigned int msg_type,
+			     const unsigned int flags) 
+{
+	int ret = 0;
+	unsigned int size = 0;
+
+	switch(msg_type) {
+		case NFCT_T_NEW:
+			ret = snprintf(buf, len, "<flow type=\"new\">");
+			break;
+		case NFCT_T_UPDATE:
+			ret = snprintf(buf, len, "<flow type=\"update\"");
+			break;
+		case NFCT_T_DESTROY:
+			ret = snprintf(buf, len, "<flow type=\"destroy\"");
+			break;
+		default:
+			ret = snprintf(buf, len, "<flow>");
+			break;
+	}
+
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = __snprintf_tuple_xml(buf+size, len, ct, __DIR_ORIG);
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	ret = __snprintf_tuple_xml(buf+size, len, ct, __DIR_REPL);
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	if (test_bit(ATTR_TIMEOUT, ct->set) ||
+	    test_bit(ATTR_MARK, ct->set) ||
+	    test_bit(ATTR_USE, ct->set) ||
+	    test_bit(ATTR_STATUS, ct->set)) {
+		ret = snprintf(buf+size, len, 
+			       "<meta direction=\"independent\">");
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+	}
+
+	if (test_bit(ATTR_TIMEOUT, ct->set)) {
+		ret = snprintf(buf+size, len,
+				"<timeout>%u</timeout>", ct->timeout);
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+	}
+
+	if (test_bit(ATTR_MARK, ct->set)) {
+		ret = snprintf(buf+size, len, "<mark>%u</mark>", ct->mark);
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+	}
+
+	if (test_bit(ATTR_USE, ct->set)) {
+		ret = snprintf(buf+size, len, "<use>%u</use>", ct->use);
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+	}
+
+	if (test_bit(ATTR_STATUS, ct->set)
+	    && ct->status & IPS_ASSURED) {
+		ret = snprintf(buf+size, len, "<assured/>");
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+	}
+
+	if (test_bit(ATTR_STATUS, ct->set) 
+	    && !(ct->status & IPS_SEEN_REPLY)) {
+		ret = snprintf(buf+size, len, "<unreplied/>");
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+	}
+
+	if (test_bit(ATTR_TIMEOUT, ct->set) ||
+	    test_bit(ATTR_MARK, ct->set) ||
+	    test_bit(ATTR_USE, ct->set) ||
+	    test_bit(ATTR_STATUS, ct->set)) {
+	    	ret = snprintf(buf+size, len, "</meta>");
+		if (ret == -1)
+			return -1;
+		buffer_size(ret, &size, &len);
+	}
+
+	ret = snprintf(buf+size, len, "</flow>");
+	if (ret == -1)
+		return -1;
+	buffer_size(ret, &size, &len);
+
+	return size;
+}

Modified: trunk/libnetfilter_conntrack/src/libnetfilter_conntrack.c
===================================================================
--- trunk/libnetfilter_conntrack/src/libnetfilter_conntrack.c	2006-12-19 17:12:21 UTC (rev 6714)
+++ trunk/libnetfilter_conntrack/src/libnetfilter_conntrack.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -22,21 +22,10 @@
 #include <libnetfilter_conntrack/libnetfilter_conntrack_l3extensions.h>
 #include <libnetfilter_conntrack/libnetfilter_conntrack_extensions.h>
 
+#include "internal.h"
+
 #define NFCT_BUFSIZE 4096
 
-typedef int (*nfct_handler)(struct nfct_handle *cth, struct nlmsghdr *nlh,
-			    void *arg);
-
-/* Harald says: "better for encapsulation" ;) */
-struct nfct_handle {
-	struct nfnl_handle *nfnlh;
-	struct nfnl_subsys_handle *nfnlssh_ct;
-	struct nfnl_subsys_handle *nfnlssh_exp;
-	nfct_callback callback;		/* user callback */
-	void *callback_data;		/* user data for callback */
-	nfct_handler handler;		/* netlink handler */
-};
-
 static char *lib_dir = LIBNETFILTER_CONNTRACK_DIR;
 static LIST_HEAD(proto_list);
 static LIST_HEAD(l3proto_list);
@@ -151,6 +140,14 @@
 		cth->nfnlssh_ct = NULL;
 	}
 
+	/* required by the new API */
+	cth->cb = NULL;
+	free(cth->nfnl_cb.data);
+
+	cth->nfnl_cb.call = NULL; 
+	cth->nfnl_cb.data = NULL;
+	cth->nfnl_cb.attr_count = 0;
+
 	err = nfnl_close(cth->nfnlh);
 	free(cth);
 

Modified: trunk/libnetfilter_conntrack/utils/Makefile.am
===================================================================
--- trunk/libnetfilter_conntrack/utils/Makefile.am	2006-12-19 17:12:21 UTC (rev 6714)
+++ trunk/libnetfilter_conntrack/utils/Makefile.am	2006-12-19 17:41:53 UTC (rev 6715)
@@ -1,7 +1,11 @@
 include $(top_srcdir)/Make_global.am
 
-bin_PROGRAMS = ctnl_test
+bin_PROGRAMS = ctnl_test new_api_test
 
+new_api_test_SOURCES = new_api_test.c
+new_api_test_LDADD = ../src/libnetfilter_conntrack.la
+new_api_test_LDFLAGS = -dynamic -ldl
+
 ctnl_test_SOURCES = ctnl_test.c
 ctnl_test_LDADD = ../src/libnetfilter_conntrack.la
 ctnl_test_LDFLAGS = -dynamic -ldl

Added: trunk/libnetfilter_conntrack/utils/new_api_test.c
===================================================================
--- trunk/libnetfilter_conntrack/utils/new_api_test.c	                        (rev 0)
+++ trunk/libnetfilter_conntrack/utils/new_api_test.c	2006-12-19 17:41:53 UTC (rev 6715)
@@ -0,0 +1,124 @@
+#include <stdio.h>
+#include <errno.h>
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
+
+static int cb(enum nf_conntrack_msg_type type,
+	      struct nf_conntrack *ct,
+	      void *data)
+{
+	char buf[1024];
+
+	nfct_snprintf(buf, 1024, ct, NFCT_T_UNKNOWN, NFCT_O_DEFAULT, NFCT_OF_SHOW_LAYER3);
+	printf("%s\n", buf);
+
+	return NFCT_CB_CONTINUE;
+}
+
+static int event_cb(enum nf_conntrack_msg_type type,
+		    struct nf_conntrack *ct,
+		    void *data)
+{
+	static int n = 0;
+	char buf[1024];
+
+	nfct_snprintf(buf, 1024, ct, type, NFCT_O_XML, 0);
+	printf("%s\n", buf);
+
+	if (++n == 10)
+		return NFCT_CB_STOP;
+
+	return NFCT_CB_CONTINUE;
+}
+
+int main()
+{
+	int ret;
+	u_int8_t family = AF_INET;
+	struct nfct_handle *h;
+	struct nf_conntrack *ct;
+	char buf[1024];
+
+	printf("Test for NEW libnetfilter_conntrack API\n");
+	printf("=======================================\n");
+
+	ct = nfct_new();
+	if (!ct) {
+		perror("nfct_new");
+		return 0;
+	}
+
+	nfct_set_attr_u8(ct, ATTR_ORIG_L3PROTO, AF_INET);
+	nfct_set_attr_u32(ct, ATTR_ORIG_IPV4_SRC, inet_addr("1.1.1.1"));
+	nfct_set_attr_u32(ct, ATTR_ORIG_IPV4_DST, inet_addr("2.2.2.2"));
+	
+	nfct_set_attr_u8(ct, ATTR_ORIG_L4PROTO, IPPROTO_TCP);
+	nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, ntohs(20));
+	nfct_set_attr_u16(ct, ATTR_ORIG_PORT_DST, ntohs(10));
+
+	nfct_set_attr_u8(ct, ATTR_REPL_L3PROTO, AF_INET);
+	nfct_set_attr_u32(ct, ATTR_REPL_IPV4_SRC, inet_addr("2.2.2.2"));
+	nfct_set_attr_u32(ct, ATTR_REPL_IPV4_DST, inet_addr("1.1.1.1"));
+	
+	nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_TCP);
+	nfct_set_attr_u16(ct, ATTR_REPL_PORT_SRC, ntohs(10));
+	nfct_set_attr_u16(ct, ATTR_REPL_PORT_DST, ntohs(20));
+
+	nfct_set_attr_u8(ct, ATTR_TCP_STATE, TCP_CONNTRACK_LISTEN);
+	nfct_set_attr_u32(ct, ATTR_TIMEOUT, 100);
+
+	h = nfct_open(CONNTRACK, 0);
+	if (!h) {
+		perror("nfct_open");
+		return -1;
+	}
+
+	ret = nfct_query(h, NFCT_Q_CREATE, ct);
+
+	printf("TEST 1: create conntrack (%d)(%s)\n", ret, strerror(errno));
+	
+	ret = nfct_query(h, NFCT_Q_UPDATE, ct);
+
+	printf("TEST 2: update conntrack (%d)(%s)\n", ret, strerror(errno));
+
+	nfct_callback_register(h, NFCT_T_ALL, cb, NULL);
+	ret = nfct_query(h, NFCT_Q_GET, ct);
+
+	printf("TEST 3: get conntrack (%d)(%s)\n", ret, strerror(errno));
+
+	ret = nfct_query(h, NFCT_Q_DESTROY, ct);
+
+	printf("TEST 4: destroy conntrack (%d)(%s)\n", ret, strerror(errno));
+
+	nfct_set_attr_u32(ct, ATTR_SNAT_IPV4, inet_addr("8.8.8.8"));
+	ret = nfct_query(h, NFCT_Q_CREATE, ct);
+
+	printf("TEST 5: create NAT conntrack (%d)(%s)\n", ret, strerror(errno));
+
+	ret = nfct_query(h, NFCT_Q_GET, ct);
+
+	printf("TEST 6: get NAT conntrack (%d)(%s)\n", ret, strerror(errno));
+
+	ret = nfct_query(h, NFCT_Q_DESTROY, ct);
+
+	printf("TEST 7: destroy NAT conntrack (%d)(%s)\n",ret,strerror(errno));
+
+	nfct_close(h);
+
+	h = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
+	if (!h) {
+		perror("nfct_open");
+		return -1;
+	}
+
+	nfct_callback_register(h, NFCT_T_ALL, event_cb, NULL);
+
+	printf("TEST 8: waiting for 10 events...\n");
+
+	ret = nfct_catch(h);
+
+	printf("TEST 8: OK (%d)(%s)\n", ret, strerror(errno));
+
+	nfct_close(h);
+}




More information about the netfilter-cvslog mailing list