2 * RTSP extension for TCP NAT alteration
3 * (C) 2003 by Tom Marshall <tmarshall@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.
12 * insmod ip_nat_rtsp.o ports=port1,port2,...port<MAX_PORTS>
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
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 <linux/netfilter_ipv4.h>
36 #include <linux/tcp.h>
37 #include <linux/udp.h>
38 #include <linux/kernel.h>
40 #include <linux/netfilter_ipv4/ip_nat.h>
41 #include <linux/netfilter_ipv4/ip_nat_helper.h>
42 #include <linux/netfilter_ipv4/ip_nat_rule.h>
43 #include <linux/netfilter_ipv4/ip_nat_rtsp.h>
44 #include <linux/netfilter_ipv4/ip_conntrack_rtsp.h>
45 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
47 #include <linux/inet.h>
48 #include <linux/ctype.h>
49 #define NF_NEED_STRNCASECMP
50 #define NF_NEED_STRTOU16
51 #include <linux/netfilter_helpers.h>
52 #define NF_NEED_MIME_NEXTLINE
53 #include <linux/netfilter_mime.h>
56 * To enable debugging, replace the line below with #define IP_NF_RTSP_DEBUG 1
58 #undef IP_NF_RTSP_DEBUG
59 #define INFOP(args...) printk(args)
60 #ifdef IP_NF_RTSP_DEBUG
61 #define DUMP_TUPLE(args...)
62 #define DEBUGP(args...) printk(KERN_DEBUG "%s:%s ", __FILE__, __FUNCTION__); \
65 #define DEBUGP(args...)
70 #define DSTACT_STRIP 1
72 #define MAX_NAT_PORTS 16
74 static int ports[MAX_PORTS];
75 static char* stunaddr = NULL;
76 static char* destaction = NULL;
78 static int num_ports = 0;
79 static u_int32_t extip = 0;
80 static int dstact = 0;
82 MODULE_AUTHOR("Tom Marshall <tmarshall@real.com>");
83 MODULE_DESCRIPTION("RTSP network address translation module");
84 MODULE_LICENSE("GPL");
86 MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
87 MODULE_PARM_DESC(ports, "port numbers of RTSP servers");
88 MODULE_PARM(stunaddr, "s");
89 MODULE_PARM_DESC(stunaddr, "Address for detecting STUN");
90 MODULE_PARM(destaction, "s");
91 MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)");
94 /* protects rtsp part of conntracks */
95 DECLARE_LOCK_EXTERN(ip_rtsp_lock);
97 #define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
99 /*** helper functions ***/
102 rtsp_nat_find_char(void *str, int ch, size_t len)
104 unsigned char *pStr = NULL;
110 return ((void *)(pStr - 1));
112 } while (--len != 0);
117 get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen)
119 struct iphdr* iph = (struct iphdr*)skb->nh.iph;
120 struct tcphdr* tcph = (struct tcphdr*)((char*)iph + iph->ihl*4);
122 *pptcpdata = (char*)tcph + tcph->doff*4;
123 *ptcpdatalen = ((char*)skb->h.raw + skb->len) - *pptcpdata;
126 /*** nat functions ***/
129 * Mangle the "Transport:" header:
130 * - Replace all occurences of "client_port=<spec>"
131 * - Handle destination parameter
134 * ct, ctinfo = conntrack context
136 * tranoff = Transport header offset from TCP data
137 * tranlen = Transport header length (incl. CRLF)
138 * rport_lo = replacement low port (host endian)
139 * rport_hi = replacement high port (host endian)
141 * Returns packet size difference.
143 * Assumes that a complete transport header is present, ending with CR or LF
146 rtsp_mangle_tran(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo,
147 struct ip_conntrack_expect* exp,
148 struct sk_buff** pskb, uint tranoff, uint tranlen)
153 char rbuf1[16]; /* Replacement buffer (one port) */
154 uint rbuf1len; /* Replacement len (one port) */
155 char rbufa[16]; /* Replacement buffer (all ports) */
156 uint rbufalen; /* Replacement len (all ports) */
158 u_int16_t loport, hiport;
160 uint diff; /* Number of bytes we removed */
162 struct ip_ct_rtsp_expect* prtspexp = &exp->help.exp_rtsp_info;
163 struct ip_conntrack_tuple t;
165 char szextaddr[15+1];
169 get_skb_tcpdata(*pskb, &ptcp, &tcplen);
170 ptran = ptcp+tranoff;
172 if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
173 tranlen < 10 || !iseol(ptran[tranlen-1]) ||
174 nf_strncasecmp(ptran, "Transport:", 10) != 0)
176 INFOP("sanity check failed\n");
180 SKIP_WSPACE(ptcp+tranoff, tranlen, off);
182 newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
186 extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(extip))
187 : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(newip));
188 DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));
189 DEBUGP("Found Transport message %s\n", ptran);
191 rbuf1len = rbufalen = 0;
192 switch (prtspexp->pbtype)
195 loport = prtspexp->loport;
196 DEBUGP("PB_SINGLE: LO_PORT %hu\n", loport);
199 rbuf1len = sprintf(rbuf1, "%hu", loport);
200 rbufalen = sprintf(rbufa, "%hu", loport);
204 loport = prtspexp->loport;
205 DEBUGP("PB_RANGE: LO_PORT %hu\n", loport);
208 rbuf1len = sprintf(rbuf1, "%hu", loport);
209 rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1);
210 DEBUGP("MANGLING to ports (%hu-%hu) rbuf1 %s rbufa %s\n", prtspexp->loport, prtspexp->loport+1,
215 DEBUGP("PB_DISCON:n");
216 for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
218 DEBUGP("Original UDP PORT value is %hu exp loport %hu hiport %hu\n", t.dst.u.udp.port,
219 prtspexp->loport, prtspexp->hiport);
220 // Do not transpose the ports yet. If you do, you better register a helper
221 // to mangle them correctly when you receive packets on those ports.
222 //t.dst.u.udp.port = htons(loport);
223 if (ip_conntrack_change_expect(exp, &t) == 0)
225 DEBUGP("using port %hu (1 of 2)\n", loport);
229 for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */
231 t.dst.u.udp.port = htons(hiport);
232 if (ip_conntrack_change_expect(exp, &t) == 0)
234 DEBUGP("using port %hu (2 of 2)\n", hiport);
238 if (loport != 0 && hiport != 0)
240 rbuf1len = sprintf(rbuf1, "%hu", loport);
241 if (hiport == loport+1)
243 rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
244 DEBUGP("Ports %hu-%hu\n", loport, hiport);
248 rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport);
249 DEBUGP("ports %hu-%hu\n", loport, hiport);
260 DEBUGP("Cannot get replacement ports\n");
261 return 0; /* cannot get replacement port(s) */
264 /* Transport: tran;field;field=val,tran;field;field=val,... */
265 while (off < tranlen)
268 const char* pparamend;
271 pparamend = rtsp_nat_find_char(ptran+off, ',', tranlen-off);
272 pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
273 nextparamoff = pparamend-ptcp;
276 * We pass over each param twice. On the first pass, we look for a
277 * destination= field. It is handled by the security policy. If it
278 * is present, allowed, and equal to our external address, we assume
279 * that STUN is being used and we leave the client_port= field alone.
283 while (off < nextparamoff)
285 const char* pfieldend;
288 pfieldend = rtsp_nat_find_char(ptran+off, ';', nextparamoff-off);
289 nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
291 if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0)
293 if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
297 if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun))
299 diff = nextfieldoff-off;
300 if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
303 /* mangle failed, all we can do is bail */
304 DEBUGP("mangle failed bailing out now\n");
307 get_skb_tcpdata(*pskb, &ptcp, &tcplen);
308 ptran = ptcp+tranoff;
310 nextparamoff -= diff;
311 nextfieldoff -= diff;
322 while (off < nextparamoff)
324 const char* pfieldend;
327 pfieldend = rtsp_nat_find_char(ptran+off, ';', nextparamoff-off);
328 nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
330 DEBUGP("off %d nextparamoff %d %s\n", off, nextparamoff, ptran+off);
331 if (strncmp(ptran+off, "client_port=", 12) == 0)
338 uint rbuflen = rbuf1len;
341 origoff = (ptran-ptcp)+off;
343 numlen = nf_strtou16(ptran+off, &port);
346 DEBUGP("Checking port %hu expec port %hu rbufa %s rbufalen %d\n",
347 port, prtspexp->loport, rbufa, rbufalen);
348 if (ptran[off] == '-' || ptran[off] == '/')
352 numlen = nf_strtou16(ptran+off, &port);
360 * note we cannot just memcpy() if the sizes are the same.
361 * the mangle function does skb resizing, checks for a
362 * cloned skb, and updates the checksums.
364 * parameter 4 below is offset from start of tcp data.
366 diff = origlen-rbuflen;
367 DEBUGP("Before mangle rbuf %s diff %d ptran %s\n", rbuf, diff, ptran+off);
368 if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
369 origoff, origlen, rbuf, rbuflen))
371 /* mangle failed, all we can do is bail */
372 DEBUGP("MANGLE Failed\n");
375 get_skb_tcpdata(*pskb, &ptcp, &tcplen);
376 ptran = ptcp+tranoff;
378 nextparamoff -= diff;
379 nextfieldoff -= diff;
380 DEBUGP("After mangle nextparamoff %d nextfieldoff %d ptran %s\n",
381 nextparamoff, nextfieldoff, ptran);
394 expected(struct sk_buff** pskb, uint hooknum,
395 struct ip_conntrack* ct, struct ip_nat_info* info)
397 struct ip_nat_multi_range mr;
398 u_int32_t newdstip, newsrcip, newip;
400 struct ip_conntrack *master = master_ct(ct);
403 IP_NF_ASSERT(master);
405 IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
407 newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
408 newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
409 newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip;
411 DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
412 DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
413 DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.%u.%u CLIENT PORT %hu\n",
414 NIPQUAD(newsrcip), NIPQUAD(newdstip), NIPQUAD(newip), ct->nat.rtsp_info.orig_port);
415 DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
416 DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple);
420 * We don't want to manip the per-protocol, just the IPs. Actually we
421 * did manipulate the UDP ports
423 mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
424 mr.range[0].min_ip = mr.range[0].max_ip = newip;
425 mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
426 mr.range[0].min.udp.port = mr.range[0].max.udp.port = ct->nat.rtsp_info.orig_port;
428 return ip_nat_setup_info(ct, &mr, hooknum);
432 help_out(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo,
433 struct ip_conntrack_expect* exp, struct sk_buff** pskb)
443 struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
444 struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
446 struct ip_ct_rtsp_expect* prtspexp = &exp->help.exp_rtsp_info;
448 get_skb_tcpdata(*pskb, &ptcp, &tcplen);
450 hdrsoff = exp->seq - ntohl(tcph->seq);
451 hdrslen = prtspexp->len;
454 DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
455 DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
457 DEBUGP("SRC IP %u.%u.%u.%u DST IP %u.%u.%u.%u PORTS %hu-%hu\n",
458 NIPQUAD(iph->saddr), NIPQUAD(iph->daddr), prtspexp->loport,
460 while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen))
466 if (off > hdrsoff+hdrslen)
468 INFOP("!! overrun !!\n");
471 DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
473 if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0)
475 uint oldtcplen = tcplen;
476 if (!rtsp_mangle_tran(ct, ctinfo, exp, pskb, lineoff, linelen))
480 get_skb_tcpdata(*pskb, &ptcp, &tcplen);
481 hdrslen -= (oldtcplen-tcplen);
482 off -= (oldtcplen-tcplen);
483 lineoff -= (oldtcplen-tcplen);
484 linelen -= (oldtcplen-tcplen);
485 DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
488 DEBUGP("SRC IP %u.%u.%u.%u DST IP %u.%u.%u.%u PORTS (%hu-%hu)\n",
489 NIPQUAD(iph->saddr), NIPQUAD(iph->daddr), tcph->source, tcph->dest);
495 help_in(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo,
496 struct ip_conntrack_expect* exp, struct sk_buff** pskb)
499 DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
500 DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
505 help(struct ip_conntrack* ct,
506 struct ip_conntrack_expect* exp,
507 struct ip_nat_info* info,
508 enum ip_conntrack_info ctinfo,
509 unsigned int hooknum,
510 struct sk_buff** pskb)
512 struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
513 struct tcphdr* tcph = (struct tcphdr*)((char*)iph + iph->ihl * 4);
516 struct ip_ct_rtsp_expect* ct_rtsp_info;
519 DEBUGP("SRC IP %u.%u.%u.%u DST IP %u.%u.%u.%u PORTS %hu-%hu\n",
520 NIPQUAD(iph->saddr), NIPQUAD(iph->daddr), tcph->source,
522 if (ct == NULL || exp == NULL || info == NULL || pskb == NULL)
524 DEBUGP("!! null ptr (%p,%p,%p,%p) !!\n", ct, exp, info, pskb);
528 ct_rtsp_info = &exp->help.exp_rtsp_info;
531 * Only mangle things once: original direction in POST_ROUTING
532 * and reply direction on PRE_ROUTING.
534 dir = CTINFO2DIR(ctinfo);
535 if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
536 || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY)))
538 DEBUGP("Not touching dir %s at hook %s\n",
539 dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
540 hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
541 : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
542 : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
545 DEBUGP("got beyond not touching\n");
547 datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
549 LOCK_BH(&ip_rtsp_lock);
553 case IP_CT_DIR_ORIGINAL:
554 rc = help_out(ct, ctinfo, exp, pskb);
556 case IP_CT_DIR_REPLY:
557 rc = help_in(ct, ctinfo, exp, pskb);
563 UNLOCK_BH(&ip_rtsp_lock);
568 static struct ip_nat_helper ip_nat_rtsp_helpers[MAX_PORTS];
569 static char rtsp_names[MAX_PORTS][10];
571 /* This function is intentionally _NOT_ defined as __exit */
577 for (i = 0; i < num_ports; i++)
579 DEBUGP("unregistering helper for port %d\n", ports[i]);
580 ip_nat_helper_unregister(&ip_nat_rtsp_helpers[i]);
589 struct ip_nat_helper* hlpr;
592 printk("ip_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");
596 ports[0] = RTSP_PORT;
599 for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++)
601 hlpr = &ip_nat_rtsp_helpers[i];
602 memset(hlpr, 0, sizeof(struct ip_nat_helper));
604 hlpr->tuple.dst.protonum = IPPROTO_TCP;
605 hlpr->tuple.src.u.tcp.port = htons(ports[i]);
606 hlpr->mask.src.u.tcp.port = 0xFFFF;
607 hlpr->mask.dst.protonum = 0xFFFF;
610 hlpr->me = THIS_MODULE;
611 hlpr->expect = expected;
613 tmpname = &rtsp_names[i][0];
614 if (ports[i] == RTSP_PORT)
616 sprintf(tmpname, "rtsp");
620 sprintf(tmpname, "rtsp-%d", i);
622 hlpr->name = tmpname;
624 DEBUGP("registering helper for port %d: name %s\n", ports[i], hlpr->name);
625 ret = ip_nat_helper_register(hlpr);
629 printk("ip_nat_rtsp: error registering helper for port %d\n", ports[i]);
635 if (stunaddr != NULL)
637 extip = in_aton(stunaddr);
639 if (destaction != NULL)
641 if (strcmp(destaction, "auto") == 0)
643 dstact = DSTACT_AUTO;
645 if (strcmp(destaction, "strip") == 0)
647 dstact = DSTACT_STRIP;
649 if (strcmp(destaction, "none") == 0)
651 dstact = DSTACT_NONE;
657 NEEDS_CONNTRACK(rtsp);