summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
18 files changed, 976 insertions, 0 deletions
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