NETFILTER: remove unnecessary goto statement for error recovery
[tomato.git] / release / src-rt / linux / linux-2.6 / net / ipv4 / netfilter / nf_nat_rtsp.c
blob4258c513997504f8ab44c0483a090e1ea168229a
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 INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
49 #if 0
50 #define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
51 #else
52 #define DEBUGP(fmt, args...)
53 #endif
55 #define MAX_PORTS 8
56 #define DSTACT_AUTO 0
57 #define DSTACT_STRIP 1
58 #define DSTACT_NONE 2
60 static char* stunaddr = NULL;
61 static char* destaction = NULL;
63 static u_int32_t extip = 0;
64 static int dstact = 0;
66 MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
67 MODULE_DESCRIPTION("RTSP network address translation module");
68 MODULE_LICENSE("GPL");
69 module_param(stunaddr, charp, 0644);
70 MODULE_PARM_DESC(stunaddr, "Address for detecting STUN");
71 module_param(destaction, charp, 0644);
72 MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)");
74 #define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
76 /*** helper functions ***/
78 static void
79 get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen)
81 struct iphdr* iph = ip_hdr(skb);
82 struct tcphdr* tcph = (void *)iph + ip_hdrlen(skb);
84 *pptcpdata = (char*)tcph + tcph->doff*4;
85 *ptcpdatalen = ((char*)skb_transport_header(skb) + skb->len) - *pptcpdata;
88 /*** nat functions ***/
91 * Mangle the "Transport:" header:
92 * - Replace all occurences of "client_port=<spec>"
93 * - Handle destination parameter
95 * In:
96 * ct, ctinfo = conntrack context
97 * skb = packet
98 * tranoff = Transport header offset from TCP data
99 * tranlen = Transport header length (incl. CRLF)
100 * rport_lo = replacement low port (host endian)
101 * rport_hi = replacement high port (host endian)
103 * Returns packet size difference.
105 * Assumes that a complete transport header is present, ending with CR or LF
107 static int
108 rtsp_mangle_tran(enum ip_conntrack_info ctinfo,
109 struct nf_conntrack_expect *exp,
110 struct ip_ct_rtsp_expect *prtspexp,
111 struct sk_buff *skb, uint tranoff, uint tranlen)
113 char* ptcp;
114 uint tcplen;
115 char* ptran;
116 char rbuf1[16]; /* Replacement buffer (one port) */
117 uint rbuf1len; /* Replacement len (one port) */
118 char rbufa[16]; /* Replacement buffer (all ports) */
119 uint rbufalen; /* Replacement len (all ports) */
120 u_int32_t newip;
121 u_int16_t loport, hiport;
122 uint off = 0;
123 uint diff; /* Number of bytes we removed */
125 struct nf_conn *ct = exp->master;
126 struct nf_conntrack_tuple *t;
128 char szextaddr[15+1];
129 uint extaddrlen;
130 int is_stun;
132 get_skb_tcpdata(skb, &ptcp, &tcplen);
133 ptran = ptcp+tranoff;
135 if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
136 tranlen < 10 || !iseol(ptran[tranlen-1]) ||
137 nf_strncasecmp(ptran, "Transport:", 10) != 0)
139 INFOP("sanity check failed\n");
140 return 0;
142 off += 10;
143 SKIP_WSPACE(ptcp+tranoff, tranlen, off);
145 newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
146 t = &exp->tuple;
147 t->dst.u3.ip = newip;
149 extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(extip))
150 : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(newip));
151 DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));
153 rbuf1len = rbufalen = 0;
154 switch (prtspexp->pbtype)
156 case pb_single:
157 for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
159 t->dst.u.udp.port = htons(loport);
160 if (nf_conntrack_expect_related(exp) == 0)
162 DEBUGP("using port %hu\n", loport);
163 break;
166 if (loport != 0)
168 rbuf1len = sprintf(rbuf1, "%hu", loport);
169 rbufalen = sprintf(rbufa, "%hu", loport);
171 break;
172 case pb_range:
173 for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */
175 t->dst.u.udp.port = htons(loport);
176 if (nf_conntrack_expect_related(exp) == 0)
178 hiport = loport + 1; //~exp->mask.src.u.udp.port;
179 DEBUGP("using ports %hu-%hu\n", loport, hiport);
180 break;
183 if (loport != 0)
185 rbuf1len = sprintf(rbuf1, "%hu", loport);
186 rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1);
188 break;
189 case pb_discon:
190 for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
192 t->dst.u.udp.port = htons(loport);
193 if (nf_conntrack_expect_related(exp) == 0)
195 DEBUGP("using port %hu (1 of 2)\n", loport);
196 break;
199 for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */
201 t->dst.u.udp.port = htons(hiport);
202 if (nf_conntrack_expect_related(exp) == 0)
204 DEBUGP("using port %hu (2 of 2)\n", hiport);
205 break;
208 if (loport != 0 && hiport != 0)
210 rbuf1len = sprintf(rbuf1, "%hu", loport);
211 if (hiport == loport+1)
213 rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
215 else
217 rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport);
220 break;
223 if (rbuf1len == 0)
225 return 0; /* cannot get replacement port(s) */
228 /* Transport: tran;field;field=val,tran;field;field=val,... */
229 while (off < tranlen)
231 uint saveoff;
232 const char* pparamend;
233 uint nextparamoff;
235 pparamend = memchr(ptran+off, ',', tranlen-off);
236 pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
237 nextparamoff = pparamend-ptcp;
240 * We pass over each param twice. On the first pass, we look for a
241 * destination= field. It is handled by the security policy. If it
242 * is present, allowed, and equal to our external address, we assume
243 * that STUN is being used and we leave the client_port= field alone.
245 is_stun = 0;
246 saveoff = off;
247 while (off < nextparamoff)
249 const char* pfieldend;
250 uint nextfieldoff;
252 pfieldend = memchr(ptran+off, ';', nextparamoff-off);
253 nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
255 if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0)
257 if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
259 is_stun = 1;
261 if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun))
263 diff = nextfieldoff-off;
264 if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
265 off, diff, NULL, 0))
267 /* mangle failed, all we can do is bail */
268 nf_conntrack_unexpect_related(exp);
269 return 0;
271 get_skb_tcpdata(skb, &ptcp, &tcplen);
272 ptran = ptcp+tranoff;
273 tranlen -= diff;
274 nextparamoff -= diff;
275 nextfieldoff -= diff;
279 off = nextfieldoff;
281 if (is_stun)
283 continue;
285 off = saveoff;
286 while (off < nextparamoff)
288 const char* pfieldend;
289 uint nextfieldoff;
291 pfieldend = memchr(ptran+off, ';', nextparamoff-off);
292 nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
294 if (strncmp(ptran+off, "client_port=", 12) == 0)
296 u_int16_t port;
297 uint numlen;
298 uint origoff;
299 uint origlen;
300 char* rbuf = rbuf1;
301 uint rbuflen = rbuf1len;
303 off += 12;
304 origoff = (ptran-ptcp)+off;
305 origlen = 0;
306 numlen = nf_strtou16(ptran+off, &port);
307 off += numlen;
308 origlen += numlen;
309 if (port != prtspexp->loport)
311 DEBUGP("multiple ports found, port %hu ignored\n", port);
313 else
315 if (ptran[off] == '-' || ptran[off] == '/')
317 off++;
318 origlen++;
319 numlen = nf_strtou16(ptran+off, &port);
320 off += numlen;
321 origlen += numlen;
322 rbuf = rbufa;
323 rbuflen = rbufalen;
327 * note we cannot just memcpy() if the sizes are the same.
328 * the mangle function does skb resizing, checks for a
329 * cloned skb, and updates the checksums.
331 * parameter 4 below is offset from start of tcp data.
333 diff = origlen-rbuflen;
334 if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
335 origoff, origlen, rbuf, rbuflen))
337 /* mangle failed, all we can do is bail */
338 nf_conntrack_unexpect_related(exp);
339 return 0;
341 get_skb_tcpdata(skb, &ptcp, &tcplen);
342 ptran = ptcp+tranoff;
343 tranlen -= diff;
344 nextparamoff -= diff;
345 nextfieldoff -= diff;
349 off = nextfieldoff;
352 off = nextparamoff;
355 return 1;
358 static uint
359 help_out(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
360 unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp,
361 struct nf_conntrack_expect* exp)
363 char* ptcp;
364 uint tcplen;
365 uint hdrsoff;
366 uint hdrslen;
367 uint lineoff;
368 uint linelen;
369 uint off;
371 //struct iphdr* iph = (struct iphdr*)skb->nh.iph;
372 //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
374 get_skb_tcpdata(skb, &ptcp, &tcplen);
375 hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq);
376 hdrslen = matchlen;
377 off = hdrsoff;
378 DEBUGP("NAT rtsp help_out\n");
380 while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen))
382 if (linelen == 0)
384 break;
386 if (off > hdrsoff+hdrslen)
388 INFOP("!! overrun !!");
389 break;
391 DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
393 if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0)
395 uint oldtcplen = tcplen;
396 DEBUGP("hdr: Transport\n");
397 if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, skb, lineoff, linelen))
399 DEBUGP("hdr: Transport mangle failed");
400 break;
402 get_skb_tcpdata(skb, &ptcp, &tcplen);
403 hdrslen -= (oldtcplen-tcplen);
404 off -= (oldtcplen-tcplen);
405 lineoff -= (oldtcplen-tcplen);
406 linelen -= (oldtcplen-tcplen);
407 DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
411 return NF_ACCEPT;
414 static unsigned int
415 help(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
416 unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp,
417 struct nf_conntrack_expect* exp)
419 int dir = CTINFO2DIR(ctinfo);
420 int rc = NF_ACCEPT;
422 switch (dir)
424 case IP_CT_DIR_ORIGINAL:
425 rc = help_out(skb, ctinfo, matchoff, matchlen, prtspexp, exp);
426 break;
427 case IP_CT_DIR_REPLY:
428 DEBUGP("unmangle ! %u\n", ctinfo);
429 /* XXX: unmangle */
430 rc = NF_ACCEPT;
431 break;
433 //UNLOCK_BH(&ip_rtsp_lock);
435 return rc;
438 static void expected(struct nf_conn* ct, struct nf_conntrack_expect *exp)
440 struct nf_nat_multi_range_compat mr;
441 u_int32_t newdstip, newsrcip, newip;
443 struct nf_conn *master = ct->master;
445 newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
446 newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
447 //FIXME (how to port that ?)
448 //code from 2.4 : newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip;
449 newip = newdstip;
451 DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.%u.%u\n",
452 NIPQUAD(newsrcip), NIPQUAD(newdstip), NIPQUAD(newip));
454 mr.rangesize = 1;
455 // We don't want to manip the per-protocol, just the IPs.
456 mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
457 mr.range[0].min_ip = mr.range[0].max_ip = newip;
459 nf_nat_setup_info(ct, &mr.range[0], NF_IP_PRE_ROUTING);
463 static void __exit fini(void)
465 rcu_assign_pointer(nf_nat_rtsp_hook, NULL);
466 rcu_assign_pointer(nf_nat_rtsp_hook_expectfn, NULL);
467 synchronize_rcu();
470 static int __init init(void)
472 printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");
474 BUG_ON(rcu_dereference(nf_nat_rtsp_hook));
475 rcu_assign_pointer(nf_nat_rtsp_hook, help);
477 BUG_ON(rcu_dereference(nf_nat_rtsp_hook_expectfn));
478 rcu_assign_pointer(nf_nat_rtsp_hook_expectfn, expected);
480 if (stunaddr != NULL)
481 extip = in_aton(stunaddr);
483 if (destaction != NULL) {
484 if (strcmp(destaction, "auto") == 0)
485 dstact = DSTACT_AUTO;
487 if (strcmp(destaction, "strip") == 0)
488 dstact = DSTACT_STRIP;
490 if (strcmp(destaction, "none") == 0)
491 dstact = DSTACT_NONE;
494 return 0;
497 module_init(init);
498 module_exit(fini);