GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / net / ipv4 / netfilter / nf_nat_rtsp.c
blob96b4af33a5384c737ec12e9c957e13a0a9c6ae14
1 /*
2 * RTSP extension for TCP NAT alteration
3 * (C) 2003 by Tom Marshall <tmarshall at real.com>
4 * based on ip_nat_irc.c
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
11 * Module load syntax:
12 * insmod nf_nat_rtsp.o ports=port1,port2,...port<MAX_PORTS>
13 * stunaddr=<address>
14 * destaction=[auto|strip|none]
16 * If no ports are specified, the default will be port 554 only.
18 * stunaddr specifies the address used to detect that a client is using STUN.
19 * If this address is seen in the destination parameter, it is assumed that
20 * the client has already punched a UDP hole in the firewall, so we don't
21 * mangle the client_port. If none is specified, it is autodetected. It
22 * only needs to be set if you have multiple levels of NAT. It should be
23 * set to the external address that the STUN clients detect. Note that in
24 * this case, it will not be possible for clients to use UDP with servers
25 * between the NATs.
27 * If no destaction is specified, auto is used.
28 * destaction=auto: strip destination parameter if it is not stunaddr.
29 * destaction=strip: always strip destination parameter (not recommended).
30 * destaction=none: do not touch destination parameter (not recommended).
33 #include <linux/module.h>
34 #include <net/tcp.h>
35 #include <net/netfilter/nf_nat_helper.h>
36 #include <net/netfilter/nf_nat_rule.h>
37 #include <linux/netfilter/nf_conntrack_rtsp.h>
38 #include <net/netfilter/nf_conntrack_expect.h>
40 #include <linux/inet.h>
41 #include <linux/ctype.h>
42 #define NF_NEED_STRNCASECMP
43 #define NF_NEED_STRTOU16
44 #include <linux/netfilter_helpers.h>
45 #define NF_NEED_MIME_NEXTLINE
46 #include <linux/netfilter_mime.h>
48 #define MAX_PORTS 8
49 #define DSTACT_AUTO 0
50 #define DSTACT_STRIP 1
51 #define DSTACT_NONE 2
53 static char* stunaddr = NULL;
54 static char* destaction = NULL;
56 static u_int32_t extip = 0;
57 static int dstact = 0;
59 MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
60 MODULE_DESCRIPTION("RTSP network address translation module");
61 MODULE_LICENSE("GPL");
62 module_param(stunaddr, charp, 0644);
63 MODULE_PARM_DESC(stunaddr, "Address for detecting STUN");
64 module_param(destaction, charp, 0644);
65 MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)");
67 #define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
69 /*** helper functions ***/
71 static void
72 get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen)
74 struct iphdr* iph = ip_hdr(skb);
75 struct tcphdr* tcph = (void *)iph + ip_hdrlen(skb);
77 *pptcpdata = (char*)tcph + tcph->doff*4;
78 *ptcpdatalen = ((char*)skb_transport_header(skb) + skb->len) - *pptcpdata;
81 /*** nat functions ***/
84 * Mangle the "Transport:" header:
85 * - Replace all occurences of "client_port=<spec>"
86 * - Handle destination parameter
88 * In:
89 * ct, ctinfo = conntrack context
90 * skb = packet
91 * tranoff = Transport header offset from TCP data
92 * tranlen = Transport header length (incl. CRLF)
93 * rport_lo = replacement low port (host endian)
94 * rport_hi = replacement high port (host endian)
96 * Returns packet size difference.
98 * Assumes that a complete transport header is present, ending with CR or LF
100 static int
101 rtsp_mangle_tran(enum ip_conntrack_info ctinfo,
102 struct nf_conntrack_expect* exp,
103 struct ip_ct_rtsp_expect* prtspexp,
104 struct sk_buff* skb, uint tranoff, uint tranlen)
106 char* ptcp;
107 uint tcplen;
108 char* ptran;
109 char rbuf1[16]; /* Replacement buffer (one port) */
110 uint rbuf1len; /* Replacement len (one port) */
111 char rbufa[16]; /* Replacement buffer (all ports) */
112 uint rbufalen; /* Replacement len (all ports) */
113 u_int32_t newip;
114 u_int16_t loport, hiport;
115 uint off = 0;
116 uint diff; /* Number of bytes we removed */
118 struct nf_conn *ct = exp->master;
119 struct nf_conntrack_tuple *t;
121 char szextaddr[15+1];
122 uint extaddrlen;
123 int is_stun;
125 get_skb_tcpdata(skb, &ptcp, &tcplen);
126 ptran = ptcp+tranoff;
128 if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
129 tranlen < 10 || !iseol(ptran[tranlen-1]) ||
130 nf_strncasecmp(ptran, "Transport:", 10) != 0)
132 pr_info("sanity check failed\n");
133 return 0;
135 off += 10;
136 SKIP_WSPACE(ptcp+tranoff, tranlen, off);
138 newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
139 t = &exp->tuple;
140 t->dst.u3.ip = newip;
142 extaddrlen = extip ? sprintf(szextaddr, "%pI4", &extip)
143 : sprintf(szextaddr, "%pI4", &newip);
144 pr_debug("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));
146 rbuf1len = rbufalen = 0;
147 switch (prtspexp->pbtype)
149 case pb_single:
150 for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
152 t->dst.u.udp.port = htons(loport);
153 if (nf_ct_expect_related(exp) == 0)
155 pr_debug("using port %hu\n", loport);
156 break;
159 if (loport != 0)
161 rbuf1len = sprintf(rbuf1, "%hu", loport);
162 rbufalen = sprintf(rbufa, "%hu", loport);
164 break;
165 case pb_range:
166 for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */
168 t->dst.u.udp.port = htons(loport);
169 if (nf_ct_expect_related(exp) == 0)
171 hiport = loport + 1; //~exp->mask.dst.u.udp.port;
172 pr_debug("using ports %hu-%hu\n", loport, hiport);
173 break;
176 if (loport != 0)
178 rbuf1len = sprintf(rbuf1, "%hu", loport);
179 rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1);
181 break;
182 case pb_discon:
183 for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
185 t->dst.u.udp.port = htons(loport);
186 if (nf_ct_expect_related(exp) == 0)
188 pr_debug("using port %hu (1 of 2)\n", loport);
189 break;
192 for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */
194 t->dst.u.udp.port = htons(hiport);
195 if (nf_ct_expect_related(exp) == 0)
197 pr_debug("using port %hu (2 of 2)\n", hiport);
198 break;
201 if (loport != 0 && hiport != 0)
203 rbuf1len = sprintf(rbuf1, "%hu", loport);
204 if (hiport == loport+1)
206 rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
208 else
210 rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport);
213 break;
216 if (rbuf1len == 0)
218 return 0; /* cannot get replacement port(s) */
221 /* Transport: tran;field;field=val,tran;field;field=val,... */
222 while (off < tranlen)
224 uint saveoff;
225 const char* pparamend;
226 uint nextparamoff;
228 pparamend = memchr(ptran+off, ',', tranlen-off);
229 pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
230 nextparamoff = pparamend-ptcp;
233 * We pass over each param twice. On the first pass, we look for a
234 * destination= field. It is handled by the security policy. If it
235 * is present, allowed, and equal to our external address, we assume
236 * that STUN is being used and we leave the client_port= field alone.
238 is_stun = 0;
239 saveoff = off;
240 while (off < nextparamoff)
242 const char* pfieldend;
243 uint nextfieldoff;
245 pfieldend = memchr(ptran+off, ';', nextparamoff-off);
246 nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
248 if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0)
250 if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
252 is_stun = 1;
254 if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun))
256 diff = nextfieldoff-off;
257 if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
258 off, diff, NULL, 0))
260 /* mangle failed, all we can do is bail */
261 nf_ct_unexpect_related(exp);
262 return 0;
264 get_skb_tcpdata(skb, &ptcp, &tcplen);
265 ptran = ptcp+tranoff;
266 tranlen -= diff;
267 nextparamoff -= diff;
268 nextfieldoff -= diff;
272 off = nextfieldoff;
274 if (is_stun)
276 continue;
278 off = saveoff;
279 while (off < nextparamoff)
281 const char* pfieldend;
282 uint nextfieldoff;
284 pfieldend = memchr(ptran+off, ';', nextparamoff-off);
285 nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
287 if (strncmp(ptran+off, "client_port=", 12) == 0)
289 u_int16_t port;
290 uint numlen;
291 uint origoff;
292 uint origlen;
293 char* rbuf = rbuf1;
294 uint rbuflen = rbuf1len;
296 off += 12;
297 origoff = (ptran-ptcp)+off;
298 origlen = 0;
299 numlen = nf_strtou16(ptran+off, &port);
300 off += numlen;
301 origlen += numlen;
302 if (port != prtspexp->loport)
304 pr_debug("multiple ports found, port %hu ignored\n", port);
306 else
308 if (ptran[off] == '-' || ptran[off] == '/')
310 off++;
311 origlen++;
312 numlen = nf_strtou16(ptran+off, &port);
313 off += numlen;
314 origlen += numlen;
315 rbuf = rbufa;
316 rbuflen = rbufalen;
320 * note we cannot just memcpy() if the sizes are the same.
321 * the mangle function does skb resizing, checks for a
322 * cloned skb, and updates the checksums.
324 * parameter 4 below is offset from start of tcp data.
326 diff = origlen-rbuflen;
327 if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
328 origoff, origlen, rbuf, rbuflen))
330 /* mangle failed, all we can do is bail */
331 nf_ct_unexpect_related(exp);
332 return 0;
334 get_skb_tcpdata(skb, &ptcp, &tcplen);
335 ptran = ptcp+tranoff;
336 tranlen -= diff;
337 nextparamoff -= diff;
338 nextfieldoff -= diff;
342 off = nextfieldoff;
345 off = nextparamoff;
348 return 1;
351 static uint
352 help_out(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
353 unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp,
354 struct nf_conntrack_expect* exp)
356 char* ptcp;
357 uint tcplen;
358 uint hdrsoff;
359 uint hdrslen;
360 uint lineoff;
361 uint linelen;
362 uint off;
364 //struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
365 //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
367 get_skb_tcpdata(skb, &ptcp, &tcplen);
368 hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq);
369 hdrslen = matchlen;
370 off = hdrsoff;
371 pr_debug("NAT rtsp help_out\n");
373 while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen))
375 if (linelen == 0)
377 break;
379 if (off > hdrsoff+hdrslen)
381 pr_info("!! overrun !!");
382 break;
384 pr_debug("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
386 if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0)
388 uint oldtcplen = tcplen;
389 pr_debug("hdr: Transport\n");
390 if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, skb, lineoff, linelen))
392 pr_debug("hdr: Transport mangle failed");
393 break;
395 get_skb_tcpdata(skb, &ptcp, &tcplen);
396 hdrslen -= (oldtcplen-tcplen);
397 off -= (oldtcplen-tcplen);
398 lineoff -= (oldtcplen-tcplen);
399 linelen -= (oldtcplen-tcplen);
400 pr_debug("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
404 return NF_ACCEPT;
407 static unsigned int
408 help(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
409 unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp,
410 struct nf_conntrack_expect* exp)
412 int dir = CTINFO2DIR(ctinfo);
413 int rc = NF_ACCEPT;
415 switch (dir)
417 case IP_CT_DIR_ORIGINAL:
418 rc = help_out(skb, ctinfo, matchoff, matchlen, prtspexp, exp);
419 break;
420 case IP_CT_DIR_REPLY:
421 pr_debug("unmangle ! %u\n", ctinfo);
422 /* XXX: unmangle */
423 rc = NF_ACCEPT;
424 break;
426 //UNLOCK_BH(&ip_rtsp_lock);
428 return rc;
431 static void expected(struct nf_conn* ct, struct nf_conntrack_expect *exp)
433 struct nf_nat_multi_range_compat mr;
434 u_int32_t newdstip, newsrcip, newip;
436 struct nf_conn *master = ct->master;
438 newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
439 newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
440 //FIXME (how to port that ?)
441 //code from 2.4 : newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip;
442 newip = newdstip;
444 pr_debug("newsrcip=%pI4, newdstip=%pI4, newip=%pI4\n",
445 &newsrcip, &newdstip, &newip);
447 mr.rangesize = 1;
448 // We don't want to manip the per-protocol, just the IPs.
449 mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
450 mr.range[0].min_ip = mr.range[0].max_ip = newip;
452 nf_nat_setup_info(ct, &mr.range[0], IP_NAT_MANIP_DST);
456 static void __exit fini(void)
458 nf_nat_rtsp_hook = NULL;
459 nf_nat_rtsp_hook_expectfn = NULL;
460 synchronize_net();
463 static int __init init(void)
465 printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");
467 BUG_ON(nf_nat_rtsp_hook);
468 nf_nat_rtsp_hook = help;
469 nf_nat_rtsp_hook_expectfn = &expected;
471 if (stunaddr != NULL)
472 extip = in_aton(stunaddr);
474 if (destaction != NULL) {
475 if (strcmp(destaction, "auto") == 0)
476 dstact = DSTACT_AUTO;
478 if (strcmp(destaction, "strip") == 0)
479 dstact = DSTACT_STRIP;
481 if (strcmp(destaction, "none") == 0)
482 dstact = DSTACT_NONE;
485 return 0;
488 module_init(init);
489 module_exit(fini);