nf_conntrack_tftp.c (3854B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu> 3 * (C) 2006-2012 Patrick McHardy <kaber@trash.net> 4 */ 5 6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7 8#include <linux/module.h> 9#include <linux/moduleparam.h> 10#include <linux/in.h> 11#include <linux/udp.h> 12#include <linux/netfilter.h> 13 14#include <net/netfilter/nf_conntrack.h> 15#include <net/netfilter/nf_conntrack_tuple.h> 16#include <net/netfilter/nf_conntrack_expect.h> 17#include <net/netfilter/nf_conntrack_ecache.h> 18#include <net/netfilter/nf_conntrack_helper.h> 19#include <linux/netfilter/nf_conntrack_tftp.h> 20 21#define HELPER_NAME "tftp" 22 23MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>"); 24MODULE_DESCRIPTION("TFTP connection tracking helper"); 25MODULE_LICENSE("GPL"); 26MODULE_ALIAS("ip_conntrack_tftp"); 27MODULE_ALIAS_NFCT_HELPER(HELPER_NAME); 28 29#define MAX_PORTS 8 30static unsigned short ports[MAX_PORTS]; 31static unsigned int ports_c; 32module_param_array(ports, ushort, &ports_c, 0400); 33MODULE_PARM_DESC(ports, "Port numbers of TFTP servers"); 34 35unsigned int (*nf_nat_tftp_hook)(struct sk_buff *skb, 36 enum ip_conntrack_info ctinfo, 37 struct nf_conntrack_expect *exp) __read_mostly; 38EXPORT_SYMBOL_GPL(nf_nat_tftp_hook); 39 40static 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) != 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 100static struct nf_conntrack_helper tftp[MAX_PORTS * 2] __read_mostly; 101 102static const struct nf_conntrack_expect_policy tftp_exp_policy = { 103 .max_expected = 1, 104 .timeout = 5 * 60, 105}; 106 107static void __exit nf_conntrack_tftp_fini(void) 108{ 109 nf_conntrack_helpers_unregister(tftp, ports_c * 2); 110} 111 112static int __init nf_conntrack_tftp_init(void) 113{ 114 int i, ret; 115 116 NF_CT_HELPER_BUILD_BUG_ON(0); 117 118 if (ports_c == 0) 119 ports[ports_c++] = TFTP_PORT; 120 121 for (i = 0; i < ports_c; i++) { 122 nf_ct_helper_init(&tftp[2 * i], AF_INET, IPPROTO_UDP, 123 HELPER_NAME, TFTP_PORT, ports[i], i, 124 &tftp_exp_policy, 0, tftp_help, NULL, 125 THIS_MODULE); 126 nf_ct_helper_init(&tftp[2 * i + 1], AF_INET6, IPPROTO_UDP, 127 HELPER_NAME, TFTP_PORT, ports[i], i, 128 &tftp_exp_policy, 0, tftp_help, NULL, 129 THIS_MODULE); 130 } 131 132 ret = nf_conntrack_helpers_register(tftp, ports_c * 2); 133 if (ret < 0) { 134 pr_err("failed to register helpers\n"); 135 return ret; 136 } 137 return 0; 138} 139 140module_init(nf_conntrack_tftp_init); 141module_exit(nf_conntrack_tftp_fini);