1 /* (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu> 2 * (C) 2006-2012 Patrick McHardy <kaber@trash.net> 3 * This program is free software; you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License version 2 as 5 * published by the Free Software Foundation. 6 */ 7 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10 #include <linux/module.h> 11 #include <linux/moduleparam.h> 12 #include <linux/in.h> 13 #include <linux/udp.h> 14 #include <linux/netfilter.h> 15 16 #include <net/netfilter/nf_conntrack.h> 17 #include <net/netfilter/nf_conntrack_tuple.h> 18 #include <net/netfilter/nf_conntrack_expect.h> 19 #include <net/netfilter/nf_conntrack_ecache.h> 20 #include <net/netfilter/nf_conntrack_helper.h> 21 #include <linux/netfilter/nf_conntrack_tftp.h> 22 23 MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>"); 24 MODULE_DESCRIPTION("TFTP connection tracking helper"); 25 MODULE_LICENSE("GPL"); 26 MODULE_ALIAS("ip_conntrack_tftp"); 27 MODULE_ALIAS_NFCT_HELPER("tftp"); 28 29 #define MAX_PORTS 8 30 static unsigned short ports[MAX_PORTS]; 31 static unsigned int ports_c; 32 module_param_array(ports, ushort, &ports_c, 0400); 33 MODULE_PARM_DESC(ports, "Port numbers of TFTP servers"); 34 35 unsigned int (*nf_nat_tftp_hook)(struct sk_buff *skb, 36 enum ip_conntrack_info ctinfo, 37 struct nf_conntrack_expect *exp) __read_mostly; 38 EXPORT_SYMBOL_GPL(nf_nat_tftp_hook); 39 40 static int tftp_help(struct sk_buff *skb, 41 unsigned int protoff, 42 struct nf_conn *ct, 43 enum ip_conntrack_info ctinfo) 44 { 45 const struct tftphdr *tfh; 46 struct tftphdr _tftph; 47 struct nf_conntrack_expect *exp; 48 struct nf_conntrack_tuple *tuple; 49 unsigned int ret = NF_ACCEPT; 50 typeof(nf_nat_tftp_hook) nf_nat_tftp; 51 52 tfh = skb_header_pointer(skb, protoff + sizeof(struct udphdr), 53 sizeof(_tftph), &_tftph); 54 if (tfh == NULL) 55 return NF_ACCEPT; 56 57 switch (ntohs(tfh->opcode)) { 58 case TFTP_OPCODE_READ: 59 case TFTP_OPCODE_WRITE: 60 /* RRQ and WRQ works the same way */ 61 nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); 62 nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); 63 64 exp = nf_ct_expect_alloc(ct); 65 if (exp == NULL) { 66 nf_ct_helper_log(skb, ct, "cannot alloc expectation"); 67 return NF_DROP; 68 } 69 tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; 70 nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, 71 nf_ct_l3num(ct), 72 &tuple->src.u3, &tuple->dst.u3, 73 IPPROTO_UDP, NULL, &tuple->dst.u.udp.port); 74 75 pr_debug("expect: "); 76 nf_ct_dump_tuple(&exp->tuple); 77 78 nf_nat_tftp = rcu_dereference(nf_nat_tftp_hook); 79 if (nf_nat_tftp && ct->status & IPS_NAT_MASK) 80 ret = nf_nat_tftp(skb, ctinfo, exp); 81 else if (nf_ct_expect_related(exp) != 0) { 82 nf_ct_helper_log(skb, ct, "cannot add expectation"); 83 ret = NF_DROP; 84 } 85 nf_ct_expect_put(exp); 86 break; 87 case TFTP_OPCODE_DATA: 88 case TFTP_OPCODE_ACK: 89 pr_debug("Data/ACK opcode\n"); 90 break; 91 case TFTP_OPCODE_ERROR: 92 pr_debug("Error opcode\n"); 93 break; 94 default: 95 pr_debug("Unknown opcode\n"); 96 } 97 return ret; 98 } 99 100 static struct nf_conntrack_helper tftp[MAX_PORTS * 2] __read_mostly; 101 102 static const struct nf_conntrack_expect_policy tftp_exp_policy = { 103 .max_expected = 1, 104 .timeout = 5 * 60, 105 }; 106 107 static void nf_conntrack_tftp_fini(void) 108 { 109 nf_conntrack_helpers_unregister(tftp, ports_c * 2); 110 } 111 112 static int __init nf_conntrack_tftp_init(void) 113 { 114 int i, ret; 115 116 if (ports_c == 0) 117 ports[ports_c++] = TFTP_PORT; 118 119 for (i = 0; i < ports_c; i++) { 120 nf_ct_helper_init(&tftp[2 * i], AF_INET, IPPROTO_UDP, "tftp", 121 TFTP_PORT, ports[i], i, &tftp_exp_policy, 122 0, 0, tftp_help, NULL, THIS_MODULE); 123 nf_ct_helper_init(&tftp[2 * i + 1], AF_INET6, IPPROTO_UDP, "tftp", 124 TFTP_PORT, ports[i], i, &tftp_exp_policy, 125 0, 0, tftp_help, NULL, THIS_MODULE); 126 } 127 128 ret = nf_conntrack_helpers_register(tftp, ports_c * 2); 129 if (ret < 0) { 130 pr_err("failed to register helpers\n"); 131 return ret; 132 } 133 return 0; 134 } 135 136 module_init(nf_conntrack_tftp_init); 137 module_exit(nf_conntrack_tftp_fini); 138