/* * (C) 2013 by Pablo Neira Ayuso * * Adapted from: * * (C) 2001-2002 Magnus Boden * (C) 2006-2012 Patrick McHardy * 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 #include #include #include #include #include #include #include #include #include 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); nfct_destroy(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); }