diff options
Diffstat (limited to 'tests/conntrackd/cthelper')
| -rw-r--r-- | tests/conntrackd/cthelper/.gitignore | 14 | ||||
| -rw-r--r-- | tests/conntrackd/cthelper/Make_global.am | 7 | ||||
| -rw-r--r-- | tests/conntrackd/cthelper/Makefile.am | 20 | ||||
| -rw-r--r-- | tests/conntrackd/cthelper/README | 2 | ||||
| -rw-r--r-- | tests/conntrackd/cthelper/configure.ac | 64 | ||||
| -rwxr-xr-x | tests/conntrackd/cthelper/ct.c | 91 | ||||
| -rwxr-xr-x | tests/conntrackd/cthelper/ct.h | 22 | ||||
| -rw-r--r-- | tests/conntrackd/cthelper/expect.c | 199 | ||||
| -rwxr-xr-x | tests/conntrackd/cthelper/l3_ipv4.c | 86 | ||||
| -rwxr-xr-x | tests/conntrackd/cthelper/l4_tcp.c | 88 | ||||
| -rwxr-xr-x | tests/conntrackd/cthelper/l4_udp.c | 88 | ||||
| -rwxr-xr-x | tests/conntrackd/cthelper/main.c | 175 | ||||
| -rw-r--r-- | tests/conntrackd/cthelper/pcaps/nfsv3.pcap | bin | 0 -> 6824 bytes | |||
| -rw-r--r-- | tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap | bin | 0 -> 1095 bytes | |||
| -rwxr-xr-x | tests/conntrackd/cthelper/proto.c | 49 | ||||
| -rwxr-xr-x | tests/conntrackd/cthelper/proto.h | 50 | ||||
| -rw-r--r-- | tests/conntrackd/cthelper/run-test.sh | 8 | ||||
| -rw-r--r-- | tests/conntrackd/cthelper/test.h | 13 | 
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.pcapBinary files differ new file mode 100644 index 0000000..04606bd --- /dev/null +++ b/tests/conntrackd/cthelper/pcaps/nfsv3.pcap diff --git a/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap b/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcapBinary files differ new file mode 100644 index 0000000..32f8952 --- /dev/null +++ b/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap 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 | 
