• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/net/netfilter/
1/*
2 * RTSP extension for IP connection tracking
3 * (C) 2003 by Tom Marshall <tmarshall at real.com>
4 * based on ip_conntrack_irc.c
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 * Module load syntax:
12 *   insmod nf_conntrack_rtsp.o ports=port1,port2,...port<MAX_PORTS>
13 *                              max_outstanding=n setup_timeout=secs
14 *
15 * If no ports are specified, the default will be port 554.
16 *
17 * With max_outstanding you can define the maximum number of not yet
18 * answered SETUP requests per RTSP session (default 8).
19 * With setup_timeout you can specify how long the system waits for
20 * an expected data channel (default 300 seconds).
21 *
22 * 2005-02-13: Harald Welte <laforge at netfilter.org>
23 * 	- port to 2.6
24 * 	- update to recent post-2.6.11 api changes
25 * 2006-09-14: Steven Van Acker <deepstar at singularity.be>
26 *      - removed calls to NAT code from conntrack helper: NAT no longer needed to use rtsp-conntrack
27 * 2007-04-18: Michael Guntsche <mike at it-loops.com>
28 * 			- Port to new NF API
29 */
30
31#include <linux/module.h>
32#include <linux/netfilter.h>
33#include <linux/ip.h>
34#include <linux/inet.h>
35#include <net/tcp.h>
36
37#include <net/netfilter/nf_conntrack.h>
38#include <net/netfilter/nf_conntrack_expect.h>
39#include <net/netfilter/nf_conntrack_helper.h>
40#include <linux/netfilter/nf_conntrack_rtsp.h>
41
42#define NF_NEED_STRNCASECMP
43#define NF_NEED_STRTOU16
44#define NF_NEED_STRTOU32
45#define NF_NEED_NEXTLINE
46#include <linux/netfilter_helpers.h>
47#define NF_NEED_MIME_NEXTLINE
48#include <linux/netfilter_mime.h>
49
50#include <linux/ctype.h>
51#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */
52
53#define MAX_PORTS 8
54static int ports[MAX_PORTS];
55static int num_ports = 0;
56static int max_outstanding = 8;
57static unsigned int setup_timeout = 300;
58
59MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
60MODULE_DESCRIPTION("RTSP connection tracking module");
61MODULE_LICENSE("GPL");
62module_param_array(ports, int, &num_ports, 0400);
63MODULE_PARM_DESC(ports, "port numbers of RTSP servers");
64module_param(max_outstanding, int, 0400);
65MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session");
66module_param(setup_timeout, int, 0400);
67MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels");
68
69static char *rtsp_buffer;
70static DEFINE_SPINLOCK(rtsp_buffer_lock);
71
72static struct nf_conntrack_expect_policy rtsp_exp_policy;
73
74unsigned int (*nf_nat_rtsp_hook)(struct sk_buff *skb,
75				 enum ip_conntrack_info ctinfo,
76				 unsigned int matchoff, unsigned int matchlen,struct ip_ct_rtsp_expect* prtspexp,
77				 struct nf_conntrack_expect *exp);
78void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp);
79
80EXPORT_SYMBOL_GPL(nf_nat_rtsp_hook);
81
82/*
83 * Max mappings we will allow for one RTSP connection (for RTP, the number
84 * of allocated ports is twice this value).  Note that SMIL burns a lot of
85 * ports so keep this reasonably high.  If this is too low, you will see a
86 * lot of "no free client map entries" messages.
87 */
88#define MAX_PORT_MAPS 16
89
90/*** default port list was here in the masq code: 554, 3030, 4040 ***/
91
92#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
93
94/*
95 * Parse an RTSP packet.
96 *
97 * Returns zero if parsing failed.
98 *
99 * Parameters:
100 *  IN      ptcp        tcp data pointer
101 *  IN      tcplen      tcp data len
102 *  IN/OUT  ptcpoff     points to current tcp offset
103 *  OUT     phdrsoff    set to offset of rtsp headers
104 *  OUT     phdrslen    set to length of rtsp headers
105 *  OUT     pcseqoff    set to offset of CSeq header
106 *  OUT     pcseqlen    set to length of CSeq header
107 */
108static int
109rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff,
110                   uint* phdrsoff, uint* phdrslen,
111                   uint* pcseqoff, uint* pcseqlen,
112                   uint* transoff, uint* translen)
113{
114	uint    entitylen = 0;
115	uint    lineoff;
116	uint    linelen;
117
118	if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen))
119		return 0;
120
121	*phdrsoff = *ptcpoff;
122	while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) {
123		if (linelen == 0) {
124			if (entitylen > 0)
125				*ptcpoff += min(entitylen, tcplen - *ptcpoff);
126			break;
127		}
128		if (lineoff+linelen > tcplen) {
129			pr_info("!! overrun !!\n");
130			break;
131		}
132
133		if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) {
134			*pcseqoff = lineoff;
135			*pcseqlen = linelen;
136		}
137
138		if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) {
139			*transoff = lineoff;
140			*translen = linelen;
141		}
142
143		if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) {
144			uint off = lineoff+15;
145			SKIP_WSPACE(ptcp+lineoff, linelen, off);
146			nf_strtou32(ptcp+off, &entitylen);
147		}
148	}
149	*phdrslen = (*ptcpoff) - (*phdrsoff);
150
151	return 1;
152}
153
154/*
155 * Find lo/hi client ports (if any) in transport header
156 * In:
157 *   ptcp, tcplen = packet
158 *   tranoff, tranlen = buffer to search
159 *
160 * Out:
161 *   pport_lo, pport_hi = lo/hi ports (host endian)
162 *
163 * Returns nonzero if any client ports found
164 *
165 * Note: it is valid (and expected) for the client to request multiple
166 * transports, so we need to parse the entire line.
167 */
168static int
169rtsp_parse_transport(char* ptran, uint tranlen,
170                     struct ip_ct_rtsp_expect* prtspexp)
171{
172	int     rc = 0;
173	uint    off = 0;
174
175	if (tranlen < 10 || !iseol(ptran[tranlen-1]) ||
176	    nf_strncasecmp(ptran, "Transport:", 10) != 0) {
177		pr_info("sanity check failed\n");
178		return 0;
179	}
180
181	pr_debug("tran='%.*s'\n", (int)tranlen, ptran);
182	off += 10;
183	SKIP_WSPACE(ptran, tranlen, off);
184
185	/* Transport: tran;field;field=val,tran;field;field=val,... */
186	while (off < tranlen) {
187		const char* pparamend;
188		uint        nextparamoff;
189
190		pparamend = memchr(ptran+off, ',', tranlen-off);
191		pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
192		nextparamoff = pparamend-ptran;
193
194		while (off < nextparamoff) {
195			const char* pfieldend;
196			uint        nextfieldoff;
197
198			pfieldend = memchr(ptran+off, ';', nextparamoff-off);
199			nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
200
201			if (strncmp(ptran+off, "client_port=", 12) == 0) {
202				u_int16_t   port;
203				uint        numlen;
204
205				off += 12;
206				numlen = nf_strtou16(ptran+off, &port);
207				off += numlen;
208				if (prtspexp->loport != 0 && prtspexp->loport != port)
209					pr_debug("multiple ports found, port %hu ignored\n", port);
210				else {
211					pr_debug("lo port found : %hu\n", port);
212					prtspexp->loport = prtspexp->hiport = port;
213					if (ptran[off] == '-') {
214						off++;
215						numlen = nf_strtou16(ptran+off, &port);
216						off += numlen;
217						prtspexp->pbtype = pb_range;
218						prtspexp->hiport = port;
219
220						// If we have a range, assume rtp:
221						// loport must be even, hiport must be loport+1
222						if ((prtspexp->loport & 0x0001) != 0 ||
223						    prtspexp->hiport != prtspexp->loport+1) {
224							pr_debug("incorrect range: %hu-%hu, correcting\n",
225							       prtspexp->loport, prtspexp->hiport);
226							prtspexp->loport &= 0xfffe;
227							prtspexp->hiport = prtspexp->loport+1;
228						}
229					} else if (ptran[off] == '/') {
230						off++;
231						numlen = nf_strtou16(ptran+off, &port);
232						off += numlen;
233						prtspexp->pbtype = pb_discon;
234						prtspexp->hiport = port;
235					}
236					rc = 1;
237				}
238			}
239
240			/*
241			 * Note we don't look for the destination parameter here.
242			 * If we are using NAT, the NAT module will handle it.  If not,
243			 * and the client is sending packets elsewhere, the expectation
244			 * will quietly time out.
245			 */
246
247			off = nextfieldoff;
248		}
249
250		off = nextparamoff;
251	}
252
253	return rc;
254}
255
256void expected(struct nf_conn *ct, struct nf_conntrack_expect *exp)
257{
258		typeof(nf_nat_rtsp_hook_expectfn) nf_nat_rtsp_expectfn;
259		nf_nat_rtsp_expectfn = rcu_dereference(nf_nat_rtsp_hook_expectfn);
260    if(nf_nat_rtsp_expectfn && ct->master->status & IPS_NAT_MASK) {
261        nf_nat_rtsp_expectfn(ct,exp);
262    }
263}
264
265/*** conntrack functions ***/
266
267/* outbound packet: client->server */
268
269static inline int
270help_out(struct sk_buff *skb, unsigned char *rb_ptr, unsigned int datalen,
271                struct nf_conn *ct, enum ip_conntrack_info ctinfo)
272{
273	struct ip_ct_rtsp_expect expinfo;
274
275	int dir = CTINFO2DIR(ctinfo);   /* = IP_CT_DIR_ORIGINAL */
276	//struct  tcphdr* tcph = (void*)iph + iph->ihl * 4;
277	//uint    tcplen = pktlen - iph->ihl * 4;
278	char*   pdata = rb_ptr;
279	//uint    datalen = tcplen - tcph->doff * 4;
280	uint    dataoff = 0;
281	int ret = NF_ACCEPT;
282
283	struct nf_conntrack_expect *exp;
284
285	__be16 be_loport;
286
287	typeof(nf_nat_rtsp_hook) nf_nat_rtsp;
288
289	memset(&expinfo, 0, sizeof(expinfo));
290
291	while (dataoff < datalen) {
292		uint    cmdoff = dataoff;
293		uint    hdrsoff = 0;
294		uint    hdrslen = 0;
295		uint    cseqoff = 0;
296		uint    cseqlen = 0;
297		uint    transoff = 0;
298		uint    translen = 0;
299		uint    off;
300
301		if (!rtsp_parse_message(pdata, datalen, &dataoff,
302					&hdrsoff, &hdrslen,
303					&cseqoff, &cseqlen,
304					&transoff, &translen))
305			break;      /* not a valid message */
306
307		if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0)
308			continue;   /* not a SETUP message */
309		pr_debug("found a setup message\n");
310
311		off = 0;
312		if(translen) {
313			rtsp_parse_transport(pdata+transoff, translen, &expinfo);
314		}
315
316		if (expinfo.loport == 0) {
317			pr_debug("no udp transports found\n");
318			continue;   /* no udp transports found */
319		}
320
321		pr_debug("udp transport found, ports=(%d,%hu,%hu)\n",
322		       (int)expinfo.pbtype, expinfo.loport, expinfo.hiport);
323
324		exp = nf_ct_expect_alloc(ct);
325		if (!exp) {
326			ret = NF_DROP;
327			goto out;
328		}
329
330		be_loport = htons(expinfo.loport);
331
332		nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
333			/* media stream source can be different from the RTSP server address */
334			// &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3,
335			NULL, &ct->tuplehash[!dir].tuple.dst.u3,
336			IPPROTO_UDP, NULL, &be_loport);
337
338		exp->master = ct;
339
340		exp->expectfn = expected;
341		exp->flags = 0;
342
343		if (expinfo.pbtype == pb_range) {
344			pr_debug("Changing expectation mask to handle multiple ports\n");
345			//exp->mask.dst.u.udp.port  = 0xfffe;
346		}
347
348		pr_debug("expect_related %pI4:%u-%pI4:%u\n",
349		       &exp->tuple.src.u3.ip,
350		       ntohs(exp->tuple.src.u.udp.port),
351		       &exp->tuple.dst.u3.ip,
352		       ntohs(exp->tuple.dst.u.udp.port));
353
354		nf_nat_rtsp = rcu_dereference(nf_nat_rtsp_hook);
355		if (nf_nat_rtsp && ct->status & IPS_NAT_MASK)
356			/* pass the request off to the nat helper */
357			ret = nf_nat_rtsp(skb, ctinfo, hdrsoff, hdrslen, &expinfo, exp);
358		else if (nf_ct_expect_related(exp) != 0) {
359			pr_info("nf_conntrack_expect_related failed\n");
360			ret  = NF_DROP;
361		}
362		nf_ct_expect_put(exp);
363		goto out;
364	}
365out:
366
367	return ret;
368}
369
370
371static inline int
372help_in(struct sk_buff *skb, size_t pktlen,
373 struct nf_conn* ct, enum ip_conntrack_info ctinfo)
374{
375 return NF_ACCEPT;
376}
377
378static int help(struct sk_buff *skb, unsigned int protoff,
379		struct nf_conn *ct, enum ip_conntrack_info ctinfo)
380{
381	struct tcphdr _tcph, *th;
382	unsigned int dataoff, datalen;
383	char *rb_ptr;
384	int ret = NF_DROP;
385
386	/* Until there's been traffic both ways, don't look in packets. */
387	if (ctinfo != IP_CT_ESTABLISHED &&
388	    ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
389		pr_debug("conntrackinfo = %u\n", ctinfo);
390		return NF_ACCEPT;
391	}
392
393	/* Not whole TCP header? */
394	th = skb_header_pointer(skb,protoff, sizeof(_tcph), &_tcph);
395
396	if (!th)
397		return NF_ACCEPT;
398
399	/* No data ? */
400	dataoff = protoff + th->doff*4;
401	datalen = skb->len - dataoff;
402	if (dataoff >= skb->len)
403		return NF_ACCEPT;
404
405	spin_lock_bh(&rtsp_buffer_lock);
406	rb_ptr = skb_header_pointer(skb, dataoff,
407				    skb->len - dataoff, rtsp_buffer);
408	BUG_ON(rb_ptr == NULL);
409
410#if 0
411	/* Checksum invalid?  Ignore. */
412	/* FIXME: Source route IP option packets --RR */
413	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
414			 csum_partial((char*)tcph, tcplen, 0)))
415	{
416		DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
417		       tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
418		return NF_ACCEPT;
419	}
420#endif
421
422	switch (CTINFO2DIR(ctinfo)) {
423	case IP_CT_DIR_ORIGINAL:
424		ret = help_out(skb, rb_ptr, datalen, ct, ctinfo);
425		break;
426	case IP_CT_DIR_REPLY:
427		pr_debug("IP_CT_DIR_REPLY\n");
428		/* inbound packet: server->client */
429		ret = NF_ACCEPT;
430		break;
431	}
432
433	spin_unlock_bh(&rtsp_buffer_lock);
434
435	return ret;
436}
437
438static struct nf_conntrack_helper rtsp_helpers[MAX_PORTS];
439static char rtsp_names[MAX_PORTS][10];
440
441/* This function is intentionally _NOT_ defined as __exit */
442static void
443fini(void)
444{
445	int i;
446	for (i = 0; i < num_ports; i++) {
447		if (rtsp_helpers[i].me == NULL)
448			continue;
449		pr_debug("unregistering port %d\n", ports[i]);
450		nf_conntrack_helper_unregister(&rtsp_helpers[i]);
451	}
452	kfree(rtsp_buffer);
453}
454
455static int __init
456init(void)
457{
458	int i, ret;
459	struct nf_conntrack_helper *hlpr;
460	char *tmpname;
461
462	printk("nf_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n");
463
464	if (max_outstanding < 1) {
465		printk("nf_conntrack_rtsp: max_outstanding must be a positive integer\n");
466		return -EBUSY;
467	}
468	if (setup_timeout < 0) {
469		printk("nf_conntrack_rtsp: setup_timeout must be a positive integer\n");
470		return -EBUSY;
471	}
472
473  rtsp_exp_policy.max_expected = max_outstanding;
474  rtsp_exp_policy.timeout = setup_timeout;
475
476	rtsp_buffer = kmalloc(65536, GFP_KERNEL);
477	if (!rtsp_buffer)
478		return -ENOMEM;
479
480	/* If no port given, default to standard rtsp port */
481	if (ports[0] == 0) {
482		ports[0] = RTSP_PORT;
483	}
484
485	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
486		hlpr = &rtsp_helpers[i];
487		memset(hlpr, 0, sizeof(struct nf_conntrack_helper));
488		hlpr->tuple.src.l3num = AF_INET;
489		hlpr->tuple.src.u.tcp.port = htons(ports[i]);
490		hlpr->tuple.dst.protonum = IPPROTO_TCP;
491		//hlpr->mask.src.u.tcp.port = 0xFFFF;
492		//hlpr->mask.dst.protonum = 0xFF;
493		hlpr->expect_policy = &rtsp_exp_policy;
494		hlpr->me = THIS_MODULE;
495		hlpr->help = help;
496
497		tmpname = &rtsp_names[i][0];
498		if (ports[i] == RTSP_PORT) {
499			sprintf(tmpname, "rtsp");
500		} else {
501			sprintf(tmpname, "rtsp-%d", i);
502		}
503		hlpr->name = tmpname;
504
505		pr_debug("port #%d: %d\n", i, ports[i]);
506
507		ret = nf_conntrack_helper_register(hlpr);
508
509		if (ret) {
510			printk("nf_conntrack_rtsp: ERROR registering port %d\n", ports[i]);
511			fini();
512			return -EBUSY;
513		}
514		num_ports++;
515	}
516	return 0;
517}
518
519module_init(init);
520module_exit(fini);
521
522EXPORT_SYMBOL(nf_nat_rtsp_hook_expectfn);
523
524