summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2012-05-25 03:03:33 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2012-08-01 19:20:33 +0200
commitff5eb25aa6963536e3f640c5c38c341ce02809bb (patch)
tree6e29bb091b6e99d21894b31dade3f851f2bc5ff0
parent40f4330e6b50ed2b198549b1006c6fcb349f5a3b (diff)
tests: conntrackd: add cthelper-test infrastructure
This patch adds the automated testing infrastructure the user-space helpers. Basically, this adds the `cthelper-test' program that can be invoked from the command line: ./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521 To test the helper with one PCAP file that contains traces of Oracle TNS traffic. It also provides tweaks to test the DNAT content mangling code: ./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521 dnat This will also allow fuzzy testing of user-space helper, for further validation, not yet implemented. To compile this tool, you have to run: ./configure make check under the qa/cthelper-test/ directory. I'm doing like this because this directory is not included in the standalone tarball that make distcheck generates (I don't want to bloat it with development tools that can be retrieved from the git repository). Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-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.c220
-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.sh11
-rw-r--r--tests/conntrackd/cthelper/test.h13
18 files changed, 1024 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..f229c5d
--- /dev/null
+++ b/tests/conntrackd/cthelper/main.c
@@ -0,0 +1,220 @@
+#include <stdio.h>
+#include <pcap.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.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;
+
+enum {
+ TEST_NORMAL = 0,
+ TEST_DNAT,
+};
+
+static int
+cthelper_process_packet(const uint8_t *pkt, uint32_t pktlen,
+ struct ctd_helper *h, int proto, uint16_t port,
+ int type)
+{
+ 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;
+ }
+
+ /* tweak to run DNAT mangling code using the same PCAP file. */
+ if (type == TEST_DNAT) {
+ struct nf_conntrack *tmp = ct->myct->ct;
+ /* as long as this is tested, who cares the destination IP? */
+ in_addr_t addr = inet_addr("1.1.1.1");
+
+ /* clone the real conntrack, to add DNAT information */
+ ct->myct->ct = nfct_clone(ct->myct->ct);
+ /* set fake DNAT information */
+ nfct_set_attr_u32(ct->myct->ct, ATTR_STATUS, IPS_DST_NAT);
+ nfct_set_attr_u32(ct->myct->ct, ATTR_ORIG_IPV4_DST, addr);
+ /* pass it to helper */
+ ret = h->cb(pktb, dataoff, ct->myct, ctinfo);
+ /* restore real conntrack */
+ nfct_destroy(ct->myct->ct);
+ ct->myct->ct = tmp;
+
+ if (pktb_mangled(pktb)) {
+ int i;
+ uint8_t *data = pktb_network_header(pktb);
+
+ printf("\e[1;31mmangled content: ", pktb_len(pktb));
+
+ for (i=0; i < pktb_len(pktb); i++)
+ printf("%c", data[i]);
+
+ printf("\e[0m\n");
+ }
+ } else
+ 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, int type)
+{
+ 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,
+ type);
+ }
+
+ ct_flush();
+ pcap_close(handle);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret, l4proto, type = TEST_NORMAL;
+
+ if (argc < 5 || argc > 6) {
+ fprintf(stderr, "Wrong usage:\n");
+ fprintf(stderr, "%s <pcap_file> <helper-name> <proto> "
+ "<port> [dnat]\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);
+ }
+ if (argc == 6) {
+ if (strncmp("dnat", argv[5], strlen("dnat")) == 0) {
+ type = TEST_DNAT;
+ printf("test dnat\n");
+ }
+ }
+
+ /* 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]), type) < 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..fe31602
--- /dev/null
+++ b/tests/conntrackd/cthelper/run-test.sh
@@ -0,0 +1,11 @@
+echo "Running test for oracle TNS port 1521"
+./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521
+
+echo "Running test for oracle TNS port 1521"
+./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521 dnat
+
+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