summaryrefslogtreecommitdiff
path: root/src/helpers/amanda.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/helpers/amanda.c')
-rw-r--r--src/helpers/amanda.c203
1 files changed, 203 insertions, 0 deletions
diff --git a/src/helpers/amanda.c b/src/helpers/amanda.c
new file mode 100644
index 0000000..9e6c4e7
--- /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")];
+ uint16_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;
+ uint16_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);
+}