summaryrefslogtreecommitdiffstats
path: root/src/helpers/tftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/helpers/tftp.c')
-rw-r--r--src/helpers/tftp.c138
1 files changed, 138 insertions, 0 deletions
diff --git a/src/helpers/tftp.c b/src/helpers/tftp.c
new file mode 100644
index 0000000..45591c6
--- /dev/null
+++ b/src/helpers/tftp.c
@@ -0,0 +1,138 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * Adapted from:
+ *
+ * (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu>
+ * (C) 2006-2012 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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>
+
+struct tftphdr {
+ uint16_t opcode;
+};
+
+#define TFTP_OPCODE_READ 1
+#define TFTP_OPCODE_WRITE 2
+#define TFTP_OPCODE_DATA 3
+#define TFTP_OPCODE_ACK 4
+#define TFTP_OPCODE_ERROR 5
+
+static unsigned int nat_tftp(struct pkt_buff *pkt, uint32_t ctinfo,
+ struct nf_conntrack *ct, struct nf_expect *exp)
+{
+ struct nf_conntrack *nat_tuple;
+ static uint32_t zero[4] = { 0, 0, 0, 0 };
+
+ nat_tuple = nfct_new();
+ if (nat_tuple == NULL)
+ return NF_ACCEPT;
+
+ switch (nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
+ case AF_INET:
+ 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);
+ break;
+ case AF_INET6:
+ nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET6);
+ nfct_set_attr(nat_tuple, ATTR_IPV6_SRC, &zero);
+ nfct_set_attr(nat_tuple, ATTR_IPV6_DST, &zero);
+ break;
+ }
+ nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_UDP);
+ nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC,
+ nfct_get_attr_u16(ct, ATTR_PORT_SRC));
+ nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0);
+
+ nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, MYCT_DIR_REPL);
+ nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master");
+ nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple);
+
+ return NF_ACCEPT;
+}
+
+static int
+tftp_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+ struct myct *myct, uint32_t ctinfo)
+{
+ const struct tftphdr *tfh;
+ struct nf_expect *exp;
+ unsigned int ret = NF_ACCEPT;
+ union nfct_attr_grp_addr saddr, daddr;
+ uint16_t dport;
+
+ tfh = (struct tftphdr *)(pktb_network_header(pkt) + protoff + sizeof(struct udphdr));
+
+ switch (ntohs(tfh->opcode)) {
+ case TFTP_OPCODE_READ:
+ case TFTP_OPCODE_WRITE:
+ /* RRQ and WRQ works the same way */
+ exp = nfexp_new();
+ if (exp == NULL) {
+ pr_debug("cannot alloc expectation\n");
+ return NF_DROP;
+ }
+
+ cthelper_get_addr_src(myct->ct, MYCT_DIR_REPL, &saddr);
+ cthelper_get_addr_dst(myct->ct, MYCT_DIR_REPL, &daddr);
+ cthelper_get_port_dst(myct->ct, MYCT_DIR_REPL, &dport);
+
+ if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr,
+ IPPROTO_UDP, NULL, &dport, 0)) {
+ nfexp_destroy(exp);
+ return NF_DROP;
+ }
+
+ if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK)
+ ret = nat_tftp(pkt, ctinfo, myct->ct, exp);
+
+ myct->exp = exp;
+ break;
+ case TFTP_OPCODE_DATA:
+ case TFTP_OPCODE_ACK:
+ pr_debug("Data/ACK opcode\n");
+ break;
+ case TFTP_OPCODE_ERROR:
+ pr_debug("Error opcode\n");
+ break;
+ default:
+ pr_debug("Unknown opcode\n");
+ }
+ return ret;
+}
+
+static struct ctd_helper tftp_helper = {
+ .name = "tftp",
+ .l4proto = IPPROTO_UDP,
+ .cb = tftp_helper_cb,
+ .policy = {
+ [0] = {
+ .name = "tftp",
+ .expect_max = 1,
+ .expect_timeout = 5 * 60,
+ },
+ },
+};
+
+static void __attribute__ ((constructor)) tftp_init(void)
+{
+ helper_register(&tftp_helper);
+}