All of lore.kernel.org
 help / color / mirror / Atom feed
* RTSP support for kernels >= 2.6.20
@ 2007-04-15 15:31 Michael Guntsche
  2007-04-15 19:48 ` Patrick McHardy
  0 siblings, 1 reply; 15+ messages in thread
From: Michael Guntsche @ 2007-04-15 15:31 UTC (permalink / raw)
  To: netfilter-devel

[-- Attachment #1: Type: text/plain, Size: 718 bytes --]

Hello list,

Up to know I used Steven Van Acker's patch for kernels < 2.6.20 to  
get RTSP conntrack support.
Since 2.6.20 brought some big changes I tried to port his patch to  
the new API.
The attached patch is work in progress it is not finished and has at  
least three parts that do not compile. Nevertheless I thought I might  
post it to the list so that the real netfilter cracks can take a look  
at it and help me finish the port.
I am by no means a kernel hacker and the stuff I have up to know just  
exists because I had a look at the two different irc conntrack  
implementations in the source and tried to to the same with the RTSP  
module.
So any help is really appreciated.

Kind regards,
Michael



[-- Attachment #2: rtsp-nf.patch.gz --]
[-- Type: application/x-gzip, Size: 10998 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20
  2007-04-15 15:31 RTSP support for kernels >= 2.6.20 Michael Guntsche
@ 2007-04-15 19:48 ` Patrick McHardy
  2007-04-16 13:15   ` Michael Guntsche
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick McHardy @ 2007-04-15 19:48 UTC (permalink / raw)
  To: Michael Guntsche; +Cc: netfilter-devel

Michael Guntsche wrote:
> The attached patch is work in progress it is not finished and has at 
> least three parts that do not compile. Nevertheless I thought I might 
> post it to the list so that the real netfilter cracks can take a look 
> at it and help me finish the port.


If you want anyone to look at the patch you should attach it in a way
that allows reading in the mail client, nobody is going to save and
unpack it.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20
  2007-04-15 19:48 ` Patrick McHardy
@ 2007-04-16 13:15   ` Michael Guntsche
  2007-04-16 14:32     ` Michael Guntsche
  0 siblings, 1 reply; 15+ messages in thread
From: Michael Guntsche @ 2007-04-16 13:15 UTC (permalink / raw)
  To: netfilter-devel


On Apr 15, 2007, at 21:48, Patrick McHardy wrote:

> If you want anyone to look at the patch you should attach it in a way
> that allows reading in the mail client, nobody is going to save and
> unpack it.

I am sorry, I just wanted to keep the size down.
I was able to make some progress today. Both modules compiled and I  
see that at least part of it is working.
One of the parts of the code I was not able to port is this.

exp->tuple = ((struct ip_conntrack_tuple)
        {
          { ct->tuplehash[!dir].tuple.src.ip,
            { 0 }
          },
          {
            ct->tuplehash[!dir].tuple.dst.ip,
            { .udp = { htons(expinfo.loport) } },
            IPPROTO_UDP
          }
        });
exp->mask = ((struct ip_conntrack_tuple)
        {
          { 0,
            { 0 }
          },
          {
            0xFFFFFFFF,
            { .udp = { 0xFFFF } }, 0xFF
          }
        });

I think I need to use a nf_conntrack_tuple instead but I was not able  
to figure out how.
Maybe someone can take a look at this and tell me what's missing for  
a nf_conntrack_tuple.


Kind regards,
Michael

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20
  2007-04-16 13:15   ` Michael Guntsche
@ 2007-04-16 14:32     ` Michael Guntsche
  2007-04-16 14:48       ` Patrick McHardy
  0 siblings, 1 reply; 15+ messages in thread
From: Michael Guntsche @ 2007-04-16 14:32 UTC (permalink / raw)
  To: netfilter-devel


On Apr 16, 2007, at 15:15, Michael Guntsche wrote:

>
> I am sorry, I just wanted to keep the size down.
> I was able to make some progress today. Both modules compiled and I  
> see that at least part of it is working.
> One of the parts of the code I was not able to port is this.

Answering my own E-Mail. I found my problem with the struct for the  
tuple.

exp->tuple = ((struct nf_conntrack_tuple)
         {
           { ct->tuplehash[!dir].tuple.src.u3,
             { 0 },
           },
           {
             ct->tuplehash[!dir].tuple.dst.u3,
             { .udp = { htons(expinfo.loport) } },
             IPPROTO_UDP
           }
         });

This worked. I was not able to get the ->mask correctly though. For  
testing purposes I used this.


exp->mask = ((struct nf_conntrack_tuple)
         {
           { ct->tuplehash[!dir].tuple.src.u3,
             { 0 }
           },
           {
             ct->tuplehash[!dir].tuple.src.u3,
             { .udp = { 0xFFFF } }, 0xFF
           }
         });

and it worked with several test streams. I am pretty sure that this  
is completely wrong though and I would appreciate an< hints/tips on  
how to get this right.
If this is fixed I'll start cleaning everything up and send a patch  
to the list.


Kind regards,
Michael

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20
  2007-04-16 14:32     ` Michael Guntsche
@ 2007-04-16 14:48       ` Patrick McHardy
  2007-04-16 15:53         ` Michael Guntsche
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick McHardy @ 2007-04-16 14:48 UTC (permalink / raw)
  To: Michael Guntsche; +Cc: netfilter-devel

Michael Guntsche wrote:
> 
> On Apr 16, 2007, at 15:15, Michael Guntsche wrote:
> 
>>
>> I am sorry, I just wanted to keep the size down.
>> I was able to make some progress today. Both modules compiled and I 
>> see that at least part of it is working.
>> One of the parts of the code I was not able to port is this.
> 
> 
> Answering my own E-Mail. I found my problem with the struct for the  tuple.
> 
> exp->tuple = ((struct nf_conntrack_tuple)
>         {
>           { ct->tuplehash[!dir].tuple.src.u3,
>             { 0 },
>           },
>           {
>             ct->tuplehash[!dir].tuple.dst.u3,
>             { .udp = { htons(expinfo.loport) } },
>             IPPROTO_UDP
>           }
>         });
> 
> This worked. I was not able to get the ->mask correctly though. For 
> testing purposes I used this.
> 
> 
> exp->mask = ((struct nf_conntrack_tuple)
>         {
>           { ct->tuplehash[!dir].tuple.src.u3,
>             { 0 }
>           },
>           {
>             ct->tuplehash[!dir].tuple.src.u3,
>             { .udp = { 0xFFFF } }, 0xFF
>           }
>         });
> 
> and it worked with several test streams. I am pretty sure that this  is
> completely wrong though and I would appreciate an< hints/tips on  how to
> get this right.
> If this is fixed I'll start cleaning everything up and send a patch  to
> the list.


In case you're porting it to nf_conntrack (which is the only thing that
makes sense since ip_conntrack will be gone in 2.6.22), I'd suggest
you use nf_conntrack_expect_init().

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20
  2007-04-16 14:48       ` Patrick McHardy
@ 2007-04-16 15:53         ` Michael Guntsche
  2007-04-17 22:25           ` Michael Guntsche
  0 siblings, 1 reply; 15+ messages in thread
From: Michael Guntsche @ 2007-04-16 15:53 UTC (permalink / raw)
  To: netfilter-devel

[-- Attachment #1: Type: text/plain, Size: 622 bytes --]

On Apr 16, 2007, at 16:48, Patrick McHardy wrote:

>
> In case you're porting it to nf_conntrack (which is the only thing  
> that
> makes sense since ip_conntrack will be gone in 2.6.22), I'd suggest
> you use nf_conntrack_expect_init().
>

Attached you will find the two files that I am currently working on.  
I tried to use expect_init as you suggested but It does not work.  
Everything seems to be working, the packets get parsed, but I see no  
traffic coming through.
I left the old code as comment, since it worked with it. Maybe you  
can take a look at it and tell me what I did wrong.

Kind regards,
Michael



[-- Attachment #2: nf_conntrack_rtsp.c --]
[-- Type: application/octet-stream, Size: 14787 bytes --]

/*
 * RTSP extension for IP connection tracking
 * (C) 2003 by Tom Marshall <tmarshall at real.com>
 * based on ip_conntrack_irc.c
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 * Module load syntax:
 *   insmod ip_conntrack_rtsp.o ports=port1,port2,...port<MAX_PORTS>
 *                              max_outstanding=n setup_timeout=secs
 *
 * If no ports are specified, the default will be port 554.
 *
 * With max_outstanding you can define the maximum number of not yet
 * answered SETUP requests per RTSP session (default 8).
 * With setup_timeout you can specify how long the system waits for
 * an expected data channel (default 300 seconds).
 *
 * 2005-02-13: Harald Welte <laforge at netfilter.org>
 * 	- port to 2.6
 * 	- update to recent post-2.6.11 api changes
 * 2006-09-14: Steven Van Acker <deepstar at singularity.be>
 *      - removed calls to NAT code from conntrack helper: NAT no longer needed to use rtsp-conntrack
 */

#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/ip.h>
#include <linux/inet.h>
#include <net/checksum.h>
#include <net/tcp.h>

#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_tuple.h>
#include <linux/netfilter/nf_conntrack_rtsp.h>

#include <linux/ctype.h>
#define NF_NEED_STRNCASECMP
#define NF_NEED_STRTOU16
#define NF_NEED_STRTOU32
#define NF_NEED_NEXTLINE
#include <linux/netfilter_helpers.h>
#define NF_NEED_MIME_NEXTLINE
#include <linux/netfilter_mime.h>

#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */
#define IP_NF_RTSP_DEBUG
#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
#ifdef IP_NF_RTSP_DEBUG
#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
#else
#define DEBUGP(fmt, args...)
#endif

#define MAX_PORTS 8
static int ports[MAX_PORTS];
static int num_ports = 0;
static int max_outstanding = 8;
static unsigned int setup_timeout = 300;

MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
MODULE_DESCRIPTION("RTSP connection tracking module");
MODULE_LICENSE("GPL");
module_param_array(ports, int, &num_ports, 0400);
MODULE_PARM_DESC(ports, "port numbers of RTSP servers");
module_param(max_outstanding, int, 0400);
MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session");
module_param(setup_timeout, int, 0400);
MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels");

static char *rtsp_buffer;
static DEFINE_SPINLOCK(rtsp_buffer_lock);

unsigned int (*nf_nat_rtsp_hook)(struct sk_buff **pskb,
				 enum ip_conntrack_info ctinfo,
				 unsigned int matchoff, unsigned int matchlen,struct ip_ct_rtsp_expect* prtspexp,
				 struct nf_conntrack_expect *exp);
void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp);

EXPORT_SYMBOL_GPL(nf_nat_rtsp_hook);

/*
 * Max mappings we will allow for one RTSP connection (for RTP, the number
 * of allocated ports is twice this value).  Note that SMIL burns a lot of
 * ports so keep this reasonably high.  If this is too low, you will see a
 * lot of "no free client map entries" messages.
 */
#define MAX_PORT_MAPS 16

/*** default port list was here in the masq code: 554, 3030, 4040 ***/

#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }

/*
 * Parse an RTSP packet.
 *
 * Returns zero if parsing failed.
 *
 * Parameters:
 *  IN      ptcp        tcp data pointer
 *  IN      tcplen      tcp data len
 *  IN/OUT  ptcpoff     points to current tcp offset
 *  OUT     phdrsoff    set to offset of rtsp headers
 *  OUT     phdrslen    set to length of rtsp headers
 *  OUT     pcseqoff    set to offset of CSeq header
 *  OUT     pcseqlen    set to length of CSeq header
 */
static int
rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff,
                   uint* phdrsoff, uint* phdrslen,
                   uint* pcseqoff, uint* pcseqlen,
                   uint* transoff, uint* translen)
{
	uint    entitylen = 0;
	uint    lineoff;
	uint    linelen;
	
	if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen))
		return 0;
	
	*phdrsoff = *ptcpoff;
	while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) {
		if (linelen == 0) {
			if (entitylen > 0)
				*ptcpoff += min(entitylen, tcplen - *ptcpoff);
			break;
		}
		if (lineoff+linelen > tcplen) {
			INFOP("!! overrun !!\n");
			break;
		}
		
		if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) {
			*pcseqoff = lineoff;
			*pcseqlen = linelen;
		} 

		if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) {
			*transoff = lineoff;
			*translen = linelen;
		}
		
		if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) {
			uint off = lineoff+15;
			SKIP_WSPACE(ptcp+lineoff, linelen, off);
			nf_strtou32(ptcp+off, &entitylen);
		}
	}
	*phdrslen = (*ptcpoff) - (*phdrsoff);
	
	return 1;
}

/*
 * Find lo/hi client ports (if any) in transport header
 * In:
 *   ptcp, tcplen = packet
 *   tranoff, tranlen = buffer to search
 *
 * Out:
 *   pport_lo, pport_hi = lo/hi ports (host endian)
 *
 * Returns nonzero if any client ports found
 *
 * Note: it is valid (and expected) for the client to request multiple
 * transports, so we need to parse the entire line.
 */
static int
rtsp_parse_transport(char* ptran, uint tranlen,
                     struct ip_ct_rtsp_expect* prtspexp)
{
	int     rc = 0;
	uint    off = 0;
	
	if (tranlen < 10 || !iseol(ptran[tranlen-1]) ||
	    nf_strncasecmp(ptran, "Transport:", 10) != 0) {
		INFOP("sanity check failed\n");
		return 0;
	}
	
	DEBUGP("tran='%.*s'\n", (int)tranlen, ptran);
	off += 10;
	SKIP_WSPACE(ptran, tranlen, off);
	
	/* Transport: tran;field;field=val,tran;field;field=val,... */
	while (off < tranlen) {
		const char* pparamend;
		uint        nextparamoff;
		
		pparamend = memchr(ptran+off, ',', tranlen-off);
		pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
		nextparamoff = pparamend-ptran;
		
		while (off < nextparamoff) {
			const char* pfieldend;
			uint        nextfieldoff;
			
			pfieldend = memchr(ptran+off, ';', nextparamoff-off);
			nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
		   
			if (strncmp(ptran+off, "client_port=", 12) == 0) {
				u_int16_t   port;
				uint        numlen;
		    
				off += 12;
				numlen = nf_strtou16(ptran+off, &port);
				off += numlen;
				if (prtspexp->loport != 0 && prtspexp->loport != port)
					DEBUGP("multiple ports found, port %hu ignored\n", port);
				else {
					DEBUGP("lo port found : %hu\n", port);
					prtspexp->loport = prtspexp->hiport = port;
					if (ptran[off] == '-') {
						off++;
						numlen = nf_strtou16(ptran+off, &port);
						off += numlen;
						prtspexp->pbtype = pb_range;
						prtspexp->hiport = port;
						
						// If we have a range, assume rtp:
						// loport must be even, hiport must be loport+1
						if ((prtspexp->loport & 0x0001) != 0 ||
						    prtspexp->hiport != prtspexp->loport+1) {
							DEBUGP("incorrect range: %hu-%hu, correcting\n",
							       prtspexp->loport, prtspexp->hiport);
							prtspexp->loport &= 0xfffe;
							prtspexp->hiport = prtspexp->loport+1;
						}
					} else if (ptran[off] == '/') {
						off++;
						numlen = nf_strtou16(ptran+off, &port);
						off += numlen;
						prtspexp->pbtype = pb_discon;
						prtspexp->hiport = port;
					}
					rc = 1;
				}
			}
			
			/*
			 * Note we don't look for the destination parameter here.
			 * If we are using NAT, the NAT module will handle it.  If not,
			 * and the client is sending packets elsewhere, the expectation
			 * will quietly time out.
			 */
			
			off = nextfieldoff;
		}
		
		off = nextparamoff;
	}
	
	return rc;
}

void expected(struct nf_conn *ct, struct nf_conntrack_expect *exp)
{
    if(nf_nat_rtsp_hook_expectfn) {
        nf_nat_rtsp_hook_expectfn(ct,exp);
    }
}

/*** conntrack functions ***/

/* outbound packet: client->server */

static inline int
help_out(struct sk_buff **pskb, unsigned char *rb_ptr, unsigned int datalen,
                struct nf_conn *ct, enum ip_conntrack_info ctinfo)
{
	struct ip_ct_rtsp_expect expinfo;
	
	int dir = CTINFO2DIR(ctinfo);   /* = IP_CT_DIR_ORIGINAL */
	//struct  tcphdr* tcph = (void*)iph + iph->ihl * 4;
	//uint    tcplen = pktlen - iph->ihl * 4;
	char*   pdata = rb_ptr;
	//uint    datalen = tcplen - tcph->doff * 4;
	uint    dataoff = 0;
	int ret = NF_ACCEPT;
	
	struct nf_conntrack_expect *exp;
	
	__be16 be_loport;
	
	memset(&expinfo, 0, sizeof(expinfo));
	
	while (dataoff < datalen) {
		uint    cmdoff = dataoff;
		uint    hdrsoff = 0;
		uint    hdrslen = 0;
		uint    cseqoff = 0;
		uint    cseqlen = 0;
		uint    transoff = 0;
		uint    translen = 0;
		uint    off;
		
		if (!rtsp_parse_message(pdata, datalen, &dataoff,
					&hdrsoff, &hdrslen,
					&cseqoff, &cseqlen,
					&transoff, &translen))
			break;      /* not a valid message */
		
		if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0)
			continue;   /* not a SETUP message */
		DEBUGP("found a setup message\n");

		off = 0;
		if(translen) {
			rtsp_parse_transport(pdata+transoff, translen, &expinfo);
		}

		if (expinfo.loport == 0) {
			DEBUGP("no udp transports found\n");
			continue;   /* no udp transports found */
		}

		DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n",
		       (int)expinfo.pbtype, expinfo.loport, expinfo.hiport);

		exp = nf_conntrack_expect_alloc(ct);
		if (!exp) {
			ret = NF_DROP;
			goto out;
		}

		exp->master = ct;

		exp->expectfn = expected;
		exp->flags = 0;

		/* FIXME This is really broken right now */
//		exp->tuple = ((struct nf_conntrack_tuple)
//				{
//					{ ct->tuplehash[!dir].tuple.src.u3,
//						{ 0 },
//					},
//					{ 	
//						ct->tuplehash[!dir].tuple.dst.u3,
//						{ .udp = { htons(expinfo.loport) } },
//						IPPROTO_UDP 
//					}
//				});	
//		exp->mask = ((struct nf_conntrack_tuple)
//				{ 
//					{ ct->tuplehash[!dir].tuple.src.u3,
//						{ 0 } 
//					},
//					{
//						ct->tuplehash[!dir].tuple.src.u3,
//						{ .udp = { 0xFFFF } }, 0xFF 
//					}
//				});

		be_loport = htons(expinfo.loport);

		nf_conntrack_expect_init(exp, ct->tuplehash[!dir].tuple.src.l3num,
			&ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3,
			IPPROTO_UDP, NULL, &be_loport); 

		if (expinfo.pbtype == pb_range) {
			DEBUGP("Changing expectation mask to handle multiple ports\n");
			exp->mask.dst.u.udp.port  = 0xfffe;
		}

		DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
		       NIPQUAD(exp->tuple.src.u3.ip),
		       ntohs(exp->tuple.src.u.udp.port),
		       NIPQUAD(exp->tuple.dst.u3.ip),
		       ntohs(exp->tuple.dst.u.udp.port));

		if (nf_nat_rtsp_hook)
			/* pass the request off to the nat helper */
			ret = nf_nat_rtsp_hook(pskb, ctinfo, hdrsoff, hdrslen, &expinfo, exp);
		else if (nf_conntrack_expect_related(exp) != 0) {
			INFOP("nf_conntrack_expect_related failed\n");
			ret  = NF_DROP;
		}
		nf_conntrack_expect_put(exp);
		goto out;
	}
out:

	return ret;
}


static inline int
help_in(struct sk_buff **pskb, size_t pktlen,
 struct nf_conn* ct, enum ip_conntrack_info ctinfo)
{
 return NF_ACCEPT;
}

static int help(struct sk_buff **pskb, unsigned int protoff,
		struct nf_conn *ct, enum ip_conntrack_info ctinfo) 
{
	struct tcphdr _tcph, *th;
	unsigned int dataoff, datalen;
	char *rb_ptr;
	int ret = NF_DROP;

	/* Until there's been traffic both ways, don't look in packets. */
	if (ctinfo != IP_CT_ESTABLISHED && 
	    ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
		DEBUGP("conntrackinfo = %u\n", ctinfo);
		return NF_ACCEPT;
	}

	/* Not whole TCP header? */
	th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4,
				sizeof(_tcph), &_tcph);
	if (!th)
		return NF_ACCEPT;
   
	/* No data ? */
	dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4;
	datalen = (*pskb)->len - dataoff;
	if (dataoff >= (*pskb)->len)
		return NF_ACCEPT;

	spin_lock_bh(&rtsp_buffer_lock);
	rb_ptr = skb_header_pointer(*pskb, dataoff,
				    (*pskb)->len - dataoff, rtsp_buffer);
	BUG_ON(rb_ptr == NULL);

#if 0
	/* Checksum invalid?  Ignore. */
	/* FIXME: Source route IP option packets --RR */
	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
			 csum_partial((char*)tcph, tcplen, 0)))
	{
		DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
		       tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
		return NF_ACCEPT;
	}
#endif

	switch (CTINFO2DIR(ctinfo)) {
	case IP_CT_DIR_ORIGINAL:
		ret = help_out(pskb, rb_ptr, datalen, ct, ctinfo);
		break;
	case IP_CT_DIR_REPLY:
		DEBUGP("IP_CT_DIR_REPLY\n");
		/* inbound packet: server->client */
		ret = NF_ACCEPT;
		break;
	}

	spin_unlock_bh(&rtsp_buffer_lock);

	return ret;
}

static struct nf_conntrack_helper rtsp_helpers[MAX_PORTS];
static char rtsp_names[MAX_PORTS][10];

/* This function is intentionally _NOT_ defined as __exit */
static void
fini(void)
{
	int i;
	for (i = 0; i < num_ports; i++) {
		DEBUGP("unregistering port %d\n", ports[i]);
		nf_conntrack_helper_unregister(&rtsp_helpers[i]);
	}
	kfree(rtsp_buffer);
}

static int __init
init(void)
{
	int i, ret;
	struct nf_conntrack_helper *hlpr;
	char *tmpname;

	printk("ip_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n");

	if (max_outstanding < 1) {
		printk("ip_conntrack_rtsp: max_outstanding must be a positive integer\n");
		return -EBUSY;
	}
	if (setup_timeout < 0) {
		printk("ip_conntrack_rtsp: setup_timeout must be a positive integer\n");
		return -EBUSY;
	}

	rtsp_buffer = kmalloc(65536, GFP_KERNEL);
	if (!rtsp_buffer) 
		return -ENOMEM;

	/* If no port given, default to standard rtsp port */
	if (ports[0] == 0) {
		ports[0] = RTSP_PORT;
	}

	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
		hlpr = &rtsp_helpers[i];
		memset(hlpr, 0, sizeof(struct nf_conntrack_helper));
		hlpr->tuple.src.u.tcp.port = htons(ports[i]);
		hlpr->tuple.dst.protonum = IPPROTO_TCP;
		hlpr->mask.src.u.tcp.port = 0xFFFF;
		hlpr->mask.dst.protonum = 0xFF;
		hlpr->max_expected = max_outstanding;
		hlpr->timeout = setup_timeout;
		hlpr->me = THIS_MODULE;
		hlpr->help = help;

		tmpname = &rtsp_names[i][0];
		if (ports[i] == RTSP_PORT) {
			sprintf(tmpname, "rtsp");
		} else {
			sprintf(tmpname, "rtsp-%d", i);
		}
		hlpr->name = tmpname;

		DEBUGP("port #%d: %d\n", i, ports[i]);

		ret = nf_conntrack_helper_register(hlpr);

		if (ret) {
			printk("ip_conntrack_rtsp: ERROR registering port %d\n", ports[i]);
			fini();
			return -EBUSY;
		}
		num_ports++;
	}
	return 0;
}

module_init(init);
module_exit(fini);

EXPORT_SYMBOL(nf_nat_rtsp_hook_expectfn);


[-- Attachment #3: nf_nat_rtsp.c --]
[-- Type: application/octet-stream, Size: 16207 bytes --]

/*
 * RTSP extension for TCP NAT alteration
 * (C) 2003 by Tom Marshall <tmarshall at real.com>
 * based on ip_nat_irc.c
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 * Module load syntax:
 *      insmod ip_nat_rtsp.o ports=port1,port2,...port<MAX_PORTS>
 *                           stunaddr=<address>
 *                           destaction=[auto|strip|none]
 *
 * If no ports are specified, the default will be port 554 only.
 *
 * stunaddr specifies the address used to detect that a client is using STUN.
 * If this address is seen in the destination parameter, it is assumed that
 * the client has already punched a UDP hole in the firewall, so we don't
 * mangle the client_port.  If none is specified, it is autodetected.  It
 * only needs to be set if you have multiple levels of NAT.  It should be
 * set to the external address that the STUN clients detect.  Note that in
 * this case, it will not be possible for clients to use UDP with servers
 * between the NATs.
 *
 * If no destaction is specified, auto is used.
 *   destaction=auto:  strip destination parameter if it is not stunaddr.
 *   destaction=strip: always strip destination parameter (not recommended).
 *   destaction=none:  do not touch destination parameter (not recommended).
 */

#include <linux/module.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/kernel.h>
#include <net/tcp.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_helper.h>
#include <net/netfilter/nf_nat_rule.h>
#include <linux/netfilter/nf_conntrack_rtsp.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_expect.h>

#include <linux/inet.h>
#include <linux/ctype.h>
#define NF_NEED_STRNCASECMP
#define NF_NEED_STRTOU16
#include <linux/netfilter_helpers.h>
#define NF_NEED_MIME_NEXTLINE
#include <linux/netfilter_mime.h>

#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
#define IP_NF_RTSP_DEBUG
#ifdef IP_NF_RTSP_DEBUG
#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
#else
#define DEBUGP(fmt, args...)
#endif

#define MAX_PORTS       8
#define DSTACT_AUTO     0
#define DSTACT_STRIP    1
#define DSTACT_NONE     2

static char*    stunaddr = NULL;
static char*    destaction = NULL;

static u_int32_t extip = 0;
static int       dstact = 0;

MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
MODULE_DESCRIPTION("RTSP network address translation module");
MODULE_LICENSE("GPL");
module_param(stunaddr, charp, 0644);
MODULE_PARM_DESC(stunaddr, "Address for detecting STUN");
module_param(destaction, charp, 0644);
MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)");

#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }

/*** helper functions ***/

static void
get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen)
{
    struct iphdr*   iph  = (struct iphdr*)skb->nh.iph;
    struct tcphdr*  tcph = (struct tcphdr*)((char*)iph + iph->ihl*4);

    *pptcpdata = (char*)tcph + tcph->doff*4;
    *ptcpdatalen = ((char*)skb->h.raw + skb->len) - *pptcpdata;
}

/*** nat functions ***/

/*
 * Mangle the "Transport:" header:
 *   - Replace all occurences of "client_port=<spec>"
 *   - Handle destination parameter
 *
 * In:
 *   ct, ctinfo = conntrack context
 *   pskb       = packet
 *   tranoff    = Transport header offset from TCP data
 *   tranlen    = Transport header length (incl. CRLF)
 *   rport_lo   = replacement low  port (host endian)
 *   rport_hi   = replacement high port (host endian)
 *
 * Returns packet size difference.
 *
 * Assumes that a complete transport header is present, ending with CR or LF
 */
static int
rtsp_mangle_tran(enum ip_conntrack_info ctinfo,
                 struct nf_conntrack_expect* exp,
								 struct ip_ct_rtsp_expect* prtspexp,
                 struct sk_buff** pskb, uint tranoff, uint tranlen)
{
    char*       ptcp;
    uint        tcplen;
    char*       ptran;
    char        rbuf1[16];      /* Replacement buffer (one port) */
    uint        rbuf1len;       /* Replacement len (one port) */
    char        rbufa[16];      /* Replacement buffer (all ports) */
    uint        rbufalen;       /* Replacement len (all ports) */
    u_int32_t   newip;
    u_int16_t   loport, hiport;
    uint        off = 0;
    uint        diff;           /* Number of bytes we removed */

    struct nf_conn *ct = exp->master;
    struct nf_conntrack_tuple t;

    char    szextaddr[15+1];
    uint    extaddrlen;
    int     is_stun;

    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
    ptran = ptcp+tranoff;

    if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
        tranlen < 10 || !iseol(ptran[tranlen-1]) ||
        nf_strncasecmp(ptran, "Transport:", 10) != 0)
    {
        INFOP("sanity check failed\n");
        return 0;
    }
    off += 10;
    SKIP_WSPACE(ptcp+tranoff, tranlen, off);

    newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
    t = exp->tuple;
    t.dst.u3.ip = newip;

    extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(extip))
                       : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(newip));
    DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));

    rbuf1len = rbufalen = 0;
    switch (prtspexp->pbtype)
    {
    case pb_single:
        for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
        {
            t.dst.u.udp.port = htons(loport);
            if (nf_conntrack_expect_related(exp) == 0)
            {
                DEBUGP("using port %hu\n", loport);
                break;
            }
        }
        if (loport != 0)
        {
            rbuf1len = sprintf(rbuf1, "%hu", loport);
            rbufalen = sprintf(rbufa, "%hu", loport);
        }
        break;
    case pb_range:
        for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */
        {
            t.dst.u.udp.port = htons(loport);
            if (nf_conntrack_expect_related(exp) == 0)
            {
                hiport = loport + ~exp->mask.dst.u.udp.port;
                DEBUGP("using ports %hu-%hu\n", loport, hiport);
                break;
            }
        }
        if (loport != 0)
        {
            rbuf1len = sprintf(rbuf1, "%hu", loport);
            rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1);
        }
        break;
    case pb_discon:
        for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
        {
            t.dst.u.udp.port = htons(loport);
            if (nf_conntrack_expect_related(exp) == 0)
            {
                DEBUGP("using port %hu (1 of 2)\n", loport);
                break;
            }
        }
        for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */
        {
            t.dst.u.udp.port = htons(hiport);
            if (nf_conntrack_expect_related(exp) == 0)
            {
                DEBUGP("using port %hu (2 of 2)\n", hiport);
                break;
            }
        }
        if (loport != 0 && hiport != 0)
        {
            rbuf1len = sprintf(rbuf1, "%hu", loport);
            if (hiport == loport+1)
            {
                rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
            }
            else
            {
                rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport);
            }
        }
        break;
    }

    if (rbuf1len == 0)
    {
        return 0;   /* cannot get replacement port(s) */
    }

    /* Transport: tran;field;field=val,tran;field;field=val,... */
    while (off < tranlen)
    {
        uint        saveoff;
        const char* pparamend;
        uint        nextparamoff;

        pparamend = memchr(ptran+off, ',', tranlen-off);
        pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
        nextparamoff = pparamend-ptcp;

        /*
         * We pass over each param twice.  On the first pass, we look for a
         * destination= field.  It is handled by the security policy.  If it
         * is present, allowed, and equal to our external address, we assume
         * that STUN is being used and we leave the client_port= field alone.
         */
        is_stun = 0;
        saveoff = off;
        while (off < nextparamoff)
        {
            const char* pfieldend;
            uint        nextfieldoff;

            pfieldend = memchr(ptran+off, ';', nextparamoff-off);
            nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;

            if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0)
            {
                if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
                {
                    is_stun = 1;
                }
                if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun))
                {
                    diff = nextfieldoff-off;
                    if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo,
                                                         off, diff, NULL, 0))
                    {
                        /* mangle failed, all we can do is bail */
			nf_conntrack_unexpect_related(exp);
                        return 0;
                    }
                    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
                    ptran = ptcp+tranoff;
                    tranlen -= diff;
                    nextparamoff -= diff;
                    nextfieldoff -= diff;
                }
            }

            off = nextfieldoff;
        }
        if (is_stun)
        {
            continue;
        }
        off = saveoff;
        while (off < nextparamoff)
        {
            const char* pfieldend;
            uint        nextfieldoff;

            pfieldend = memchr(ptran+off, ';', nextparamoff-off);
            nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;

            if (strncmp(ptran+off, "client_port=", 12) == 0)
            {
                u_int16_t   port;
                uint        numlen;
                uint        origoff;
                uint        origlen;
                char*       rbuf    = rbuf1;
                uint        rbuflen = rbuf1len;

                off += 12;
                origoff = (ptran-ptcp)+off;
                origlen = 0;
                numlen = nf_strtou16(ptran+off, &port);
                off += numlen;
                origlen += numlen;
                if (port != prtspexp->loport)
                {
                    DEBUGP("multiple ports found, port %hu ignored\n", port);
                }
                else
                {
                    if (ptran[off] == '-' || ptran[off] == '/')
                    {
                        off++;
                        origlen++;
                        numlen = nf_strtou16(ptran+off, &port);
                        off += numlen;
                        origlen += numlen;
                        rbuf = rbufa;
                        rbuflen = rbufalen;
                    }

                    /*
                     * note we cannot just memcpy() if the sizes are the same.
                     * the mangle function does skb resizing, checks for a
                     * cloned skb, and updates the checksums.
                     *
                     * parameter 4 below is offset from start of tcp data.
                     */
                    diff = origlen-rbuflen;
                    if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo,
                                              origoff, origlen, rbuf, rbuflen))
                    {
                        /* mangle failed, all we can do is bail */
			nf_conntrack_unexpect_related(exp);
                        return 0;
                    }
                    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
                    ptran = ptcp+tranoff;
                    tranlen -= diff;
                    nextparamoff -= diff;
                    nextfieldoff -= diff;
                }
            }

            off = nextfieldoff;
        }

        off = nextparamoff;
    }

    return 1;
}

static uint
help_out(struct sk_buff **pskb, enum ip_conntrack_info ctinfo,
	 unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp, 
	 struct nf_conntrack_expect* exp)
{
    char*   ptcp;
    uint    tcplen;
    uint    hdrsoff;
    uint    hdrslen;
    uint    lineoff;
    uint    linelen;
    uint    off;

    //struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
    //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);

    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
    hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq);
    hdrslen = matchlen;
    off = hdrsoff;
    DEBUGP("NAT rtsp help_out\n");

    while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen))
    {
        if (linelen == 0)
        {
            break;
        }
        if (off > hdrsoff+hdrslen)
        {
            INFOP("!! overrun !!");
            break;
        }
        DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);

        if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0)
        {
            uint oldtcplen = tcplen;
	    DEBUGP("hdr: Transport\n");
            if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, pskb, lineoff, linelen))
            {
		DEBUGP("hdr: Transport mangle failed");
                break;
            }
            get_skb_tcpdata(*pskb, &ptcp, &tcplen);
            hdrslen -= (oldtcplen-tcplen);
            off -= (oldtcplen-tcplen);
            lineoff -= (oldtcplen-tcplen);
            linelen -= (oldtcplen-tcplen);
            DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
        }
    }

    return NF_ACCEPT;
}

static unsigned int
help(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, 
     unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp,
     struct nf_conntrack_expect* exp)
{
    int dir = CTINFO2DIR(ctinfo);
    int rc = NF_ACCEPT;

    switch (dir)
    {
    case IP_CT_DIR_ORIGINAL:
        rc = help_out(pskb, ctinfo, matchoff, matchlen, prtspexp, exp);
        break;
    case IP_CT_DIR_REPLY:
	DEBUGP("unmangle ! %u\n", ctinfo);
    	/* XXX: unmangle */
	rc = NF_ACCEPT;
        break;
    }
    //UNLOCK_BH(&ip_rtsp_lock);

    return rc;
}

static void expected(struct ip_conntrack* ct, struct nf_conntrack_expect *exp)
{
    struct ip_nat_multi_range_compat mr;
    u_int32_t newdstip, newsrcip, newip;

    struct ip_conntrack *master = master_ct(ct);

    newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
    newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
    //FIXME (how to port that ?)
    //code from 2.4 : newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip;
    newip = newdstip;

    DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.%u.%u\n",
           NIPQUAD(newsrcip), NIPQUAD(newdstip), NIPQUAD(newip));

    mr.rangesize = 1;
    // We don't want to manip the per-protocol, just the IPs. 
    mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
    mr.range[0].min_ip = mr.range[0].max_ip = newip;

    nf_nat_setup_info(ct, &mr.range[0], NF_IP_PRE_ROUTING);
}


static void __exit fini(void)
{
	nf_nat_rtsp_hook = NULL;
        nf_nat_rtsp_hook_expectfn = NULL;
	synchronize_net();
}

static int __init init(void)
{
	printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");

	BUG_ON(nf_nat_rtsp_hook);
	nf_nat_rtsp_hook = help;
        nf_nat_rtsp_hook_expectfn = &expected;

	if (stunaddr != NULL)
		extip = in_aton(stunaddr);

	if (destaction != NULL) {
	        if (strcmp(destaction, "auto") == 0)
			dstact = DSTACT_AUTO;

		if (strcmp(destaction, "strip") == 0)
			dstact = DSTACT_STRIP;

		if (strcmp(destaction, "none") == 0)
			dstact = DSTACT_NONE;
	}

	return 0;
}

module_init(init);
module_exit(fini);

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20
  2007-04-16 15:53         ` Michael Guntsche
@ 2007-04-17 22:25           ` Michael Guntsche
  2007-04-18  4:33             ` Patrick McHardy
  0 siblings, 1 reply; 15+ messages in thread
From: Michael Guntsche @ 2007-04-17 22:25 UTC (permalink / raw)
  To: netfilter-devel


On Apr 16, 2007, at 17:53, Michael Guntsche wrote:

> Attached you will find the two files that I am currently working  
> on. I tried to use expect_init as you suggested but It does not  
> work. Everything seems to be working, the packets get parsed, but I  
> see no traffic coming through.
> I left the old code as comment, since it worked with it. Maybe you  
> can take a look at it and tell me what I did wrong.
>

Ok, it looks like I was not really paying attention to what I was  
doing here, of course nf_conntrack_expect_init() works if you use it  
correctly. I fixed it and I will provide a full patch soon.
My question is against what codebase should I create the patch,  
2.6.20 or the latest 2.6.21-pre version?


Kind regards,
Michael

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20
  2007-04-17 22:25           ` Michael Guntsche
@ 2007-04-18  4:33             ` Patrick McHardy
  2007-04-18 21:34               ` Michael Guntsche
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick McHardy @ 2007-04-18  4:33 UTC (permalink / raw)
  To: Michael Guntsche; +Cc: netfilter-devel

Michael Guntsche wrote:
> Ok, it looks like I was not really paying attention to what I was  doing
> here, of course nf_conntrack_expect_init() works if you use it 
> correctly. I fixed it and I will provide a full patch soon.
> My question is against what codebase should I create the patch,  2.6.20
> or the latest 2.6.21-pre version?


Shouldn't make a difference I think, but just to be safe I'd suggest
to use the latest rc.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20
  2007-04-18  4:33             ` Patrick McHardy
@ 2007-04-18 21:34               ` Michael Guntsche
  2007-04-21 12:05                 ` RTSP support for kernels >= 2.6.20 [patchomatic patch] Michael Guntsche
  0 siblings, 1 reply; 15+ messages in thread
From: Michael Guntsche @ 2007-04-18 21:34 UTC (permalink / raw)
  To: netfilter-devel


On Apr 18, 2007, at 6:33, Patrick McHardy wrote:

>
> Shouldn't make a difference I think, but just to be safe I'd suggest
> to use the latest rc.
>

Hello,

Attached you'll find a patch against 2.6.21-rc7, it compiles and  
works for me.
Most of the RTSP related code is untouched just some small cleanups  
here and there.
I was not sure if it was ok to keep netfilters_helpers.h and  
netfilter_mime.h as separate files since they are only used by the  
rtsp code right now but I left them in there anyway. If this is not  
wanted we could put the neccessary code in nf_conntrack_rtsp.h.

Kind regards,
Michael

diff -urN linux-2.6.21-rc7/include/linux/netfilter/ 
nf_conntrack_rtsp.h linux-2.6.21-rc7.rtsp/include/linux/netfilter/ 
nf_conntrack_rtsp.h
--- linux-2.6.21-rc7/include/linux/netfilter/nf_conntrack_rtsp.h	 
1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.21-rc7.rtsp/include/linux/netfilter/nf_conntrack_rtsp.h	 
2007-04-16 14:07:06.000000000 +0200
@@ -0,0 +1,63 @@
+/*
+ * RTSP extension for IP connection tracking.
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_conntrack_irc.h
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+#ifndef _IP_CONNTRACK_RTSP_H
+#define _IP_CONNTRACK_RTSP_H
+
+//#define IP_NF_RTSP_DEBUG 1
+#define IP_NF_RTSP_VERSION "0.6.21"
+
+#ifdef __KERNEL__
+/* port block types */
+typedef enum {
+    pb_single,  /* client_port=x */
+    pb_range,   /* client_port=x-y */
+    pb_discon   /* client_port=x/y (rtspbis) */
+} portblock_t;
+
+/* We record seq number and length of rtsp headers here, all in host  
order. */
+
+/*
+ * This structure is per expected connection.  It is a member of struct
+ * ip_conntrack_expect.  The TCP SEQ for the conntrack expect is stored
+ * there and we are expected to only store the length of the data which
+ * needs replaced.  If a packet contains multiple RTSP messages, we  
create
+ * one expected connection per message.
+ *
+ * We use these variables to mark the entire header block.  This may  
seem
+ * like overkill, but the nature of RTSP requires it.  A header may  
appear
+ * multiple times in a message.  We must treat two Transport headers  
the
+ * same as one Transport header with two entries.
+ */
+struct ip_ct_rtsp_expect
+{
+    u_int32_t   len;        /* length of header block */
+    portblock_t pbtype;     /* Type of port block that was requested */
+    u_int16_t   loport;     /* Port that was requested, low or first */
+    u_int16_t   hiport;     /* Port that was requested, high or  
second */
+#if 0
+    uint        method;     /* RTSP method */
+    uint        cseq;       /* CSeq from request */
+#endif
+};
+
+extern unsigned int (*nf_nat_rtsp_hook)(struct sk_buff **pskb,
+				 enum ip_conntrack_info ctinfo,
+				 unsigned int matchoff, unsigned int matchlen,
+				 struct ip_ct_rtsp_expect *prtspexp,
+				 struct nf_conntrack_expect *exp);
+
+extern void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct  
nf_conntrack_expect *exp);
+
+#define RTSP_PORT   554
+
+#endif /* __KERNEL__ */
+
+#endif /* _IP_CONNTRACK_RTSP_H */
diff -urN linux-2.6.21-rc7/include/linux/netfilter_helpers.h  
linux-2.6.21-rc7.rtsp/include/linux/netfilter_helpers.h
--- linux-2.6.21-rc7/include/linux/netfilter_helpers.h	1970-01-01  
01:00:00.000000000 +0100
+++ linux-2.6.21-rc7.rtsp/include/linux/netfilter_helpers.h	 
2007-04-15 00:34:57.000000000 +0200
@@ -0,0 +1,133 @@
+/*
+ * Helpers for netfiler modules.  This file provides implementations  
for basic
+ * functions such as strncasecmp(), etc.
+ *
+ * gcc will warn for defined but unused functions, so we only  
include the
+ * functions requested.  The following macros are used:
+ *   NF_NEED_STRNCASECMP        nf_strncasecmp()
+ *   NF_NEED_STRTOU16           nf_strtou16()
+ *   NF_NEED_STRTOU32           nf_strtou32()
+ */
+#ifndef _NETFILTER_HELPERS_H
+#define _NETFILTER_HELPERS_H
+
+/* Only include these functions for kernel code. */
+#ifdef __KERNEL__
+
+#include <linux/ctype.h>
+#define iseol(c) ( (c) == '\r' || (c) == '\n' )
+
+/*
+ * The standard strncasecmp()
+ */
+#ifdef NF_NEED_STRNCASECMP
+static int
+nf_strncasecmp(const char* s1, const char* s2, u_int32_t len)
+{
+    if (s1 == NULL || s2 == NULL)
+    {
+        if (s1 == NULL && s2 == NULL)
+        {
+            return 0;
+        }
+        return (s1 == NULL) ? -1 : 1;
+    }
+    while (len > 0 && tolower(*s1) == tolower(*s2))
+    {
+        len--;
+        s1++;
+        s2++;
+    }
+    return ( (len == 0) ? 0 : (tolower(*s1) - tolower(*s2)) );
+}
+#endif /* NF_NEED_STRNCASECMP */
+
+/*
+ * Parse a string containing a 16-bit unsigned integer.
+ * Returns the number of chars used, or zero if no number is found.
+ */
+#ifdef NF_NEED_STRTOU16
+static int
+nf_strtou16(const char* pbuf, u_int16_t* pval)
+{
+    int n = 0;
+
+    *pval = 0;
+    while (isdigit(pbuf[n]))
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+#endif /* NF_NEED_STRTOU16 */
+
+/*
+ * Parse a string containing a 32-bit unsigned integer.
+ * Returns the number of chars used, or zero if no number is found.
+ */
+#ifdef NF_NEED_STRTOU32
+static int
+nf_strtou32(const char* pbuf, u_int32_t* pval)
+{
+    int n = 0;
+
+    *pval = 0;
+    while (pbuf[n] >= '0' && pbuf[n] <= '9')
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+#endif /* NF_NEED_STRTOU32 */
+
+/*
+ * Given a buffer and length, advance to the next line and mark the  
current
+ * line.
+ */
+#ifdef NF_NEED_NEXTLINE
+static int
+nf_nextline(char* p, uint len, uint* poff, uint* plineoff, uint*  
plinelen)
+{
+    uint    off = *poff;
+    uint    physlen = 0;
+
+    if (off >= len)
+    {
+        return 0;
+    }
+
+    while (p[off] != '\n')
+    {
+        if (len-off <= 1)
+        {
+            return 0;
+        }
+
+        physlen++;
+        off++;
+    }
+
+    /* if we saw a crlf, physlen needs adjusted */
+    if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r')
+    {
+        physlen--;
+    }
+
+    /* advance past the newline */
+    off++;
+
+    *plineoff = *poff;
+    *plinelen = physlen;
+    *poff = off;
+
+    return 1;
+}
+#endif /* NF_NEED_NEXTLINE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _NETFILTER_HELPERS_H */
diff -urN linux-2.6.21-rc7/include/linux/netfilter_mime.h  
linux-2.6.21-rc7.rtsp/include/linux/netfilter_mime.h
--- linux-2.6.21-rc7/include/linux/netfilter_mime.h	1970-01-01  
01:00:00.000000000 +0100
+++ linux-2.6.21-rc7.rtsp/include/linux/netfilter_mime.h	2007-04-15  
00:34:57.000000000 +0200
@@ -0,0 +1,89 @@
+/*
+ * MIME functions for netfilter modules.  This file provides  
implementations
+ * for basic MIME parsing.  MIME headers are used in many protocols,  
such as
+ * HTTP, RTSP, SIP, etc.
+ *
+ * gcc will warn for defined but unused functions, so we only  
include the
+ * functions requested.  The following macros are used:
+ *   NF_NEED_MIME_NEXTLINE      nf_mime_nextline()
+ */
+#ifndef _NETFILTER_MIME_H
+#define _NETFILTER_MIME_H
+
+/* Only include these functions for kernel code. */
+#ifdef __KERNEL__
+
+#include <linux/ctype.h>
+
+/*
+ * Given a buffer and length, advance to the next line and mark the  
current
+ * line.  If the current line is empty, *plinelen will be set to  
zero.  If
+ * not, it will be set to the actual line length (including CRLF).
+ *
+ * 'line' in this context means logical line (includes LWS  
continuations).
+ * Returns 1 on success, 0 on failure.
+ */
+#ifdef NF_NEED_MIME_NEXTLINE
+static int
+nf_mime_nextline(char* p, uint len, uint* poff, uint* plineoff,  
uint* plinelen)
+{
+    uint    off = *poff;
+    uint    physlen = 0;
+    int     is_first_line = 1;
+
+    if (off >= len)
+    {
+        return 0;
+    }
+
+    do
+    {
+        while (p[off] != '\n')
+        {
+            if (len-off <= 1)
+            {
+                return 0;
+            }
+
+            physlen++;
+            off++;
+        }
+
+        /* if we saw a crlf, physlen needs adjusted */
+        if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r')
+        {
+            physlen--;
+        }
+
+        /* advance past the newline */
+        off++;
+
+        /* check for an empty line */
+        if (physlen == 0)
+        {
+            break;
+        }
+
+        /* check for colon on the first physical line */
+        if (is_first_line)
+        {
+            is_first_line = 0;
+            if (memchr(p+(*poff), ':', physlen) == NULL)
+            {
+                return 0;
+            }
+        }
+    }
+    while (p[off] == ' ' || p[off] == '\t');
+
+    *plineoff = *poff;
+    *plinelen = (physlen == 0) ? 0 : (off - *poff);
+    *poff = off;
+
+    return 1;
+}
+#endif /* NF_NEED_MIME_NEXTLINE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _NETFILTER_MIME_H */
diff -urN linux-2.6.21-rc7/net/ipv4/netfilter/Kconfig linux-2.6.21- 
rc7.rtsp/net/ipv4/netfilter/Kconfig
--- linux-2.6.21-rc7/net/ipv4/netfilter/Kconfig	2007-04-18  
23:26:10.000000000 +0200
+++ linux-2.6.21-rc7.rtsp/net/ipv4/netfilter/Kconfig	2007-04-18  
23:01:31.000000000 +0200
@@ -498,6 +498,11 @@
	depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
	default NF_NAT && NF_CONNTRACK_IRC
+config NF_NAT_RTSP
+	tristate
+	depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
+	default NF_NAT && NF_CONNTRACK_RTSP
+
config IP_NF_NAT_TFTP
	tristate
	depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n
diff -urN linux-2.6.21-rc7/net/ipv4/netfilter/Makefile linux-2.6.21- 
rc7.rtsp/net/ipv4/netfilter/Makefile
--- linux-2.6.21-rc7/net/ipv4/netfilter/Makefile	2007-04-18  
23:26:10.000000000 +0200
+++ linux-2.6.21-rc7.rtsp/net/ipv4/netfilter/Makefile	2007-04-18  
21:17:33.000000000 +0200
@@ -64,6 +66,7 @@
obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o
obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o
obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o
+obj-$(CONFIG_NF_NAT_RTSP) += nf_nat_rtsp.o
obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o
obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o
obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o
diff -urN linux-2.6.21-rc7/net/ipv4/netfilter/nf_nat_rtsp.c  
linux-2.6.21-rc7.rtsp/net/ipv4/netfilter/nf_nat_rtsp.c
--- linux-2.6.21-rc7/net/ipv4/netfilter/nf_nat_rtsp.c	1970-01-01  
01:00:00.000000000 +0100
+++ linux-2.6.21-rc7.rtsp/net/ipv4/netfilter/nf_nat_rtsp.c	2007-04-18  
23:21:26.000000000 +0200
@@ -0,0 +1,496 @@
+/*
+ * RTSP extension for TCP NAT alteration
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_nat_irc.c
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ * Module load syntax:
+ *      insmod nf_nat_rtsp.o ports=port1,port2,...port<MAX_PORTS>
+ *                           stunaddr=<address>
+ *                           destaction=[auto|strip|none]
+ *
+ * If no ports are specified, the default will be port 554 only.
+ *
+ * stunaddr specifies the address used to detect that a client is  
using STUN.
+ * If this address is seen in the destination parameter, it is  
assumed that
+ * the client has already punched a UDP hole in the firewall, so we  
don't
+ * mangle the client_port.  If none is specified, it is  
autodetected.  It
+ * only needs to be set if you have multiple levels of NAT.  It  
should be
+ * set to the external address that the STUN clients detect.  Note  
that in
+ * this case, it will not be possible for clients to use UDP with  
servers
+ * between the NATs.
+ *
+ * If no destaction is specified, auto is used.
+ *   destaction=auto:  strip destination parameter if it is not  
stunaddr.
+ *   destaction=strip: always strip destination parameter (not  
recommended).
+ *   destaction=none:  do not touch destination parameter (not  
recommended).
+ */
+
+#include <linux/module.h>
+#include <net/tcp.h>
+#include <net/netfilter/nf_nat_helper.h>
+#include <net/netfilter/nf_nat_rule.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+
+#include <linux/inet.h>
+#include <linux/ctype.h>
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#include <linux/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter_mime.h>
+
+#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#if 0
+#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#else
+#define DEBUGP(fmt, args...)
+#endif
+
+#define MAX_PORTS       8
+#define DSTACT_AUTO     0
+#define DSTACT_STRIP    1
+#define DSTACT_NONE     2
+
+static char*    stunaddr = NULL;
+static char*    destaction = NULL;
+
+static u_int32_t extip = 0;
+static int       dstact = 0;
+
+MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
+MODULE_DESCRIPTION("RTSP network address translation module");
+MODULE_LICENSE("GPL");
+module_param(stunaddr, charp, 0644);
+MODULE_PARM_DESC(stunaddr, "Address for detecting STUN");
+module_param(destaction, charp, 0644);
+MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/ 
strip/none)");
+
+#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr 
+off))) { off++; }
+
+/*** helper functions ***/
+
+static void
+get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint*  
ptcpdatalen)
+{
+    struct iphdr*   iph  = (struct iphdr*)skb->nh.iph;
+    struct tcphdr*  tcph = (struct tcphdr*)((char*)iph + iph->ihl*4);
+
+    *pptcpdata = (char*)tcph + tcph->doff*4;
+    *ptcpdatalen = ((char*)skb->h.raw + skb->len) - *pptcpdata;
+}
+
+/*** nat functions ***/
+
+/*
+ * Mangle the "Transport:" header:
+ *   - Replace all occurences of "client_port=<spec>"
+ *   - Handle destination parameter
+ *
+ * In:
+ *   ct, ctinfo = conntrack context
+ *   pskb       = packet
+ *   tranoff    = Transport header offset from TCP data
+ *   tranlen    = Transport header length (incl. CRLF)
+ *   rport_lo   = replacement low  port (host endian)
+ *   rport_hi   = replacement high port (host endian)
+ *
+ * Returns packet size difference.
+ *
+ * Assumes that a complete transport header is present, ending with  
CR or LF
+ */
+static int
+rtsp_mangle_tran(enum ip_conntrack_info ctinfo,
+                 struct nf_conntrack_expect* exp,
+								 struct ip_ct_rtsp_expect* prtspexp,
+                 struct sk_buff** pskb, uint tranoff, uint tranlen)
+{
+    char*       ptcp;
+    uint        tcplen;
+    char*       ptran;
+    char        rbuf1[16];      /* Replacement buffer (one port) */
+    uint        rbuf1len;       /* Replacement len (one port) */
+    char        rbufa[16];      /* Replacement buffer (all ports) */
+    uint        rbufalen;       /* Replacement len (all ports) */
+    u_int32_t   newip;
+    u_int16_t   loport, hiport;
+    uint        off = 0;
+    uint        diff;           /* Number of bytes we removed */
+
+    struct nf_conn *ct = exp->master;
+    struct nf_conntrack_tuple t;
+
+    char    szextaddr[15+1];
+    uint    extaddrlen;
+    int     is_stun;
+
+    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+    ptran = ptcp+tranoff;
+
+    if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
+        tranlen < 10 || !iseol(ptran[tranlen-1]) ||
+        nf_strncasecmp(ptran, "Transport:", 10) != 0)
+    {
+        INFOP("sanity check failed\n");
+        return 0;
+    }
+    off += 10;
+    SKIP_WSPACE(ptcp+tranoff, tranlen, off);
+
+    newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
+    t = exp->tuple;
+    t.dst.u3.ip = newip;
+
+    extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD 
(extip))
+                       : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD 
(newip));
+    DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));
+
+    rbuf1len = rbufalen = 0;
+    switch (prtspexp->pbtype)
+    {
+    case pb_single:
+        for (loport = prtspexp->loport; loport != 0; loport++) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(loport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu\n", loport);
+                break;
+            }
+        }
+        if (loport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            rbufalen = sprintf(rbufa, "%hu", loport);
+        }
+        break;
+    case pb_range:
+        for (loport = prtspexp->loport; loport != 0; loport += 2) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(loport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                hiport = loport + ~exp->mask.dst.u.udp.port;
+                DEBUGP("using ports %hu-%hu\n", loport, hiport);
+                break;
+            }
+        }
+        if (loport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1);
+        }
+        break;
+    case pb_discon:
+        for (loport = prtspexp->loport; loport != 0; loport++) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(loport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu (1 of 2)\n", loport);
+                break;
+            }
+        }
+        for (hiport = prtspexp->hiport; hiport != 0; hiport++) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(hiport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu (2 of 2)\n", hiport);
+                break;
+            }
+        }
+        if (loport != 0 && hiport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            if (hiport == loport+1)
+            {
+                rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
+            }
+            else
+            {
+                rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport);
+            }
+        }
+        break;
+    }
+
+    if (rbuf1len == 0)
+    {
+        return 0;   /* cannot get replacement port(s) */
+    }
+
+    /* Transport: tran;field;field=val,tran;field;field=val,... */
+    while (off < tranlen)
+    {
+        uint        saveoff;
+        const char* pparamend;
+        uint        nextparamoff;
+
+        pparamend = memchr(ptran+off, ',', tranlen-off);
+        pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
+        nextparamoff = pparamend-ptcp;
+
+        /*
+         * We pass over each param twice.  On the first pass, we  
look for a
+         * destination= field.  It is handled by the security  
policy.  If it
+         * is present, allowed, and equal to our external address,  
we assume
+         * that STUN is being used and we leave the client_port=  
field alone.
+         */
+        is_stun = 0;
+        saveoff = off;
+        while (off < nextparamoff)
+        {
+            const char* pfieldend;
+            uint        nextfieldoff;
+
+            pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+            nextfieldoff = (pfieldend == NULL) ? nextparamoff :  
pfieldend-ptran+1;
+
+            if (dstact != DSTACT_NONE && strncmp(ptran+off,  
"destination=", 12) == 0)
+            {
+                if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
+                {
+                    is_stun = 1;
+                }
+                if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO  
&& !is_stun))
+                {
+                    diff = nextfieldoff-off;
+                    if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+                                                         off, diff,  
NULL, 0))
+                    {
+                        /* mangle failed, all we can do is bail */
+			nf_conntrack_unexpect_related(exp);
+                        return 0;
+                    }
+                    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+                    ptran = ptcp+tranoff;
+                    tranlen -= diff;
+                    nextparamoff -= diff;
+                    nextfieldoff -= diff;
+                }
+            }
+
+            off = nextfieldoff;
+        }
+        if (is_stun)
+        {
+            continue;
+        }
+        off = saveoff;
+        while (off < nextparamoff)
+        {
+            const char* pfieldend;
+            uint        nextfieldoff;
+
+            pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+            nextfieldoff = (pfieldend == NULL) ? nextparamoff :  
pfieldend-ptran+1;
+
+            if (strncmp(ptran+off, "client_port=", 12) == 0)
+            {
+                u_int16_t   port;
+                uint        numlen;
+                uint        origoff;
+                uint        origlen;
+                char*       rbuf    = rbuf1;
+                uint        rbuflen = rbuf1len;
+
+                off += 12;
+                origoff = (ptran-ptcp)+off;
+                origlen = 0;
+                numlen = nf_strtou16(ptran+off, &port);
+                off += numlen;
+                origlen += numlen;
+                if (port != prtspexp->loport)
+                {
+                    DEBUGP("multiple ports found, port %hu ignored 
\n", port);
+                }
+                else
+                {
+                    if (ptran[off] == '-' || ptran[off] == '/')
+                    {
+                        off++;
+                        origlen++;
+                        numlen = nf_strtou16(ptran+off, &port);
+                        off += numlen;
+                        origlen += numlen;
+                        rbuf = rbufa;
+                        rbuflen = rbufalen;
+                    }
+
+                    /*
+                     * note we cannot just memcpy() if the sizes are  
the same.
+                     * the mangle function does skb resizing, checks  
for a
+                     * cloned skb, and updates the checksums.
+                     *
+                     * parameter 4 below is offset from start of tcp  
data.
+                     */
+                    diff = origlen-rbuflen;
+                    if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+                                              origoff, origlen,  
rbuf, rbuflen))
+                    {
+                        /* mangle failed, all we can do is bail */
+			nf_conntrack_unexpect_related(exp);
+                        return 0;
+                    }
+                    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+                    ptran = ptcp+tranoff;
+                    tranlen -= diff;
+                    nextparamoff -= diff;
+                    nextfieldoff -= diff;
+                }
+            }
+
+            off = nextfieldoff;
+        }
+
+        off = nextparamoff;
+    }
+
+    return 1;
+}
+
+static uint
+help_out(struct sk_buff **pskb, enum ip_conntrack_info ctinfo,
+	 unsigned int matchoff, unsigned int matchlen, struct  
ip_ct_rtsp_expect* prtspexp,
+	 struct nf_conntrack_expect* exp)
+{
+    char*   ptcp;
+    uint    tcplen;
+    uint    hdrsoff;
+    uint    hdrslen;
+    uint    lineoff;
+    uint    linelen;
+    uint    off;
+
+    //struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
+    //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
+
+    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+    hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq);
+    hdrslen = matchlen;
+    off = hdrsoff;
+    DEBUGP("NAT rtsp help_out\n");
+
+    while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff,  
&linelen))
+    {
+        if (linelen == 0)
+        {
+            break;
+        }
+        if (off > hdrsoff+hdrslen)
+        {
+            INFOP("!! overrun !!");
+            break;
+        }
+        DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp 
+lineoff);
+
+        if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0)
+        {
+            uint oldtcplen = tcplen;
+	    DEBUGP("hdr: Transport\n");
+            if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, pskb,  
lineoff, linelen))
+            {
+		DEBUGP("hdr: Transport mangle failed");
+                break;
+            }
+            get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+            hdrslen -= (oldtcplen-tcplen);
+            off -= (oldtcplen-tcplen);
+            lineoff -= (oldtcplen-tcplen);
+            linelen -= (oldtcplen-tcplen);
+            DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp 
+lineoff);
+        }
+    }
+
+    return NF_ACCEPT;
+}
+
+static unsigned int
+help(struct sk_buff **pskb, enum ip_conntrack_info ctinfo,
+     unsigned int matchoff, unsigned int matchlen, struct  
ip_ct_rtsp_expect* prtspexp,
+     struct nf_conntrack_expect* exp)
+{
+    int dir = CTINFO2DIR(ctinfo);
+    int rc = NF_ACCEPT;
+
+    switch (dir)
+    {
+    case IP_CT_DIR_ORIGINAL:
+        rc = help_out(pskb, ctinfo, matchoff, matchlen, prtspexp, exp);
+        break;
+    case IP_CT_DIR_REPLY:
+	DEBUGP("unmangle ! %u\n", ctinfo);
+    	/* XXX: unmangle */
+	rc = NF_ACCEPT;
+        break;
+    }
+    //UNLOCK_BH(&ip_rtsp_lock);
+
+    return rc;
+}
+
+static void expected(struct ip_conntrack* ct, struct  
nf_conntrack_expect *exp)
+{
+    struct ip_nat_multi_range_compat mr;
+    u_int32_t newdstip, newsrcip, newip;
+
+    struct ip_conntrack *master = master_ct(ct);
+
+    newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
+    newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
+    //FIXME (how to port that ?)
+    //code from 2.4 : newip = (HOOK2MANIP(hooknum) ==  
IP_NAT_MANIP_SRC) ? newsrcip : newdstip;
+    newip = newdstip;
+
+    DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.% 
u.%u\n",
+           NIPQUAD(newsrcip), NIPQUAD(newdstip), NIPQUAD(newip));
+
+    mr.rangesize = 1;
+    // We don't want to manip the per-protocol, just the IPs.
+    mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
+    mr.range[0].min_ip = mr.range[0].max_ip = newip;
+
+    nf_nat_setup_info(ct, &mr.range[0], NF_IP_PRE_ROUTING);
+}
+
+
+static void __exit fini(void)
+{
+	nf_nat_rtsp_hook = NULL;
+        nf_nat_rtsp_hook_expectfn = NULL;
+	synchronize_net();
+}
+
+static int __init init(void)
+{
+	printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");
+
+	BUG_ON(nf_nat_rtsp_hook);
+	nf_nat_rtsp_hook = help;
+        nf_nat_rtsp_hook_expectfn = &expected;
+
+	if (stunaddr != NULL)
+		extip = in_aton(stunaddr);
+
+	if (destaction != NULL) {
+	        if (strcmp(destaction, "auto") == 0)
+			dstact = DSTACT_AUTO;
+
+		if (strcmp(destaction, "strip") == 0)
+			dstact = DSTACT_STRIP;
+
+		if (strcmp(destaction, "none") == 0)
+			dstact = DSTACT_NONE;
+	}
+
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);
diff -urN linux-2.6.21-rc7/net/netfilter/Kconfig linux-2.6.21- 
rc7.rtsp/net/netfilter/Kconfig
--- linux-2.6.21-rc7/net/netfilter/Kconfig	2007-04-18  
23:26:10.000000000 +0200
+++ linux-2.6.21-rc7.rtsp/net/netfilter/Kconfig	2007-04-18  
22:25:13.000000000 +0200
@@ -271,6 +272,16 @@
	  To compile it as a module, choose M here.  If unsure, say N.
+config NF_CONNTRACK_RTSP
+	tristate "RTSP protocol support"
+	depends on NF_CONNTRACK
+	help
+		Support the RTSP protocol.  This allows UDP transports to be setup
+		properly, including RTP and RDT.
+
+		If you want to compile it as a module, say 'M' here and read
+		Documentation/modules.txt.  If unsure, say 'Y'.
+
config NF_CT_NETLINK
	tristate 'Connection tracking netlink interface (EXPERIMENTAL)'
	depends on EXPERIMENTAL && NF_CONNTRACK && NETFILTER_NETLINK
diff -urN linux-2.6.21-rc7/net/netfilter/Makefile linux-2.6.21- 
rc7.rtsp/net/netfilter/Makefile
--- linux-2.6.21-rc7/net/netfilter/Makefile	2007-04-18  
23:26:10.000000000 +0200
+++ linux-2.6.21-rc7.rtsp/net/netfilter/Makefile	2007-04-18  
21:17:33.000000000 +0200
@@ -32,6 +32,7 @@
obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o
obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o
obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o
+obj-$(CONFIG_NF_CONNTRACK_RTSP) += nf_conntrack_rtsp.o
# generic X tables
obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
diff -urN linux-2.6.21-rc7/net/netfilter/nf_conntrack_rtsp.c  
linux-2.6.21-rc7.rtsp/net/netfilter/nf_conntrack_rtsp.c
--- linux-2.6.21-rc7/net/netfilter/nf_conntrack_rtsp.c	1970-01-01  
01:00:00.000000000 +0100
+++ linux-2.6.21-rc7.rtsp/net/netfilter/nf_conntrack_rtsp.c	 
2007-04-18 23:20:31.000000000 +0200
@@ -0,0 +1,515 @@
+/*
+ * RTSP extension for IP connection tracking
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_conntrack_irc.c
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ * Module load syntax:
+ *   insmod nf_conntrack_rtsp.o ports=port1,port2,...port<MAX_PORTS>
+ *                              max_outstanding=n setup_timeout=secs
+ *
+ * If no ports are specified, the default will be port 554.
+ *
+ * With max_outstanding you can define the maximum number of not yet
+ * answered SETUP requests per RTSP session (default 8).
+ * With setup_timeout you can specify how long the system waits for
+ * an expected data channel (default 300 seconds).
+ *
+ * 2005-02-13: Harald Welte <laforge at netfilter.org>
+ * 	- port to 2.6
+ * 	- update to recent post-2.6.11 api changes
+ * 2006-09-14: Steven Van Acker <deepstar at singularity.be>
+ *      - removed calls to NAT code from conntrack helper: NAT no  
longer needed to use rtsp-conntrack
+ * 2007-04-18: Michael Guntsche <mike at it-loops.com>
+ * 			- Port to new NF API
+ */
+
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/inet.h>
+#include <net/tcp.h>
+
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#define NF_NEED_STRTOU32
+#define NF_NEED_NEXTLINE
+#include <linux/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter_mime.h>
+
+#include <linux/ctype.h>
+#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */
+#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#if 0
+#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#else
+#define DEBUGP(fmt, args...)
+#endif
+
+#define MAX_PORTS 8
+static int ports[MAX_PORTS];
+static int num_ports = 0;
+static int max_outstanding = 8;
+static unsigned int setup_timeout = 300;
+
+MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
+MODULE_DESCRIPTION("RTSP connection tracking module");
+MODULE_LICENSE("GPL");
+module_param_array(ports, int, &num_ports, 0400);
+MODULE_PARM_DESC(ports, "port numbers of RTSP servers");
+module_param(max_outstanding, int, 0400);
+MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP  
requests per RTSP session");
+module_param(setup_timeout, int, 0400);
+MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data  
channels");
+
+static char *rtsp_buffer;
+static DEFINE_SPINLOCK(rtsp_buffer_lock);
+
+unsigned int (*nf_nat_rtsp_hook)(struct sk_buff **pskb,
+				 enum ip_conntrack_info ctinfo,
+				 unsigned int matchoff, unsigned int matchlen,struct  
ip_ct_rtsp_expect* prtspexp,
+				 struct nf_conntrack_expect *exp);
+void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct  
nf_conntrack_expect *exp);
+
+EXPORT_SYMBOL_GPL(nf_nat_rtsp_hook);
+
+/*
+ * Max mappings we will allow for one RTSP connection (for RTP, the  
number
+ * of allocated ports is twice this value).  Note that SMIL burns a  
lot of
+ * ports so keep this reasonably high.  If this is too low, you will  
see a
+ * lot of "no free client map entries" messages.
+ */
+#define MAX_PORT_MAPS 16
+
+/*** default port list was here in the masq code: 554, 3030, 4040 ***/
+
+#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr 
+off))) { off++; }
+
+/*
+ * Parse an RTSP packet.
+ *
+ * Returns zero if parsing failed.
+ *
+ * Parameters:
+ *  IN      ptcp        tcp data pointer
+ *  IN      tcplen      tcp data len
+ *  IN/OUT  ptcpoff     points to current tcp offset
+ *  OUT     phdrsoff    set to offset of rtsp headers
+ *  OUT     phdrslen    set to length of rtsp headers
+ *  OUT     pcseqoff    set to offset of CSeq header
+ *  OUT     pcseqlen    set to length of CSeq header
+ */
+static int
+rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff,
+                   uint* phdrsoff, uint* phdrslen,
+                   uint* pcseqoff, uint* pcseqlen,
+                   uint* transoff, uint* translen)
+{
+	uint    entitylen = 0;
+	uint    lineoff;
+	uint    linelen;
+	
+	if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen))
+		return 0;
+	
+	*phdrsoff = *ptcpoff;
+	while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) {
+		if (linelen == 0) {
+			if (entitylen > 0)
+				*ptcpoff += min(entitylen, tcplen - *ptcpoff);
+			break;
+		}
+		if (lineoff+linelen > tcplen) {
+			INFOP("!! overrun !!\n");
+			break;
+		}
+		
+		if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) {
+			*pcseqoff = lineoff;
+			*pcseqlen = linelen;
+		}
+
+		if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) {
+			*transoff = lineoff;
+			*translen = linelen;
+		}
+		
+		if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) {
+			uint off = lineoff+15;
+			SKIP_WSPACE(ptcp+lineoff, linelen, off);
+			nf_strtou32(ptcp+off, &entitylen);
+		}
+	}
+	*phdrslen = (*ptcpoff) - (*phdrsoff);
+	
+	return 1;
+}
+
+/*
+ * Find lo/hi client ports (if any) in transport header
+ * In:
+ *   ptcp, tcplen = packet
+ *   tranoff, tranlen = buffer to search
+ *
+ * Out:
+ *   pport_lo, pport_hi = lo/hi ports (host endian)
+ *
+ * Returns nonzero if any client ports found
+ *
+ * Note: it is valid (and expected) for the client to request multiple
+ * transports, so we need to parse the entire line.
+ */
+static int
+rtsp_parse_transport(char* ptran, uint tranlen,
+                     struct ip_ct_rtsp_expect* prtspexp)
+{
+	int     rc = 0;
+	uint    off = 0;
+	
+	if (tranlen < 10 || !iseol(ptran[tranlen-1]) ||
+	    nf_strncasecmp(ptran, "Transport:", 10) != 0) {
+		INFOP("sanity check failed\n");
+		return 0;
+	}
+	
+	DEBUGP("tran='%.*s'\n", (int)tranlen, ptran);
+	off += 10;
+	SKIP_WSPACE(ptran, tranlen, off);
+	
+	/* Transport: tran;field;field=val,tran;field;field=val,... */
+	while (off < tranlen) {
+		const char* pparamend;
+		uint        nextparamoff;
+		
+		pparamend = memchr(ptran+off, ',', tranlen-off);
+		pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
+		nextparamoff = pparamend-ptran;
+		
+		while (off < nextparamoff) {
+			const char* pfieldend;
+			uint        nextfieldoff;
+			
+			pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+			nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend- 
ptran+1;
+		
+			if (strncmp(ptran+off, "client_port=", 12) == 0) {
+				u_int16_t   port;
+				uint        numlen;
+		
+				off += 12;
+				numlen = nf_strtou16(ptran+off, &port);
+				off += numlen;
+				if (prtspexp->loport != 0 && prtspexp->loport != port)
+					DEBUGP("multiple ports found, port %hu ignored\n", port);
+				else {
+					DEBUGP("lo port found : %hu\n", port);
+					prtspexp->loport = prtspexp->hiport = port;
+					if (ptran[off] == '-') {
+						off++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						prtspexp->pbtype = pb_range;
+						prtspexp->hiport = port;
+						
+						// If we have a range, assume rtp:
+						// loport must be even, hiport must be loport+1
+						if ((prtspexp->loport & 0x0001) != 0 ||
+						    prtspexp->hiport != prtspexp->loport+1) {
+							DEBUGP("incorrect range: %hu-%hu, correcting\n",
+							       prtspexp->loport, prtspexp->hiport);
+							prtspexp->loport &= 0xfffe;
+							prtspexp->hiport = prtspexp->loport+1;
+						}
+					} else if (ptran[off] == '/') {
+						off++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						prtspexp->pbtype = pb_discon;
+						prtspexp->hiport = port;
+					}
+					rc = 1;
+				}
+			}
+			
+			/*
+			 * Note we don't look for the destination parameter here.
+			 * If we are using NAT, the NAT module will handle it.  If not,
+			 * and the client is sending packets elsewhere, the expectation
+			 * will quietly time out.
+			 */
+			
+			off = nextfieldoff;
+		}
+		
+		off = nextparamoff;
+	}
+	
+	return rc;
+}
+
+void expected(struct nf_conn *ct, struct nf_conntrack_expect *exp)
+{
+    if(nf_nat_rtsp_hook_expectfn) {
+        nf_nat_rtsp_hook_expectfn(ct,exp);
+    }
+}
+
+/*** conntrack functions ***/
+
+/* outbound packet: client->server */
+
+static inline int
+help_out(struct sk_buff **pskb, unsigned char *rb_ptr, unsigned int  
datalen,
+                struct nf_conn *ct, enum ip_conntrack_info ctinfo)
+{
+	struct ip_ct_rtsp_expect expinfo;
+	
+	int dir = CTINFO2DIR(ctinfo);   /* = IP_CT_DIR_ORIGINAL */
+	//struct  tcphdr* tcph = (void*)iph + iph->ihl * 4;
+	//uint    tcplen = pktlen - iph->ihl * 4;
+	char*   pdata = rb_ptr;
+	//uint    datalen = tcplen - tcph->doff * 4;
+	uint    dataoff = 0;
+	int ret = NF_ACCEPT;
+	
+	struct nf_conntrack_expect *exp;
+	
+	__be16 be_loport;
+	
+	memset(&expinfo, 0, sizeof(expinfo));
+	
+	while (dataoff < datalen) {
+		uint    cmdoff = dataoff;
+		uint    hdrsoff = 0;
+		uint    hdrslen = 0;
+		uint    cseqoff = 0;
+		uint    cseqlen = 0;
+		uint    transoff = 0;
+		uint    translen = 0;
+		uint    off;
+		
+		if (!rtsp_parse_message(pdata, datalen, &dataoff,
+					&hdrsoff, &hdrslen,
+					&cseqoff, &cseqlen,
+					&transoff, &translen))
+			break;      /* not a valid message */
+		
+		if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0)
+			continue;   /* not a SETUP message */
+		DEBUGP("found a setup message\n");
+
+		off = 0;
+		if(translen) {
+			rtsp_parse_transport(pdata+transoff, translen, &expinfo);
+		}
+
+		if (expinfo.loport == 0) {
+			DEBUGP("no udp transports found\n");
+			continue;   /* no udp transports found */
+		}
+
+		DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n",
+		       (int)expinfo.pbtype, expinfo.loport, expinfo.hiport);
+
+		exp = nf_conntrack_expect_alloc(ct);
+		if (!exp) {
+			ret = NF_DROP;
+			goto out;
+		}
+
+		be_loport = htons(expinfo.loport);
+
+		nf_conntrack_expect_init(exp, ct->tuplehash[!dir].tuple.src.l3num,
+			&ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[! 
dir].tuple.dst.u3,
+			IPPROTO_UDP, NULL, &be_loport);
+
+		exp->master = ct;
+
+		exp->expectfn = expected;
+		exp->flags = 0;
+
+		if (expinfo.pbtype == pb_range) {
+			DEBUGP("Changing expectation mask to handle multiple ports\n");
+			exp->mask.dst.u.udp.port  = 0xfffe;
+		}
+
+		DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
+		       NIPQUAD(exp->tuple.src.u3.ip),
+		       ntohs(exp->tuple.src.u.udp.port),
+		       NIPQUAD(exp->tuple.dst.u3.ip),
+		       ntohs(exp->tuple.dst.u.udp.port));
+
+		if (nf_nat_rtsp_hook)
+			/* pass the request off to the nat helper */
+			ret = nf_nat_rtsp_hook(pskb, ctinfo, hdrsoff, hdrslen, &expinfo,  
exp);
+		else if (nf_conntrack_expect_related(exp) != 0) {
+			INFOP("nf_conntrack_expect_related failed\n");
+			ret  = NF_DROP;
+		}
+		nf_conntrack_expect_put(exp);
+		goto out;
+	}
+out:
+
+	return ret;
+}
+
+
+static inline int
+help_in(struct sk_buff **pskb, size_t pktlen,
+ struct nf_conn* ct, enum ip_conntrack_info ctinfo)
+{
+ return NF_ACCEPT;
+}
+
+static int help(struct sk_buff **pskb, unsigned int protoff,
+		struct nf_conn *ct, enum ip_conntrack_info ctinfo)
+{
+	struct tcphdr _tcph, *th;
+	unsigned int dataoff, datalen;
+	char *rb_ptr;
+	int ret = NF_DROP;
+
+	/* Until there's been traffic both ways, don't look in packets. */
+	if (ctinfo != IP_CT_ESTABLISHED &&
+	    ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
+		DEBUGP("conntrackinfo = %u\n", ctinfo);
+		return NF_ACCEPT;
+	}
+
+	/* Not whole TCP header? */
+	th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4,
+				sizeof(_tcph), &_tcph);
+	if (!th)
+		return NF_ACCEPT;
+
+	/* No data ? */
+	dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4;
+	datalen = (*pskb)->len - dataoff;
+	if (dataoff >= (*pskb)->len)
+		return NF_ACCEPT;
+
+	spin_lock_bh(&rtsp_buffer_lock);
+	rb_ptr = skb_header_pointer(*pskb, dataoff,
+				    (*pskb)->len - dataoff, rtsp_buffer);
+	BUG_ON(rb_ptr == NULL);
+
+#if 0
+	/* Checksum invalid?  Ignore. */
+	/* FIXME: Source route IP option packets --RR */
+	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
+			 csum_partial((char*)tcph, tcplen, 0)))
+	{
+		DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
+		       tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+		return NF_ACCEPT;
+	}
+#endif
+
+	switch (CTINFO2DIR(ctinfo)) {
+	case IP_CT_DIR_ORIGINAL:
+		ret = help_out(pskb, rb_ptr, datalen, ct, ctinfo);
+		break;
+	case IP_CT_DIR_REPLY:
+		DEBUGP("IP_CT_DIR_REPLY\n");
+		/* inbound packet: server->client */
+		ret = NF_ACCEPT;
+		break;
+	}
+
+	spin_unlock_bh(&rtsp_buffer_lock);
+
+	return ret;
+}
+
+static struct nf_conntrack_helper rtsp_helpers[MAX_PORTS];
+static char rtsp_names[MAX_PORTS][10];
+
+/* This function is intentionally _NOT_ defined as __exit */
+static void
+fini(void)
+{
+	int i;
+	for (i = 0; i < num_ports; i++) {
+		DEBUGP("unregistering port %d\n", ports[i]);
+		nf_conntrack_helper_unregister(&rtsp_helpers[i]);
+	}
+	kfree(rtsp_buffer);
+}
+
+static int __init
+init(void)
+{
+	int i, ret;
+	struct nf_conntrack_helper *hlpr;
+	char *tmpname;
+
+	printk("nf_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n");
+
+	if (max_outstanding < 1) {
+		printk("nf_conntrack_rtsp: max_outstanding must be a positive  
integer\n");
+		return -EBUSY;
+	}
+	if (setup_timeout < 0) {
+		printk("nf_conntrack_rtsp: setup_timeout must be a positive integer 
\n");
+		return -EBUSY;
+	}
+
+	rtsp_buffer = kmalloc(65536, GFP_KERNEL);
+	if (!rtsp_buffer)
+		return -ENOMEM;
+
+	/* If no port given, default to standard rtsp port */
+	if (ports[0] == 0) {
+		ports[0] = RTSP_PORT;
+	}
+
+	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+		hlpr = &rtsp_helpers[i];
+		memset(hlpr, 0, sizeof(struct nf_conntrack_helper));
+		hlpr->tuple.src.u.tcp.port = htons(ports[i]);
+		hlpr->tuple.dst.protonum = IPPROTO_TCP;
+		hlpr->mask.src.u.tcp.port = 0xFFFF;
+		hlpr->mask.dst.protonum = 0xFF;
+		hlpr->max_expected = max_outstanding;
+		hlpr->timeout = setup_timeout;
+		hlpr->me = THIS_MODULE;
+		hlpr->help = help;
+
+		tmpname = &rtsp_names[i][0];
+		if (ports[i] == RTSP_PORT) {
+			sprintf(tmpname, "rtsp");
+		} else {
+			sprintf(tmpname, "rtsp-%d", i);
+		}
+		hlpr->name = tmpname;
+
+		DEBUGP("port #%d: %d\n", i, ports[i]);
+
+		ret = nf_conntrack_helper_register(hlpr);
+
+		if (ret) {
+			printk("nf_conntrack_rtsp: ERROR registering port %d\n", ports[i]);
+			fini();
+			return -EBUSY;
+		}
+		num_ports++;
+	}
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);
+
+EXPORT_SYMBOL(nf_nat_rtsp_hook_expectfn);
+

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20 [patchomatic patch]
  2007-04-18 21:34               ` Michael Guntsche
@ 2007-04-21 12:05                 ` Michael Guntsche
  2007-04-21 23:12                   ` Michael Guntsche
  0 siblings, 1 reply; 15+ messages in thread
From: Michael Guntsche @ 2007-04-21 12:05 UTC (permalink / raw)
  To: netfilter-devel


On Apr 18, 2007, at 23:34, Michael Guntsche wrote:

>
> Attached you'll find a patch against 2.6.21-rc7, it compiles and  
> works for me.

This is the same patch but applied against the latest patch-o-matic- 
ng snapshot.
One open issue I already found is that you cannot watch more than one  
stream at the same time from one machine.

Kind regards,
Michael

diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter/nf_conntrack_rtsp.h patch-o- 
matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/include/linux/ 
netfilter/nf_conntrack_rtsp.h
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter/nf_conntrack_rtsp.h	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
include/linux/netfilter/nf_conntrack_rtsp.h	2007-04-21  
13:58:47.000000000 +0200
@@ -0,0 +1,63 @@
+/*
+ * RTSP extension for IP connection tracking.
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_conntrack_irc.h
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+#ifndef _IP_CONNTRACK_RTSP_H
+#define _IP_CONNTRACK_RTSP_H
+
+//#define IP_NF_RTSP_DEBUG 1
+#define IP_NF_RTSP_VERSION "0.6.21"
+
+#ifdef __KERNEL__
+/* port block types */
+typedef enum {
+    pb_single,  /* client_port=x */
+    pb_range,   /* client_port=x-y */
+    pb_discon   /* client_port=x/y (rtspbis) */
+} portblock_t;
+
+/* We record seq number and length of rtsp headers here, all in host  
order. */
+
+/*
+ * This structure is per expected connection.  It is a member of struct
+ * ip_conntrack_expect.  The TCP SEQ for the conntrack expect is stored
+ * there and we are expected to only store the length of the data which
+ * needs replaced.  If a packet contains multiple RTSP messages, we  
create
+ * one expected connection per message.
+ *
+ * We use these variables to mark the entire header block.  This may  
seem
+ * like overkill, but the nature of RTSP requires it.  A header may  
appear
+ * multiple times in a message.  We must treat two Transport headers  
the
+ * same as one Transport header with two entries.
+ */
+struct ip_ct_rtsp_expect
+{
+    u_int32_t   len;        /* length of header block */
+    portblock_t pbtype;     /* Type of port block that was requested */
+    u_int16_t   loport;     /* Port that was requested, low or first */
+    u_int16_t   hiport;     /* Port that was requested, high or  
second */
+#if 0
+    uint        method;     /* RTSP method */
+    uint        cseq;       /* CSeq from request */
+#endif
+};
+
+extern unsigned int (*nf_nat_rtsp_hook)(struct sk_buff **pskb,
+				 enum ip_conntrack_info ctinfo,
+				 unsigned int matchoff, unsigned int matchlen,
+				 struct ip_ct_rtsp_expect *prtspexp,
+				 struct nf_conntrack_expect *exp);
+
+extern void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct  
nf_conntrack_expect *exp);
+
+#define RTSP_PORT   554
+
+#endif /* __KERNEL__ */
+
+#endif /* _IP_CONNTRACK_RTSP_H */
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter_helpers.h patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/include/linux/ 
netfilter_helpers.h
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter_helpers.h	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
include/linux/netfilter_helpers.h	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,133 @@
+/*
+ * Helpers for netfiler modules.  This file provides implementations  
for basic
+ * functions such as strncasecmp(), etc.
+ *
+ * gcc will warn for defined but unused functions, so we only  
include the
+ * functions requested.  The following macros are used:
+ *   NF_NEED_STRNCASECMP        nf_strncasecmp()
+ *   NF_NEED_STRTOU16           nf_strtou16()
+ *   NF_NEED_STRTOU32           nf_strtou32()
+ */
+#ifndef _NETFILTER_HELPERS_H
+#define _NETFILTER_HELPERS_H
+
+/* Only include these functions for kernel code. */
+#ifdef __KERNEL__
+
+#include <linux/ctype.h>
+#define iseol(c) ( (c) == '\r' || (c) == '\n' )
+
+/*
+ * The standard strncasecmp()
+ */
+#ifdef NF_NEED_STRNCASECMP
+static int
+nf_strncasecmp(const char* s1, const char* s2, u_int32_t len)
+{
+    if (s1 == NULL || s2 == NULL)
+    {
+        if (s1 == NULL && s2 == NULL)
+        {
+            return 0;
+        }
+        return (s1 == NULL) ? -1 : 1;
+    }
+    while (len > 0 && tolower(*s1) == tolower(*s2))
+    {
+        len--;
+        s1++;
+        s2++;
+    }
+    return ( (len == 0) ? 0 : (tolower(*s1) - tolower(*s2)) );
+}
+#endif /* NF_NEED_STRNCASECMP */
+
+/*
+ * Parse a string containing a 16-bit unsigned integer.
+ * Returns the number of chars used, or zero if no number is found.
+ */
+#ifdef NF_NEED_STRTOU16
+static int
+nf_strtou16(const char* pbuf, u_int16_t* pval)
+{
+    int n = 0;
+
+    *pval = 0;
+    while (isdigit(pbuf[n]))
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+#endif /* NF_NEED_STRTOU16 */
+
+/*
+ * Parse a string containing a 32-bit unsigned integer.
+ * Returns the number of chars used, or zero if no number is found.
+ */
+#ifdef NF_NEED_STRTOU32
+static int
+nf_strtou32(const char* pbuf, u_int32_t* pval)
+{
+    int n = 0;
+
+    *pval = 0;
+    while (pbuf[n] >= '0' && pbuf[n] <= '9')
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+#endif /* NF_NEED_STRTOU32 */
+
+/*
+ * Given a buffer and length, advance to the next line and mark the  
current
+ * line.
+ */
+#ifdef NF_NEED_NEXTLINE
+static int
+nf_nextline(char* p, uint len, uint* poff, uint* plineoff, uint*  
plinelen)
+{
+    uint    off = *poff;
+    uint    physlen = 0;
+
+    if (off >= len)
+    {
+        return 0;
+    }
+
+    while (p[off] != '\n')
+    {
+        if (len-off <= 1)
+        {
+            return 0;
+        }
+
+        physlen++;
+        off++;
+    }
+
+    /* if we saw a crlf, physlen needs adjusted */
+    if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r')
+    {
+        physlen--;
+    }
+
+    /* advance past the newline */
+    off++;
+
+    *plineoff = *poff;
+    *plinelen = physlen;
+    *poff = off;
+
+    return 1;
+}
+#endif /* NF_NEED_NEXTLINE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _NETFILTER_HELPERS_H */
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter_mime.h patch-o-matic-ng-20070414/ 
patchlets/rtsp-conntrack/linux-2.6.20/include/linux/netfilter_mime.h
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter_mime.h	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
include/linux/netfilter_mime.h	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,89 @@
+/*
+ * MIME functions for netfilter modules.  This file provides  
implementations
+ * for basic MIME parsing.  MIME headers are used in many protocols,  
such as
+ * HTTP, RTSP, SIP, etc.
+ *
+ * gcc will warn for defined but unused functions, so we only  
include the
+ * functions requested.  The following macros are used:
+ *   NF_NEED_MIME_NEXTLINE      nf_mime_nextline()
+ */
+#ifndef _NETFILTER_MIME_H
+#define _NETFILTER_MIME_H
+
+/* Only include these functions for kernel code. */
+#ifdef __KERNEL__
+
+#include <linux/ctype.h>
+
+/*
+ * Given a buffer and length, advance to the next line and mark the  
current
+ * line.  If the current line is empty, *plinelen will be set to  
zero.  If
+ * not, it will be set to the actual line length (including CRLF).
+ *
+ * 'line' in this context means logical line (includes LWS  
continuations).
+ * Returns 1 on success, 0 on failure.
+ */
+#ifdef NF_NEED_MIME_NEXTLINE
+static int
+nf_mime_nextline(char* p, uint len, uint* poff, uint* plineoff,  
uint* plinelen)
+{
+    uint    off = *poff;
+    uint    physlen = 0;
+    int     is_first_line = 1;
+
+    if (off >= len)
+    {
+        return 0;
+    }
+
+    do
+    {
+        while (p[off] != '\n')
+        {
+            if (len-off <= 1)
+            {
+                return 0;
+            }
+
+            physlen++;
+            off++;
+        }
+
+        /* if we saw a crlf, physlen needs adjusted */
+        if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r')
+        {
+            physlen--;
+        }
+
+        /* advance past the newline */
+        off++;
+
+        /* check for an empty line */
+        if (physlen == 0)
+        {
+            break;
+        }
+
+        /* check for colon on the first physical line */
+        if (is_first_line)
+        {
+            is_first_line = 0;
+            if (memchr(p+(*poff), ':', physlen) == NULL)
+            {
+                return 0;
+            }
+        }
+    }
+    while (p[off] == ' ' || p[off] == '\t');
+
+    *plineoff = *poff;
+    *plinelen = (physlen == 0) ? 0 : (off - *poff);
+    *poff = off;
+
+    return 1;
+}
+#endif /* NF_NEED_MIME_NEXTLINE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _NETFILTER_MIME_H */
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/Kconfig.ladd patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/net/ipv4/netfilter/ 
Kconfig.ladd
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/Kconfig.ladd	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/ipv4/netfilter/Kconfig.ladd	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,9 @@
+config NF_CONNTRACK_RTSP
+ tristate "RTSP protocol support"
+ depends on NF_CONNTRACK
+ help
+   Support the RTSP protocol.  This allows UDP transports to be setup
+   properly, including RTP and RDT.
+
+   If you want to compile it as a module, say 'M' here and read
+   Documentation/modules.txt.  If unsure, say 'Y'.
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/Makefile.ladd patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/net/ipv4/netfilter/ 
Makefile.ladd
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/Makefile.ladd	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/ipv4/netfilter/Makefile.ladd	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,2 @@
+obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o
+obj-$(CONFIG_NF_NAT_RTSP) += nf_nat_rtsp.o
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/Makefile.ladd~ patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/net/ipv4/netfilter/ 
Makefile.ladd~
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/Makefile.ladd~	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/ipv4/netfilter/Makefile.ladd~	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,3 @@
+obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o
+obj-$(CONFIG_NF_NAT_RTSP) += nf_nat_rtsp.o
+
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/nf_nat_rtsp.c patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/net/ipv4/netfilter/ 
nf_nat_rtsp.c
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/nf_nat_rtsp.c	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/ipv4/netfilter/nf_nat_rtsp.c	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,496 @@
+/*
+ * RTSP extension for TCP NAT alteration
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_nat_irc.c
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ * Module load syntax:
+ *      insmod nf_nat_rtsp.o ports=port1,port2,...port<MAX_PORTS>
+ *                           stunaddr=<address>
+ *                           destaction=[auto|strip|none]
+ *
+ * If no ports are specified, the default will be port 554 only.
+ *
+ * stunaddr specifies the address used to detect that a client is  
using STUN.
+ * If this address is seen in the destination parameter, it is  
assumed that
+ * the client has already punched a UDP hole in the firewall, so we  
don't
+ * mangle the client_port.  If none is specified, it is  
autodetected.  It
+ * only needs to be set if you have multiple levels of NAT.  It  
should be
+ * set to the external address that the STUN clients detect.  Note  
that in
+ * this case, it will not be possible for clients to use UDP with  
servers
+ * between the NATs.
+ *
+ * If no destaction is specified, auto is used.
+ *   destaction=auto:  strip destination parameter if it is not  
stunaddr.
+ *   destaction=strip: always strip destination parameter (not  
recommended).
+ *   destaction=none:  do not touch destination parameter (not  
recommended).
+ */
+
+#include <linux/module.h>
+#include <net/tcp.h>
+#include <net/netfilter/nf_nat_helper.h>
+#include <net/netfilter/nf_nat_rule.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+
+#include <linux/inet.h>
+#include <linux/ctype.h>
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#include <linux/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter_mime.h>
+
+#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#if 0
+#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#else
+#define DEBUGP(fmt, args...)
+#endif
+
+#define MAX_PORTS       8
+#define DSTACT_AUTO     0
+#define DSTACT_STRIP    1
+#define DSTACT_NONE     2
+
+static char*    stunaddr = NULL;
+static char*    destaction = NULL;
+
+static u_int32_t extip = 0;
+static int       dstact = 0;
+
+MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
+MODULE_DESCRIPTION("RTSP network address translation module");
+MODULE_LICENSE("GPL");
+module_param(stunaddr, charp, 0644);
+MODULE_PARM_DESC(stunaddr, "Address for detecting STUN");
+module_param(destaction, charp, 0644);
+MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/ 
strip/none)");
+
+#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr 
+off))) { off++; }
+
+/*** helper functions ***/
+
+static void
+get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint*  
ptcpdatalen)
+{
+    struct iphdr*   iph  = (struct iphdr*)skb->nh.iph;
+    struct tcphdr*  tcph = (struct tcphdr*)((char*)iph + iph->ihl*4);
+
+    *pptcpdata = (char*)tcph + tcph->doff*4;
+    *ptcpdatalen = ((char*)skb->h.raw + skb->len) - *pptcpdata;
+}
+
+/*** nat functions ***/
+
+/*
+ * Mangle the "Transport:" header:
+ *   - Replace all occurences of "client_port=<spec>"
+ *   - Handle destination parameter
+ *
+ * In:
+ *   ct, ctinfo = conntrack context
+ *   pskb       = packet
+ *   tranoff    = Transport header offset from TCP data
+ *   tranlen    = Transport header length (incl. CRLF)
+ *   rport_lo   = replacement low  port (host endian)
+ *   rport_hi   = replacement high port (host endian)
+ *
+ * Returns packet size difference.
+ *
+ * Assumes that a complete transport header is present, ending with  
CR or LF
+ */
+static int
+rtsp_mangle_tran(enum ip_conntrack_info ctinfo,
+                 struct nf_conntrack_expect* exp,
+								 struct ip_ct_rtsp_expect* prtspexp,
+                 struct sk_buff** pskb, uint tranoff, uint tranlen)
+{
+    char*       ptcp;
+    uint        tcplen;
+    char*       ptran;
+    char        rbuf1[16];      /* Replacement buffer (one port) */
+    uint        rbuf1len;       /* Replacement len (one port) */
+    char        rbufa[16];      /* Replacement buffer (all ports) */
+    uint        rbufalen;       /* Replacement len (all ports) */
+    u_int32_t   newip;
+    u_int16_t   loport, hiport;
+    uint        off = 0;
+    uint        diff;           /* Number of bytes we removed */
+
+    struct nf_conn *ct = exp->master;
+    struct nf_conntrack_tuple t;
+
+    char    szextaddr[15+1];
+    uint    extaddrlen;
+    int     is_stun;
+
+    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+    ptran = ptcp+tranoff;
+
+    if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
+        tranlen < 10 || !iseol(ptran[tranlen-1]) ||
+        nf_strncasecmp(ptran, "Transport:", 10) != 0)
+    {
+        INFOP("sanity check failed\n");
+        return 0;
+    }
+    off += 10;
+    SKIP_WSPACE(ptcp+tranoff, tranlen, off);
+
+    newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
+    t = exp->tuple;
+    t.dst.u3.ip = newip;
+
+    extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD 
(extip))
+                       : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD 
(newip));
+    DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));
+
+    rbuf1len = rbufalen = 0;
+    switch (prtspexp->pbtype)
+    {
+    case pb_single:
+        for (loport = prtspexp->loport; loport != 0; loport++) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(loport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu\n", loport);
+                break;
+            }
+        }
+        if (loport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            rbufalen = sprintf(rbufa, "%hu", loport);
+        }
+        break;
+    case pb_range:
+        for (loport = prtspexp->loport; loport != 0; loport += 2) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(loport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                hiport = loport + ~exp->mask.dst.u.udp.port;
+                DEBUGP("using ports %hu-%hu\n", loport, hiport);
+                break;
+            }
+        }
+        if (loport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1);
+        }
+        break;
+    case pb_discon:
+        for (loport = prtspexp->loport; loport != 0; loport++) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(loport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu (1 of 2)\n", loport);
+                break;
+            }
+        }
+        for (hiport = prtspexp->hiport; hiport != 0; hiport++) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(hiport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu (2 of 2)\n", hiport);
+                break;
+            }
+        }
+        if (loport != 0 && hiport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            if (hiport == loport+1)
+            {
+                rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
+            }
+            else
+            {
+                rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport);
+            }
+        }
+        break;
+    }
+
+    if (rbuf1len == 0)
+    {
+        return 0;   /* cannot get replacement port(s) */
+    }
+
+    /* Transport: tran;field;field=val,tran;field;field=val,... */
+    while (off < tranlen)
+    {
+        uint        saveoff;
+        const char* pparamend;
+        uint        nextparamoff;
+
+        pparamend = memchr(ptran+off, ',', tranlen-off);
+        pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
+        nextparamoff = pparamend-ptcp;
+
+        /*
+         * We pass over each param twice.  On the first pass, we  
look for a
+         * destination= field.  It is handled by the security  
policy.  If it
+         * is present, allowed, and equal to our external address,  
we assume
+         * that STUN is being used and we leave the client_port=  
field alone.
+         */
+        is_stun = 0;
+        saveoff = off;
+        while (off < nextparamoff)
+        {
+            const char* pfieldend;
+            uint        nextfieldoff;
+
+            pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+            nextfieldoff = (pfieldend == NULL) ? nextparamoff :  
pfieldend-ptran+1;
+
+            if (dstact != DSTACT_NONE && strncmp(ptran+off,  
"destination=", 12) == 0)
+            {
+                if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
+                {
+                    is_stun = 1;
+                }
+                if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO  
&& !is_stun))
+                {
+                    diff = nextfieldoff-off;
+                    if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+                                                         off, diff,  
NULL, 0))
+                    {
+                        /* mangle failed, all we can do is bail */
+			nf_conntrack_unexpect_related(exp);
+                        return 0;
+                    }
+                    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+                    ptran = ptcp+tranoff;
+                    tranlen -= diff;
+                    nextparamoff -= diff;
+                    nextfieldoff -= diff;
+                }
+            }
+
+            off = nextfieldoff;
+        }
+        if (is_stun)
+        {
+            continue;
+        }
+        off = saveoff;
+        while (off < nextparamoff)
+        {
+            const char* pfieldend;
+            uint        nextfieldoff;
+
+            pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+            nextfieldoff = (pfieldend == NULL) ? nextparamoff :  
pfieldend-ptran+1;
+
+            if (strncmp(ptran+off, "client_port=", 12) == 0)
+            {
+                u_int16_t   port;
+                uint        numlen;
+                uint        origoff;
+                uint        origlen;
+                char*       rbuf    = rbuf1;
+                uint        rbuflen = rbuf1len;
+
+                off += 12;
+                origoff = (ptran-ptcp)+off;
+                origlen = 0;
+                numlen = nf_strtou16(ptran+off, &port);
+                off += numlen;
+                origlen += numlen;
+                if (port != prtspexp->loport)
+                {
+                    DEBUGP("multiple ports found, port %hu ignored 
\n", port);
+                }
+                else
+                {
+                    if (ptran[off] == '-' || ptran[off] == '/')
+                    {
+                        off++;
+                        origlen++;
+                        numlen = nf_strtou16(ptran+off, &port);
+                        off += numlen;
+                        origlen += numlen;
+                        rbuf = rbufa;
+                        rbuflen = rbufalen;
+                    }
+
+                    /*
+                     * note we cannot just memcpy() if the sizes are  
the same.
+                     * the mangle function does skb resizing, checks  
for a
+                     * cloned skb, and updates the checksums.
+                     *
+                     * parameter 4 below is offset from start of tcp  
data.
+                     */
+                    diff = origlen-rbuflen;
+                    if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+                                              origoff, origlen,  
rbuf, rbuflen))
+                    {
+                        /* mangle failed, all we can do is bail */
+			nf_conntrack_unexpect_related(exp);
+                        return 0;
+                    }
+                    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+                    ptran = ptcp+tranoff;
+                    tranlen -= diff;
+                    nextparamoff -= diff;
+                    nextfieldoff -= diff;
+                }
+            }
+
+            off = nextfieldoff;
+        }
+
+        off = nextparamoff;
+    }
+
+    return 1;
+}
+
+static uint
+help_out(struct sk_buff **pskb, enum ip_conntrack_info ctinfo,
+	 unsigned int matchoff, unsigned int matchlen, struct  
ip_ct_rtsp_expect* prtspexp,
+	 struct nf_conntrack_expect* exp)
+{
+    char*   ptcp;
+    uint    tcplen;
+    uint    hdrsoff;
+    uint    hdrslen;
+    uint    lineoff;
+    uint    linelen;
+    uint    off;
+
+    //struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
+    //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
+
+    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+    hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq);
+    hdrslen = matchlen;
+    off = hdrsoff;
+    DEBUGP("NAT rtsp help_out\n");
+
+    while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff,  
&linelen))
+    {
+        if (linelen == 0)
+        {
+            break;
+        }
+        if (off > hdrsoff+hdrslen)
+        {
+            INFOP("!! overrun !!");
+            break;
+        }
+        DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp 
+lineoff);
+
+        if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0)
+        {
+            uint oldtcplen = tcplen;
+	    DEBUGP("hdr: Transport\n");
+            if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, pskb,  
lineoff, linelen))
+            {
+		DEBUGP("hdr: Transport mangle failed");
+                break;
+            }
+            get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+            hdrslen -= (oldtcplen-tcplen);
+            off -= (oldtcplen-tcplen);
+            lineoff -= (oldtcplen-tcplen);
+            linelen -= (oldtcplen-tcplen);
+            DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp 
+lineoff);
+        }
+    }
+
+    return NF_ACCEPT;
+}
+
+static unsigned int
+help(struct sk_buff **pskb, enum ip_conntrack_info ctinfo,
+     unsigned int matchoff, unsigned int matchlen, struct  
ip_ct_rtsp_expect* prtspexp,
+     struct nf_conntrack_expect* exp)
+{
+    int dir = CTINFO2DIR(ctinfo);
+    int rc = NF_ACCEPT;
+
+    switch (dir)
+    {
+    case IP_CT_DIR_ORIGINAL:
+        rc = help_out(pskb, ctinfo, matchoff, matchlen, prtspexp, exp);
+        break;
+    case IP_CT_DIR_REPLY:
+	DEBUGP("unmangle ! %u\n", ctinfo);
+    	/* XXX: unmangle */
+	rc = NF_ACCEPT;
+        break;
+    }
+    //UNLOCK_BH(&ip_rtsp_lock);
+
+    return rc;
+}
+
+static void expected(struct ip_conntrack* ct, struct  
nf_conntrack_expect *exp)
+{
+    struct ip_nat_multi_range_compat mr;
+    u_int32_t newdstip, newsrcip, newip;
+
+    struct ip_conntrack *master = master_ct(ct);
+
+    newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
+    newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
+    //FIXME (how to port that ?)
+    //code from 2.4 : newip = (HOOK2MANIP(hooknum) ==  
IP_NAT_MANIP_SRC) ? newsrcip : newdstip;
+    newip = newdstip;
+
+    DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.% 
u.%u\n",
+           NIPQUAD(newsrcip), NIPQUAD(newdstip), NIPQUAD(newip));
+
+    mr.rangesize = 1;
+    // We don't want to manip the per-protocol, just the IPs.
+    mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
+    mr.range[0].min_ip = mr.range[0].max_ip = newip;
+
+    nf_nat_setup_info(ct, &mr.range[0], NF_IP_PRE_ROUTING);
+}
+
+
+static void __exit fini(void)
+{
+	nf_nat_rtsp_hook = NULL;
+        nf_nat_rtsp_hook_expectfn = NULL;
+	synchronize_net();
+}
+
+static int __init init(void)
+{
+	printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");
+
+	BUG_ON(nf_nat_rtsp_hook);
+	nf_nat_rtsp_hook = help;
+        nf_nat_rtsp_hook_expectfn = &expected;
+
+	if (stunaddr != NULL)
+		extip = in_aton(stunaddr);
+
+	if (destaction != NULL) {
+	        if (strcmp(destaction, "auto") == 0)
+			dstact = DSTACT_AUTO;
+
+		if (strcmp(destaction, "strip") == 0)
+			dstact = DSTACT_STRIP;
+
+		if (strcmp(destaction, "none") == 0)
+			dstact = DSTACT_NONE;
+	}
+
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/Kconfig.ladd patch-o-matic-ng-20070414/ 
patchlets/rtsp-conntrack/linux-2.6.20/net/netfilter/Kconfig.ladd
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/Kconfig.ladd	1970-01-01 01:00:00.000000000  
+0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/netfilter/Kconfig.ladd	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,9 @@
+config NF_CONNTRACK_RTSP
+ tristate "RTSP protocol support"
+ depends on NF_CONNTRACK
+ help
+   Support the RTSP protocol.  This allows UDP transports to be setup
+   properly, including RTP and RDT.
+
+   If you want to compile it as a module, say 'M' here and read
+   Documentation/modules.txt.  If unsure, say 'Y'.
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/Makefile.ladd patch-o-matic-ng-20070414/ 
patchlets/rtsp-conntrack/linux-2.6.20/net/netfilter/Makefile.ladd
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/Makefile.ladd	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/netfilter/Makefile.ladd	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,2 @@
+obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o
+obj-$(CONFIG_NF_CONNTRACK_RTSP) += nf_conntrack_rtsp.o
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/nf_conntrack_rtsp.c patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/net/netfilter/ 
nf_conntrack_rtsp.c
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/nf_conntrack_rtsp.c	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/netfilter/nf_conntrack_rtsp.c	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,515 @@
+/*
+ * RTSP extension for IP connection tracking
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_conntrack_irc.c
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ * Module load syntax:
+ *   insmod nf_conntrack_rtsp.o ports=port1,port2,...port<MAX_PORTS>
+ *                              max_outstanding=n setup_timeout=secs
+ *
+ * If no ports are specified, the default will be port 554.
+ *
+ * With max_outstanding you can define the maximum number of not yet
+ * answered SETUP requests per RTSP session (default 8).
+ * With setup_timeout you can specify how long the system waits for
+ * an expected data channel (default 300 seconds).
+ *
+ * 2005-02-13: Harald Welte <laforge at netfilter.org>
+ * 	- port to 2.6
+ * 	- update to recent post-2.6.11 api changes
+ * 2006-09-14: Steven Van Acker <deepstar at singularity.be>
+ *      - removed calls to NAT code from conntrack helper: NAT no  
longer needed to use rtsp-conntrack
+ * 2007-04-18: Michael Guntsche <mike at it-loops.com>
+ * 			- Port to new NF API
+ */
+
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/inet.h>
+#include <net/tcp.h>
+
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#define NF_NEED_STRTOU32
+#define NF_NEED_NEXTLINE
+#include <linux/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter_mime.h>
+
+#include <linux/ctype.h>
+#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */
+#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#if 0
+#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#else
+#define DEBUGP(fmt, args...)
+#endif
+
+#define MAX_PORTS 8
+static int ports[MAX_PORTS];
+static int num_ports = 0;
+static int max_outstanding = 8;
+static unsigned int setup_timeout = 300;
+
+MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
+MODULE_DESCRIPTION("RTSP connection tracking module");
+MODULE_LICENSE("GPL");
+module_param_array(ports, int, &num_ports, 0400);
+MODULE_PARM_DESC(ports, "port numbers of RTSP servers");
+module_param(max_outstanding, int, 0400);
+MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP  
requests per RTSP session");
+module_param(setup_timeout, int, 0400);
+MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data  
channels");
+
+static char *rtsp_buffer;
+static DEFINE_SPINLOCK(rtsp_buffer_lock);
+
+unsigned int (*nf_nat_rtsp_hook)(struct sk_buff **pskb,
+				 enum ip_conntrack_info ctinfo,
+				 unsigned int matchoff, unsigned int matchlen,struct  
ip_ct_rtsp_expect* prtspexp,
+				 struct nf_conntrack_expect *exp);
+void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct  
nf_conntrack_expect *exp);
+
+EXPORT_SYMBOL_GPL(nf_nat_rtsp_hook);
+
+/*
+ * Max mappings we will allow for one RTSP connection (for RTP, the  
number
+ * of allocated ports is twice this value).  Note that SMIL burns a  
lot of
+ * ports so keep this reasonably high.  If this is too low, you will  
see a
+ * lot of "no free client map entries" messages.
+ */
+#define MAX_PORT_MAPS 16
+
+/*** default port list was here in the masq code: 554, 3030, 4040 ***/
+
+#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr 
+off))) { off++; }
+
+/*
+ * Parse an RTSP packet.
+ *
+ * Returns zero if parsing failed.
+ *
+ * Parameters:
+ *  IN      ptcp        tcp data pointer
+ *  IN      tcplen      tcp data len
+ *  IN/OUT  ptcpoff     points to current tcp offset
+ *  OUT     phdrsoff    set to offset of rtsp headers
+ *  OUT     phdrslen    set to length of rtsp headers
+ *  OUT     pcseqoff    set to offset of CSeq header
+ *  OUT     pcseqlen    set to length of CSeq header
+ */
+static int
+rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff,
+                   uint* phdrsoff, uint* phdrslen,
+                   uint* pcseqoff, uint* pcseqlen,
+                   uint* transoff, uint* translen)
+{
+	uint    entitylen = 0;
+	uint    lineoff;
+	uint    linelen;
+	
+	if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen))
+		return 0;
+	
+	*phdrsoff = *ptcpoff;
+	while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) {
+		if (linelen == 0) {
+			if (entitylen > 0)
+				*ptcpoff += min(entitylen, tcplen - *ptcpoff);
+			break;
+		}
+		if (lineoff+linelen > tcplen) {
+			INFOP("!! overrun !!\n");
+			break;
+		}
+		
+		if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) {
+			*pcseqoff = lineoff;
+			*pcseqlen = linelen;
+		}
+
+		if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) {
+			*transoff = lineoff;
+			*translen = linelen;
+		}
+		
+		if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) {
+			uint off = lineoff+15;
+			SKIP_WSPACE(ptcp+lineoff, linelen, off);
+			nf_strtou32(ptcp+off, &entitylen);
+		}
+	}
+	*phdrslen = (*ptcpoff) - (*phdrsoff);
+	
+	return 1;
+}
+
+/*
+ * Find lo/hi client ports (if any) in transport header
+ * In:
+ *   ptcp, tcplen = packet
+ *   tranoff, tranlen = buffer to search
+ *
+ * Out:
+ *   pport_lo, pport_hi = lo/hi ports (host endian)
+ *
+ * Returns nonzero if any client ports found
+ *
+ * Note: it is valid (and expected) for the client to request multiple
+ * transports, so we need to parse the entire line.
+ */
+static int
+rtsp_parse_transport(char* ptran, uint tranlen,
+                     struct ip_ct_rtsp_expect* prtspexp)
+{
+	int     rc = 0;
+	uint    off = 0;
+	
+	if (tranlen < 10 || !iseol(ptran[tranlen-1]) ||
+	    nf_strncasecmp(ptran, "Transport:", 10) != 0) {
+		INFOP("sanity check failed\n");
+		return 0;
+	}
+	
+	DEBUGP("tran='%.*s'\n", (int)tranlen, ptran);
+	off += 10;
+	SKIP_WSPACE(ptran, tranlen, off);
+	
+	/* Transport: tran;field;field=val,tran;field;field=val,... */
+	while (off < tranlen) {
+		const char* pparamend;
+		uint        nextparamoff;
+		
+		pparamend = memchr(ptran+off, ',', tranlen-off);
+		pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
+		nextparamoff = pparamend-ptran;
+		
+		while (off < nextparamoff) {
+			const char* pfieldend;
+			uint        nextfieldoff;
+			
+			pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+			nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend- 
ptran+1;
+		
+			if (strncmp(ptran+off, "client_port=", 12) == 0) {
+				u_int16_t   port;
+				uint        numlen;
+		
+				off += 12;
+				numlen = nf_strtou16(ptran+off, &port);
+				off += numlen;
+				if (prtspexp->loport != 0 && prtspexp->loport != port)
+					DEBUGP("multiple ports found, port %hu ignored\n", port);
+				else {
+					DEBUGP("lo port found : %hu\n", port);
+					prtspexp->loport = prtspexp->hiport = port;
+					if (ptran[off] == '-') {
+						off++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						prtspexp->pbtype = pb_range;
+						prtspexp->hiport = port;
+						
+						// If we have a range, assume rtp:
+						// loport must be even, hiport must be loport+1
+						if ((prtspexp->loport & 0x0001) != 0 ||
+						    prtspexp->hiport != prtspexp->loport+1) {
+							DEBUGP("incorrect range: %hu-%hu, correcting\n",
+							       prtspexp->loport, prtspexp->hiport);
+							prtspexp->loport &= 0xfffe;
+							prtspexp->hiport = prtspexp->loport+1;
+						}
+					} else if (ptran[off] == '/') {
+						off++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						prtspexp->pbtype = pb_discon;
+						prtspexp->hiport = port;
+					}
+					rc = 1;
+				}
+			}
+			
+			/*
+			 * Note we don't look for the destination parameter here.
+			 * If we are using NAT, the NAT module will handle it.  If not,
+			 * and the client is sending packets elsewhere, the expectation
+			 * will quietly time out.
+			 */
+			
+			off = nextfieldoff;
+		}
+		
+		off = nextparamoff;
+	}
+	
+	return rc;
+}
+
+void expected(struct nf_conn *ct, struct nf_conntrack_expect *exp)
+{
+    if(nf_nat_rtsp_hook_expectfn) {
+        nf_nat_rtsp_hook_expectfn(ct,exp);
+    }
+}
+
+/*** conntrack functions ***/
+
+/* outbound packet: client->server */
+
+static inline int
+help_out(struct sk_buff **pskb, unsigned char *rb_ptr, unsigned int  
datalen,
+                struct nf_conn *ct, enum ip_conntrack_info ctinfo)
+{
+	struct ip_ct_rtsp_expect expinfo;
+	
+	int dir = CTINFO2DIR(ctinfo);   /* = IP_CT_DIR_ORIGINAL */
+	//struct  tcphdr* tcph = (void*)iph + iph->ihl * 4;
+	//uint    tcplen = pktlen - iph->ihl * 4;
+	char*   pdata = rb_ptr;
+	//uint    datalen = tcplen - tcph->doff * 4;
+	uint    dataoff = 0;
+	int ret = NF_ACCEPT;
+	
+	struct nf_conntrack_expect *exp;
+	
+	__be16 be_loport;
+	
+	memset(&expinfo, 0, sizeof(expinfo));
+	
+	while (dataoff < datalen) {
+		uint    cmdoff = dataoff;
+		uint    hdrsoff = 0;
+		uint    hdrslen = 0;
+		uint    cseqoff = 0;
+		uint    cseqlen = 0;
+		uint    transoff = 0;
+		uint    translen = 0;
+		uint    off;
+		
+		if (!rtsp_parse_message(pdata, datalen, &dataoff,
+					&hdrsoff, &hdrslen,
+					&cseqoff, &cseqlen,
+					&transoff, &translen))
+			break;      /* not a valid message */
+		
+		if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0)
+			continue;   /* not a SETUP message */
+		DEBUGP("found a setup message\n");
+
+		off = 0;
+		if(translen) {
+			rtsp_parse_transport(pdata+transoff, translen, &expinfo);
+		}
+
+		if (expinfo.loport == 0) {
+			DEBUGP("no udp transports found\n");
+			continue;   /* no udp transports found */
+		}
+
+		DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n",
+		       (int)expinfo.pbtype, expinfo.loport, expinfo.hiport);
+
+		exp = nf_conntrack_expect_alloc(ct);
+		if (!exp) {
+			ret = NF_DROP;
+			goto out;
+		}
+
+		be_loport = htons(expinfo.loport);
+
+		nf_conntrack_expect_init(exp, ct->tuplehash[!dir].tuple.src.l3num,
+			&ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[! 
dir].tuple.dst.u3,
+			IPPROTO_UDP, NULL, &be_loport);
+
+		exp->master = ct;
+
+		exp->expectfn = expected;
+		exp->flags = 0;
+
+		if (expinfo.pbtype == pb_range) {
+			DEBUGP("Changing expectation mask to handle multiple ports\n");
+			exp->mask.dst.u.udp.port  = 0xfffe;
+		}
+
+		DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
+		       NIPQUAD(exp->tuple.src.u3.ip),
+		       ntohs(exp->tuple.src.u.udp.port),
+		       NIPQUAD(exp->tuple.dst.u3.ip),
+		       ntohs(exp->tuple.dst.u.udp.port));
+
+		if (nf_nat_rtsp_hook)
+			/* pass the request off to the nat helper */
+			ret = nf_nat_rtsp_hook(pskb, ctinfo, hdrsoff, hdrslen, &expinfo,  
exp);
+		else if (nf_conntrack_expect_related(exp) != 0) {
+			INFOP("nf_conntrack_expect_related failed\n");
+			ret  = NF_DROP;
+		}
+		nf_conntrack_expect_put(exp);
+		goto out;
+	}
+out:
+
+	return ret;
+}
+
+
+static inline int
+help_in(struct sk_buff **pskb, size_t pktlen,
+ struct nf_conn* ct, enum ip_conntrack_info ctinfo)
+{
+ return NF_ACCEPT;
+}
+
+static int help(struct sk_buff **pskb, unsigned int protoff,
+		struct nf_conn *ct, enum ip_conntrack_info ctinfo)
+{
+	struct tcphdr _tcph, *th;
+	unsigned int dataoff, datalen;
+	char *rb_ptr;
+	int ret = NF_DROP;
+
+	/* Until there's been traffic both ways, don't look in packets. */
+	if (ctinfo != IP_CT_ESTABLISHED &&
+	    ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
+		DEBUGP("conntrackinfo = %u\n", ctinfo);
+		return NF_ACCEPT;
+	}
+
+	/* Not whole TCP header? */
+	th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4,
+				sizeof(_tcph), &_tcph);
+	if (!th)
+		return NF_ACCEPT;
+
+	/* No data ? */
+	dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4;
+	datalen = (*pskb)->len - dataoff;
+	if (dataoff >= (*pskb)->len)
+		return NF_ACCEPT;
+
+	spin_lock_bh(&rtsp_buffer_lock);
+	rb_ptr = skb_header_pointer(*pskb, dataoff,
+				    (*pskb)->len - dataoff, rtsp_buffer);
+	BUG_ON(rb_ptr == NULL);
+
+#if 0
+	/* Checksum invalid?  Ignore. */
+	/* FIXME: Source route IP option packets --RR */
+	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
+			 csum_partial((char*)tcph, tcplen, 0)))
+	{
+		DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
+		       tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+		return NF_ACCEPT;
+	}
+#endif
+
+	switch (CTINFO2DIR(ctinfo)) {
+	case IP_CT_DIR_ORIGINAL:
+		ret = help_out(pskb, rb_ptr, datalen, ct, ctinfo);
+		break;
+	case IP_CT_DIR_REPLY:
+		DEBUGP("IP_CT_DIR_REPLY\n");
+		/* inbound packet: server->client */
+		ret = NF_ACCEPT;
+		break;
+	}
+
+	spin_unlock_bh(&rtsp_buffer_lock);
+
+	return ret;
+}
+
+static struct nf_conntrack_helper rtsp_helpers[MAX_PORTS];
+static char rtsp_names[MAX_PORTS][10];
+
+/* This function is intentionally _NOT_ defined as __exit */
+static void
+fini(void)
+{
+	int i;
+	for (i = 0; i < num_ports; i++) {
+		DEBUGP("unregistering port %d\n", ports[i]);
+		nf_conntrack_helper_unregister(&rtsp_helpers[i]);
+	}
+	kfree(rtsp_buffer);
+}
+
+static int __init
+init(void)
+{
+	int i, ret;
+	struct nf_conntrack_helper *hlpr;
+	char *tmpname;
+
+	printk("nf_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n");
+
+	if (max_outstanding < 1) {
+		printk("nf_conntrack_rtsp: max_outstanding must be a positive  
integer\n");
+		return -EBUSY;
+	}
+	if (setup_timeout < 0) {
+		printk("nf_conntrack_rtsp: setup_timeout must be a positive integer 
\n");
+		return -EBUSY;
+	}
+
+	rtsp_buffer = kmalloc(65536, GFP_KERNEL);
+	if (!rtsp_buffer)
+		return -ENOMEM;
+
+	/* If no port given, default to standard rtsp port */
+	if (ports[0] == 0) {
+		ports[0] = RTSP_PORT;
+	}
+
+	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+		hlpr = &rtsp_helpers[i];
+		memset(hlpr, 0, sizeof(struct nf_conntrack_helper));
+		hlpr->tuple.src.u.tcp.port = htons(ports[i]);
+		hlpr->tuple.dst.protonum = IPPROTO_TCP;
+		hlpr->mask.src.u.tcp.port = 0xFFFF;
+		hlpr->mask.dst.protonum = 0xFF;
+		hlpr->max_expected = max_outstanding;
+		hlpr->timeout = setup_timeout;
+		hlpr->me = THIS_MODULE;
+		hlpr->help = help;
+
+		tmpname = &rtsp_names[i][0];
+		if (ports[i] == RTSP_PORT) {
+			sprintf(tmpname, "rtsp");
+		} else {
+			sprintf(tmpname, "rtsp-%d", i);
+		}
+		hlpr->name = tmpname;
+
+		DEBUGP("port #%d: %d\n", i, ports[i]);
+
+		ret = nf_conntrack_helper_register(hlpr);
+
+		if (ret) {
+			printk("nf_conntrack_rtsp: ERROR registering port %d\n", ports[i]);
+			fini();
+			return -EBUSY;
+		}
+		num_ports++;
+	}
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);
+
+EXPORT_SYMBOL(nf_nat_rtsp_hook_expectfn);
+

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20 [patchomatic patch]
  2007-04-21 12:05                 ` RTSP support for kernels >= 2.6.20 [patchomatic patch] Michael Guntsche
@ 2007-04-21 23:12                   ` Michael Guntsche
  2007-04-23 11:42                     ` Patrick McHardy
  0 siblings, 1 reply; 15+ messages in thread
From: Michael Guntsche @ 2007-04-21 23:12 UTC (permalink / raw)
  To: netfilter-devel

	
On Apr 21, 2007, at 14:05, Michael Guntsche wrote:

>
> This is the same patch but applied against the latest patch-o-matic- 
> ng snapshot.
> One open issue I already found is that you cannot watch more than  
> one stream at the same time from one machine.
>

One of the Kconfig entries is wrong in the previous patch, here is  
the fixed version.

diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter/nf_conntrack_rtsp.h patch-o- 
matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/include/linux/ 
netfilter/nf_conntrack_rtsp.h
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter/nf_conntrack_rtsp.h	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
include/linux/netfilter/nf_conntrack_rtsp.h	2007-04-21  
13:58:47.000000000 +0200
@@ -0,0 +1,63 @@
+/*
+ * RTSP extension for IP connection tracking.
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_conntrack_irc.h
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+#ifndef _IP_CONNTRACK_RTSP_H
+#define _IP_CONNTRACK_RTSP_H
+
+//#define IP_NF_RTSP_DEBUG 1
+#define IP_NF_RTSP_VERSION "0.6.21"
+
+#ifdef __KERNEL__
+/* port block types */
+typedef enum {
+    pb_single,  /* client_port=x */
+    pb_range,   /* client_port=x-y */
+    pb_discon   /* client_port=x/y (rtspbis) */
+} portblock_t;
+
+/* We record seq number and length of rtsp headers here, all in host  
order. */
+
+/*
+ * This structure is per expected connection.  It is a member of struct
+ * ip_conntrack_expect.  The TCP SEQ for the conntrack expect is stored
+ * there and we are expected to only store the length of the data which
+ * needs replaced.  If a packet contains multiple RTSP messages, we  
create
+ * one expected connection per message.
+ *
+ * We use these variables to mark the entire header block.  This may  
seem
+ * like overkill, but the nature of RTSP requires it.  A header may  
appear
+ * multiple times in a message.  We must treat two Transport headers  
the
+ * same as one Transport header with two entries.
+ */
+struct ip_ct_rtsp_expect
+{
+    u_int32_t   len;        /* length of header block */
+    portblock_t pbtype;     /* Type of port block that was requested */
+    u_int16_t   loport;     /* Port that was requested, low or first */
+    u_int16_t   hiport;     /* Port that was requested, high or  
second */
+#if 0
+    uint        method;     /* RTSP method */
+    uint        cseq;       /* CSeq from request */
+#endif
+};
+
+extern unsigned int (*nf_nat_rtsp_hook)(struct sk_buff **pskb,
+				 enum ip_conntrack_info ctinfo,
+				 unsigned int matchoff, unsigned int matchlen,
+				 struct ip_ct_rtsp_expect *prtspexp,
+				 struct nf_conntrack_expect *exp);
+
+extern void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct  
nf_conntrack_expect *exp);
+
+#define RTSP_PORT   554
+
+#endif /* __KERNEL__ */
+
+#endif /* _IP_CONNTRACK_RTSP_H */
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter_helpers.h patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/include/linux/ 
netfilter_helpers.h
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter_helpers.h	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
include/linux/netfilter_helpers.h	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,133 @@
+/*
+ * Helpers for netfiler modules.  This file provides implementations  
for basic
+ * functions such as strncasecmp(), etc.
+ *
+ * gcc will warn for defined but unused functions, so we only  
include the
+ * functions requested.  The following macros are used:
+ *   NF_NEED_STRNCASECMP        nf_strncasecmp()
+ *   NF_NEED_STRTOU16           nf_strtou16()
+ *   NF_NEED_STRTOU32           nf_strtou32()
+ */
+#ifndef _NETFILTER_HELPERS_H
+#define _NETFILTER_HELPERS_H
+
+/* Only include these functions for kernel code. */
+#ifdef __KERNEL__
+
+#include <linux/ctype.h>
+#define iseol(c) ( (c) == '\r' || (c) == '\n' )
+
+/*
+ * The standard strncasecmp()
+ */
+#ifdef NF_NEED_STRNCASECMP
+static int
+nf_strncasecmp(const char* s1, const char* s2, u_int32_t len)
+{
+    if (s1 == NULL || s2 == NULL)
+    {
+        if (s1 == NULL && s2 == NULL)
+        {
+            return 0;
+        }
+        return (s1 == NULL) ? -1 : 1;
+    }
+    while (len > 0 && tolower(*s1) == tolower(*s2))
+    {
+        len--;
+        s1++;
+        s2++;
+    }
+    return ( (len == 0) ? 0 : (tolower(*s1) - tolower(*s2)) );
+}
+#endif /* NF_NEED_STRNCASECMP */
+
+/*
+ * Parse a string containing a 16-bit unsigned integer.
+ * Returns the number of chars used, or zero if no number is found.
+ */
+#ifdef NF_NEED_STRTOU16
+static int
+nf_strtou16(const char* pbuf, u_int16_t* pval)
+{
+    int n = 0;
+
+    *pval = 0;
+    while (isdigit(pbuf[n]))
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+#endif /* NF_NEED_STRTOU16 */
+
+/*
+ * Parse a string containing a 32-bit unsigned integer.
+ * Returns the number of chars used, or zero if no number is found.
+ */
+#ifdef NF_NEED_STRTOU32
+static int
+nf_strtou32(const char* pbuf, u_int32_t* pval)
+{
+    int n = 0;
+
+    *pval = 0;
+    while (pbuf[n] >= '0' && pbuf[n] <= '9')
+    {
+        *pval = (*pval * 10) + (pbuf[n] - '0');
+        n++;
+    }
+
+    return n;
+}
+#endif /* NF_NEED_STRTOU32 */
+
+/*
+ * Given a buffer and length, advance to the next line and mark the  
current
+ * line.
+ */
+#ifdef NF_NEED_NEXTLINE
+static int
+nf_nextline(char* p, uint len, uint* poff, uint* plineoff, uint*  
plinelen)
+{
+    uint    off = *poff;
+    uint    physlen = 0;
+
+    if (off >= len)
+    {
+        return 0;
+    }
+
+    while (p[off] != '\n')
+    {
+        if (len-off <= 1)
+        {
+            return 0;
+        }
+
+        physlen++;
+        off++;
+    }
+
+    /* if we saw a crlf, physlen needs adjusted */
+    if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r')
+    {
+        physlen--;
+    }
+
+    /* advance past the newline */
+    off++;
+
+    *plineoff = *poff;
+    *plinelen = physlen;
+    *poff = off;
+
+    return 1;
+}
+#endif /* NF_NEED_NEXTLINE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _NETFILTER_HELPERS_H */
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter_mime.h patch-o-matic-ng-20070414/ 
patchlets/rtsp-conntrack/linux-2.6.20/include/linux/netfilter_mime.h
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/include/linux/netfilter_mime.h	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
include/linux/netfilter_mime.h	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,89 @@
+/*
+ * MIME functions for netfilter modules.  This file provides  
implementations
+ * for basic MIME parsing.  MIME headers are used in many protocols,  
such as
+ * HTTP, RTSP, SIP, etc.
+ *
+ * gcc will warn for defined but unused functions, so we only  
include the
+ * functions requested.  The following macros are used:
+ *   NF_NEED_MIME_NEXTLINE      nf_mime_nextline()
+ */
+#ifndef _NETFILTER_MIME_H
+#define _NETFILTER_MIME_H
+
+/* Only include these functions for kernel code. */
+#ifdef __KERNEL__
+
+#include <linux/ctype.h>
+
+/*
+ * Given a buffer and length, advance to the next line and mark the  
current
+ * line.  If the current line is empty, *plinelen will be set to  
zero.  If
+ * not, it will be set to the actual line length (including CRLF).
+ *
+ * 'line' in this context means logical line (includes LWS  
continuations).
+ * Returns 1 on success, 0 on failure.
+ */
+#ifdef NF_NEED_MIME_NEXTLINE
+static int
+nf_mime_nextline(char* p, uint len, uint* poff, uint* plineoff,  
uint* plinelen)
+{
+    uint    off = *poff;
+    uint    physlen = 0;
+    int     is_first_line = 1;
+
+    if (off >= len)
+    {
+        return 0;
+    }
+
+    do
+    {
+        while (p[off] != '\n')
+        {
+            if (len-off <= 1)
+            {
+                return 0;
+            }
+
+            physlen++;
+            off++;
+        }
+
+        /* if we saw a crlf, physlen needs adjusted */
+        if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r')
+        {
+            physlen--;
+        }
+
+        /* advance past the newline */
+        off++;
+
+        /* check for an empty line */
+        if (physlen == 0)
+        {
+            break;
+        }
+
+        /* check for colon on the first physical line */
+        if (is_first_line)
+        {
+            is_first_line = 0;
+            if (memchr(p+(*poff), ':', physlen) == NULL)
+            {
+                return 0;
+            }
+        }
+    }
+    while (p[off] == ' ' || p[off] == '\t');
+
+    *plineoff = *poff;
+    *plinelen = (physlen == 0) ? 0 : (off - *poff);
+    *poff = off;
+
+    return 1;
+}
+#endif /* NF_NEED_MIME_NEXTLINE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _NETFILTER_MIME_H */
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/Kconfig.ladd patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/net/ipv4/netfilter/ 
Kconfig.ladd
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/Kconfig.ladd	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/ipv4/netfilter/Kconfig.ladd	2007-04-22 01:07:11.000000000 +0200
@@ -0,0 +1,5 @@
+config NF_NAT_RTSP
+ tristate
+ depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT
+ default NF_NAT && NF_CONNTRACK_RTSP
+
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/Makefile.ladd patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/net/ipv4/netfilter/ 
Makefile.ladd
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/Makefile.ladd	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/ipv4/netfilter/Makefile.ladd	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,2 @@
+obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o
+obj-$(CONFIG_NF_NAT_RTSP) += nf_nat_rtsp.o
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/nf_nat_rtsp.c patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/net/ipv4/netfilter/ 
nf_nat_rtsp.c
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/ipv4/netfilter/nf_nat_rtsp.c	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/ipv4/netfilter/nf_nat_rtsp.c	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,496 @@
+/*
+ * RTSP extension for TCP NAT alteration
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_nat_irc.c
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ * Module load syntax:
+ *      insmod nf_nat_rtsp.o ports=port1,port2,...port<MAX_PORTS>
+ *                           stunaddr=<address>
+ *                           destaction=[auto|strip|none]
+ *
+ * If no ports are specified, the default will be port 554 only.
+ *
+ * stunaddr specifies the address used to detect that a client is  
using STUN.
+ * If this address is seen in the destination parameter, it is  
assumed that
+ * the client has already punched a UDP hole in the firewall, so we  
don't
+ * mangle the client_port.  If none is specified, it is  
autodetected.  It
+ * only needs to be set if you have multiple levels of NAT.  It  
should be
+ * set to the external address that the STUN clients detect.  Note  
that in
+ * this case, it will not be possible for clients to use UDP with  
servers
+ * between the NATs.
+ *
+ * If no destaction is specified, auto is used.
+ *   destaction=auto:  strip destination parameter if it is not  
stunaddr.
+ *   destaction=strip: always strip destination parameter (not  
recommended).
+ *   destaction=none:  do not touch destination parameter (not  
recommended).
+ */
+
+#include <linux/module.h>
+#include <net/tcp.h>
+#include <net/netfilter/nf_nat_helper.h>
+#include <net/netfilter/nf_nat_rule.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+
+#include <linux/inet.h>
+#include <linux/ctype.h>
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#include <linux/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter_mime.h>
+
+#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#if 0
+#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#else
+#define DEBUGP(fmt, args...)
+#endif
+
+#define MAX_PORTS       8
+#define DSTACT_AUTO     0
+#define DSTACT_STRIP    1
+#define DSTACT_NONE     2
+
+static char*    stunaddr = NULL;
+static char*    destaction = NULL;
+
+static u_int32_t extip = 0;
+static int       dstact = 0;
+
+MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
+MODULE_DESCRIPTION("RTSP network address translation module");
+MODULE_LICENSE("GPL");
+module_param(stunaddr, charp, 0644);
+MODULE_PARM_DESC(stunaddr, "Address for detecting STUN");
+module_param(destaction, charp, 0644);
+MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/ 
strip/none)");
+
+#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr 
+off))) { off++; }
+
+/*** helper functions ***/
+
+static void
+get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint*  
ptcpdatalen)
+{
+    struct iphdr*   iph  = (struct iphdr*)skb->nh.iph;
+    struct tcphdr*  tcph = (struct tcphdr*)((char*)iph + iph->ihl*4);
+
+    *pptcpdata = (char*)tcph + tcph->doff*4;
+    *ptcpdatalen = ((char*)skb->h.raw + skb->len) - *pptcpdata;
+}
+
+/*** nat functions ***/
+
+/*
+ * Mangle the "Transport:" header:
+ *   - Replace all occurences of "client_port=<spec>"
+ *   - Handle destination parameter
+ *
+ * In:
+ *   ct, ctinfo = conntrack context
+ *   pskb       = packet
+ *   tranoff    = Transport header offset from TCP data
+ *   tranlen    = Transport header length (incl. CRLF)
+ *   rport_lo   = replacement low  port (host endian)
+ *   rport_hi   = replacement high port (host endian)
+ *
+ * Returns packet size difference.
+ *
+ * Assumes that a complete transport header is present, ending with  
CR or LF
+ */
+static int
+rtsp_mangle_tran(enum ip_conntrack_info ctinfo,
+                 struct nf_conntrack_expect* exp,
+								 struct ip_ct_rtsp_expect* prtspexp,
+                 struct sk_buff** pskb, uint tranoff, uint tranlen)
+{
+    char*       ptcp;
+    uint        tcplen;
+    char*       ptran;
+    char        rbuf1[16];      /* Replacement buffer (one port) */
+    uint        rbuf1len;       /* Replacement len (one port) */
+    char        rbufa[16];      /* Replacement buffer (all ports) */
+    uint        rbufalen;       /* Replacement len (all ports) */
+    u_int32_t   newip;
+    u_int16_t   loport, hiport;
+    uint        off = 0;
+    uint        diff;           /* Number of bytes we removed */
+
+    struct nf_conn *ct = exp->master;
+    struct nf_conntrack_tuple t;
+
+    char    szextaddr[15+1];
+    uint    extaddrlen;
+    int     is_stun;
+
+    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+    ptran = ptcp+tranoff;
+
+    if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
+        tranlen < 10 || !iseol(ptran[tranlen-1]) ||
+        nf_strncasecmp(ptran, "Transport:", 10) != 0)
+    {
+        INFOP("sanity check failed\n");
+        return 0;
+    }
+    off += 10;
+    SKIP_WSPACE(ptcp+tranoff, tranlen, off);
+
+    newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
+    t = exp->tuple;
+    t.dst.u3.ip = newip;
+
+    extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD 
(extip))
+                       : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD 
(newip));
+    DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));
+
+    rbuf1len = rbufalen = 0;
+    switch (prtspexp->pbtype)
+    {
+    case pb_single:
+        for (loport = prtspexp->loport; loport != 0; loport++) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(loport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu\n", loport);
+                break;
+            }
+        }
+        if (loport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            rbufalen = sprintf(rbufa, "%hu", loport);
+        }
+        break;
+    case pb_range:
+        for (loport = prtspexp->loport; loport != 0; loport += 2) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(loport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                hiport = loport + ~exp->mask.dst.u.udp.port;
+                DEBUGP("using ports %hu-%hu\n", loport, hiport);
+                break;
+            }
+        }
+        if (loport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1);
+        }
+        break;
+    case pb_discon:
+        for (loport = prtspexp->loport; loport != 0; loport++) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(loport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu (1 of 2)\n", loport);
+                break;
+            }
+        }
+        for (hiport = prtspexp->hiport; hiport != 0; hiport++) /*  
XXX: improper wrap? */
+        {
+            t.dst.u.udp.port = htons(hiport);
+            if (nf_conntrack_expect_related(exp) == 0)
+            {
+                DEBUGP("using port %hu (2 of 2)\n", hiport);
+                break;
+            }
+        }
+        if (loport != 0 && hiport != 0)
+        {
+            rbuf1len = sprintf(rbuf1, "%hu", loport);
+            if (hiport == loport+1)
+            {
+                rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
+            }
+            else
+            {
+                rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport);
+            }
+        }
+        break;
+    }
+
+    if (rbuf1len == 0)
+    {
+        return 0;   /* cannot get replacement port(s) */
+    }
+
+    /* Transport: tran;field;field=val,tran;field;field=val,... */
+    while (off < tranlen)
+    {
+        uint        saveoff;
+        const char* pparamend;
+        uint        nextparamoff;
+
+        pparamend = memchr(ptran+off, ',', tranlen-off);
+        pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
+        nextparamoff = pparamend-ptcp;
+
+        /*
+         * We pass over each param twice.  On the first pass, we  
look for a
+         * destination= field.  It is handled by the security  
policy.  If it
+         * is present, allowed, and equal to our external address,  
we assume
+         * that STUN is being used and we leave the client_port=  
field alone.
+         */
+        is_stun = 0;
+        saveoff = off;
+        while (off < nextparamoff)
+        {
+            const char* pfieldend;
+            uint        nextfieldoff;
+
+            pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+            nextfieldoff = (pfieldend == NULL) ? nextparamoff :  
pfieldend-ptran+1;
+
+            if (dstact != DSTACT_NONE && strncmp(ptran+off,  
"destination=", 12) == 0)
+            {
+                if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
+                {
+                    is_stun = 1;
+                }
+                if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO  
&& !is_stun))
+                {
+                    diff = nextfieldoff-off;
+                    if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+                                                         off, diff,  
NULL, 0))
+                    {
+                        /* mangle failed, all we can do is bail */
+			nf_conntrack_unexpect_related(exp);
+                        return 0;
+                    }
+                    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+                    ptran = ptcp+tranoff;
+                    tranlen -= diff;
+                    nextparamoff -= diff;
+                    nextfieldoff -= diff;
+                }
+            }
+
+            off = nextfieldoff;
+        }
+        if (is_stun)
+        {
+            continue;
+        }
+        off = saveoff;
+        while (off < nextparamoff)
+        {
+            const char* pfieldend;
+            uint        nextfieldoff;
+
+            pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+            nextfieldoff = (pfieldend == NULL) ? nextparamoff :  
pfieldend-ptran+1;
+
+            if (strncmp(ptran+off, "client_port=", 12) == 0)
+            {
+                u_int16_t   port;
+                uint        numlen;
+                uint        origoff;
+                uint        origlen;
+                char*       rbuf    = rbuf1;
+                uint        rbuflen = rbuf1len;
+
+                off += 12;
+                origoff = (ptran-ptcp)+off;
+                origlen = 0;
+                numlen = nf_strtou16(ptran+off, &port);
+                off += numlen;
+                origlen += numlen;
+                if (port != prtspexp->loport)
+                {
+                    DEBUGP("multiple ports found, port %hu ignored 
\n", port);
+                }
+                else
+                {
+                    if (ptran[off] == '-' || ptran[off] == '/')
+                    {
+                        off++;
+                        origlen++;
+                        numlen = nf_strtou16(ptran+off, &port);
+                        off += numlen;
+                        origlen += numlen;
+                        rbuf = rbufa;
+                        rbuflen = rbufalen;
+                    }
+
+                    /*
+                     * note we cannot just memcpy() if the sizes are  
the same.
+                     * the mangle function does skb resizing, checks  
for a
+                     * cloned skb, and updates the checksums.
+                     *
+                     * parameter 4 below is offset from start of tcp  
data.
+                     */
+                    diff = origlen-rbuflen;
+                    if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo,
+                                              origoff, origlen,  
rbuf, rbuflen))
+                    {
+                        /* mangle failed, all we can do is bail */
+			nf_conntrack_unexpect_related(exp);
+                        return 0;
+                    }
+                    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+                    ptran = ptcp+tranoff;
+                    tranlen -= diff;
+                    nextparamoff -= diff;
+                    nextfieldoff -= diff;
+                }
+            }
+
+            off = nextfieldoff;
+        }
+
+        off = nextparamoff;
+    }
+
+    return 1;
+}
+
+static uint
+help_out(struct sk_buff **pskb, enum ip_conntrack_info ctinfo,
+	 unsigned int matchoff, unsigned int matchlen, struct  
ip_ct_rtsp_expect* prtspexp,
+	 struct nf_conntrack_expect* exp)
+{
+    char*   ptcp;
+    uint    tcplen;
+    uint    hdrsoff;
+    uint    hdrslen;
+    uint    lineoff;
+    uint    linelen;
+    uint    off;
+
+    //struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
+    //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
+
+    get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+    hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq);
+    hdrslen = matchlen;
+    off = hdrsoff;
+    DEBUGP("NAT rtsp help_out\n");
+
+    while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff,  
&linelen))
+    {
+        if (linelen == 0)
+        {
+            break;
+        }
+        if (off > hdrsoff+hdrslen)
+        {
+            INFOP("!! overrun !!");
+            break;
+        }
+        DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp 
+lineoff);
+
+        if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0)
+        {
+            uint oldtcplen = tcplen;
+	    DEBUGP("hdr: Transport\n");
+            if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, pskb,  
lineoff, linelen))
+            {
+		DEBUGP("hdr: Transport mangle failed");
+                break;
+            }
+            get_skb_tcpdata(*pskb, &ptcp, &tcplen);
+            hdrslen -= (oldtcplen-tcplen);
+            off -= (oldtcplen-tcplen);
+            lineoff -= (oldtcplen-tcplen);
+            linelen -= (oldtcplen-tcplen);
+            DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp 
+lineoff);
+        }
+    }
+
+    return NF_ACCEPT;
+}
+
+static unsigned int
+help(struct sk_buff **pskb, enum ip_conntrack_info ctinfo,
+     unsigned int matchoff, unsigned int matchlen, struct  
ip_ct_rtsp_expect* prtspexp,
+     struct nf_conntrack_expect* exp)
+{
+    int dir = CTINFO2DIR(ctinfo);
+    int rc = NF_ACCEPT;
+
+    switch (dir)
+    {
+    case IP_CT_DIR_ORIGINAL:
+        rc = help_out(pskb, ctinfo, matchoff, matchlen, prtspexp, exp);
+        break;
+    case IP_CT_DIR_REPLY:
+	DEBUGP("unmangle ! %u\n", ctinfo);
+    	/* XXX: unmangle */
+	rc = NF_ACCEPT;
+        break;
+    }
+    //UNLOCK_BH(&ip_rtsp_lock);
+
+    return rc;
+}
+
+static void expected(struct ip_conntrack* ct, struct  
nf_conntrack_expect *exp)
+{
+    struct ip_nat_multi_range_compat mr;
+    u_int32_t newdstip, newsrcip, newip;
+
+    struct ip_conntrack *master = master_ct(ct);
+
+    newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
+    newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
+    //FIXME (how to port that ?)
+    //code from 2.4 : newip = (HOOK2MANIP(hooknum) ==  
IP_NAT_MANIP_SRC) ? newsrcip : newdstip;
+    newip = newdstip;
+
+    DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.% 
u.%u\n",
+           NIPQUAD(newsrcip), NIPQUAD(newdstip), NIPQUAD(newip));
+
+    mr.rangesize = 1;
+    // We don't want to manip the per-protocol, just the IPs.
+    mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
+    mr.range[0].min_ip = mr.range[0].max_ip = newip;
+
+    nf_nat_setup_info(ct, &mr.range[0], NF_IP_PRE_ROUTING);
+}
+
+
+static void __exit fini(void)
+{
+	nf_nat_rtsp_hook = NULL;
+        nf_nat_rtsp_hook_expectfn = NULL;
+	synchronize_net();
+}
+
+static int __init init(void)
+{
+	printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");
+
+	BUG_ON(nf_nat_rtsp_hook);
+	nf_nat_rtsp_hook = help;
+        nf_nat_rtsp_hook_expectfn = &expected;
+
+	if (stunaddr != NULL)
+		extip = in_aton(stunaddr);
+
+	if (destaction != NULL) {
+	        if (strcmp(destaction, "auto") == 0)
+			dstact = DSTACT_AUTO;
+
+		if (strcmp(destaction, "strip") == 0)
+			dstact = DSTACT_STRIP;
+
+		if (strcmp(destaction, "none") == 0)
+			dstact = DSTACT_NONE;
+	}
+
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/Kconfig.ladd patch-o-matic-ng-20070414/ 
patchlets/rtsp-conntrack/linux-2.6.20/net/netfilter/Kconfig.ladd
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/Kconfig.ladd	1970-01-01 01:00:00.000000000  
+0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/netfilter/Kconfig.ladd	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,9 @@
+config NF_CONNTRACK_RTSP
+ tristate "RTSP protocol support"
+ depends on NF_CONNTRACK
+ help
+   Support the RTSP protocol.  This allows UDP transports to be setup
+   properly, including RTP and RDT.
+
+   If you want to compile it as a module, say 'M' here and read
+   Documentation/modules.txt.  If unsure, say 'Y'.
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/Makefile.ladd patch-o-matic-ng-20070414/ 
patchlets/rtsp-conntrack/linux-2.6.20/net/netfilter/Makefile.ladd
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/Makefile.ladd	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/netfilter/Makefile.ladd	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,2 @@
+obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o
+obj-$(CONFIG_NF_CONNTRACK_RTSP) += nf_conntrack_rtsp.o
diff -urN patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/nf_conntrack_rtsp.c patch-o-matic- 
ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/net/netfilter/ 
nf_conntrack_rtsp.c
--- patch-o-matic-ng-20070414/patchlets/rtsp-conntrack.orig/ 
linux-2.6.20/net/netfilter/nf_conntrack_rtsp.c	1970-01-01  
01:00:00.000000000 +0100
+++ patch-o-matic-ng-20070414/patchlets/rtsp-conntrack/linux-2.6.20/ 
net/netfilter/nf_conntrack_rtsp.c	2007-04-21 13:58:47.000000000 +0200
@@ -0,0 +1,515 @@
+/*
+ * RTSP extension for IP connection tracking
+ * (C) 2003 by Tom Marshall <tmarshall at real.com>
+ * based on ip_conntrack_irc.c
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ * Module load syntax:
+ *   insmod nf_conntrack_rtsp.o ports=port1,port2,...port<MAX_PORTS>
+ *                              max_outstanding=n setup_timeout=secs
+ *
+ * If no ports are specified, the default will be port 554.
+ *
+ * With max_outstanding you can define the maximum number of not yet
+ * answered SETUP requests per RTSP session (default 8).
+ * With setup_timeout you can specify how long the system waits for
+ * an expected data channel (default 300 seconds).
+ *
+ * 2005-02-13: Harald Welte <laforge at netfilter.org>
+ * 	- port to 2.6
+ * 	- update to recent post-2.6.11 api changes
+ * 2006-09-14: Steven Van Acker <deepstar at singularity.be>
+ *      - removed calls to NAT code from conntrack helper: NAT no  
longer needed to use rtsp-conntrack
+ * 2007-04-18: Michael Guntsche <mike at it-loops.com>
+ * 			- Port to new NF API
+ */
+
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/inet.h>
+#include <net/tcp.h>
+
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#define NF_NEED_STRTOU32
+#define NF_NEED_NEXTLINE
+#include <linux/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter_mime.h>
+
+#include <linux/ctype.h>
+#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */
+#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#if 0
+#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt,  
__FILE__, __FUNCTION__ , ## args)
+#else
+#define DEBUGP(fmt, args...)
+#endif
+
+#define MAX_PORTS 8
+static int ports[MAX_PORTS];
+static int num_ports = 0;
+static int max_outstanding = 8;
+static unsigned int setup_timeout = 300;
+
+MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
+MODULE_DESCRIPTION("RTSP connection tracking module");
+MODULE_LICENSE("GPL");
+module_param_array(ports, int, &num_ports, 0400);
+MODULE_PARM_DESC(ports, "port numbers of RTSP servers");
+module_param(max_outstanding, int, 0400);
+MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP  
requests per RTSP session");
+module_param(setup_timeout, int, 0400);
+MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data  
channels");
+
+static char *rtsp_buffer;
+static DEFINE_SPINLOCK(rtsp_buffer_lock);
+
+unsigned int (*nf_nat_rtsp_hook)(struct sk_buff **pskb,
+				 enum ip_conntrack_info ctinfo,
+				 unsigned int matchoff, unsigned int matchlen,struct  
ip_ct_rtsp_expect* prtspexp,
+				 struct nf_conntrack_expect *exp);
+void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct  
nf_conntrack_expect *exp);
+
+EXPORT_SYMBOL_GPL(nf_nat_rtsp_hook);
+
+/*
+ * Max mappings we will allow for one RTSP connection (for RTP, the  
number
+ * of allocated ports is twice this value).  Note that SMIL burns a  
lot of
+ * ports so keep this reasonably high.  If this is too low, you will  
see a
+ * lot of "no free client map entries" messages.
+ */
+#define MAX_PORT_MAPS 16
+
+/*** default port list was here in the masq code: 554, 3030, 4040 ***/
+
+#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr 
+off))) { off++; }
+
+/*
+ * Parse an RTSP packet.
+ *
+ * Returns zero if parsing failed.
+ *
+ * Parameters:
+ *  IN      ptcp        tcp data pointer
+ *  IN      tcplen      tcp data len
+ *  IN/OUT  ptcpoff     points to current tcp offset
+ *  OUT     phdrsoff    set to offset of rtsp headers
+ *  OUT     phdrslen    set to length of rtsp headers
+ *  OUT     pcseqoff    set to offset of CSeq header
+ *  OUT     pcseqlen    set to length of CSeq header
+ */
+static int
+rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff,
+                   uint* phdrsoff, uint* phdrslen,
+                   uint* pcseqoff, uint* pcseqlen,
+                   uint* transoff, uint* translen)
+{
+	uint    entitylen = 0;
+	uint    lineoff;
+	uint    linelen;
+	
+	if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen))
+		return 0;
+	
+	*phdrsoff = *ptcpoff;
+	while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) {
+		if (linelen == 0) {
+			if (entitylen > 0)
+				*ptcpoff += min(entitylen, tcplen - *ptcpoff);
+			break;
+		}
+		if (lineoff+linelen > tcplen) {
+			INFOP("!! overrun !!\n");
+			break;
+		}
+		
+		if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) {
+			*pcseqoff = lineoff;
+			*pcseqlen = linelen;
+		}
+
+		if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) {
+			*transoff = lineoff;
+			*translen = linelen;
+		}
+		
+		if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) {
+			uint off = lineoff+15;
+			SKIP_WSPACE(ptcp+lineoff, linelen, off);
+			nf_strtou32(ptcp+off, &entitylen);
+		}
+	}
+	*phdrslen = (*ptcpoff) - (*phdrsoff);
+	
+	return 1;
+}
+
+/*
+ * Find lo/hi client ports (if any) in transport header
+ * In:
+ *   ptcp, tcplen = packet
+ *   tranoff, tranlen = buffer to search
+ *
+ * Out:
+ *   pport_lo, pport_hi = lo/hi ports (host endian)
+ *
+ * Returns nonzero if any client ports found
+ *
+ * Note: it is valid (and expected) for the client to request multiple
+ * transports, so we need to parse the entire line.
+ */
+static int
+rtsp_parse_transport(char* ptran, uint tranlen,
+                     struct ip_ct_rtsp_expect* prtspexp)
+{
+	int     rc = 0;
+	uint    off = 0;
+	
+	if (tranlen < 10 || !iseol(ptran[tranlen-1]) ||
+	    nf_strncasecmp(ptran, "Transport:", 10) != 0) {
+		INFOP("sanity check failed\n");
+		return 0;
+	}
+	
+	DEBUGP("tran='%.*s'\n", (int)tranlen, ptran);
+	off += 10;
+	SKIP_WSPACE(ptran, tranlen, off);
+	
+	/* Transport: tran;field;field=val,tran;field;field=val,... */
+	while (off < tranlen) {
+		const char* pparamend;
+		uint        nextparamoff;
+		
+		pparamend = memchr(ptran+off, ',', tranlen-off);
+		pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
+		nextparamoff = pparamend-ptran;
+		
+		while (off < nextparamoff) {
+			const char* pfieldend;
+			uint        nextfieldoff;
+			
+			pfieldend = memchr(ptran+off, ';', nextparamoff-off);
+			nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend- 
ptran+1;
+		
+			if (strncmp(ptran+off, "client_port=", 12) == 0) {
+				u_int16_t   port;
+				uint        numlen;
+		
+				off += 12;
+				numlen = nf_strtou16(ptran+off, &port);
+				off += numlen;
+				if (prtspexp->loport != 0 && prtspexp->loport != port)
+					DEBUGP("multiple ports found, port %hu ignored\n", port);
+				else {
+					DEBUGP("lo port found : %hu\n", port);
+					prtspexp->loport = prtspexp->hiport = port;
+					if (ptran[off] == '-') {
+						off++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						prtspexp->pbtype = pb_range;
+						prtspexp->hiport = port;
+						
+						// If we have a range, assume rtp:
+						// loport must be even, hiport must be loport+1
+						if ((prtspexp->loport & 0x0001) != 0 ||
+						    prtspexp->hiport != prtspexp->loport+1) {
+							DEBUGP("incorrect range: %hu-%hu, correcting\n",
+							       prtspexp->loport, prtspexp->hiport);
+							prtspexp->loport &= 0xfffe;
+							prtspexp->hiport = prtspexp->loport+1;
+						}
+					} else if (ptran[off] == '/') {
+						off++;
+						numlen = nf_strtou16(ptran+off, &port);
+						off += numlen;
+						prtspexp->pbtype = pb_discon;
+						prtspexp->hiport = port;
+					}
+					rc = 1;
+				}
+			}
+			
+			/*
+			 * Note we don't look for the destination parameter here.
+			 * If we are using NAT, the NAT module will handle it.  If not,
+			 * and the client is sending packets elsewhere, the expectation
+			 * will quietly time out.
+			 */
+			
+			off = nextfieldoff;
+		}
+		
+		off = nextparamoff;
+	}
+	
+	return rc;
+}
+
+void expected(struct nf_conn *ct, struct nf_conntrack_expect *exp)
+{
+    if(nf_nat_rtsp_hook_expectfn) {
+        nf_nat_rtsp_hook_expectfn(ct,exp);
+    }
+}
+
+/*** conntrack functions ***/
+
+/* outbound packet: client->server */
+
+static inline int
+help_out(struct sk_buff **pskb, unsigned char *rb_ptr, unsigned int  
datalen,
+                struct nf_conn *ct, enum ip_conntrack_info ctinfo)
+{
+	struct ip_ct_rtsp_expect expinfo;
+	
+	int dir = CTINFO2DIR(ctinfo);   /* = IP_CT_DIR_ORIGINAL */
+	//struct  tcphdr* tcph = (void*)iph + iph->ihl * 4;
+	//uint    tcplen = pktlen - iph->ihl * 4;
+	char*   pdata = rb_ptr;
+	//uint    datalen = tcplen - tcph->doff * 4;
+	uint    dataoff = 0;
+	int ret = NF_ACCEPT;
+	
+	struct nf_conntrack_expect *exp;
+	
+	__be16 be_loport;
+	
+	memset(&expinfo, 0, sizeof(expinfo));
+	
+	while (dataoff < datalen) {
+		uint    cmdoff = dataoff;
+		uint    hdrsoff = 0;
+		uint    hdrslen = 0;
+		uint    cseqoff = 0;
+		uint    cseqlen = 0;
+		uint    transoff = 0;
+		uint    translen = 0;
+		uint    off;
+		
+		if (!rtsp_parse_message(pdata, datalen, &dataoff,
+					&hdrsoff, &hdrslen,
+					&cseqoff, &cseqlen,
+					&transoff, &translen))
+			break;      /* not a valid message */
+		
+		if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0)
+			continue;   /* not a SETUP message */
+		DEBUGP("found a setup message\n");
+
+		off = 0;
+		if(translen) {
+			rtsp_parse_transport(pdata+transoff, translen, &expinfo);
+		}
+
+		if (expinfo.loport == 0) {
+			DEBUGP("no udp transports found\n");
+			continue;   /* no udp transports found */
+		}
+
+		DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n",
+		       (int)expinfo.pbtype, expinfo.loport, expinfo.hiport);
+
+		exp = nf_conntrack_expect_alloc(ct);
+		if (!exp) {
+			ret = NF_DROP;
+			goto out;
+		}
+
+		be_loport = htons(expinfo.loport);
+
+		nf_conntrack_expect_init(exp, ct->tuplehash[!dir].tuple.src.l3num,
+			&ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[! 
dir].tuple.dst.u3,
+			IPPROTO_UDP, NULL, &be_loport);
+
+		exp->master = ct;
+
+		exp->expectfn = expected;
+		exp->flags = 0;
+
+		if (expinfo.pbtype == pb_range) {
+			DEBUGP("Changing expectation mask to handle multiple ports\n");
+			exp->mask.dst.u.udp.port  = 0xfffe;
+		}
+
+		DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
+		       NIPQUAD(exp->tuple.src.u3.ip),
+		       ntohs(exp->tuple.src.u.udp.port),
+		       NIPQUAD(exp->tuple.dst.u3.ip),
+		       ntohs(exp->tuple.dst.u.udp.port));
+
+		if (nf_nat_rtsp_hook)
+			/* pass the request off to the nat helper */
+			ret = nf_nat_rtsp_hook(pskb, ctinfo, hdrsoff, hdrslen, &expinfo,  
exp);
+		else if (nf_conntrack_expect_related(exp) != 0) {
+			INFOP("nf_conntrack_expect_related failed\n");
+			ret  = NF_DROP;
+		}
+		nf_conntrack_expect_put(exp);
+		goto out;
+	}
+out:
+
+	return ret;
+}
+
+
+static inline int
+help_in(struct sk_buff **pskb, size_t pktlen,
+ struct nf_conn* ct, enum ip_conntrack_info ctinfo)
+{
+ return NF_ACCEPT;
+}
+
+static int help(struct sk_buff **pskb, unsigned int protoff,
+		struct nf_conn *ct, enum ip_conntrack_info ctinfo)
+{
+	struct tcphdr _tcph, *th;
+	unsigned int dataoff, datalen;
+	char *rb_ptr;
+	int ret = NF_DROP;
+
+	/* Until there's been traffic both ways, don't look in packets. */
+	if (ctinfo != IP_CT_ESTABLISHED &&
+	    ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
+		DEBUGP("conntrackinfo = %u\n", ctinfo);
+		return NF_ACCEPT;
+	}
+
+	/* Not whole TCP header? */
+	th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4,
+				sizeof(_tcph), &_tcph);
+	if (!th)
+		return NF_ACCEPT;
+
+	/* No data ? */
+	dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4;
+	datalen = (*pskb)->len - dataoff;
+	if (dataoff >= (*pskb)->len)
+		return NF_ACCEPT;
+
+	spin_lock_bh(&rtsp_buffer_lock);
+	rb_ptr = skb_header_pointer(*pskb, dataoff,
+				    (*pskb)->len - dataoff, rtsp_buffer);
+	BUG_ON(rb_ptr == NULL);
+
+#if 0
+	/* Checksum invalid?  Ignore. */
+	/* FIXME: Source route IP option packets --RR */
+	if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
+			 csum_partial((char*)tcph, tcplen, 0)))
+	{
+		DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
+		       tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+		return NF_ACCEPT;
+	}
+#endif
+
+	switch (CTINFO2DIR(ctinfo)) {
+	case IP_CT_DIR_ORIGINAL:
+		ret = help_out(pskb, rb_ptr, datalen, ct, ctinfo);
+		break;
+	case IP_CT_DIR_REPLY:
+		DEBUGP("IP_CT_DIR_REPLY\n");
+		/* inbound packet: server->client */
+		ret = NF_ACCEPT;
+		break;
+	}
+
+	spin_unlock_bh(&rtsp_buffer_lock);
+
+	return ret;
+}
+
+static struct nf_conntrack_helper rtsp_helpers[MAX_PORTS];
+static char rtsp_names[MAX_PORTS][10];
+
+/* This function is intentionally _NOT_ defined as __exit */
+static void
+fini(void)
+{
+	int i;
+	for (i = 0; i < num_ports; i++) {
+		DEBUGP("unregistering port %d\n", ports[i]);
+		nf_conntrack_helper_unregister(&rtsp_helpers[i]);
+	}
+	kfree(rtsp_buffer);
+}
+
+static int __init
+init(void)
+{
+	int i, ret;
+	struct nf_conntrack_helper *hlpr;
+	char *tmpname;
+
+	printk("nf_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n");
+
+	if (max_outstanding < 1) {
+		printk("nf_conntrack_rtsp: max_outstanding must be a positive  
integer\n");
+		return -EBUSY;
+	}
+	if (setup_timeout < 0) {
+		printk("nf_conntrack_rtsp: setup_timeout must be a positive integer 
\n");
+		return -EBUSY;
+	}
+
+	rtsp_buffer = kmalloc(65536, GFP_KERNEL);
+	if (!rtsp_buffer)
+		return -ENOMEM;
+
+	/* If no port given, default to standard rtsp port */
+	if (ports[0] == 0) {
+		ports[0] = RTSP_PORT;
+	}
+
+	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
+		hlpr = &rtsp_helpers[i];
+		memset(hlpr, 0, sizeof(struct nf_conntrack_helper));
+		hlpr->tuple.src.u.tcp.port = htons(ports[i]);
+		hlpr->tuple.dst.protonum = IPPROTO_TCP;
+		hlpr->mask.src.u.tcp.port = 0xFFFF;
+		hlpr->mask.dst.protonum = 0xFF;
+		hlpr->max_expected = max_outstanding;
+		hlpr->timeout = setup_timeout;
+		hlpr->me = THIS_MODULE;
+		hlpr->help = help;
+
+		tmpname = &rtsp_names[i][0];
+		if (ports[i] == RTSP_PORT) {
+			sprintf(tmpname, "rtsp");
+		} else {
+			sprintf(tmpname, "rtsp-%d", i);
+		}
+		hlpr->name = tmpname;
+
+		DEBUGP("port #%d: %d\n", i, ports[i]);
+
+		ret = nf_conntrack_helper_register(hlpr);
+
+		if (ret) {
+			printk("nf_conntrack_rtsp: ERROR registering port %d\n", ports[i]);
+			fini();
+			return -EBUSY;
+		}
+		num_ports++;
+	}
+	return 0;
+}
+
+module_init(init);
+module_exit(fini);
+
+EXPORT_SYMBOL(nf_nat_rtsp_hook_expectfn);
+

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20 [patchomatic patch]
  2007-04-21 23:12                   ` Michael Guntsche
@ 2007-04-23 11:42                     ` Patrick McHardy
  2007-05-02 20:04                       ` Michael Guntsche
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick McHardy @ 2007-04-23 11:42 UTC (permalink / raw)
  To: Michael Guntsche; +Cc: netfilter-devel

Michael Guntsche wrote:
> One of the Kconfig entries is wrong in the previous patch, here is  the
> fixed version.


Would you be interested in maintaining this in an external repository?
That should save both of us work, you don't need to submit patches,
I don't need to apply them :)

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20 [patchomatic patch]
  2007-04-23 11:42                     ` Patrick McHardy
@ 2007-05-02 20:04                       ` Michael Guntsche
  2007-05-03 11:00                         ` Patrick McHardy
  0 siblings, 1 reply; 15+ messages in thread
From: Michael Guntsche @ 2007-05-02 20:04 UTC (permalink / raw)
  To: netfilter-devel


On Apr 23, 2007, at 13:42, Patrick McHardy wrote:

> Would you be interested in maintaining this in an external repository?
> That should save both of us work, you don't need to submit patches,
> I don't need to apply them :)

Just a heads up.
The patch is available at http://mike.it-loops.com/rtsp
I'll try to keep it up to date with newer kernel versions.

Kind regards,
Michael

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20 [patchomatic patch]
  2007-05-02 20:04                       ` Michael Guntsche
@ 2007-05-03 11:00                         ` Patrick McHardy
  2007-05-03 17:18                           ` Michael Guntsche
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick McHardy @ 2007-05-03 11:00 UTC (permalink / raw)
  To: Michael Guntsche; +Cc: netfilter-devel

Michael Guntsche wrote:
> 
> On Apr 23, 2007, at 13:42, Patrick McHardy wrote:
> 
>> Would you be interested in maintaining this in an external repository?
>> That should save both of us work, you don't need to submit patches,
>> I don't need to apply them :)
> 
> 
> Just a heads up.
> The patch is available at http://mike.it-loops.com/rtsp
> I'll try to keep it up to date with newer kernel versions.


If you'd add it in pom external repository format I could add it to
the pomng sources.list.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: RTSP support for kernels >= 2.6.20 [patchomatic patch]
  2007-05-03 11:00                         ` Patrick McHardy
@ 2007-05-03 17:18                           ` Michael Guntsche
  0 siblings, 0 replies; 15+ messages in thread
From: Michael Guntsche @ 2007-05-03 17:18 UTC (permalink / raw)
  To: netfilter-devel

Hi Patrick,
	
On May 3, 2007, at 13:00, Patrick McHardy wrote:

> If you'd add it in pom external repository format I could add it to
> the pomng sources.list.
>

I prepared a repository but I am not sure how to package it. Should I  
add it to the existing rtsp-conntrack module, which does not work  
right now, since --download blames that rtsp-conntrack is not  
external, or should I leave the old module as is and add a new one  
called nf-rtsp-conntrack wich requires a kernel >= 2.6.20?

Michael

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2007-05-03 17:18 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-04-15 15:31 RTSP support for kernels >= 2.6.20 Michael Guntsche
2007-04-15 19:48 ` Patrick McHardy
2007-04-16 13:15   ` Michael Guntsche
2007-04-16 14:32     ` Michael Guntsche
2007-04-16 14:48       ` Patrick McHardy
2007-04-16 15:53         ` Michael Guntsche
2007-04-17 22:25           ` Michael Guntsche
2007-04-18  4:33             ` Patrick McHardy
2007-04-18 21:34               ` Michael Guntsche
2007-04-21 12:05                 ` RTSP support for kernels >= 2.6.20 [patchomatic patch] Michael Guntsche
2007-04-21 23:12                   ` Michael Guntsche
2007-04-23 11:42                     ` Patrick McHardy
2007-05-02 20:04                       ` Michael Guntsche
2007-05-03 11:00                         ` Patrick McHardy
2007-05-03 17:18                           ` Michael Guntsche

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.