diff options
Diffstat (limited to 'package/network/utils/xtables-addons/patches/100-add-rtsp-conntrack.patch')
-rw-r--r-- | package/network/utils/xtables-addons/patches/100-add-rtsp-conntrack.patch | 1526 |
1 files changed, 0 insertions, 1526 deletions
diff --git a/package/network/utils/xtables-addons/patches/100-add-rtsp-conntrack.patch b/package/network/utils/xtables-addons/patches/100-add-rtsp-conntrack.patch deleted file mode 100644 index bd49d92de6..0000000000 --- a/package/network/utils/xtables-addons/patches/100-add-rtsp-conntrack.patch +++ /dev/null @@ -1,1526 +0,0 @@ ---- /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 |