1 /* Amanda extension for IP connection tracking
2  *
3  * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
4  * based on HW's ip_conntrack_irc.c as well as other modules
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/moduleparam.h>
14 #include <linux/textsearch.h>
15 #include <linux/skbuff.h>
16 #include <linux/in.h>
17 #include <linux/udp.h>
18 #include <linux/netfilter.h>
19 #include <linux/gfp.h>
20 
21 #include <net/netfilter/nf_conntrack.h>
22 #include <net/netfilter/nf_conntrack_expect.h>
23 #include <net/netfilter/nf_conntrack_ecache.h>
24 #include <net/netfilter/nf_conntrack_helper.h>
25 #include <linux/netfilter/nf_conntrack_amanda.h>
26 
27 static unsigned int master_timeout __read_mostly = 300;
28 static char *ts_algo = "kmp";
29 
30 MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");
31 MODULE_DESCRIPTION("Amanda connection tracking module");
32 MODULE_LICENSE("GPL");
33 MODULE_ALIAS("ip_conntrack_amanda");
34 MODULE_ALIAS_NFCT_HELPER("amanda");
35 
36 module_param(master_timeout, uint, 0600);
37 MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
38 module_param(ts_algo, charp, 0400);
39 MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)");
40 
41 unsigned int (*nf_nat_amanda_hook)(struct sk_buff *skb,
42 				   enum ip_conntrack_info ctinfo,
43 				   unsigned int matchoff,
44 				   unsigned int matchlen,
45 				   struct nf_conntrack_expect *exp)
46 				   __read_mostly;
47 EXPORT_SYMBOL_GPL(nf_nat_amanda_hook);
48 
49 enum amanda_strings {
50 	SEARCH_CONNECT,
51 	SEARCH_NEWLINE,
52 	SEARCH_DATA,
53 	SEARCH_MESG,
54 	SEARCH_INDEX,
55 };
56 
57 static struct {
58 	const char		*string;
59 	size_t			len;
60 	struct ts_config	*ts;
61 } search[] __read_mostly = {
62 	[SEARCH_CONNECT] = {
63 		.string	= "CONNECT ",
64 		.len	= 8,
65 	},
66 	[SEARCH_NEWLINE] = {
67 		.string	= "\n",
68 		.len	= 1,
69 	},
70 	[SEARCH_DATA] = {
71 		.string	= "DATA ",
72 		.len	= 5,
73 	},
74 	[SEARCH_MESG] = {
75 		.string	= "MESG ",
76 		.len	= 5,
77 	},
78 	[SEARCH_INDEX] = {
79 		.string = "INDEX ",
80 		.len	= 6,
81 	},
82 };
83 
amanda_help(struct sk_buff * skb,unsigned int protoff,struct nf_conn * ct,enum ip_conntrack_info ctinfo)84 static int amanda_help(struct sk_buff *skb,
85 		       unsigned int protoff,
86 		       struct nf_conn *ct,
87 		       enum ip_conntrack_info ctinfo)
88 {
89 	struct ts_state ts;
90 	struct nf_conntrack_expect *exp;
91 	struct nf_conntrack_tuple *tuple;
92 	unsigned int dataoff, start, stop, off, i;
93 	char pbuf[sizeof("65535")], *tmp;
94 	u_int16_t len;
95 	__be16 port;
96 	int ret = NF_ACCEPT;
97 	typeof(nf_nat_amanda_hook) nf_nat_amanda;
98 
99 	/* Only look at packets from the Amanda server */
100 	if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL)
101 		return NF_ACCEPT;
102 
103 	/* increase the UDP timeout of the master connection as replies from
104 	 * Amanda clients to the server can be quite delayed */
105 	nf_ct_refresh(ct, skb, master_timeout * HZ);
106 
107 	/* No data? */
108 	dataoff = protoff + sizeof(struct udphdr);
109 	if (dataoff >= skb->len) {
110 		if (net_ratelimit())
111 			printk(KERN_ERR "amanda_help: skblen = %u\n", skb->len);
112 		return NF_ACCEPT;
113 	}
114 
115 	memset(&ts, 0, sizeof(ts));
116 	start = skb_find_text(skb, dataoff, skb->len,
117 			      search[SEARCH_CONNECT].ts, &ts);
118 	if (start == UINT_MAX)
119 		goto out;
120 	start += dataoff + search[SEARCH_CONNECT].len;
121 
122 	memset(&ts, 0, sizeof(ts));
123 	stop = skb_find_text(skb, start, skb->len,
124 			     search[SEARCH_NEWLINE].ts, &ts);
125 	if (stop == UINT_MAX)
126 		goto out;
127 	stop += start;
128 
129 	for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) {
130 		memset(&ts, 0, sizeof(ts));
131 		off = skb_find_text(skb, start, stop, search[i].ts, &ts);
132 		if (off == UINT_MAX)
133 			continue;
134 		off += start + search[i].len;
135 
136 		len = min_t(unsigned int, sizeof(pbuf) - 1, stop - off);
137 		if (skb_copy_bits(skb, off, pbuf, len))
138 			break;
139 		pbuf[len] = '\0';
140 
141 		port = htons(simple_strtoul(pbuf, &tmp, 10));
142 		len = tmp - pbuf;
143 		if (port == 0 || len > 5)
144 			break;
145 
146 		exp = nf_ct_expect_alloc(ct);
147 		if (exp == NULL) {
148 			ret = NF_DROP;
149 			goto out;
150 		}
151 		tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
152 		nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT,
153 				  nf_ct_l3num(ct),
154 				  &tuple->src.u3, &tuple->dst.u3,
155 				  IPPROTO_TCP, NULL, &port);
156 
157 		nf_nat_amanda = rcu_dereference(nf_nat_amanda_hook);
158 		if (nf_nat_amanda && ct->status & IPS_NAT_MASK)
159 			ret = nf_nat_amanda(skb, ctinfo, off - dataoff,
160 					    len, exp);
161 		else if (nf_ct_expect_related(exp) != 0)
162 			ret = NF_DROP;
163 		nf_ct_expect_put(exp);
164 	}
165 
166 out:
167 	return ret;
168 }
169 
170 static const struct nf_conntrack_expect_policy amanda_exp_policy = {
171 	.max_expected		= 3,
172 	.timeout		= 180,
173 };
174 
175 static struct nf_conntrack_helper amanda_helper[2] __read_mostly = {
176 	{
177 		.name			= "amanda",
178 		.me			= THIS_MODULE,
179 		.help			= amanda_help,
180 		.tuple.src.l3num	= AF_INET,
181 		.tuple.src.u.udp.port	= cpu_to_be16(10080),
182 		.tuple.dst.protonum	= IPPROTO_UDP,
183 		.expect_policy		= &amanda_exp_policy,
184 	},
185 	{
186 		.name			= "amanda",
187 		.me			= THIS_MODULE,
188 		.help			= amanda_help,
189 		.tuple.src.l3num	= AF_INET6,
190 		.tuple.src.u.udp.port	= cpu_to_be16(10080),
191 		.tuple.dst.protonum	= IPPROTO_UDP,
192 		.expect_policy		= &amanda_exp_policy,
193 	},
194 };
195 
nf_conntrack_amanda_fini(void)196 static void __exit nf_conntrack_amanda_fini(void)
197 {
198 	int i;
199 
200 	nf_conntrack_helper_unregister(&amanda_helper[0]);
201 	nf_conntrack_helper_unregister(&amanda_helper[1]);
202 	for (i = 0; i < ARRAY_SIZE(search); i++)
203 		textsearch_destroy(search[i].ts);
204 }
205 
nf_conntrack_amanda_init(void)206 static int __init nf_conntrack_amanda_init(void)
207 {
208 	int ret, i;
209 
210 	for (i = 0; i < ARRAY_SIZE(search); i++) {
211 		search[i].ts = textsearch_prepare(ts_algo, search[i].string,
212 						  search[i].len,
213 						  GFP_KERNEL, TS_AUTOLOAD);
214 		if (IS_ERR(search[i].ts)) {
215 			ret = PTR_ERR(search[i].ts);
216 			goto err1;
217 		}
218 	}
219 	ret = nf_conntrack_helper_register(&amanda_helper[0]);
220 	if (ret < 0)
221 		goto err1;
222 	ret = nf_conntrack_helper_register(&amanda_helper[1]);
223 	if (ret < 0)
224 		goto err2;
225 	return 0;
226 
227 err2:
228 	nf_conntrack_helper_unregister(&amanda_helper[0]);
229 err1:
230 	while (--i >= 0)
231 		textsearch_destroy(search[i].ts);
232 
233 	return ret;
234 }
235 
236 module_init(nf_conntrack_amanda_init);
237 module_exit(nf_conntrack_amanda_fini);
238