summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/conntrack/run-test.sh20
-rw-r--r--tests/conntrack/test-conntrack.c94
-rw-r--r--tests/conntrack/testsuite/00create20
-rw-r--r--tests/conntrack/testsuite/01delete6
-rw-r--r--tests/conntrack/testsuite/02filter23
-rw-r--r--tests/conntrack/testsuite/03nat40
-rw-r--r--tests/conntrack/testsuite/04zone8
-rw-r--r--tests/conntrack/testsuite/05mark27
-rw-r--r--tests/conntrack/testsuite/06update8
-rw-r--r--tests/conntrackd/cthelper/.gitignore14
-rw-r--r--tests/conntrackd/cthelper/Make_global.am7
-rw-r--r--tests/conntrackd/cthelper/Makefile.am20
-rw-r--r--tests/conntrackd/cthelper/README2
-rw-r--r--tests/conntrackd/cthelper/configure.ac64
-rwxr-xr-xtests/conntrackd/cthelper/ct.c91
-rwxr-xr-xtests/conntrackd/cthelper/ct.h22
-rw-r--r--tests/conntrackd/cthelper/expect.c199
-rwxr-xr-xtests/conntrackd/cthelper/l3_ipv4.c86
-rwxr-xr-xtests/conntrackd/cthelper/l4_tcp.c88
-rwxr-xr-xtests/conntrackd/cthelper/l4_udp.c88
-rwxr-xr-xtests/conntrackd/cthelper/main.c175
-rw-r--r--tests/conntrackd/cthelper/pcaps/nfsv3.pcapbin0 -> 6824 bytes
-rw-r--r--tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcapbin0 -> 1095 bytes
-rwxr-xr-xtests/conntrackd/cthelper/proto.c49
-rwxr-xr-xtests/conntrackd/cthelper/proto.h50
-rw-r--r--tests/conntrackd/cthelper/run-test.sh8
-rw-r--r--tests/conntrackd/cthelper/test.h13
-rw-r--r--tests/nfct/run-test.sh20
-rw-r--r--tests/nfct/test-live.sh73
-rw-r--r--tests/nfct/test.c100
-rw-r--r--tests/nfct/timeout/00tcp16
-rw-r--r--tests/nfct/timeout/01udp16
-rw-r--r--tests/nfct/timeout/02generic16
-rw-r--r--tests/nfct/timeout/03udplite16
-rw-r--r--tests/nfct/timeout/04icmp16
-rw-r--r--tests/nfct/timeout/05icmpv616
-rw-r--r--tests/nfct/timeout/06sctp16
-rw-r--r--tests/nfct/timeout/07dccp16
-rw-r--r--tests/nfct/timeout/08gre16
39 files changed, 1559 insertions, 0 deletions
diff --git a/tests/conntrack/run-test.sh b/tests/conntrack/run-test.sh
new file mode 100644
index 0000000..2b7b6f2
--- /dev/null
+++ b/tests/conntrack/run-test.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+UID=`id -u`
+if [ $UID -ne 0 ]
+then
+ echo "Run this test as root"
+ exit 1
+fi
+
+gcc test-conntrack.c -o test
+#
+# XXX: module auto-load not support by nfnetlink_cttimeout yet :-(
+#
+modprobe nf_conntrack_ipv4
+modprobe nf_conntrack_ipv6
+modprobe nf_conntrack_proto_udplite
+modprobe nf_conntrack_proto_sctp
+modprobe nf_conntrack_proto_dccp
+modprobe nf_conntrack_proto_gre
+./test testcases
diff --git a/tests/conntrack/test-conntrack.c b/tests/conntrack/test-conntrack.c
new file mode 100644
index 0000000..c9097b6
--- /dev/null
+++ b/tests/conntrack/test-conntrack.c
@@ -0,0 +1,94 @@
+/*
+ * Very simple test-tool for the command line tool `conntrack'.
+ * This code is released under GPLv2 or any later at your option.
+ *
+ * gcc test-conntrack.c -o test
+ *
+ * Do not forget that you need *root* or CAP_NET_ADMIN capabilities ;-)
+ *
+ * (c) 2008 Pablo Neira Ayuso <pablo@netfilter.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <dirent.h>
+
+#define CT_PROG "/usr/sbin/conntrack"
+
+int main()
+{
+ int ret, ok = 0, bad = 0, line;
+ FILE *fp;
+ DIR *d;
+ char buf[1024];
+ struct dirent *dent;
+ char file[1024];
+
+ d = opendir("testsuite");
+
+ while ((dent = readdir(d)) != NULL) {
+
+ sprintf(file, "testsuite/%s", dent->d_name);
+
+ line = 0;
+
+ fp = fopen(file, "r");
+ if (fp == NULL) {
+ perror("cannot find testsuite file");
+ exit(EXIT_FAILURE);
+ }
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ char tmp[1024] = CT_PROG, *res;
+ tmp[strlen(CT_PROG)] = ' ';
+
+ line++;
+
+ if (buf[0] == '#' || buf[0] == ' ')
+ continue;
+
+ res = strchr(buf, ';');
+ if (!res) {
+ printf("malformed file %s at line %d\n",
+ dent->d_name, line);
+ exit(EXIT_FAILURE);
+ }
+ *res = '\0';
+ res+=2;
+
+ strcpy(tmp + strlen(CT_PROG) + 1, buf);
+ printf("(%d) Executing: %s\n", line, tmp);
+
+ ret = system(tmp);
+
+ if (WIFEXITED(ret) &&
+ WEXITSTATUS(ret) == EXIT_SUCCESS) {
+ if (res[0] == 'O' &&
+ res[1] == 'K')
+ ok++;
+ else {
+ bad++;
+ printf("^----- BAD\n");
+ }
+ } else {
+ if (res[0] == 'B' &&
+ res[1] == 'A' &&
+ res[2] == 'D')
+ ok++;
+ else {
+ bad++;
+ printf("^----- BAD\n");
+ }
+ }
+ printf("=====\n");
+ }
+ fclose(fp);
+ }
+ closedir(d);
+
+ fprintf(stdout, "OK: %d BAD: %d\n", ok, bad);
+}
diff --git a/tests/conntrack/testsuite/00create b/tests/conntrack/testsuite/00create
new file mode 100644
index 0000000..40e2c19
--- /dev/null
+++ b/tests/conntrack/testsuite/00create
@@ -0,0 +1,20 @@
+#missing destination
+-I -s 1.1.1.1 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing source
+-I -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing protocol
+-I -s 1.1.1.1 -d 2.2.2.2 --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing source port
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing timeout
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY ; BAD
+# create a conntrack
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# create again
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+# delete
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# create from reply
+-I -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# delete reverse
+-D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; OK
diff --git a/tests/conntrack/testsuite/01delete b/tests/conntrack/testsuite/01delete
new file mode 100644
index 0000000..3c38ac5
--- /dev/null
+++ b/tests/conntrack/testsuite/01delete
@@ -0,0 +1,6 @@
+# create dummy
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# delete bad source
+-D -s 2.2.2.2 -p tcp --sport 10 --dport 20 ; BAD
+# delete by source
+-D -s 1.1.1.1 ; OK
diff --git a/tests/conntrack/testsuite/02filter b/tests/conntrack/testsuite/02filter
new file mode 100644
index 0000000..204c4e8
--- /dev/null
+++ b/tests/conntrack/testsuite/02filter
@@ -0,0 +1,23 @@
+# create dummy
+conntrack -I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# filter by source
+conntrack -L -s 1.1.1.1 ; OK
+# filter by destination
+conntrack -L -d 2.2.2.2 ; OK
+# filter by protocol
+conntrack -L -p tcp ; OK
+# filter by status
+conntrack -L -u SEEN_REPLY ; OK
+# filter by TCP protocol state
+conntrack -L -p tcp --state LISTEN ; OK
+# update mark of dummy conntrack
+conntrack -U -s 1.1.1.1 -m 1 ; OK
+# filter by mark
+conntrack -L -m 1 ; OK
+# filter by layer 3 protocol
+conntrack -L -f ipv4 ; OK
+# filter by mark
+conntrack -L --mark 0 ; OK
+conntrack -L --mark 0/0xffffffff; OK
+# delete dummy
+conntrack -D -d 2.2.2.2 ; OK
diff --git a/tests/conntrack/testsuite/03nat b/tests/conntrack/testsuite/03nat
new file mode 100644
index 0000000..f94e8ff
--- /dev/null
+++ b/tests/conntrack/testsuite/03nat
@@ -0,0 +1,40 @@
+# create dummy
+-I -s 1.1.1.1 -d 2.2.2.2 --dst-nat 3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# show
+-L --dst-nat ; OK
+# show
+-L --dst-nat 3.3.3.3 ; OK
+# show
+-L --src-nat ; OK
+# delete
+-D -s 1.1.1.1 ; OK
+# create dummy again
+-I -s 1.1.1.1 -d 2.2.2.2 --src-nat 3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# show
+-L --src-nat ; OK
+# show
+-L --src-nat 3.3.3.3 ; OK
+# show
+-L --dst-nat ; OK
+# show any-nat
+-L --any-nat ; OK
+# delete
+-D -s 1.1.1.1 ; OK
+# bad combination
+-L --dst-nat --any-nat ; BAD
+# bad combination
+-L --src-nat --any-nat ; BAD
+# bad combination
+-L --src-nat --dst-nat --any-nat ; BAD
+# create
+-I -s 1.1.1.1 -d 2.2.2.2 --dst-nat 3.3.3.3:80 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# show
+-L --dst-nat 3.3.3.3:80 ; OK
+# show
+-L --any-nat 3.3.3.3:80 ; OK
+# show
+-L --dst-nat 3.3.3.3:81 ; OK
+# show
+-L --dst-nat 1.1.1.1:80 ; OK
+# delete
+-D -s 1.1.1.1 ; OK
diff --git a/tests/conntrack/testsuite/04zone b/tests/conntrack/testsuite/04zone
new file mode 100644
index 0000000..4ff3d34
--- /dev/null
+++ b/tests/conntrack/testsuite/04zone
@@ -0,0 +1,8 @@
+# create dummy
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --zone 1; OK
+# display dummy
+-L --zone 1; OK
+# display dummy
+-L --zone 0; OK
+# delete dummy
+-D --zone 1; OK
diff --git a/tests/conntrack/testsuite/05mark b/tests/conntrack/testsuite/05mark
new file mode 100644
index 0000000..4d99dea
--- /dev/null
+++ b/tests/conntrack/testsuite/05mark
@@ -0,0 +1,27 @@
+# create with a mark
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 42 ; OK
+# find it again using mark
+-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42 ; OK
+-L --mark 42; OK
+# ct already exists
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 42/0xffffffff ; BAD
+# delete by mark
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/0xffffffff ; OK
+# try again after del
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 417889/0xffffffff ; OK
+# delete by mark
+-D --mark 417889 ; OK
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 0xffffffff ; OK
+# zap top 16.
+-U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 0/0xffff0000 ; OK
+-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 0x0000ffff ; OK
+-U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/0xffff ; OK
+-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/0x0000ffff ; OK
+-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/42 ; OK
+-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 2/2 ; OK
+-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 2/3 ; OK
+# OK, but no flow entries should be shown here:
+-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 2/0xf ; OK
+# BAD, because no updates done (mark is already 42).
+-U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42 ; BAD
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42 ; OK
diff --git a/tests/conntrack/testsuite/06update b/tests/conntrack/testsuite/06update
new file mode 100644
index 0000000..0408303
--- /dev/null
+++ b/tests/conntrack/testsuite/06update
@@ -0,0 +1,8 @@
+# create dummy flow
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state SYN_RECV -u SEEN_REPLY,ASSURED -t 50 ; OK
+# find it again using mark
+-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# set fixed timeout
+-U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 -u FIXED_TIMEOUT; OK
+# delete it
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20; OK
diff --git a/tests/conntrackd/cthelper/.gitignore b/tests/conntrackd/cthelper/.gitignore
new file mode 100644
index 0000000..928e44b
--- /dev/null
+++ b/tests/conntrackd/cthelper/.gitignore
@@ -0,0 +1,14 @@
+.deps/
+.libs/
+Makefile
+Makefile.in
+*.o
+*.la
+*.lo
+
+/aclocal.m4
+/autom4te.cache/
+/build-aux/
+/config.*
+/configure
+/libtool
diff --git a/tests/conntrackd/cthelper/Make_global.am b/tests/conntrackd/cthelper/Make_global.am
new file mode 100644
index 0000000..06785a1
--- /dev/null
+++ b/tests/conntrackd/cthelper/Make_global.am
@@ -0,0 +1,7 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include -I../../../include
+
+AM_CFLAGS = -std=gnu99 -W -Wall \
+ -Wmissing-prototypes -Wwrite-strings -Wcast-qual -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \
+ -Wno-unused-parameter \
+ ${LIBNETFILTER_CONNTRACK_CFLAGS} \
+ ${LIBNETFILTER_CTTIMEOUT_CFLAGS}
diff --git a/tests/conntrackd/cthelper/Makefile.am b/tests/conntrackd/cthelper/Makefile.am
new file mode 100644
index 0000000..b8f0d42
--- /dev/null
+++ b/tests/conntrackd/cthelper/Makefile.am
@@ -0,0 +1,20 @@
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = cthelper-test
+
+cthelper_test_SOURCES = proto.c \
+ ct.c \
+ l3_ipv4.c \
+ l4_tcp.c \
+ l4_udp.c \
+ expect.c \
+ ../../../src/helpers.c \
+ main.c
+
+cthelper_test_LDFLAGS = -dynamic \
+ -lpcap \
+ -ldl \
+ -lmnl \
+ -lnetfilter_queue \
+ -lnetfilter_conntrack \
+ -export-dynamic
diff --git a/tests/conntrackd/cthelper/README b/tests/conntrackd/cthelper/README
new file mode 100644
index 0000000..6e8b385
--- /dev/null
+++ b/tests/conntrackd/cthelper/README
@@ -0,0 +1,2 @@
+This directory contains PCAP files with traffic traces that we can use to test
+the user-space helpers.
diff --git a/tests/conntrackd/cthelper/configure.ac b/tests/conntrackd/cthelper/configure.ac
new file mode 100644
index 0000000..8b3da5c
--- /dev/null
+++ b/tests/conntrackd/cthelper/configure.ac
@@ -0,0 +1,64 @@
+AC_INIT(cthelper-test, 0.0.1, pablo@netfilter.org)
+AC_CONFIG_AUX_DIR([build-aux])
+
+AC_CANONICAL_HOST
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE([-Wall foreign subdir-objects
+ tar-pax no-dist-gzip dist-bzip2 1.6])
+
+dnl kernel style compile messages
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AC_SEARCH_LIBS([dlopen], [dl], [libdl_LIBS="$LIBS"; LIBS=""])
+AC_SUBST([libdl_LIBS])
+
+AC_PROG_CC
+AC_DISABLE_STATIC
+AM_PROG_LIBTOOL
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AM_PROG_LEX
+AC_PROG_YACC
+
+case "$host" in
+*-*-linux*) ;;
+*) AC_MSG_ERROR([Linux only, dude!]);;
+esac
+
+PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.0])
+PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.0])
+
+AC_CHECK_HEADERS(arpa/inet.h)
+dnl check for inet_pton
+AC_CHECK_FUNCS(inet_pton)
+dnl Some systems have it, but not IPv6
+if test "$ac_cv_func_inet_pton" = "yes" ; then
+AC_MSG_CHECKING(if inet_pton supports IPv6)
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+int main()
+ {
+ struct in6_addr addr6;
+ if (inet_pton(AF_INET6, "::1", &addr6) < 1)
+ exit(1);
+ else
+ exit(0);
+ }
+ ]])],[ AC_MSG_RESULT(yes)
+ AC_DEFINE_UNQUOTED(HAVE_INET_PTON_IPV6, 1, [Define to 1 if inet_pton supports IPv6.])
+ ],[AC_MSG_RESULT(no)],[AC_MSG_RESULT(no)])
+fi
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/tests/conntrackd/cthelper/ct.c b/tests/conntrackd/cthelper/ct.c
new file mode 100755
index 0000000..1c17336
--- /dev/null
+++ b/tests/conntrackd/cthelper/ct.c
@@ -0,0 +1,91 @@
+#include <stdlib.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <linux/if_ether.h>
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+#include "proto.h"
+#include "helper.h"
+#include "myct.h"
+#include "ct.h"
+
+static LIST_HEAD(ct_list);
+
+struct nf_ct_entry *
+ct_alloc(const uint8_t *pkt, unsigned int l3hdr_len,
+ struct cthelper_proto_l2l3_helper *l3h,
+ struct cthelper_proto_l4_helper *l4h)
+{
+ struct nf_ct_entry *ct;
+
+ ct = calloc(1, sizeof(struct nf_ct_entry));
+ if (ct == NULL)
+ return NULL;
+
+ ct->myct = calloc(1, sizeof(struct myct));
+ if (ct->myct == NULL) {
+ free(ct);
+ return NULL;
+ }
+ ct->myct->ct = nfct_new();
+ if (ct->myct->ct == NULL) {
+ free(ct->myct);
+ free(ct);
+ return NULL;
+ }
+ /* FIXME: use good private helper size */
+ ct->myct->priv_data = calloc(1, 128);
+ if (ct->myct->priv_data == NULL) {
+ nfct_destroy(ct->myct->ct);
+ free(ct->myct);
+ free(ct);
+ return NULL;
+ }
+
+ l3h->l3ct_build(pkt, ct->myct->ct);
+ l4h->l4ct_build(pkt + l3hdr_len, ct->myct->ct);
+
+ return ct;
+}
+
+struct nf_ct_entry *
+ct_find(const uint8_t *pkt, unsigned int l3hdr_len,
+ struct cthelper_proto_l2l3_helper *l3h,
+ struct cthelper_proto_l4_helper *l4h, unsigned int *ctinfo)
+{
+ struct nf_ct_entry *cur;
+
+ list_for_each_entry(cur, &ct_list, head) {
+ if (l3h->l3ct_cmp_orig(pkt, cur->myct->ct) &&
+ l4h->l4ct_cmp_orig(pkt + l3hdr_len, cur->myct->ct)) {
+ *ctinfo = 0;
+ return cur;
+ }
+ if (l3h->l3ct_cmp_repl(pkt, cur->myct->ct) &&
+ l4h->l4ct_cmp_repl(pkt + l3hdr_len, cur->myct->ct)) {
+ *ctinfo = IP_CT_IS_REPLY;
+ return cur;
+ }
+ }
+ return NULL;
+}
+
+void ct_add(struct nf_ct_entry *ct)
+{
+ list_add(&ct->head, &ct_list);
+}
+
+void ct_flush(void)
+{
+ struct nf_ct_entry *cur, *tmp;
+
+ list_for_each_entry_safe(cur, tmp, &ct_list, head) {
+ list_del(&cur->head);
+ free(cur->myct->priv_data);
+ free(cur->myct->ct);
+ free(cur->myct);
+ free(cur);
+ }
+}
diff --git a/tests/conntrackd/cthelper/ct.h b/tests/conntrackd/cthelper/ct.h
new file mode 100755
index 0000000..f01d49d
--- /dev/null
+++ b/tests/conntrackd/cthelper/ct.h
@@ -0,0 +1,22 @@
+#ifndef _CT_H_
+#define _CT_H_
+
+#include "../../../include/linux_list.h"
+#include "../../../include/myct.h"
+
+struct nf_ct_entry {
+ struct list_head head;
+ struct myct *myct;
+};
+
+struct cthelper_proto_l2l3_helper;
+struct cthelper_proto_l4_helper;
+
+struct nf_ct_entry *ct_alloc(const uint8_t *pkt, unsigned int l3hdr_len, struct cthelper_proto_l2l3_helper *l3h, struct cthelper_proto_l4_helper *l4h);
+
+struct nf_ct_entry *ct_find(const uint8_t *pkt, unsigned int l3hdr_len, struct cthelper_proto_l2l3_helper *l3h, struct cthelper_proto_l4_helper *l4h, unsigned int *ctinfo);
+
+void ct_add(struct nf_ct_entry *ct);
+void ct_flush(void);
+
+#endif
diff --git a/tests/conntrackd/cthelper/expect.c b/tests/conntrackd/cthelper/expect.c
new file mode 100644
index 0000000..c667293
--- /dev/null
+++ b/tests/conntrackd/cthelper/expect.c
@@ -0,0 +1,199 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation (or any later at your option).
+ *
+ * This code has been sponsored by Vyatta Inc. <http://www.vyatta.com>
+ */
+
+#include "../../../include/helper.h"
+#include "test.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+int
+cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master,
+ uint32_t class,
+ union nfct_attr_grp_addr *saddr,
+ union nfct_attr_grp_addr *daddr,
+ uint8_t l4proto, uint16_t *sport, uint16_t *dport)
+{
+ struct nf_conntrack *expected, *mask;
+
+ expected = nfct_new();
+ if (!expected)
+ return -1;
+
+ mask = nfct_new();
+ if (!mask)
+ return -1;
+
+ if (saddr) {
+ switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) {
+ int i;
+ uint32_t addr[4] = {};
+
+ case AF_INET:
+ nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET);
+ nfct_set_attr_u32(expected, ATTR_IPV4_SRC, saddr->ip);
+
+ nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET);
+ nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0xffffffff);
+ break;
+ case AF_INET6:
+ nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6);
+ nfct_set_attr(expected, ATTR_IPV6_SRC, saddr->ip6);
+
+ for (i=0; i<4; i++)
+ memset(addr, 0xffffffff, sizeof(uint32_t));
+
+ nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6);
+ nfct_set_attr(mask, ATTR_IPV6_SRC, addr);
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) {
+ int i;
+ uint32_t addr[4] = {};
+
+ case AF_INET:
+ nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET);
+ nfct_set_attr_u32(expected, ATTR_IPV4_SRC, 0x00000000);
+
+ nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET);
+ nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0x00000000);
+ break;
+ case AF_INET6:
+ for (i=0; i<4; i++)
+ memset(addr, 0x00000000, sizeof(uint32_t));
+
+ nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6);
+ nfct_set_attr(expected, ATTR_IPV6_SRC, addr);
+
+ nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6);
+ nfct_set_attr(mask, ATTR_IPV6_SRC, addr);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (sport) {
+ switch(l4proto) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto);
+ nfct_set_attr_u16(expected, ATTR_PORT_SRC, *sport);
+ nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto);
+ nfct_set_attr_u16(mask, ATTR_PORT_SRC, 0xffff);
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch(l4proto) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto);
+ nfct_set_attr_u16(expected, ATTR_PORT_SRC, 0x0000);
+ nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto);
+ nfct_set_attr_u16(mask, ATTR_PORT_SRC, 0x0000);
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) {
+ uint32_t addr[4] = {};
+ int i;
+
+ case AF_INET:
+ nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET);
+ nfct_set_attr_u32(expected, ATTR_IPV4_DST, daddr->ip);
+ nfct_set_attr_u32(mask, ATTR_IPV4_DST, 0xffffffff);
+ break;
+ case AF_INET6:
+ nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6);
+ nfct_set_attr(expected, ATTR_IPV6_DST, daddr->ip6);
+
+ for (i=0; i<4; i++)
+ memset(addr, 0xffffffff, sizeof(uint32_t));
+
+ nfct_set_attr(mask, ATTR_IPV6_DST, addr);
+ break;
+ default:
+ break;
+ }
+
+ switch(l4proto) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto);
+ nfct_set_attr_u16(expected, ATTR_PORT_DST, *dport);
+ nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto);
+ nfct_set_attr_u16(mask, ATTR_PORT_DST, 0xffff);
+ break;
+ default:
+ break;
+ }
+
+ nfexp_set_attr(exp, ATTR_EXP_MASTER, master);
+ nfexp_set_attr(exp, ATTR_EXP_EXPECTED, expected);
+ nfexp_set_attr(exp, ATTR_EXP_MASK, mask);
+
+ nfct_destroy(expected);
+ nfct_destroy(mask);
+
+ return 0;
+}
+
+int cthelper_add_expect(struct nf_expect *exp)
+{
+ cthelper_test_stats.ct_expect_created++;
+ return 0;
+}
+
+int cthelper_del_expect(struct nf_expect *exp)
+{
+ return 0;
+}
+
+void
+cthelper_get_addr_src(struct nf_conntrack *ct, int dir,
+ union nfct_attr_grp_addr *addr)
+{
+ switch (dir) {
+ case MYCT_DIR_ORIG:
+ nfct_get_attr_grp(ct, ATTR_GRP_ORIG_ADDR_SRC, addr);
+ break;
+ case MYCT_DIR_REPL:
+ nfct_get_attr_grp(ct, ATTR_GRP_REPL_ADDR_SRC, addr);
+ break;
+ }
+}
+
+void
+cthelper_get_addr_dst(struct nf_conntrack *ct, int dir,
+ union nfct_attr_grp_addr *addr)
+{
+ switch (dir) {
+ case MYCT_DIR_ORIG:
+ nfct_get_attr_grp(ct, ATTR_GRP_ORIG_ADDR_DST, addr);
+ break;
+ case MYCT_DIR_REPL:
+ nfct_get_attr_grp(ct, ATTR_GRP_REPL_ADDR_DST, addr);
+ break;
+ }
+}
diff --git a/tests/conntrackd/cthelper/l3_ipv4.c b/tests/conntrackd/cthelper/l3_ipv4.c
new file mode 100755
index 0000000..8edfd2e
--- /dev/null
+++ b/tests/conntrackd/cthelper/l3_ipv4.c
@@ -0,0 +1,86 @@
+#include <stdlib.h>
+#include <netinet/ip.h>
+#include <linux/if_ether.h>
+
+#include "proto.h"
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+#define PRINT_CMP(...)
+
+static void
+l3_ipv4_ct_build_tuple(const uint8_t *pkt, struct nf_conntrack *ct)
+{
+ const struct iphdr *iph = (const struct iphdr *)pkt;
+
+ nfct_set_attr_u16(ct, ATTR_ORIG_L3PROTO, AF_INET);
+ nfct_set_attr_u16(ct, ATTR_REPL_L3PROTO, AF_INET);
+ nfct_set_attr_u32(ct, ATTR_ORIG_IPV4_SRC, iph->saddr);
+ nfct_set_attr_u32(ct, ATTR_ORIG_IPV4_DST, iph->daddr);
+ nfct_set_attr_u32(ct, ATTR_REPL_IPV4_SRC, iph->daddr);
+ nfct_set_attr_u32(ct, ATTR_REPL_IPV4_DST, iph->saddr);
+}
+
+static int
+l3_ipv4_ct_cmp_tuple_orig(const uint8_t *pkt, struct nf_conntrack *ct)
+{
+ const struct iphdr *iph = (const struct iphdr *)pkt;
+
+ PRINT_CMP("cmp_orig iph->saddr: %x == %x\n",
+ iph->saddr, nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC));
+ PRINT_CMP("cmp_orig iph->daddr: %x == %x\n",
+ iph->daddr, nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST));
+
+ if (iph->saddr == nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC) &&
+ iph->daddr == nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST))
+ return 1;
+
+ return 0;
+}
+
+static int
+l3_ipv4_ct_cmp_tuple_repl(const uint8_t *pkt, struct nf_conntrack *ct)
+{
+ const struct iphdr *iph = (const struct iphdr *)pkt;
+
+ PRINT_CMP("cmp_repl iph->saddr: %x == %x\n",
+ iph->saddr, nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC));
+ PRINT_CMP("cmp_repl iph->daddr: %x == %x\n",
+ iph->daddr, nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST));
+
+ if (iph->saddr == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC) &&
+ iph->daddr == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST))
+ return 1;
+
+ return 0;
+}
+
+static int l3_ipv4_pkt_l4proto_num(const uint8_t *pkt)
+{
+ const struct iphdr *iph = (const struct iphdr *)pkt;
+
+ return iph->protocol;
+}
+
+static int l3_ipv4_pkt_l3hdr_len(const uint8_t *pkt)
+{
+ const struct iphdr *iph = (const struct iphdr *)pkt;
+
+ return iph->ihl << 2;
+}
+
+static struct cthelper_proto_l2l3_helper ipv4 = {
+ .l2protonum = ETH_P_IP,
+ .l3protonum = AF_INET,
+ .l2hdr_len = ETH_HLEN,
+ .l3ct_build = l3_ipv4_ct_build_tuple,
+ .l3ct_cmp_orig = l3_ipv4_ct_cmp_tuple_orig,
+ .l3ct_cmp_repl = l3_ipv4_ct_cmp_tuple_repl,
+ .l3pkt_hdr_len = l3_ipv4_pkt_l3hdr_len,
+ .l4pkt_proto = l3_ipv4_pkt_l4proto_num,
+};
+
+void l2l3_ipv4_init(void)
+{
+ cthelper_proto_l2l3_helper_register(&ipv4);
+}
diff --git a/tests/conntrackd/cthelper/l4_tcp.c b/tests/conntrackd/cthelper/l4_tcp.c
new file mode 100755
index 0000000..f27c85d
--- /dev/null
+++ b/tests/conntrackd/cthelper/l4_tcp.c
@@ -0,0 +1,88 @@
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include "proto.h"
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+#define PRINT_CMP(...)
+
+static void l4_tcp_ct_build_tuple(const uint8_t *pkt, struct nf_conntrack *ct)
+{
+ const struct tcphdr *tcph = (const struct tcphdr *)pkt;
+
+ nfct_set_attr_u8(ct, ATTR_ORIG_L4PROTO, IPPROTO_TCP);
+ nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_TCP);
+ nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, tcph->source);
+ nfct_set_attr_u16(ct, ATTR_ORIG_PORT_DST, tcph->dest);
+ nfct_set_attr_u16(ct, ATTR_REPL_PORT_SRC, tcph->dest);
+ nfct_set_attr_u16(ct, ATTR_REPL_PORT_DST, tcph->source);
+}
+
+static int l4_tcp_ct_cmp_tuple_orig(const uint8_t *pkt, struct nf_conntrack *ct)
+{
+ const struct tcphdr *tcph = (const struct tcphdr *)pkt;
+
+ PRINT_CMP("cmp_orig tcph->source: %u == %u\n",
+ tcph->source, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC));
+ PRINT_CMP("cmp_orig tcph->dest: %u == %u\n",
+ tcph->dest, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST));
+
+ if (tcph->source == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) &&
+ tcph->dest == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST))
+ return 1;
+
+ return 0;
+}
+
+static int
+l4_tcp_ct_cmp_tuple_repl(const uint8_t *pkt, struct nf_conntrack *ct)
+{
+ const struct tcphdr *tcph = (const struct tcphdr *)pkt;
+
+ PRINT_CMP("cmp_repl tcph->source: %u == %u\n",
+ tcph->source, nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC));
+ PRINT_CMP("cmp_repl tcph->dest: %u == %u\n",
+ tcph->dest, nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST));
+
+ if (tcph->source == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC) &&
+ tcph->dest == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST))
+ return 1;
+
+ return 0;
+}
+
+static int
+l4_tcp_ct_cmp_port(struct nf_conntrack *ct, uint16_t port)
+{
+ PRINT_CMP("cmp_port src: %u == %u\n",
+ port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC));
+ PRINT_CMP("cmp_port dst: %u == %u\n",
+ port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST));
+
+ if (port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) ||
+ port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST))
+ return 1;
+
+ return 0;
+}
+
+static int l4_tcp_pkt_no_data(const uint8_t *pkt)
+{
+ const struct tcphdr *tcph = (const struct tcphdr *)pkt;
+ return tcph->syn || tcph->fin || tcph->rst || !tcph->psh;
+}
+
+static struct cthelper_proto_l4_helper tcp = {
+ .l4protonum = IPPROTO_TCP,
+ .l4ct_build = l4_tcp_ct_build_tuple,
+ .l4ct_cmp_orig = l4_tcp_ct_cmp_tuple_orig,
+ .l4ct_cmp_repl = l4_tcp_ct_cmp_tuple_repl,
+ .l4ct_cmp_port = l4_tcp_ct_cmp_port,
+ .l4pkt_no_data = l4_tcp_pkt_no_data,
+};
+
+void l4_tcp_init(void)
+{
+ cthelper_proto_l4_helper_register(&tcp);
+}
diff --git a/tests/conntrackd/cthelper/l4_udp.c b/tests/conntrackd/cthelper/l4_udp.c
new file mode 100755
index 0000000..4d52d0a
--- /dev/null
+++ b/tests/conntrackd/cthelper/l4_udp.c
@@ -0,0 +1,88 @@
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "proto.h"
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+#define PRINT_CMP(...)
+
+static void l4_udp_ct_build_tuple(const uint8_t *pkt, struct nf_conntrack *ct)
+{
+ const struct udphdr *udph = (const struct udphdr *)pkt;
+
+ nfct_set_attr_u8(ct, ATTR_ORIG_L4PROTO, IPPROTO_UDP);
+ nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_UDP);
+ nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, udph->source);
+ nfct_set_attr_u16(ct, ATTR_ORIG_PORT_DST, udph->dest);
+ nfct_set_attr_u16(ct, ATTR_REPL_PORT_SRC, udph->dest);
+ nfct_set_attr_u16(ct, ATTR_REPL_PORT_DST, udph->source);
+}
+
+static int l4_udp_ct_cmp_tuple_orig(const uint8_t *pkt, struct nf_conntrack *ct)
+{
+ const struct udphdr *udph = (const struct udphdr *)pkt;
+
+ PRINT_CMP("cmp_orig udph->source: %u == %u\n",
+ udph->source, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC));
+ PRINT_CMP("cmp_orig udph->dest: %u == %u\n",
+ udph->dest, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST));
+
+ if (udph->source == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) &&
+ udph->dest == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST))
+ return 1;
+
+ return 0;
+}
+
+static int
+l4_udp_ct_cmp_tuple_repl(const uint8_t *pkt, struct nf_conntrack *ct)
+{
+ const struct udphdr *udph = (const struct udphdr *)pkt;
+
+ PRINT_CMP("cmp_repl udph->source: %u == %u\n",
+ udph->source, nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC));
+ PRINT_CMP("cmp_repl udph->dest: %u == %u\n",
+ udph->dest, nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST));
+
+ if (udph->source == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC) &&
+ udph->dest == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST))
+ return 1;
+
+ return 0;
+}
+
+static int
+l4_udp_ct_cmp_port(struct nf_conntrack *ct, uint16_t port)
+{
+ PRINT_CMP("cmp_port src: %u == %u\n",
+ port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC));
+ PRINT_CMP("cmp_port dst: %u == %u\n",
+ port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST));
+
+ if (port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) ||
+ port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST))
+ return 1;
+
+ return 0;
+}
+
+static int l4_udp_pkt_no_data(const uint8_t *pkt)
+{
+ /* UDP has no control packets. */
+ return 1;
+}
+
+static struct cthelper_proto_l4_helper tcp = {
+ .l4protonum = IPPROTO_UDP,
+ .l4ct_build = l4_udp_ct_build_tuple,
+ .l4ct_cmp_orig = l4_udp_ct_cmp_tuple_orig,
+ .l4ct_cmp_repl = l4_udp_ct_cmp_tuple_repl,
+ .l4ct_cmp_port = l4_udp_ct_cmp_port,
+ .l4pkt_no_data = l4_udp_pkt_no_data,
+};
+
+void l4_udp_init(void)
+{
+ cthelper_proto_l4_helper_register(&tcp);
+}
diff --git a/tests/conntrackd/cthelper/main.c b/tests/conntrackd/cthelper/main.c
new file mode 100755
index 0000000..695054a
--- /dev/null
+++ b/tests/conntrackd/cthelper/main.c
@@ -0,0 +1,175 @@
+#include <stdio.h>
+#include <pcap.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include "ct.h"
+#include "proto.h"
+#include "../../../include/helper.h"
+#include "test.h"
+
+#include <libnetfilter_queue/pktbuff.h>
+
+struct cthelper_test_stats cthelper_test_stats;
+
+static int
+cthelper_process_packet(const uint8_t *pkt, uint32_t pktlen,
+ struct ctd_helper *h, int proto, uint16_t port)
+{
+ struct pkt_buff *pktb;
+ struct cthelper_proto_l2l3_helper *l3h;
+ struct cthelper_proto_l4_helper *l4h;
+ unsigned int l3hdr_len, l4protonum;
+ struct nf_ct_entry *ct;
+ int ret, this_proto;
+ uint32_t dataoff, ctinfo = 0;
+
+ l3h = cthelper_proto_l2l3_helper_find(pkt, &l4protonum, &l3hdr_len);
+ if (l3h == NULL) {
+ fprintf(stderr, "Unsupported layer 3 protocol, skipping.\n");
+ return -1;
+ }
+
+ l4h = cthelper_proto_l4_helper_find(pkt, l4protonum);
+ if (l4h == NULL) {
+ fprintf(stderr, "Unsupported layer 4 protocol, skipping.\n");
+ return -1;
+ }
+ /* get layer 3 header. */
+ pkt += l3h->l2hdr_len;
+ pktlen -= l3h->l2hdr_len;
+
+ /* skip packet with mismatching protocol */
+ this_proto = l3h->l4pkt_proto(pkt);
+ if (this_proto != proto) {
+ cthelper_test_stats.pkt_mismatch_proto++;
+ return 0;
+ }
+
+ /* Look for the fake conntrack. */
+ ct = ct_find(pkt, l3hdr_len, l3h, l4h, &ctinfo);
+ if (ct == NULL) {
+ /* It doesn't exist any, create one. */
+ ct = ct_alloc(pkt, l3hdr_len, l3h, l4h);
+ if (ct == NULL) {
+ fprintf(stderr, "Not enough memory\n");
+ return -1;
+ }
+ ct_add(ct);
+ ctinfo += IP_CT_NEW;
+ } else
+ ctinfo += IP_CT_ESTABLISHED;
+
+ /* skip packets with mismatching ports */
+ if (!l4h->l4ct_cmp_port(ct->myct->ct, ntohs(port))) {
+ cthelper_test_stats.pkt_mismatch_port++;
+ return -1;
+ }
+
+ /*
+ * FIXME: reminder, implement this below in the kernel for cthelper.
+ */
+
+ /* This packet contains no data, skip it. */
+/* if (l4h->l4pkt_no_data && l4h->l4pkt_no_data(pkt + l3hdr_len)) {
+ NFG_DEBUG("skipping packet with no data\n");
+ continue;
+ } */
+
+ /* Create the fake network buffer. */
+ pktb = pktb_alloc(AF_INET, pkt, pktlen, 128);
+ if (pktb == NULL) {
+ fprintf(stderr, "Not enough memory\n");
+ return -1;
+ }
+
+ dataoff = l3h->l3pkt_hdr_len(pkt);
+ if (dataoff > pktb_len(pktb)) {
+ fprintf(stderr, "wrong layer 3 offset: %d > %d\n",
+ dataoff, pktb_len(pktb));
+ return -1;
+ }
+
+ ret = h->cb(pktb, dataoff, ct->myct, ctinfo);
+ pktb_free(pktb);
+
+ return ret;
+}
+
+static int
+cthelper_test(const char *pcapfile, const char *helper_name,
+ int l4proto, uint16_t port)
+{
+ struct pcap_pkthdr pcaph;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ const u_char *pkt;
+ pcap_t *handle;
+ struct ctd_helper *h;
+
+ h = helper_find("/usr/lib/conntrack-tools",
+ helper_name, l4proto, RTLD_NOW);
+ if (h == NULL) {
+ fprintf(stderr, "couldn't find helper: %s\n", helper_name);
+ return -1;
+ }
+
+ handle = pcap_open_offline(pcapfile, errbuf);
+ if (handle == NULL) {
+ fprintf(stderr, "couldn't open pcap file %s: %s\n",
+ pcapfile, errbuf);
+ return -1;
+ }
+ while ((pkt = pcap_next(handle, &pcaph)) != NULL) {
+ cthelper_test_stats.pkts++;
+ cthelper_process_packet(pkt, pcaph.caplen, h, l4proto, port);
+ }
+
+ ct_flush();
+ pcap_close(handle);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret, l4proto;
+
+ if (argc != 5) {
+ fprintf(stderr, "Wrong usage:\n");
+ fprintf(stderr, "%s [pcap_file] [helper-name] [proto] [port]\n",
+ argv[0]);
+ fprintf(stderr, "example: %s file.pcap ftp tcp 21\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ if (strncmp("tcp", argv[3], strlen("tcp")) == 0)
+ l4proto = IPPROTO_TCP;
+ else if (strncmp("udp", argv[3], strlen("udp")) == 0)
+ l4proto = IPPROTO_UDP;
+ else {
+ fprintf(stderr, "%s not supported, send a patch to Pablo\n",
+ argv[3]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Initialization of supported layer 3 and 4 protocols here. */
+ l2l3_ipv4_init();
+ l4_tcp_init();
+ l4_udp_init();
+
+ if (cthelper_test(argv[1], argv[2], l4proto, atoi(argv[4])) < 0)
+ ret = EXIT_FAILURE;
+ else
+ ret = EXIT_SUCCESS;
+
+ printf("\e[1;34mTest results: expect_created=%d packets=%d "
+ "packets_skipped=%d\e[0m\n",
+ cthelper_test_stats.ct_expect_created,
+ cthelper_test_stats.pkts,
+ cthelper_test_stats.pkt_mismatch_proto +
+ cthelper_test_stats.pkt_mismatch_port);
+
+ return ret;
+}
diff --git a/tests/conntrackd/cthelper/pcaps/nfsv3.pcap b/tests/conntrackd/cthelper/pcaps/nfsv3.pcap
new file mode 100644
index 0000000..04606bd
--- /dev/null
+++ b/tests/conntrackd/cthelper/pcaps/nfsv3.pcap
Binary files differ
diff --git a/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap b/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap
new file mode 100644
index 0000000..32f8952
--- /dev/null
+++ b/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap
Binary files differ
diff --git a/tests/conntrackd/cthelper/proto.c b/tests/conntrackd/cthelper/proto.c
new file mode 100755
index 0000000..6a1f345
--- /dev/null
+++ b/tests/conntrackd/cthelper/proto.c
@@ -0,0 +1,49 @@
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <linux/if_ether.h>
+
+#include "linux_list.h"
+#include "proto.h"
+
+static LIST_HEAD(l2l3_helper_list);
+static LIST_HEAD(l4_helper_list);
+
+struct cthelper_proto_l2l3_helper *
+cthelper_proto_l2l3_helper_find(const uint8_t *pkt,
+ unsigned int *l4protonum,
+ unsigned int *l3hdr_len)
+{
+ const struct ethhdr *eh = (const struct ethhdr *)pkt;
+ struct cthelper_proto_l2l3_helper *cur;
+
+ list_for_each_entry(cur, &l2l3_helper_list, head) {
+ if (ntohs(cur->l2protonum) == eh->h_proto) {
+ *l4protonum = cur->l4pkt_proto(pkt + ETH_HLEN);
+ *l3hdr_len = cur->l3pkt_hdr_len(pkt + ETH_HLEN);
+ return cur;
+ }
+ }
+ return NULL;
+}
+
+void cthelper_proto_l2l3_helper_register(struct cthelper_proto_l2l3_helper *h)
+{
+ list_add(&h->head, &l2l3_helper_list);
+}
+
+struct cthelper_proto_l4_helper *
+cthelper_proto_l4_helper_find(const uint8_t *pkt, unsigned int l4protocol)
+{
+ struct cthelper_proto_l4_helper *cur;
+
+ list_for_each_entry(cur, &l4_helper_list, head) {
+ if (cur->l4protonum == l4protocol)
+ return cur;
+ }
+ return NULL;
+}
+
+void cthelper_proto_l4_helper_register(struct cthelper_proto_l4_helper *h)
+{
+ list_add(&h->head, &l4_helper_list);
+}
diff --git a/tests/conntrackd/cthelper/proto.h b/tests/conntrackd/cthelper/proto.h
new file mode 100755
index 0000000..9e99eea
--- /dev/null
+++ b/tests/conntrackd/cthelper/proto.h
@@ -0,0 +1,50 @@
+#ifndef _HELPER_H_
+#define _HELPER_H_
+
+#include <stdint.h>
+
+#include "../../../include/linux_list.h"
+
+struct nf_conntrack;
+
+struct cthelper_proto_l4_helper {
+ struct list_head head;
+
+ unsigned int l4protonum;
+
+ void (*l4ct_build)(const uint8_t *pkt, struct nf_conntrack *ct);
+ int (*l4ct_cmp_orig)(const uint8_t *pkt, struct nf_conntrack *ct);
+ int (*l4ct_cmp_repl)(const uint8_t *pkt, struct nf_conntrack *ct);
+ int (*l4ct_cmp_port)(struct nf_conntrack *ct, uint16_t port);
+
+ int (*l4pkt_no_data)(const uint8_t *pkt);
+};
+
+struct cthelper_proto_l2l3_helper {
+ struct list_head head;
+
+ unsigned int l2protonum;
+ unsigned int l2hdr_len;
+
+ unsigned int l3protonum;
+
+ void (*l3ct_build)(const uint8_t *pkt, struct nf_conntrack *ct);
+ int (*l3ct_cmp_orig)(const uint8_t *pkt, struct nf_conntrack *ct);
+ int (*l3ct_cmp_repl)(const uint8_t *pkt, struct nf_conntrack *ct);
+
+ int (*l3pkt_hdr_len)(const uint8_t *pkt);
+ int (*l4pkt_proto)(const uint8_t *pkt);
+};
+
+struct cthelper_proto_l2l3_helper *cthelper_proto_l2l3_helper_find(const uint8_t *pkt, unsigned int *l4protonum, unsigned int *l3hdr_len);
+void cthelper_proto_l2l3_helper_register(struct cthelper_proto_l2l3_helper *h);
+
+struct cthelper_proto_l4_helper *cthelper_proto_l4_helper_find(const uint8_t *pkt, unsigned int l4protonum);
+void cthelper_proto_l4_helper_register(struct cthelper_proto_l4_helper *h);
+
+/* Initialization of supported protocols here. */
+void l2l3_ipv4_init(void);
+void l4_tcp_init(void);
+void l4_udp_init(void);
+
+#endif
diff --git a/tests/conntrackd/cthelper/run-test.sh b/tests/conntrackd/cthelper/run-test.sh
new file mode 100644
index 0000000..ccce3ac
--- /dev/null
+++ b/tests/conntrackd/cthelper/run-test.sh
@@ -0,0 +1,8 @@
+echo "Running test for oracle TNS port 1521"
+./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521
+
+echo "Running test for NFSv3 UDP port 111"
+./cthelper-test pcaps/nfsv3.pcap rpc udp 111
+
+echo "Running test for NFSv3 TCP port 111"
+./cthelper-test pcaps/nfsv3.pcap rpc tcp 111
diff --git a/tests/conntrackd/cthelper/test.h b/tests/conntrackd/cthelper/test.h
new file mode 100644
index 0000000..4f5a6b6
--- /dev/null
+++ b/tests/conntrackd/cthelper/test.h
@@ -0,0 +1,13 @@
+#ifndef _CTHELPER_TEST_H_
+#define _CTHELPER_TEST_H_
+
+struct cthelper_test_stats {
+ int pkts;
+ int pkt_mismatch_proto;
+ int pkt_mismatch_port;
+ int ct_expect_created;
+};
+
+extern struct cthelper_test_stats cthelper_test_stats;
+
+#endif
diff --git a/tests/nfct/run-test.sh b/tests/nfct/run-test.sh
new file mode 100644
index 0000000..9bcad0d
--- /dev/null
+++ b/tests/nfct/run-test.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+UID=`id -u`
+if [ $UID -ne 0 ]
+then
+ echo "Run this test as root"
+ exit 1
+fi
+
+gcc test.c -o test
+#
+# XXX: module auto-load not support by nfnetlink_cttimeout yet :-(
+#
+modprobe nf_conntrack_ipv4
+modprobe nf_conntrack_ipv6
+modprobe nf_conntrack_proto_udplite
+modprobe nf_conntrack_proto_sctp
+modprobe nf_conntrack_proto_dccp
+modprobe nf_conntrack_proto_gre
+./test timeout
diff --git a/tests/nfct/test-live.sh b/tests/nfct/test-live.sh
new file mode 100644
index 0000000..c338e63
--- /dev/null
+++ b/tests/nfct/test-live.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# simple testing for cttimeout infrastructure using one single computer
+#
+
+WAIT_BETWEEN_TESTS=10
+
+# flush cttimeout table
+nfct timeout flush
+
+# flush the conntrack table
+conntrack -F
+
+#
+# No.1: test generic timeout policy
+#
+
+echo "---- test no. 1 ----"
+
+conntrack -E -p 13 &
+
+nfct timeout add test-generic inet generic timeout 100
+iptables -I OUTPUT -t raw -p all -j CT --timeout test-generic
+hping3 -c 1 -V -I eth0 -0 8.8.8.8 -H 13
+
+killall -15 conntrack
+
+echo "---- end test no. 1 ----"
+
+sleep $WAIT_BETWEEN_TESTS
+
+iptables -D OUTPUT -t raw -p all -j CT --timeout test-generic
+nfct timeout del test-generic
+
+#
+# No.2: test TCP timeout policy
+#
+
+echo "---- test no. 2 ----"
+
+conntrack -E -p tcp &
+
+nfct timeout add test-tcp inet tcp syn_sent 100
+iptables -I OUTPUT -t raw -p tcp -j CT --timeout test-tcp
+hping3 -V -S -p 80 -s 5050 8.8.8.8 -c 1
+
+sleep $WAIT_BETWEEN_TESTS
+
+iptables -D OUTPUT -t raw -p tcp -j CT --timeout test-tcp
+nfct timeout del test-tcp
+
+killall -15 conntrack
+
+echo "---- end test no. 2 ----"
+
+#
+# No. 3: test ICMP timeout policy
+#
+
+echo "---- test no. 3 ----"
+
+conntrack -E -p icmp &
+
+nfct timeout add test-icmp inet icmp timeout 50
+iptables -I OUTPUT -t raw -p icmp -j CT --timeout test-icmp
+hping3 -1 8.8.8.8 -c 2
+
+iptables -D OUTPUT -t raw -p icmp -j CT --timeout test-icmp
+nfct timeout del test-icmp
+
+killall -15 conntrack
+
+echo "---- end test no. 3 ----"
diff --git a/tests/nfct/test.c b/tests/nfct/test.c
new file mode 100644
index 0000000..a833dcc
--- /dev/null
+++ b/tests/nfct/test.c
@@ -0,0 +1,100 @@
+/*
+ * (c) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * Extremely simple test utility for the command line tools.
+ *
+ * Based on test-conntrack.c
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <dirent.h>
+
+#define PATH "/usr/sbin"
+
+int main(int argc, char *argv[])
+{
+ int ret, ok = 0, bad = 0, line;
+ FILE *fp;
+ DIR *d;
+ char buf[1024];
+ struct dirent *dent;
+ char file[1024];
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s directory\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ d = opendir(argv[1]);
+ if (d == NULL) {
+ perror("opendir");
+ exit(EXIT_FAILURE);
+ }
+
+ setenv("PATH", PATH, 1);
+
+ while ((dent = readdir(d)) != NULL) {
+
+ sprintf(file, "%s/%s", argv[1], dent->d_name);
+
+ line = 0;
+
+ fp = fopen(file, "r");
+ if (fp == NULL) {
+ perror("cannot find testsuite file");
+ exit(EXIT_FAILURE);
+ }
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ char *res;
+
+ line++;
+
+ if (buf[0] == '#' || buf[0] == ' ')
+ continue;
+
+ res = strchr(buf, ';');
+ if (!res) {
+ printf("malformed file %s at line %d\n",
+ dent->d_name, line);
+ exit(EXIT_FAILURE);
+ }
+ *res = '\0';
+ res+=2;
+
+ printf("(%d) Executing: %s\n", line, buf);
+
+ ret = system(buf);
+
+ if (WIFEXITED(ret) &&
+ WEXITSTATUS(ret) == EXIT_SUCCESS) {
+ if (res[0] == 'O' &&
+ res[1] == 'K')
+ ok++;
+ else {
+ bad++;
+ printf("^----- BAD\n");
+ }
+ } else {
+ if (res[0] == 'B' &&
+ res[1] == 'A' &&
+ res[2] == 'D')
+ ok++;
+ else {
+ bad++;
+ printf("^----- BAD\n");
+ }
+ }
+ printf("=====\n");
+ }
+ fclose(fp);
+ }
+ closedir(d);
+
+ fprintf(stdout, "OK: %d BAD: %d\n", ok, bad);
+}
diff --git a/tests/nfct/timeout/00tcp b/tests/nfct/timeout/00tcp
new file mode 100644
index 0000000..c9d7d24
--- /dev/null
+++ b/tests/nfct/timeout/00tcp
@@ -0,0 +1,16 @@
+# add policy object `test'
+nfct timeout add test inet tcp established 100 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
+# get unexistent policy object `dummy'
+nfct timeout get test ; BAD
+# delete policy object `test', however, it does not exists anymore
+nfct timeout delete test ; BAD
+# add policy object `test'
+nfct timeout add test inet tcp syn_sent 1 syn_recv 2 established 3 fin_wait 4 close_wait 5 last_ack 6 time_wait 7 close 8 syn_sent2 9 retrans 10 unacknowledged 11 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
diff --git a/tests/nfct/timeout/01udp b/tests/nfct/timeout/01udp
new file mode 100644
index 0000000..952526c
--- /dev/null
+++ b/tests/nfct/timeout/01udp
@@ -0,0 +1,16 @@
+# add policy object `test'
+nfct timeout add test inet udp unreplied 10 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
+# get unexistent policy object `dummy'
+nfct timeout get test ; BAD
+# delete policy object `test', however, it does not exists anymore
+nfct timeout delete test ; BAD
+# add policy object `test'
+nfct timeout add test inet udp unreplied 1 replied 2 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
diff --git a/tests/nfct/timeout/02generic b/tests/nfct/timeout/02generic
new file mode 100644
index 0000000..b6ca699
--- /dev/null
+++ b/tests/nfct/timeout/02generic
@@ -0,0 +1,16 @@
+# add policy object `test'
+nfct timeout add test inet generic timeout 10 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
+# get unexistent policy object `dummy'
+nfct timeout get test ; BAD
+# delete policy object `test', however, it does not exists anymore
+nfct timeout delete test ; BAD
+# add policy object `test'
+nfct timeout add test inet generic timeout 1 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
diff --git a/tests/nfct/timeout/03udplite b/tests/nfct/timeout/03udplite
new file mode 100644
index 0000000..69dda15
--- /dev/null
+++ b/tests/nfct/timeout/03udplite
@@ -0,0 +1,16 @@
+# add policy object `test'
+nfct timeout add test inet udplite unreplied 10 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
+# get unexistent policy object `dummy'
+nfct timeout get test ; BAD
+# delete policy object `test', however, it does not exists anymore
+nfct timeout delete test ; BAD
+# add policy object `test'
+nfct timeout add test inet udplite unreplied 1 replied 2 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
diff --git a/tests/nfct/timeout/04icmp b/tests/nfct/timeout/04icmp
new file mode 100644
index 0000000..606e8b9
--- /dev/null
+++ b/tests/nfct/timeout/04icmp
@@ -0,0 +1,16 @@
+# add policy object `test'
+nfct timeout add test inet icmp timeout 10 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
+# get unexistent policy object `dummy'
+nfct timeout get test ; BAD
+# delete policy object `test', however, it does not exists anymore
+nfct timeout delete test ; BAD
+# add policy object `test'
+nfct timeout add test inet icmp timeout 1 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
diff --git a/tests/nfct/timeout/05icmpv6 b/tests/nfct/timeout/05icmpv6
new file mode 100644
index 0000000..16541f5
--- /dev/null
+++ b/tests/nfct/timeout/05icmpv6
@@ -0,0 +1,16 @@
+# add policy object `test'
+nfct timeout add test inet6 icmpv6 timeout 10 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
+# get unexistent policy object `dummy'
+nfct timeout get test ; BAD
+# delete policy object `test', however, it does not exists anymore
+nfct timeout delete test ; BAD
+# add policy object `test'
+nfct timeout add test inet6 icmpv6 timeout 1 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
diff --git a/tests/nfct/timeout/06sctp b/tests/nfct/timeout/06sctp
new file mode 100644
index 0000000..f475215
--- /dev/null
+++ b/tests/nfct/timeout/06sctp
@@ -0,0 +1,16 @@
+# add policy object `test'
+nfct timeout add test inet sctp established 100 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
+# get unexistent policy object `dummy'
+nfct timeout get test ; BAD
+# delete policy object `test', however, it does not exists anymore
+nfct timeout delete test ; BAD
+# add policy object `test'
+nfct timeout add test inet sctp closed 1 cookie_wait 2 cookie_echoed 3 established 4 shutdown_sent 5 shutdown_recd 6 shutdown_ack_sent 7 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
diff --git a/tests/nfct/timeout/07dccp b/tests/nfct/timeout/07dccp
new file mode 100644
index 0000000..1bd4fa5
--- /dev/null
+++ b/tests/nfct/timeout/07dccp
@@ -0,0 +1,16 @@
+# add policy object `test'
+nfct timeout add test inet dccp request 100 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
+# get unexistent policy object `dummy'
+nfct timeout get test ; BAD
+# delete policy object `test', however, it does not exists anymore
+nfct timeout delete test ; BAD
+# add policy object `test'
+nfct timeout add test inet dccp request 1 respond 2 partopen 3 open 4 closereq 5 closing 6 timewait 7 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
diff --git a/tests/nfct/timeout/08gre b/tests/nfct/timeout/08gre
new file mode 100644
index 0000000..7ef4bdb
--- /dev/null
+++ b/tests/nfct/timeout/08gre
@@ -0,0 +1,16 @@
+# add policy object `test'
+nfct timeout add test inet gre unreplied 10 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK
+# get unexistent policy object `dummy'
+nfct timeout get test ; BAD
+# delete policy object `test', however, it does not exists anymore
+nfct timeout delete test ; BAD
+# add policy object `test'
+nfct timeout add test inet gre unreplied 1 replied 2 ; OK
+# get policy object `test'
+nfct timeout get test ; OK
+# delete policy object `test'
+nfct timeout delete test ; OK