text: --- /dev/null
  +++ b/extensions/rtsp/Kbuild
  @@ -0,0 +1,4 @@
  +# -*- Makefile -*-
  +
  +obj-m += nf_nat_rtsp.o
  +obj-m += nf_conntrack_rtsp.o
  --- /dev/null
  +++ b/extensions/rtsp/netfilter_helpers.h
  @@ -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 */
  --- /dev/null
  +++ b/extensions/rtsp/netfilter_mime.h
  @@ -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 */
  --- /dev/null
  +++ b/extensions/rtsp/nf_conntrack_rtsp.c
  @@ -0,0 +1,576 @@
  +/*
  + * RTSP extension for IP connection tracking
  + * (C) 2003 by Tom Marshall <tmarshall at real.com>
  + *
  + * 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
  + * 2013-03-04: Il'inykh Sergey <sergeyi at inango-sw.com>. Inango Systems Ltd
  + *	- fixed rtcp nat mapping and other port mapping fixes
  + *	- simple TEARDOWN request handling
  + *	- codestyle fixes and other less significant bug fixes 
  + *
  + * 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).
  + *
  + */
  +
  +#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 "nf_conntrack_rtsp.h"
  +
  +#define NF_NEED_STRNCASECMP
  +#define NF_NEED_STRTOU16
  +#define NF_NEED_STRTOU32
  +#define NF_NEED_NEXTLINE
  +#include "netfilter_helpers.h"
  +#define NF_NEED_MIME_NEXTLINE
  +#include "netfilter_mime.h"
  +
  +#include <linux/ctype.h>
  +
  +#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);
  +
  +static struct nf_conntrack_expect_policy rtsp_exp_policy;
  +
  +unsigned int (*nf_nat_rtsp_hook)(struct sk_buff *skb,
  +				 enum ip_conntrack_info ctinfo,
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +				 unsigned int protoff,
  +#endif
  +				 unsigned int matchoff, unsigned int matchlen,
  +				 struct ip_ct_rtsp_expect* prtspexp,
  +				 struct nf_conntrack_expect *rtp_exp,
  +				 struct nf_conntrack_expect *rtcp_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) {
  +			pr_info("!! 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) {
  +		pr_info("sanity check failed\n");
  +		return 0;
  +	}
  +	
  +	pr_debug("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)
  +					pr_debug("multiple ports found, port %hu ignored\n", port);
  +				else {
  +					pr_debug("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) {
  +							pr_debug("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;
  +}
  +
  +
  +/*** conntrack functions ***/
  +
  +/* outbound packet: client->server */
  +
  +static inline int
  +help_out(struct sk_buff *skb, unsigned char *rb_ptr, unsigned int datalen,
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +	 struct nf_conn *ct, enum ip_conntrack_info ctinfo,
  +	 unsigned int protoff)
  +#else
  +	 struct nf_conn *ct, enum ip_conntrack_info ctinfo)
  +#endif
  +{
  +	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 *rtp_exp;
  +	struct nf_conntrack_expect *rtcp_exp = NULL;
  +	
  +	__be16 be_loport;
  +	__be16 be_hiport;
  +	
  +	typeof(nf_nat_rtsp_hook) nf_nat_rtsp;
  +
  +	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, "TEARDOWN ", 9) == 0) {
  +			pr_debug("teardown handled\n");
  +			nf_ct_remove_expectations(ct); /* FIXME must be session id aware */
  +			break;
  +		}
  +
  +		if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0)
  +			continue;   /* not a SETUP message */
  +
  +		pr_debug("found a setup message\n");
  +
  +		off = 0;
  +		if(translen)
  +			rtsp_parse_transport(pdata+transoff, translen, &expinfo);
  +
  +		if (expinfo.loport == 0) {
  +			pr_debug("no udp transports found\n");
  +			continue;   /* no udp transports found */
  +		}
  +
  +		pr_debug("udp transport found, ports=(%d,%hu,%hu)\n",
  +			 (int)expinfo.pbtype, expinfo.loport, expinfo.hiport);
  +
  +
  +		be_loport = htons(expinfo.loport);
  +
  +		rtp_exp = nf_ct_expect_alloc(ct);
  +		if (rtp_exp == NULL) {
  +			ret = NF_DROP;
  +			goto out;
  +		}
  +
  +		nf_ct_expect_init(rtp_exp, NF_CT_EXPECT_CLASS_DEFAULT,
  +				  nf_ct_l3num(ct),
  +				  NULL, /* &ct->tuplehash[!dir].tuple.src.u3, */
  +				  &ct->tuplehash[!dir].tuple.dst.u3,
  +				  IPPROTO_UDP, NULL, &be_loport);
  +
  +		rtp_exp->flags = 0;
  +
  +		if (expinfo.pbtype == pb_range) {
  +			pr_debug("setup expectation for rtcp\n");
  +
  +			be_hiport = htons(expinfo.hiport);
  +			rtcp_exp = nf_ct_expect_alloc(ct);
  +			if (rtcp_exp == NULL) {
  +				ret = NF_DROP;
  +				goto out1;
  +			}
  +
  +			nf_ct_expect_init(rtcp_exp, NF_CT_EXPECT_CLASS_DEFAULT,
  +					  nf_ct_l3num(ct),
  +					  NULL, /* &ct->tuplehash[!dir].tuple.src.u3, */
  +					  &ct->tuplehash[!dir].tuple.dst.u3,
  +					  IPPROTO_UDP, NULL, &be_hiport);
  +
  +			rtcp_exp->flags = 0;
  +
  +			pr_debug("expect_related %pI4:%u-%u-%pI4:%u-%u\n",
  +				   &rtp_exp->tuple.src.u3.ip,
  +				   ntohs(rtp_exp->tuple.src.u.udp.port),
  +				   ntohs(rtcp_exp->tuple.src.u.udp.port),
  +				   &rtp_exp->tuple.dst.u3.ip,
  +				   ntohs(rtp_exp->tuple.dst.u.udp.port),
  +				   ntohs(rtcp_exp->tuple.dst.u.udp.port));
  +		} else {
  +			pr_debug("expect_related %pI4:%u-%pI4:%u\n",
  +					&rtp_exp->tuple.src.u3.ip,
  +					ntohs(rtp_exp->tuple.src.u.udp.port),
  +					&rtp_exp->tuple.dst.u3.ip,
  +					ntohs(rtp_exp->tuple.dst.u.udp.port));
  +		}
  +
  +		nf_nat_rtsp = rcu_dereference(nf_nat_rtsp_hook);
  +		if (nf_nat_rtsp && ct->status & IPS_NAT_MASK)
  +			/* pass the request off to the nat helper */
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +			ret = nf_nat_rtsp(skb, ctinfo, protoff, hdrsoff, hdrslen,
  +					  &expinfo, rtp_exp, rtcp_exp);
  +#else
  +			ret = nf_nat_rtsp(skb, ctinfo, hdrsoff, hdrslen,
  +					  &expinfo, rtp_exp, rtcp_exp);
  +#endif
  +		else {
  +			if (nf_ct_expect_related(rtp_exp) == 0) {
  +				if (rtcp_exp && nf_ct_expect_related(rtcp_exp) != 0) {
  +					nf_ct_unexpect_related(rtp_exp);
  +					pr_info("nf_conntrack_expect_related failed for rtcp\n");
  +					ret = NF_DROP;
  +				}
  +			} else {
  +				pr_info("nf_conntrack_expect_related failed for rtp\n");
  +				ret = NF_DROP;
  +			}
  +		}
  +		if (rtcp_exp) {
  +			nf_ct_expect_put(rtcp_exp);
  +		}
  +out1:
  +		nf_ct_expect_put(rtp_exp);
  +		goto out;
  +	}
  +out:
  +
  +	return ret;
  +}
  +
  +
  +static inline int
  +help_in(struct sk_buff *skb, size_t pktlen,
  +	struct nf_conn* ct, enum ip_conntrack_info ctinfo)
  +{
  +	return NF_ACCEPT;
  +}
  +
  +static int help(struct sk_buff *skb, 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) {
  +		pr_debug("conntrackinfo = %u\n", ctinfo);
  +		return NF_ACCEPT;
  +	} 
  +
  +	/* Not whole TCP header? */
  +	th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
  +
  +	if (!th)
  +		return NF_ACCEPT;
  +   
  +	/* No data ? */
  +	dataoff = protoff + th->doff*4;
  +	datalen = skb->len - dataoff;
  +	if (dataoff >= skb->len)
  +		return NF_ACCEPT;
  +
  +	spin_lock_bh(&rtsp_buffer_lock);
  +	rb_ptr = skb_header_pointer(skb, dataoff,
  +				    skb->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:
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +		ret = help_out(skb, rb_ptr, datalen, ct, ctinfo, protoff);
  +#else
  +		ret = help_out(skb, rb_ptr, datalen, ct, ctinfo);
  +#endif
  +		break;
  +	case IP_CT_DIR_REPLY:
  +		pr_debug("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++) {
  +		pr_debug("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_exp_policy.max_expected = max_outstanding;
  +	rtsp_exp_policy.timeout = setup_timeout;
  +	
  +	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;
  +		num_ports = 1;
  +	}
  +
  +	for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
  +		hlpr = &rtsp_helpers[i];
  +		memset(hlpr, 0, sizeof(struct nf_conntrack_helper));
  +		hlpr->tuple.src.l3num = AF_INET;
  +		hlpr->tuple.src.u.tcp.port = htons(ports[i]);
  +		hlpr->tuple.dst.protonum = IPPROTO_TCP;
  +		hlpr->expect_policy = &rtsp_exp_policy;
  +		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);
  +		}
  +
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
  +		strlcpy(hlpr->name, tmpname, sizeof(hlpr->name));
  +#else
  +		hlpr->name = tmpname;
  +#endif
  +		pr_debug("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;
  +		}
  +	}
  +	return 0;
  +}
  +
  +module_init(init);
  +module_exit(fini);
  --- /dev/null
  +++ b/extensions/rtsp/nf_conntrack_rtsp.h
  @@ -0,0 +1,72 @@
  +/*
  + * 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.
  + *
  + * 2013-03-04: Il'inykh Sergey <sergeyi at inango-sw.com>. Inango Systems Ltd
  + *	- conditional compilation for kernel 3.7
  + *	- port mapping improvements
  +*/
  +#ifndef _IP_CONNTRACK_RTSP_H
  +#define _IP_CONNTRACK_RTSP_H
  +
  +#include <linux/version.h>
  +
  +//#define IP_NF_RTSP_DEBUG 1
  +#define IP_NF_RTSP_VERSION "0.7"
  +
  +#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 *skb,
  +					enum ip_conntrack_info ctinfo,
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +					unsigned int protoff,
  +#endif
  +					unsigned int matchoff,
  +					unsigned int matchlen,
  +					struct ip_ct_rtsp_expect *prtspexp,
  +					struct nf_conntrack_expect *rtp_exp,
  +					struct nf_conntrack_expect *rtcp_exp);
  +
  +#define RTSP_PORT   554
  +
  +#endif /* __KERNEL__ */
  +
  +#endif /* _IP_CONNTRACK_RTSP_H */
  --- /dev/null
  +++ b/extensions/rtsp/nf_nat_rtsp.c
  @@ -0,0 +1,617 @@
  +/*
  + * RTSP extension for TCP NAT alteration
  + * (C) 2003 by Tom Marshall <tmarshall at real.com>
  + *
  + * 2013-03-04: Il'inykh Sergey <sergeyi at inango-sw.com>. Inango Systems Ltd
  + *	- fixed rtcp nat mapping and other port mapping fixes
  + *	- fixed system hard lock because of bug in the parser
  + *	- codestyle fixes and less significant fixes
  + *
  + * 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 <linux/version.h>
  +#include <net/tcp.h>
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +# include <net/netfilter/nf_nat.h>
  +#else
  +# include <net/netfilter/nf_nat_rule.h>
  +#endif
  +#include <net/netfilter/nf_nat_helper.h>
  +#include "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 "netfilter_helpers.h"
  +#define NF_NEED_MIME_NEXTLINE
  +#include "netfilter_mime.h"
  +
  +#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;
  +
  +static void nf_nat_rtsp_expected(struct nf_conn* ct, struct nf_conntrack_expect *exp);
  +
  +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  = ip_hdr(skb);
  +	struct tcphdr*  tcph = (void *)iph + ip_hdrlen(skb);
  +
  +	*pptcpdata = (char*)tcph +  tcph->doff*4;
  +	*ptcpdatalen = ((char*)skb_transport_header(skb) + skb->len) - *pptcpdata;
  +}
  +
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +/* copy of sip_sprintf_addr */
  +static int rtsp_sprintf_addr(const struct nf_conn *ct, char *buffer,
  +			     const union nf_inet_addr *addr, bool delim)
  +{
  +	if (nf_ct_l3num(ct) == NFPROTO_IPV4) {
  +		return sprintf(buffer, "%pI4", &addr->ip);
  +	} else {
  +		if (delim)
  +			return sprintf(buffer, "[%pI6c]", &addr->ip6);
  +		else
  +			return sprintf(buffer, "%pI6c", &addr->ip6);
  +	}
  +}
  +#endif
  +
  +/*** nat functions ***/
  +
  +/*
  + * Mangle the "Transport:" header:
  + *   - Replace all occurences of "client_port=<spec>"
  + *   - Handle destination parameter
  + *
  + * In:
  + *   ct, ctinfo = conntrack context
  + *   skb        = 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,
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +		 unsigned int protoff,
  +#endif
  +		 struct nf_conntrack_expect* rtp_exp,
  +		 struct nf_conntrack_expect* rtcp_exp,
  +		 struct ip_ct_rtsp_expect* prtspexp,
  +		 struct sk_buff* skb, 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) */
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +	union nf_inet_addr newip;
  +#else
  +	u_int32_t  newip;
  +#endif
  +	u_int16_t loport, hiport;
  +	uint      off = 0;
  +	uint      diff;		   /* Number of bytes we removed */
  +
  +	struct nf_conn *ct = rtp_exp->master;
  +	/* struct nf_conn *ct = nf_ct_get(skb, &ctinfo); */
  +	struct nf_conntrack_tuple *rtp_t;
  +
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +	char szextaddr[INET6_ADDRSTRLEN];
  +#else
  +	char szextaddr[INET_ADDRSTRLEN];
  +#endif
  +	uint extaddrlen;
  +	int  is_stun;
  +
  +	get_skb_tcpdata(skb, &ptcp, &tcplen);
  +	ptran = ptcp+tranoff;
  +
  +	if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
  +	    tranlen < 10 || !iseol(ptran[tranlen-1]) ||
  +	    nf_strncasecmp(ptran, "Transport:", 10) != 0) {
  +		pr_info("sanity check failed\n");
  +		return 0;
  +	}
  +	off += 10;
  +	SKIP_WSPACE(ptcp+tranoff, tranlen, off);
  +
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +	newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3;
  +	rtp_t = &rtp_exp->tuple;
  +	rtp_t->dst.u3 = newip;
  +	if (rtcp_exp) {
  +		rtcp_exp->tuple.dst.u3 = newip;
  +	}
  +	extaddrlen = rtsp_sprintf_addr(ct, szextaddr, &newip, true); // FIXME handle extip
  +	pr_debug("stunaddr=%s (auto)\n", szextaddr);
  +#else
  +	newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
  +	rtp_t = &rtp_exp->tuple;
  +	rtp_t->dst.u3.ip = newip;
  +	if (rtcp_exp) {
  +		rtcp_exp->tuple.dst.u3.ip = newip;
  +	}
  +	extaddrlen = extip ? sprintf(szextaddr, "%pI4", &extip)
  +			   : sprintf(szextaddr, "%pI4", &newip);
  +	pr_debug("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));
  +#endif
  +	hiport = 0;
  +	rbuf1len = rbufalen = 0;
  +	switch (prtspexp->pbtype) {
  +	case pb_single:
  +		for (loport = prtspexp->loport; loport != 0; loport++) { /* XXX: improper wrap? */
  +			rtp_t->dst.u.udp.port = htons(loport);
  +			if (nf_ct_expect_related(rtp_exp) == 0) {
  +				pr_debug("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? */
  +			rtp_t->dst.u.udp.port = htons(loport);
  +			if (nf_ct_expect_related(rtp_exp) != 0) {
  +				continue;
  +			}
  +			hiport = loport + 1;
  +			rtcp_exp->tuple.dst.u.udp.port = htons(hiport);
  +			if (nf_ct_expect_related(rtcp_exp) != 0) {
  +				nf_ct_unexpect_related(rtp_exp);
  +				continue;
  +			}
  +
  +			/* FIXME: invalid print in case of ipv6 */
  +			pr_debug("nat expect_related %pI4:%u-%u-%pI4:%u-%u\n",
  +				 &rtp_exp->tuple.src.u3.ip,
  +				 ntohs(rtp_exp->tuple.src.u.udp.port),
  +				 ntohs(rtcp_exp->tuple.src.u.udp.port),
  +				 &rtp_exp->tuple.dst.u3.ip,
  +				 ntohs(rtp_exp->tuple.dst.u.udp.port),
  +				 ntohs(rtcp_exp->tuple.dst.u.udp.port));
  +			break;
  +		}
  +		if (loport != 0) {
  +			rbuf1len = sprintf(rbuf1, "%hu", loport);
  +			rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
  +		}
  +		break;
  +	case pb_discon:
  +		for (loport = prtspexp->loport; loport != 0; loport++) { /* XXX: improper wrap? */
  +			rtp_t->dst.u.udp.port = htons(loport);
  +			if (nf_ct_expect_related(rtp_exp) == 0) {
  +				pr_debug("using port %hu (1 of 2)\n", loport);
  +				break;
  +			}
  +		}
  +		for (hiport = prtspexp->hiport; hiport != 0; hiport++) { /* XXX: improper wrap? */
  +			rtp_t->dst.u.udp.port = htons(hiport);
  +			if (nf_ct_expect_related(rtp_exp) == 0) {
  +				pr_debug("using port %hu (2 of 2)\n", hiport);
  +				break;
  +			}
  +		}
  +		if (loport != 0 && hiport != 0) {
  +			rbuf1len = sprintf(rbuf1, "%hu", loport);
  +			rbufalen = sprintf(rbufa, hiport == loport+1 ?
  +					   "%hu-%hu":"%hu/%hu", loport, hiport);
  +		}
  +		break;
  +	}
  +
  +	if (rbuf1len == 0)
  +		return 0;   /* cannot get replacement port(s) */
  +
  +	/* Transport: tran;field;field=val,tran;field;field=val,...
  +	   `off` is set to the start of Transport value from start of line
  +	*/
  +	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-ptran;
  +
  +		/*
  +		 * 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)) {
  +					uint dstoff = (ptran-ptcp)+off;
  +					uint dstlen = nextfieldoff-off;
  +					char* pdstrep = NULL;
  +					uint dstreplen = 0;
  +					diff = dstlen;
  +					if (dstact == DSTACT_AUTO && !is_stun) {
  +						pr_debug("RTSP: replace dst addr\n");
  +						dstoff += 12;
  +						dstlen -= 13;
  +						pdstrep = szextaddr;
  +						dstreplen = extaddrlen;
  +						diff = nextfieldoff-off-13-extaddrlen;
  +					}
  +
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +					if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff,
  +								      dstoff, dstlen, pdstrep, dstreplen)) {
  +#else
  +					if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
  +								      dstoff, dstlen, pdstrep, dstreplen)) {
  +#endif
  +						/* mangle failed, all we can do is bail */
  +						nf_ct_unexpect_related(rtp_exp);
  +						if (rtcp_exp)
  +							nf_ct_unexpect_related(rtcp_exp);
  +						return 0;
  +					}
  +					get_skb_tcpdata(skb, &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) {
  +					pr_debug("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 LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +					if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff,
  +								      origoff, origlen, rbuf, rbuflen)) {
  +#else
  +					if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
  +								      origoff, origlen, rbuf, rbuflen)) {
  +#endif
  +						/* mangle failed, all we can do is bail */
  +						nf_ct_unexpect_related(rtp_exp);
  +						if (rtcp_exp)
  +							nf_ct_unexpect_related(rtcp_exp);
  +						return 0;
  +					}
  +					get_skb_tcpdata(skb, &ptcp, &tcplen);
  +					ptran = ptcp+tranoff;
  +					tranlen -= diff;
  +					nextparamoff -= diff;
  +					nextfieldoff -= diff;
  +				}
  +			}
  +
  +			off = nextfieldoff;
  +		}
  +
  +		off = nextparamoff;
  +	}
  +
  +	return 1;
  +}
  +
  +static uint
  +help_out(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +	 unsigned int protoff,
  +#endif
  +	 unsigned int matchoff, unsigned int matchlen,
  +	 struct ip_ct_rtsp_expect* prtspexp,
  +	 struct nf_conntrack_expect* rtp_exp,
  +	 struct nf_conntrack_expect* rtcp_exp)
  +{
  +	char* ptcp;
  +	uint  tcplen;
  +	uint  hdrsoff;
  +	uint  hdrslen;
  +	uint  lineoff;
  +	uint  linelen;
  +	uint  off;
  +	int   dir = CTINFO2DIR(ctinfo);
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +	union nf_inet_addr saddr = rtp_exp->master->tuplehash[dir].tuple.src.u3;
  +#else
  +	__be32 saddr = rtp_exp->master->tuplehash[dir].tuple.src.u3.ip;
  +#endif
  +
  +	//struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
  +	//struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
  +
  +	get_skb_tcpdata(skb, &ptcp, &tcplen);
  +	hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq);
  +	hdrslen = matchlen;
  +	off = hdrsoff;
  +	pr_debug("NAT rtsp help_out\n");
  +
  +	while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen)) {
  +		if (linelen == 0)
  +			break;
  +
  +		if (off > hdrsoff+hdrslen) {
  +			pr_info("!! overrun !!");
  +			break;
  +		}
  +		pr_debug("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
  +
  +		if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) {
  +			uint oldtcplen = tcplen;
  +			pr_debug("hdr: Transport\n");
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +			if (!rtsp_mangle_tran(ctinfo, protoff, rtp_exp, rtcp_exp,
  +					      prtspexp, skb, lineoff, linelen)) {
  +#else
  +			if (!rtsp_mangle_tran(ctinfo, rtp_exp, rtcp_exp, prtspexp,
  +					      skb, lineoff, linelen)) {
  +#endif
  +				pr_debug("hdr: Transport mangle failed");
  +				break;
  +			}
  +			rtp_exp->expectfn = nf_nat_rtsp_expected;
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +			rtp_exp->saved_addr = saddr;
  +#else
  +			rtp_exp->saved_ip = saddr;
  +#endif
  +			rtp_exp->saved_proto.udp.port = htons(prtspexp->loport);
  +			rtp_exp->dir = !dir;
  +			if (rtcp_exp) {
  +				rtcp_exp->expectfn = nf_nat_rtsp_expected;
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +				rtcp_exp->saved_addr = saddr;
  +#else
  +				rtcp_exp->saved_ip = saddr;
  +#endif
  +				rtcp_exp->saved_proto.udp.port = htons(prtspexp->hiport);
  +				rtcp_exp->dir = !dir;
  +			}
  +			get_skb_tcpdata(skb, &ptcp, &tcplen);
  +			hdrslen -= (oldtcplen-tcplen);
  +			off -= (oldtcplen-tcplen);
  +			lineoff -= (oldtcplen-tcplen);
  +			linelen -= (oldtcplen-tcplen);
  +			pr_debug("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
  +		}
  +	}
  +
  +	return NF_ACCEPT;
  +}
  +
  +static unsigned int
  +nf_nat_rtsp(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +	    unsigned int protoff,
  +#endif
  +	    unsigned int matchoff, unsigned int matchlen,
  +	    struct ip_ct_rtsp_expect* prtspexp,
  +	    struct nf_conntrack_expect* rtp_exp,
  +	    struct nf_conntrack_expect* rtcp_exp)
  +{
  +	int dir = CTINFO2DIR(ctinfo);
  +	int rc = NF_ACCEPT;
  +
  +	switch (dir) {
  +	case IP_CT_DIR_ORIGINAL:
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +		rc = help_out(skb, ctinfo, protoff, matchoff, matchlen, prtspexp,
  +			      rtp_exp, rtcp_exp);
  +#else
  +		rc = help_out(skb, ctinfo, matchoff, matchlen, prtspexp,
  +			      rtp_exp, rtcp_exp);
  +#endif
  +		break;
  +	case IP_CT_DIR_REPLY:
  +		pr_debug("unmangle ! %u\n", ctinfo);
  +		/* XXX: unmangle */
  +		rc = NF_ACCEPT;
  +		break;
  +	}
  +	//UNLOCK_BH(&ip_rtsp_lock);
  +
  +	return rc;
  +}
  +
  +static void nf_nat_rtsp_expected(struct nf_conn* ct, struct nf_conntrack_expect *exp)
  +{
  +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) || LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +	struct nf_nat_range range;
  +#else
  +	struct nf_nat_ipv4_range range;
  +#endif
  +
  +	/* This must be a fresh one. */
  +	BUG_ON(ct->status & IPS_NAT_DONE_MASK);
  +
  +	/* For DST manip, map port here to where it's expected. */
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +	range.min_proto = range.max_proto = exp->saved_proto;
  +	range.min_addr = range.max_addr = exp->saved_addr;
  +#else
  +	range.min = range.max = exp->saved_proto;
  +	range.min_ip = range.max_ip = exp->saved_ip;
  +#endif
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)
  +	range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED);
  +	nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
  +#else
  +	range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
  +	nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST);
  +#endif
  +
  +	/* Change src to where master sends to, but only if the connection
  +	 * actually came from the same source. */
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
  +	if (nf_inet_addr_cmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3,
  +			     &ct->master->tuplehash[exp->dir].tuple.src.u3)) {
  +		range.min_addr = range.max_addr
  +			= ct->master->tuplehash[!exp->dir].tuple.dst.u3;
  +#else
  +	if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip ==
  +	    ct->master->tuplehash[exp->dir].tuple.src.u3.ip) {
  +		range.min_ip = range.max_ip
  +			= ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip;
  +#endif
  +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)
  +		range.flags = NF_NAT_RANGE_MAP_IPS;
  +		nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
  +#else
  +		range.flags = IP_NAT_RANGE_MAP_IPS;
  +		nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC);
  +#endif
  +	}
  +}
  +
  +
  +static void __exit fini(void)
  +{
  +	rcu_assign_pointer(nf_nat_rtsp_hook, 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);
  +	rcu_assign_pointer(nf_nat_rtsp_hook, nf_nat_rtsp);
  +
  +	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);
  --- a/extensions/Kbuild
  +++ b/extensions/Kbuild
  @@ -26,6 +26,7 @@ obj-${build_lscan}       += xt_lscan.o
   obj-${build_pknock}      += pknock/
   obj-${build_psd}         += xt_psd.o
   obj-${build_quota2}      += xt_quota2.o
  +obj-${build_rtsp}        += rtsp/
   
   -include ${M}/*.Kbuild
   -include ${M}/Kbuild.*
  --- a/mconfig
  +++ b/mconfig
  @@ -22,3 +22,4 @@ build_lscan=m
   build_pknock=m
   build_psd=m
   build_quota2=m
  +build_rtsp=m
  