From 716ca530e1c4515d8683c9d5be3d56b301758b66 Mon Sep 17 00:00:00 2001 From: James <> Date: Wed, 4 Nov 2015 11:49:21 +0000 Subject: trunk-47381 --- .../patches/100-add-rtsp-conntrack.patch | 1526 ++++++++++++++++++++ 1 file changed, 1526 insertions(+) create mode 100644 package/network/utils/xtables-addons/patches/100-add-rtsp-conntrack.patch (limited to 'package/network/utils/xtables-addons/patches/100-add-rtsp-conntrack.patch') 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 new file mode 100644 index 0000000..6c169ee --- /dev/null +++ b/package/network/utils/xtables-addons/patches/100-add-rtsp-conntrack.patch @@ -0,0 +1,1526 @@ +--- /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 ++#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 ++ ++/* ++ * 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 ++ * ++ * 2005-02-13: Harald Welte ++ * - port to 2.6 ++ * - update to recent post-2.6.11 api changes ++ * 2006-09-14: Steven Van Acker ++ * - removed calls to NAT code from conntrack helper: NAT no longer needed to use rtsp-conntrack ++ * 2007-04-18: Michael Guntsche ++ * - Port to new NF API ++ * 2013-03-04: Il'inykh Sergey . 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_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 ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#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 ++ ++#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 "); ++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 ++ * 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 . Inango Systems Ltd ++ * - conditional compilation for kernel 3.7 ++ * - port mapping improvements ++*/ ++#ifndef _IP_CONNTRACK_RTSP_H ++#define _IP_CONNTRACK_RTSP_H ++ ++#include ++ ++//#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 ++ * ++ * 2013-03-04: Il'inykh Sergey . 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 ++ * stunaddr=
++ * 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 ++#include ++#include ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++# include ++#else ++# include ++#endif ++#include ++#include "nf_conntrack_rtsp.h" ++#include ++ ++#include ++#include ++#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 "); ++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=" ++ * - 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 +@@ -28,6 +28,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 -- cgit v1.2.3