diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/helpers/Makefile.am | 7 | ||||
| -rw-r--r-- | src/helpers/amanda.c | 203 | 
2 files changed, 209 insertions, 1 deletions
| diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index 216a5a7..fe28e83 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,12 +1,17 @@  include $(top_srcdir)/Make_global.am -pkglib_LTLIBRARIES = ct_helper_dhcpv6.la \ +pkglib_LTLIBRARIES = ct_helper_amanda.la \ +		     ct_helper_dhcpv6.la \  		     ct_helper_ftp.la	\  		     ct_helper_rpc.la	\  		     ct_helper_tftp.la	\  		     ct_helper_tns.la	\  		     ct_helper_sane.la +ct_helper_amanda_la_SOURCES = amanda.c +ct_helper_amanda_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_amanda_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) +  ct_helper_dhcpv6_la_SOURCES = dhcpv6.c  ct_helper_dhcpv6_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS)  ct_helper_dhcpv6_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) diff --git a/src/helpers/amanda.c b/src/helpers/amanda.c new file mode 100644 index 0000000..c0cf701 --- /dev/null +++ b/src/helpers/amanda.c @@ -0,0 +1,203 @@ +/* + * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * Adapted from: + * + * Amanda extension for IP connection tracking + * + * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca> + * based on HW's ip_conntrack_irc.c as well as other modules + * (C) 2006 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include <errno.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/udp.h> +#include <libmnl/libmnl.h> +#include <libnetfilter_conntrack/libnetfilter_conntrack.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +#include <libnetfilter_queue/libnetfilter_queue_udp.h> +#include <libnetfilter_queue/pktbuff.h> +#include <linux/netfilter.h> + +static int nat_amanda(struct pkt_buff *pkt, uint32_t ctinfo, +		      unsigned int matchoff, unsigned int matchlen, +		      struct nf_expect *exp) +{ +	char buffer[sizeof("65535")]; +	u_int16_t port, initial_port; +	unsigned int ret; +	const struct nf_conntrack *expected; +	struct nf_conntrack *nat_tuple; + +	nat_tuple = nfct_new(); +	if (nat_tuple == NULL) +		return NF_ACCEPT; + +	expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); + +	/* Connection comes from client. */ +	initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); +	nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, IP_CT_DIR_ORIGINAL); + +	/* libnetfilter_conntrack needs this */ +	nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET); +	nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0); +	nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0); +	nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_TCP); +	nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0); + +	/* When you see the packet, we need to NAT it the same as the +	 * this one (ie. same IP: it will be TCP and master is UDP). */ +	nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master"); + +	/* Try to get same port: if not, try to change it. */ +	for (port = ntohs(initial_port); port != 0; port++) { +		int res; + +		nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); +		nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + +		res = cthelper_add_expect(exp); +		if (res == 0) +			break; +		else if (res != -EBUSY) { +			port = 0; +			break; +		} +	} + +	if (port == 0) { +		pr_debug("all ports in use\n"); +		return NF_DROP; +	} + +	sprintf(buffer, "%u", port); +	ret = nfq_udp_mangle_ipv4(pkt, matchoff, matchlen, buffer, +				  strlen(buffer)); +	if (ret != NF_ACCEPT) { +		pr_debug("cannot mangle packet\n"); +		cthelper_del_expect(exp); +	} +	return ret; +} + +static char amanda_buffer[65536]; +static unsigned int master_timeout = 300; + +enum amanda_strings { +	SEARCH_CONNECT, +	SEARCH_NEWLINE, +	SEARCH_DATA, +	SEARCH_MESG, +	SEARCH_INDEX, +}; + +static const char *conns[] = { "DATA ", "MESG ", "INDEX " }; + +static int +amanda_helper_cb(struct pkt_buff *pkt, uint32_t protoff, +		 struct myct *myct, uint32_t ctinfo) +{ +	struct nf_expect *exp; +	char *data, *data_limit, *tmp; +	unsigned int dataoff, i; +	u_int16_t port, len; +	int ret = NF_ACCEPT; +	struct iphdr *iph; +	union nfct_attr_grp_addr saddr, daddr; + +	/* Only look at packets from the Amanda server */ +	if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) +		return NF_ACCEPT; + +	/* increase the UDP timeout of the master connection as replies from +	 * Amanda clients to the server can be quite delayed */ +	nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT, master_timeout); + +	/* No data? */ +	iph = (struct iphdr *)pktb_network_header(pkt); +	dataoff = iph->ihl*4 + sizeof(struct udphdr); +	if (dataoff >= pktb_len(pkt)) { +		pr_debug("amanda_help: pktlen = %u\n", pktb_len(pkt)); +		return NF_ACCEPT; +	} + +	memcpy(amanda_buffer, pktb_network_header(pkt) + dataoff, +	       pktb_len(pkt) - dataoff); +	data = amanda_buffer; +	data_limit = amanda_buffer + pktb_len(pkt) - dataoff; +	*data_limit = '\0'; + +	/* Search for the CONNECT string */ +	data = strstr(data, "CONNECT "); +	if (!data) +		goto out; +	data += strlen("CONNECT "); + +	/* Only search first line. */ +	if ((tmp = strchr(data, '\n'))) +		*tmp = '\0'; + +	for (i = 0; i < ARRAY_SIZE(conns); i++) { +		char *match = strstr(data, conns[i]); +		if (!match) +			continue; +		tmp = data = match + strlen(conns[i]); +		port = strtoul(data, &data, 10); +		len = data - tmp; +		if (port == 0 || len > 5) +			break; + +		exp = nfexp_new(); +		if (exp == NULL) +			return NF_DROP; + +		cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); +		cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); +		cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port); + +		if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr, +					 IPPROTO_TCP, NULL, &port, 0)) { +			nfexp_destroy(exp); +			return NF_DROP; +		} + +		if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { +			ret = nat_amanda(pkt, ctinfo, tmp - amanda_buffer, +					 len, exp); +		} else +			myct->exp = exp; +	} +out: +	return ret; +} + +static struct ctd_helper amanda_helper = { +	.name		= "amanda", +	.l4proto	= IPPROTO_UDP, +	.cb		= amanda_helper_cb, +	.policy		= { +		[0] = { +			.name			= "amanda", +			.expect_max		= ARRAY_SIZE(conns), +			.expect_timeout		= 180, +		}, +	}, +}; + +void __attribute__ ((constructor)) amanda_init(void); + +void amanda_init(void) +{ +	helper_register(&amanda_helper); +} | 
