summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author/C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=laforge/emailAddress=laforge@netfilter.org </C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=laforge/emailAddress=laforge@netfilter.org>2005-07-30 21:10:38 +0000
committer/C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=laforge/emailAddress=laforge@netfilter.org </C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=laforge/emailAddress=laforge@netfilter.org>2005-07-30 21:10:38 +0000
commit8021789a9c6f98a42e30b70a782842a1dcc45efc (patch)
tree32c3f5f9c30b9919c3133e81008b85b805d45337
restructuring libctnetlink -> libnfnetlink_conntrack
-rw-r--r--Makefile.am11
-rw-r--r--configure.in67
-rw-r--r--include/libnfnetlink_conntrack/libnfnetlink_conntrack.h122
-rw-r--r--src/Makefile.am12
-rw-r--r--src/libnfnetlink_conntrack.c470
-rw-r--r--utils/Makefile.am8
-rw-r--r--utils/ctnl_test.c188
7 files changed, 878 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..04bb1f6
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,11 @@
+AUTOMAKE_OPTIONS = foreign
+
+INCLUDES =$(all_includes) -I$(top_srcdir)/include -I${KERNELDIR}
+SUBDIRS = include src utils
+LINKOPTS = -lnfnetlink
+
+man_MANS = #nfnetlink_conntrack.3 nfnetlink_conntrack.7
+
+$(OBJECTS): libtool
+libtool: $(LIBTOOL_DEPS)
+ $(SHELL) ./config.status --recheck
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..1516c80
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,67 @@
+dnl Process this file with autoconf to create configure.
+
+AC_INIT
+
+AC_CANONICAL_SYSTEM
+
+AM_INIT_AUTOMAKE(libnfnetlink_conntrack, 0.10)
+
+AC_PROG_CC
+AM_PROG_LIBTOOL
+AC_PROG_INSTALL
+AC_PROG_LN_S
+
+AC_SUBST(LIBTOOL_DEPS)
+
+case $target in
+*-*-linux*) ;;
+*) AC_MSG_ERROR([Linux only, dude!]);;
+esac
+
+AC_CHECK_LIB([nfnetlink], [nfnl_listen])
+
+
+
+dnl--------------------------------
+
+AC_DEFUN([NF_KERNEL_SOURCE],[
+
+ if test "$with_kernel" = ""; then
+ KERNEL="`uname -r`"
+ else
+ KERNEL="$with_kernel"
+ fi
+
+ THIS_PREFIX=""
+ for i in "/lib/modules/$KERNEL/build/include" "$KERNEL" "$KERNEL/include" "/usr/src/linux-$KERNEL" "/usr/src/kernel-$KERNEL" "/usr/src/linux-headers-$KERNEL" "/usr/src/kernel-headers-$KERNEL"
+ do
+ AC_MSG_CHECKING([Looking for kernel source or headers in $i])
+ if test -r "$i/linux/config.h"
+ then
+ THIS_PREFIX="$i"
+ AC_MSG_RESULT([found])
+ break
+ fi
+ AC_MSG_RESULT([ ])
+ done
+ if test -r "$THIS_PREFIX/linux/config.h" ; then
+ AC_SUBST(KERNELDIR,[$THIS_PREFIX])
+ AC_MSG_RESULT([found])
+ else
+ AC_MSG_ERROR([not found $THIS_PREFIX])
+ fi
+
+ # somehow add this as an include path
+])
+
+AC_ARG_WITH(kernel,
+ AC_HELP_STRING([--with-kernel=DIR],
+ [ Show location of kernel source. Default is to use uname -r and look in /lib/modules/KERNEL/build/include. ]),
+ NF_KERNEL_SOURCE($with_kernel),NF_KERNEL_SOURCE())
+
+dnl--------------------------------
+
+
+dnl Output the makefile
+AC_OUTPUT(Makefile src/Makefile include/Makefile include/libnfnetlink_conntrack/Makefile utils/Makefile)
+
diff --git a/include/libnfnetlink_conntrack/libnfnetlink_conntrack.h b/include/libnfnetlink_conntrack/libnfnetlink_conntrack.h
new file mode 100644
index 0000000..7f66510
--- /dev/null
+++ b/include/libnfnetlink_conntrack/libnfnetlink_conntrack.h
@@ -0,0 +1,122 @@
+/* libctnetlink.h: Header file for the Connection Tracking library.
+ *
+ * Jay Schulist <jschlst@samba.org>, Copyright (c) 2001.
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef __LIBCTNETLINK_H
+#define __LIBCTNETLINK_H
+
+#include <netinet/in.h>
+#include <asm/types.h>
+#include <linux/if.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+#include "libnfnetlink.h"
+
+#define CTNL_BUFFSIZE 4096
+
+union ctnl_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;
+};
+
+struct ctnl_tuple {
+ union {
+ u_int32_t v4;
+ u_int64_t v6;
+ } src;
+
+ union {
+ u_int32_t v4;
+ u_int64_t v6;
+ } dst;
+
+ u_int8_t protonum;
+ union ctnl_l4 l4src;
+ union ctnl_l4 l4dst;
+};
+
+union ctnl_protoinfo {
+ struct {
+ u_int8_t state;
+ } tcp;
+};
+
+struct ctnl_counters {
+ u_int64_t packets;
+ u_int64_t bytes;
+};
+
+struct ctnl_nat {
+ u_int32_t min_ip, max_ip;
+ union ctnl_l4 l4min, l4max;
+};
+
+#define CTNL_DIR_ORIGINAL 0
+#define CTNL_DIR_REPLY 1
+#define CTNL_DIR_MAX CTNL_DIR_REPLY+1
+
+struct ctnl_conntrack {
+ struct ctnl_tuple tuple[CTNL_DIR_MAX];
+
+ unsigned long timeout;
+ unsigned long mark;
+ unsigned int status;
+ unsigned int use;
+ unsigned int id;
+
+ union ctnl_protoinfo protoinfo;
+ struct ctnl_counters counters[CTNL_DIR_MAX];
+ struct ctnl_nat nat;
+};
+
+struct ctnl_msg_handler {
+ int type;
+ int (*handler)(struct sockaddr_nl *, struct nlmsghdr *, void *arg);
+};
+
+struct ctnl_handle {
+ struct nfnl_handle nfnlh;
+ struct ctnl_msg_handler *handler[IPCTNL_MSG_MAX];
+};
+
+extern int ctnl_open(struct ctnl_handle *, unsigned);
+extern int ctnl_close(struct ctnl_handle *);
+extern int ctnl_unregister_handler(struct ctnl_handle *, int);
+extern int ctnl_register_handler(struct ctnl_handle *,
+ struct ctnl_msg_handler *);
+extern int ctnl_new_conntrack(struct ctnl_handle *, struct ctnl_conntrack *);
+extern int ctnl_upd_conntrack(struct ctnl_handle *, struct ctnl_conntrack *);
+extern int ctnl_get_conntrack(struct ctnl_handle *, struct ctnl_tuple *, int);
+extern int ctnl_del_conntrack(struct ctnl_handle *, struct ctnl_tuple *, int);
+extern int ctnl_list_conntrack(struct ctnl_handle *, int);
+extern int ctnl_list_conntrack_zero_counters(struct ctnl_handle *, int);
+extern int ctnl_event_conntrack(struct ctnl_handle *, int);
+extern int ctnl_flush_conntrack(struct ctnl_handle *);
+
+extern int ctnl_list_expect(struct ctnl_handle *, int);
+extern int ctnl_event_expect(struct ctnl_handle *, int);
+extern int ctnl_flush_expect(struct ctnl_handle *);
+
+extern int ctnl_send(struct ctnl_handle *, struct nlmsghdr *);
+extern int ctnl_wilddump_request(struct ctnl_handle *, int , int);
+
+#endif /* __LIBCTNETLINK_H */
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..599e4ba
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,12 @@
+#AUTOMAKE_OPTIONS = no-dependencies foreign
+
+#EXTRA_DIST = $(man_MANS) acinclude.m4
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I${KERNELDIR}
+AM_CFLAGS=-fPIC -Wall
+LIBS=
+
+lib_LTLIBRARIES = libnfnetlink_conntrack.la
+
+libnfnetlink_conntrack_la_LDFLAGS = -Wc,-nostartfiles
+libnfnetlink_conntrack_la_SOURCES = libnfnetlink_conntrack.c
diff --git a/src/libnfnetlink_conntrack.c b/src/libnfnetlink_conntrack.c
new file mode 100644
index 0000000..1039e25
--- /dev/null
+++ b/src/libnfnetlink_conntrack.c
@@ -0,0 +1,470 @@
+/* libctnetlink.c: generic library for access to connection tracking.
+ *
+ * (C) 2001 by Jay Schulist <jschlst@samba.org>
+ * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com)
+ *
+ * this software may be used and distributed according to the terms
+ * of the gnu general public license, incorporated herein by reference.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <asm/types.h>
+#include <linux/if.h>
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+#include "libctnetlink.h"
+
+#define ctnl_error printf
+
+/***********************************************************************
+ * low level stuff
+ ***********************************************************************/
+int ctnl_send(struct ctnl_handle *cth, struct nlmsghdr *n)
+{
+ return nfnl_send(&cth->nfnlh, n);
+}
+
+int ctnl_wilddump_request(struct ctnl_handle *cth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct nfgenmsg g;
+ } req;
+
+ nfnl_fill_hdr(&cth->nfnlh, &req.nlh, 0, AF_INET, 0,
+ type, NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST);
+
+ return nfnl_send(&cth->nfnlh, &req.nlh);
+}
+
+/* handler used for nfnl_listen */
+static int list_conntrack_handler(struct sockaddr_nl *nladdr,
+ struct nlmsghdr *n, void *arg)
+{
+ struct ctnl_handle *cth = (struct ctnl_handle *) arg;
+ int type = NFNL_MSG_TYPE(n->nlmsg_type);
+ struct ctnl_msg_handler *hdlr = cth->handler[type];
+ int ret;
+
+ if (NFNL_SUBSYS_ID(n->nlmsg_type) != NFNL_SUBSYS_CTNETLINK) {
+ ctnl_error("received message for wrong subsys, skipping\n");
+ nfnl_dump_packet(n, n->nlmsg_len, "list_conntrack_handler");
+ return 0;
+ }
+
+ if (!hdlr) {
+ ctnl_error("no handler for type %d\n", type);
+ return 0;
+ }
+
+ if (!hdlr->handler) {
+ ctnl_error("no handler function for type %d\n", type);
+ return 0;
+ }
+
+ ret = hdlr->handler(nladdr, n, arg);
+
+ return ret;
+}
+
+/***********************************************************************
+ * high level stuff
+ ***********************************************************************/
+
+/**
+ * ctnl_open - open a libctnetlink handle
+ *
+ * cth: pointer to already allocated library handle
+ * subscriptions: netlink groups we are interested in
+ */
+int ctnl_open(struct ctnl_handle *cth, unsigned subscriptions)
+{
+ int err;
+
+ memset(cth, 0, sizeof(*cth));
+
+ err = nfnl_open(&cth->nfnlh, NFNL_SUBSYS_CTNETLINK, subscriptions);
+ if (err < 0) {
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * ctnl_close - close a libctnetlink handle
+ *
+ * cth: libctnetlink handle
+ */
+int ctnl_close(struct ctnl_handle *cth)
+{
+ int err;
+
+ err = nfnl_close(&cth->nfnlh);
+
+ return err;
+}
+
+/* ctnl_register_handler - register handler for ctnetlink mesage type
+ *
+ * cth: libctnetlink handle
+ * hndlr: handler structure
+ */
+int ctnl_register_handler(struct ctnl_handle *cth,
+ struct ctnl_msg_handler *hndlr)
+{
+ if (hndlr->type >= IPCTNL_MSG_MAX)
+ return -EINVAL;
+
+ cth->handler[hndlr->type] = hndlr;
+
+ return 0;
+}
+
+/**
+ * ctnl_unregister_handler - unregister handler for ctnetlink msgtype
+ *
+ * cth: libctnetlink handle
+ * type: message type
+ */
+int ctnl_unregister_handler(struct ctnl_handle *cth, int type)
+{
+ if (type >= IPCTNL_MSG_MAX)
+ return -EINVAL;
+
+ cth->handler[type] = NULL;
+ return 0;
+}
+
+int ctnl_flush_conntrack(struct ctnl_handle *cth)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct nfgenmsg g;
+ } *req;
+
+ char buf[sizeof(*req)];
+ memset(&buf, 0, sizeof(buf));
+
+ req = (void *) &buf;
+
+ nfnl_fill_hdr(&cth->nfnlh, (struct nlmsghdr *) &buf,
+ 0, AF_INET, 0, IPCTNL_MSG_CT_DELETE,
+ NLM_F_REQUEST|NLM_F_ACK);
+
+ if (nfnl_send(&cth->nfnlh, (struct nlmsghdr *)&buf) < 0 )
+ return -1;
+
+ return nfnl_listen(&cth->nfnlh, &list_conntrack_handler, cth);
+}
+
+/**
+ * ctnl_list_conntrack - list connection tracking table
+ * cth: libctnetlink handle
+ * family: AF_INET, ...
+ */
+int ctnl_list_conntrack(struct ctnl_handle *cth, int family)
+{
+ if (ctnl_wilddump_request(cth, family, IPCTNL_MSG_CT_GET) < 0)
+ return -1;
+
+ return nfnl_listen(&cth->nfnlh, &list_conntrack_handler, cth);
+}
+
+int ctnl_list_conntrack_zero_counters(struct ctnl_handle *cth, int family)
+{
+ if (ctnl_wilddump_request(cth, family, IPCTNL_MSG_CT_GET_CTRZERO) < 0)
+ return -1;
+
+ return nfnl_listen(&cth->nfnlh, &list_conntrack_handler, cth);
+}
+
+int ctnl_event_conntrack(struct ctnl_handle *cth, int family)
+{
+ return nfnl_listen(&cth->nfnlh, &list_conntrack_handler, cth);
+}
+
+struct nfnlhdr {
+ struct nlmsghdr nlh;
+ struct nfgenmsg nfmsg;
+};
+
+static void ctnl_build_tuple_ip(struct nfnlhdr *req, int size,
+ struct ctnl_tuple *t)
+{
+ struct nfattr *nest;
+
+ nest = nfnl_nest(&req->nlh, size, CTA_TUPLE_IP);
+
+ 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));
+
+ nfnl_nest_end(&req->nlh, nest);
+}
+
+static void ctnl_build_tuple_proto(struct nfnlhdr *req, int size,
+ struct ctnl_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_int16_t));
+
+ switch(t->protonum) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ 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->l4src.icmp.code, sizeof(u_int8_t));
+ nfnl_addattr_l(&req->nlh, size, CTA_PROTO_ICMP_TYPE,
+ &t->l4dst.icmp.type, sizeof(u_int8_t));
+ break;
+ }
+ nfnl_nest_end(&req->nlh, nest);
+}
+
+static void ctnl_build_tuple(struct nfnlhdr *req, int size,
+ struct ctnl_tuple *t, int dir)
+{
+ enum ctattr_type type = dir ? CTA_TUPLE_REPLY : CTA_TUPLE_ORIG;
+ struct nfattr *nest;
+
+ nest = nfnl_nest(&req->nlh, size, type);
+
+ ctnl_build_tuple_ip(req, size, t);
+ ctnl_build_tuple_proto(req, size, t);
+
+ nfnl_nest_end(&req->nlh, nest);
+}
+
+static void ctnl_build_protoinfo(struct nfnlhdr *req, int size,
+ struct ctnl_conntrack *ct)
+{
+ struct nfattr *nest;
+
+ nest = nfnl_nest(&req->nlh, size, CTA_PROTOINFO);
+
+ switch (ct->tuple[CTNL_DIR_ORIGINAL].protonum) {
+ case IPPROTO_TCP:
+ nfnl_addattr_l(&req->nlh, size, CTA_PROTOINFO_TCP_STATE,
+ &ct->protoinfo.tcp.state, sizeof(u_int8_t));
+ break;
+ }
+
+ nfnl_nest_end(&req->nlh, nest);
+}
+
+static void ctnl_build_protonat(struct nfnlhdr *req, int size,
+ struct ctnl_conntrack *ct)
+{
+ struct nfattr *nest;
+
+ nest = nfnl_nest(&req->nlh, size, CTA_NAT_PROTO);
+
+ switch (ct->tuple[CTNL_DIR_ORIGINAL].protonum) {
+#if 0
+ case IPPROTO_TCP:
+ nfnl_addattr_l(&req->nlh, size, CTA_PROTONAT_TCP_MIN,
+ &ct->nat.l4min.tcp.port, sizeof(u_int16_t));
+ nfnl_addattr_l(&req->nlh, size, CTA_PROTONAT_TCP_MAX,
+ &ct->nat.l4max.tcp.port, sizeof(u_int16_t));
+ break;
+ case IPPROTO_UDP:
+ nfnl_addattr_l(&req->nlh, size, CTA_PROTONAT_UDP_MIN,
+ &ct->nat.l4min.udp.port, sizeof(u_int16_t));
+ nfnl_addattr_l(&req->nlh, size, CTA_PROTONAT_UDP_MAX,
+ &ct->nat.l4max.udp.port, sizeof(u_int16_t));
+ break;
+#endif
+ }
+ nfnl_nest_end(&req->nlh, nest);
+}
+
+static void ctnl_build_nat(struct nfnlhdr *req, int size,
+ struct ctnl_conntrack *ct)
+{
+ struct nfattr *nest;
+
+ nest = nfnl_nest(&req->nlh, size, CTA_NAT);
+
+ nfnl_addattr_l(&req->nlh, size, CTA_NAT_MINIP,
+ &ct->nat.min_ip, sizeof(u_int32_t));
+
+ if (ct->nat.min_ip != ct->nat.max_ip)
+ nfnl_addattr_l(&req->nlh, size, CTA_NAT_MAXIP,
+ &ct->nat.max_ip, sizeof(u_int32_t));
+
+ if (ct->nat.l4min.all != ct->nat.l4max.all)
+ ctnl_build_protonat(req, size, ct);
+
+ nfnl_nest_end(&req->nlh, nest);
+}
+
+static void ctnl_build_conntrack(struct nfnlhdr *req, int size,
+ struct ctnl_conntrack *ct)
+{
+ ctnl_build_tuple(req, size, &ct->tuple[CTNL_DIR_ORIGINAL],
+ CTNL_DIR_ORIGINAL);
+ ctnl_build_tuple(req, size, &ct->tuple[CTNL_DIR_REPLY],
+ CTNL_DIR_REPLY);
+
+ nfnl_addattr_l(&req->nlh, size, CTA_STATUS, &ct->status,
+ sizeof(unsigned int));
+ nfnl_addattr_l(&req->nlh, size, CTA_TIMEOUT, &ct->timeout,
+ sizeof(unsigned long));
+
+ ctnl_build_protoinfo(req, size, ct);
+ if (ct->nat.min_ip != 0)
+ ctnl_build_nat(req, size, ct);
+}
+
+/**
+ * ctnl_get_conntrack - get a connection from conntrack hashtable
+ * cth: libctnetlink handle
+ * t: tuple of connection to get
+ * cb: a struct nfattr to put the connection in
+ */
+int ctnl_get_conntrack(struct ctnl_handle *cth,
+ struct ctnl_tuple *tuple,
+ int dir)
+{
+ struct nfnlhdr *req;
+ char buf[CTNL_BUFFSIZE];
+
+ memset(&buf, 0, sizeof(buf));
+ req = (void *) &buf;
+
+ nfnl_fill_hdr(&cth->nfnlh, (struct nlmsghdr *) &buf,
+ 0, AF_INET, 0, IPCTNL_MSG_CT_GET,
+ NLM_F_REQUEST|NLM_F_ACK);
+
+ ctnl_build_tuple(req, sizeof(buf), tuple, dir);
+
+ if (nfnl_send(&cth->nfnlh, (struct nlmsghdr *)&buf) < 0)
+ return -1;
+
+ return nfnl_listen(&cth->nfnlh, &list_conntrack_handler, cth);
+}
+
+/**
+ * ctnl_del_conntrack - delete a connection from conntrack hashtable
+ * cth: libctnetlink handle
+ * t: tuple of to-be-deleted connection
+ */
+int ctnl_del_conntrack(struct ctnl_handle *cth,
+ struct ctnl_tuple *tuple,
+ int dir)
+{
+ struct nfnlhdr *req;
+ char buf[CTNL_BUFFSIZE];
+
+ memset(&buf, 0, sizeof(buf));
+ req = (void *) &buf;
+
+ nfnl_fill_hdr(&cth->nfnlh, (struct nlmsghdr *) &buf,
+ 0, AF_INET, 0, IPCTNL_MSG_CT_DELETE,
+ NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST|NLM_F_ACK);
+
+ ctnl_build_tuple(req, sizeof(buf), tuple, dir);
+
+ if (nfnl_send(&cth->nfnlh, (struct nlmsghdr *)&buf) < 0)
+ return -1;
+
+ return nfnl_listen(&cth->nfnlh, &list_conntrack_handler, cth);
+}
+static int new_update_conntrack(struct ctnl_handle *cth,
+ struct ctnl_conntrack *ct,
+ u_int16_t msg_flags)
+{
+ struct nfnlhdr *req;
+ char buf[CTNL_BUFFSIZE];
+
+ memset(&buf, 0, sizeof(buf));
+ req = (void *) &buf;
+
+ nfnl_fill_hdr(&cth->nfnlh, (struct nlmsghdr *) &buf,
+ 0, AF_INET, 0, IPCTNL_MSG_CT_NEW,
+ NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK|msg_flags);
+
+ ctnl_build_conntrack(req, sizeof(buf), ct);
+
+ if (nfnl_send(&cth->nfnlh, (struct nlmsghdr *)&buf) < 0 )
+ return -1;
+
+ return nfnl_listen(&cth->nfnlh, &list_conntrack_handler, cth);
+}
+
+/**
+ * ctnl_new_conntrack - create a connection in the conntrack hashtable
+ * cth: libctnetlink handle
+ * t: tuple of to-be-created connection
+ */
+int ctnl_new_conntrack(struct ctnl_handle *cth, struct ctnl_conntrack *ct)
+{
+ return new_update_conntrack(cth, ct, NLM_F_EXCL);
+}
+
+int ctnl_upd_conntrack(struct ctnl_handle *cth, struct ctnl_conntrack *ct)
+{
+ return new_update_conntrack(cth, ct, 0);
+}
+
+/**
+ * ctnl_list_expect - retrieve a list of expectations from conntrack subsys
+ * cth: libctnetlink handle
+ * family: AF_INET, ...
+ */
+int ctnl_list_expect(struct ctnl_handle *cth, int family)
+{
+ if (ctnl_wilddump_request(cth, family, IPCTNL_MSG_EXP_GET) < 0)
+ return -1;
+
+ return nfnl_listen(&cth->nfnlh, &list_conntrack_handler, cth);
+
+}
+
+int ctnl_event_expect(struct ctnl_handle *cth, int family)
+{
+ return nfnl_listen(&cth->nfnlh, &list_conntrack_handler, cth);
+}
+
+int ctnl_flush_expect(struct ctnl_handle *cth)
+{
+ struct nfnlhdr *req;
+ char buf[sizeof(*req)];
+
+ memset(&buf, 0, sizeof(buf));
+ req = (void *) &buf;
+
+ nfnl_fill_hdr(&cth->nfnlh, (struct nlmsghdr *) &buf,
+ 0, AF_INET, 0, IPCTNL_MSG_EXP_DELETE,
+ NLM_F_REQUEST|NLM_F_ACK);
+
+ if (nfnl_send(&cth->nfnlh, (struct nlmsghdr *)&buf) < 0 )
+ return -1;
+
+ return nfnl_listen(&cth->nfnlh, &list_conntrack_handler, cth);
+}
diff --git a/utils/Makefile.am b/utils/Makefile.am
new file mode 100644
index 0000000..f78bdbe
--- /dev/null
+++ b/utils/Makefile.am
@@ -0,0 +1,8 @@
+bin_PROGRAMS = ctnl_test
+ctnl_test_SOURCES = ctnl_test.c
+
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I${KERNELDIR}
+
+ctnl_test_LDFLAGS = $(all_libraries) -lnfnetlink_conntrack -lnfnetlink
+
diff --git a/utils/ctnl_test.c b/utils/ctnl_test.c
new file mode 100644
index 0000000..bd3173f
--- /dev/null
+++ b/utils/ctnl_test.c
@@ -0,0 +1,188 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+
+#include "libctnetlink.h"
+
+static struct ctnl_handle *cth;
+
+char *display_tuple_flat(struct ip_conntrack_tuple *tuple)
+{
+ static char buff[250];
+ char psb[20];
+ int len = 0;
+
+ memset(buff, '\0', sizeof(buff));
+ len += sprintf(buff + len, "%s:", inet_ntoa((struct in_addr){tuple->src.ip}));
+ switch(tuple->dst.protonum) {
+ case (IPPROTO_ICMP):
+ len += sprintf(buff + len, "Icmp (id %d)",
+ ntohs(tuple->src.u.icmp.id));
+ break;
+ case (IPPROTO_TCP):
+ sprintf(psb, "%d", ntohs(tuple->src.u.tcp.port));
+ len += sprintf(buff + len, "%s", psb);
+ break;
+ case (IPPROTO_UDP):
+ sprintf(psb, "%d", ntohs(tuple->src.u.udp.port));
+ len += sprintf(buff + len, "%s", psb);
+ break;
+ default:
+ len += sprintf(buff + len, "Unknown");
+ break;
+ }
+
+ len += sprintf(buff + len, "->");
+ len += sprintf(buff + len, "%s:", inet_ntoa((struct in_addr){tuple->dst.ip}));
+ switch(tuple->dst.protonum) {
+ case (IPPROTO_ICMP):
+ len += sprintf(buff + len, "Icmp (%d, code %d)",
+ tuple->dst.u.icmp.type,
+ tuple->dst.u.icmp.code);
+ break;
+ case (IPPROTO_TCP):
+ sprintf(psb, "%d", ntohs(tuple->dst.u.tcp.port));
+ len += sprintf(buff + len, "%s", psb);
+ break;
+ case (IPPROTO_UDP):
+ sprintf(psb, "%d", ntohs(tuple->dst.u.udp.port));
+ len += sprintf(buff + len, "%s", psb);
+ break;
+ default:
+ len += sprintf(buff + len, "Unknown");
+ break;
+ }
+
+ return (buff);
+}
+
+int ctnl_parse_attr(struct nfattr *tb[], int max, struct nfattr *cta, int len)
+{
+ while(NFA_OK(cta, len)) {
+ if(cta->nfa_type <= max)
+ tb[cta->nfa_type] = cta;
+ cta = NFA_NEXT(cta,len);
+ }
+ if (len)
+ printf("ctnl_parse_attr: deficit (%d) len (%d).\n",
+ len, cta->nfa_len);
+ return 0;
+}
+
+#if 0
+int dump()
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct nfgenmsg nfmsg;
+ } req;
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8)|CTNL_MSG_CT_GET;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_DUMP|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = 1;
+ req.nfmsg.nfgen_family = AF_INET;
+
+ return (sendto(ctnlfd, &req, sizeof(req), 0,
+ (struct sockaddr *) &nladdr, sizeof(nladdr)));
+
+}
+#endif
+
+int print_msg(struct nfgenmsg *cm, size_t len)
+{
+ struct nfattr *cb[CTA_MAX + 1];
+
+ printf("ctm_family=0x%x\n", cm->nfgen_family);
+
+ ctnl_parse_attr(cb, CTA_MAX, NFM_NFA(cm), len);
+
+ if (cb[CTA_ORIG]) {
+ printf("orig: %s\n",
+ display_tuple_flat(NFA_DATA(cb[CTA_ORIG])));
+ ctnl_del_conntrack(cth, NFA_DATA(cb[CTA_ORIG]), CTA_ORIG);
+ }
+ if (cb[CTA_RPLY])
+ printf("rply: %s\n",
+ display_tuple_flat(NFA_DATA(cb[CTA_RPLY])));
+
+
+ return 0;
+}
+
+struct nlmsghdr *ctnl_get_packet(struct nlmsghdr **last_nlhdr,
+ char *buf, size_t len)
+{
+ struct nlmsghdr *nlh;
+ size_t remain_len;
+
+ if ((char *)(*last_nlhdr) > (buf + len) ||
+ (char *)(*last_nlhdr) < buf)
+ *last_nlhdr = NULL;
+
+ if (!*last_nlhdr) {
+ nlh = (struct nlmsghdr *) buf;
+ if (!NLMSG_OK(nlh, len)) {
+ printf("error parsing nlmsg\n");
+ return NULL;
+ }
+ } else {
+ /* we are n-th part of multipart mesasge */
+ if ((*last_nlhdr)->nlmsg_type == NLMSG_DONE ||
+ !((*last_nlhdr)->nlmsg_flags & NLM_F_MULTI)) {
+ *last_nlhdr = NULL;
+ return NULL;
+ }
+
+ remain_len = (len - ((char *)(*last_nlhdr) - buf));
+ nlh = NLMSG_NEXT(*last_nlhdr, remain_len);
+ }
+
+ *last_nlhdr = nlh;
+ return nlh;
+}
+
+int main(int argc, char **argv)
+{
+ char buf[20480];
+ struct nfgenmsg *last_cm = NULL, *cm;
+ struct nlmsghdr *nlh;
+ int len;
+
+ cth = malloc(sizeof(*cth));
+ if (ctnl_open(cth, 0) < 0) {
+ exit(2);
+ }
+
+ ctnl_wilddump_request(cth, AF_INET, IPCTNL_MSG_CT_GET);
+
+ while (len = recv(cth->nfnlh.fd, &buf, sizeof(buf), 0)) {
+ printf("pkt received\n");
+ while (nlh = ctnl_get_packet(&last_cm, (char *)&buf, len)) {
+ printf(" decoding msg type 0x%04x\n", nlh->nlmsg_type);
+ if (NFNL_SUBSYS_ID(nlh->nlmsg_type) ==
+ NFNL_SUBSYS_CTNETLINK) {
+ cm = NLMSG_DATA(nlh);
+ print_msg(cm, nlh->nlmsg_len);
+ }
+ }
+ }
+
+ return 0;
+}
+