• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/conntrack-tools/conntrack-tools-1.4.0/src/helpers/
1/*
2 * (C) 2012 by Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
3 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
4 *
5 * Sponsored by Vyatta Inc. <http://www.vyatta.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include "conntrackd.h"
13#include "network.h"	/* for before and after */
14#include "helper.h"
15#include "myct.h"
16#include "log.h"
17
18#include <ctype.h>	/* for isdigit */
19#include <errno.h>
20
21#include <netinet/tcp.h>
22
23#include <libmnl/libmnl.h>
24#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
25#include <libnetfilter_queue/libnetfilter_queue.h>
26#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
27#include <libnetfilter_queue/pktbuff.h>
28#include <linux/netfilter.h>
29
30/* TNS SQL*Net Version 2 */
31enum tns_types {
32       TNS_TYPE_CONNECT        = 1,
33       TNS_TYPE_ACCEPT	       = 2,
34       TNS_TYPE_ACK	       = 3,
35       TNS_TYPE_REFUSE	       = 4,
36       TNS_TYPE_REDIRECT       = 5,
37       TNS_TYPE_DATA	       = 6,
38       TNS_TYPE_NULL	       = 7,
39       TNS_TYPE_ABORT	       = 9,
40       TNS_TYPE_RESEND	       = 11,
41       TNS_TYPE_MARKER	       = 12,
42       TNS_TYPE_ATTENTION      = 13,
43       TNS_TYPE_CONTROL        = 14,
44       TNS_TYPE_MAX	       = 19,
45};
46
47struct tns_header {
48       uint16_t len;
49       uint16_t csum;
50       uint8_t type;
51       uint8_t reserved;
52       uint16_t header_csum;
53};
54
55struct tns_redirect {
56       uint16_t data_len;
57};
58
59struct tns_info {
60       /* Scan next DATA|REDIRECT packet */
61       bool parse;
62};
63
64static int try_number(const char *data, size_t dlen, uint32_t array[],
65		      int array_size, char sep, char term)
66{
67	uint32_t len;
68	int i;
69
70	memset(array, 0, sizeof(array[0])*array_size);
71
72	/* Keep data pointing at next char. */
73	for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) {
74		if (*data >= '0' && *data <= '9') {
75			array[i] = array[i]*10 + *data - '0';
76		}
77		else if (*data == sep)
78			i++;
79		else {
80			/* Skip spaces. */
81			if (*data == ' ')
82				continue;
83			/* Unexpected character; true if it's the
84			   terminator and we're finished. */
85			if (*data == term && i == array_size - 1)
86				return len;
87			pr_debug("Char %u (got %u nums) `%c' unexpected\n",
88				 len, i, *data);
89			return 0;
90		}
91	}
92	pr_debug("Failed to fill %u numbers separated by %c\n",
93		 array_size, sep);
94	return 0;
95}
96
97/* Grab port: number up to delimiter */
98static int get_port(const char *data, size_t dlen, char delim,
99		    struct myct_man *cmd)
100{
101	uint16_t tmp_port = 0;
102	uint32_t i;
103
104	for (i = 0; i < dlen; i++) {
105		/* Finished? */
106		if (data[i] == delim) {
107			if (tmp_port == 0)
108				break;
109			cmd->u.port = htons(tmp_port);
110			pr_debug("get_port: return %d\n", tmp_port);
111			return i + 1;
112		}
113		else if (data[i] >= '0' && data[i] <= '9')
114			tmp_port = tmp_port*10 + data[i] - '0';
115		else if (data[i] == ' ') /* Skip spaces */
116			continue;
117		else { /* Some other crap */
118			pr_debug("get_port: invalid char `%c'\n", data[i]);
119			break;
120		}
121	}
122	return 0;
123}
124
125/* (ADDRESS=(PROTOCOL=tcp)(DEV=x)(HOST=a.b.c.d)(PORT=a)) */
126/* FIXME: handle hostnames */
127
128/* Returns 0, or length of port number */
129static unsigned int
130find_pattern(struct pkt_buff *pkt, unsigned int dataoff, size_t dlen,
131	     struct myct_man *cmd, unsigned int *numoff)
132{
133	const char *data = (const char *)pktb_network_header(pkt) + dataoff
134						+ sizeof(struct tns_header);
135	int length, offset, ret;
136	uint32_t array[4];
137	const char *p, *start;
138
139	p = strstr(data, "(");
140	if (!p)
141		return 0;
142
143	p = strstr(p+1, "HOST=");
144	if (!p) {
145		pr_debug("HOST= not found\n");
146		return 0;
147	}
148
149	start = p + strlen("HOST=");
150	offset = (int)(p - data) + strlen("HOST=");
151	*numoff = offset + sizeof(struct tns_header);
152	data += offset;
153
154	length = try_number(data, dlen - offset, array, 4, '.', ')');
155	if (length == 0)
156		return 0;
157
158	cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) |
159			   (array[2] << 8) | array[3]);
160
161	p = strstr(data+length, "(");
162	if (!p)
163		return 0;
164
165	p = strstr(p, "PORT=");
166	if (!p) {
167		pr_debug("PORT= not found\n");
168		return 0;
169	}
170
171	p += strlen("PORT=");
172	ret = get_port(p, dlen - offset - length, ')', cmd);
173	if (ret == 0)
174		return 0;
175
176	p += ret;
177	return (int)(p - start);
178}
179
180static inline uint16_t
181nton(uint16_t len, unsigned int matchoff, unsigned int matchlen)
182{
183	uint32_t l = (uint32_t)ntohs(len) + matchoff - matchlen;
184
185	return htons(l);
186}
187
188/* So, this packet has hit the connection tracking matching code.
189   Mangle it, and change the expectation to match the new version. */
190static unsigned int
191nf_nat_tns(struct pkt_buff *pkt, struct tns_header *tns, struct nf_expect *exp,
192	   struct nf_conntrack *ct, int dir,
193	   unsigned int matchoff, unsigned int matchlen)
194{
195	union nfct_attr_grp_addr newip;
196	char buffer[sizeof("255.255.255.255)(PORT=65535)")];
197	unsigned int buflen;
198	const struct nf_conntrack *expected;
199	struct nf_conntrack *nat_tuple;
200	uint16_t initial_port, port;
201
202	/* Connection will come from wherever this packet goes, hence !dir */
203	cthelper_get_addr_dst(ct, !dir, &newip);
204
205	expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED);
206
207	nat_tuple = nfct_new();
208	if (nat_tuple == NULL)
209		return NF_ACCEPT;
210
211	initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST);
212
213	nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, !dir);
214
215	/* libnetfilter_conntrack needs this */
216	nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET);
217	nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0);
218	nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0);
219	nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_TCP);
220	nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0);
221
222	/* When you see the packet, we need to NAT it the same as the
223	 * this one. */
224	nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master");
225
226	/* Try to get same port: if not, try to change it. */
227	for (port = ntohs(initial_port); port != 0; port++) {
228		int ret;
229
230		nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port));
231		nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple);
232
233		ret = cthelper_add_expect(exp);
234		if (ret == 0)
235			break;
236		else if (ret != -EBUSY) {
237			port = 0;
238			break;
239		}
240	}
241	nfct_destroy(nat_tuple);
242
243	if (port == 0)
244		return NF_DROP;
245
246	buflen = snprintf(buffer, sizeof(buffer),
247				"%u.%u.%u.%u)(PORT=%u)",
248                                ((unsigned char *)&newip.ip)[0],
249                                ((unsigned char *)&newip.ip)[1],
250                                ((unsigned char *)&newip.ip)[2],
251                                ((unsigned char *)&newip.ip)[3], port);
252	if (!buflen)
253		goto out;
254
255	if (!nfq_tcp_mangle_ipv4(pkt, matchoff, matchlen, buffer, buflen))
256		goto out;
257
258	if (buflen != matchlen) {
259		/* FIXME: recalculate checksum */
260		tns->csum = 0;
261		tns->header_csum = 0;
262
263		tns->len = nton(tns->len,  matchlen, buflen);
264		if (tns->type == TNS_TYPE_REDIRECT) {
265			struct tns_redirect *r;
266
267			r = (struct tns_redirect *)((char *)tns + sizeof(struct tns_header));
268
269			r->data_len = nton(r->data_len, matchlen, buflen);
270		}
271	}
272
273	return NF_ACCEPT;
274
275out:
276	cthelper_del_expect(exp);
277	return NF_DROP;
278}
279
280static int
281tns_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
282	      struct myct *myct, uint32_t ctinfo)
283{
284	struct tcphdr *th;
285	struct tns_header *tns;
286	int dir = CTINFO2DIR(ctinfo);
287	unsigned int dataoff, datalen, numoff = 0, numlen;
288	struct tns_info *tns_info = myct->priv_data;
289	union nfct_attr_grp_addr addr;
290	struct nf_expect *exp = NULL;
291	struct myct_man cmd;
292	int ret = NF_ACCEPT;
293
294	memset(&cmd, 0, sizeof(struct myct_man));
295	memset(&addr, 0, sizeof(union nfct_attr_grp_addr));
296
297	/* Until there's been traffic both ways, don't look into TCP packets. */
298	if (ctinfo != IP_CT_ESTABLISHED
299	    && ctinfo != IP_CT_ESTABLISHED_REPLY) {
300		pr_debug("TNS: Conntrackinfo = %u\n", ctinfo);
301		goto out;
302	}
303	/* Parse server direction only */
304	if (dir != MYCT_DIR_REPL) {
305		pr_debug("TNS: skip client direction\n");
306		goto out;
307	}
308
309	th = (struct tcphdr *) (pktb_network_header(pkt) + protoff);
310
311	dataoff = protoff + th->doff * 4;
312	datalen = pktb_len(pkt);
313
314	if (datalen < sizeof(struct tns_header)) {
315		pr_debug("TNS: skip packet with short header\n");
316		goto out;
317	}
318
319	tns = (struct tns_header *)(pktb_network_header(pkt) + dataoff);
320
321	if (tns->type == TNS_TYPE_REDIRECT) {
322		struct tns_redirect *redirect;
323
324		dataoff += sizeof(struct tns_header);
325		datalen -= sizeof(struct tns_header);
326		redirect = (struct tns_redirect *)(pktb_network_header(pkt) + dataoff);
327		tns_info->parse = false;
328
329		if (ntohs(redirect->data_len) == 0) {
330			tns_info->parse = true;
331			goto out;
332		}
333		goto parse;
334	}
335
336	/* Parse only the very next DATA packet */
337	if (!(tns_info->parse && tns->type == TNS_TYPE_DATA)) {
338		tns_info->parse = false;
339		goto out;
340	}
341parse:
342	numlen = find_pattern(pkt, dataoff, datalen, &cmd, &numoff);
343	tns_info->parse = false;
344	if (!numlen)
345		goto out;
346
347	/* We refer to the reverse direction ("!dir") tuples here,
348	 * because we're expecting something in the other direction.
349	 * Doesn't matter unless NAT is happening.  */
350	cthelper_get_addr_src(myct->ct, !dir, &addr);
351
352	exp = nfexp_new();
353	if (exp == NULL)
354		goto out;
355
356	if (cthelper_expect_init(exp, myct->ct, 0,
357				 &addr, &cmd.u3,
358				 IPPROTO_TCP,
359				 NULL, &cmd.u.port, 0)) {
360		pr_debug("TNS: failed to init expectation\n");
361		goto out_exp;
362	}
363
364	/* Now, NAT might want to mangle the packet, and register the
365	 * (possibly changed) expectation itself.
366	 */
367	if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) {
368		ret = nf_nat_tns(pkt, tns, exp, myct->ct, dir,
369				numoff + sizeof(struct tns_header), numlen);
370		goto out_exp;
371	}
372
373	/* Can't expect this?  Best to drop packet now. */
374	if (cthelper_add_expect(exp) < 0) {
375		pr_debug("TNS: cannot add expectation: %s\n",
376			 strerror(errno));
377		ret = NF_DROP;
378		goto out_exp;
379	}
380	goto out;
381
382out_exp:
383	nfexp_destroy(exp);
384out:
385	return ret;
386}
387
388static struct ctd_helper tns_helper = {
389	.name		= "tns",
390	.l4proto	= IPPROTO_TCP,
391	.cb		= tns_helper_cb,
392	.priv_data_len	= sizeof(struct tns_info),
393	.policy		= {
394		[0] = {
395			.name		= "tns",
396			.expect_max	= 1,
397			.expect_timeout	= 300,
398		},
399	},
400};
401
402void __attribute__ ((constructor)) tns_init(void);
403
404void tns_init(void)
405{
406	helper_register(&tns_helper);
407}
408