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.
12 * insmod nf_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>
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)
50 #define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args)
52 #define DEBUGP(fmt, args...)
57 #define DSTACT_STRIP 1
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 ***/
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
96 * ct, ctinfo = conntrack context
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
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
)
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) */
121 u_int16_t loport
, hiport
;
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];
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");
143 SKIP_WSPACE(ptcp
+tranoff
, tranlen
, off
);
145 newip
= ct
->tuplehash
[IP_CT_DIR_REPLY
].tuple
.dst
.u3
.ip
;
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
)
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
);
168 rbuf1len
= sprintf(rbuf1
, "%hu", loport
);
169 rbufalen
= sprintf(rbufa
, "%hu", loport
);
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
);
185 rbuf1len
= sprintf(rbuf1
, "%hu", loport
);
186 rbufalen
= sprintf(rbufa
, "%hu-%hu", loport
, loport
+1);
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
);
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
);
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
);
217 rbufalen
= sprintf(rbufa
, "%hu/%hu", loport
, hiport
);
225 return 0; /* cannot get replacement port(s) */
228 /* Transport: tran;field;field=val,tran;field;field=val,... */
229 while (off
< tranlen
)
232 const char* pparamend
;
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.
247 while (off
< nextparamoff
)
249 const char* pfieldend
;
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)
261 if (dstact
== DSTACT_STRIP
|| (dstact
== DSTACT_AUTO
&& !is_stun
))
263 diff
= nextfieldoff
-off
;
264 if (!nf_nat_mangle_tcp_packet(skb
, ct
, ctinfo
,
267 /* mangle failed, all we can do is bail */
268 nf_conntrack_unexpect_related(exp
);
271 get_skb_tcpdata(skb
, &ptcp
, &tcplen
);
272 ptran
= ptcp
+tranoff
;
274 nextparamoff
-= diff
;
275 nextfieldoff
-= diff
;
286 while (off
< nextparamoff
)
288 const char* pfieldend
;
291 pfieldend
= memchr(ptran
+off
, ';', nextparamoff
-off
);
292 nextfieldoff
= (pfieldend
== NULL
) ? nextparamoff
: pfieldend
-ptran
+1;
294 if (strncmp(ptran
+off
, "client_port=", 12) == 0)
301 uint rbuflen
= rbuf1len
;
304 origoff
= (ptran
-ptcp
)+off
;
306 numlen
= nf_strtou16(ptran
+off
, &port
);
309 if (port
!= prtspexp
->loport
)
311 DEBUGP("multiple ports found, port %hu ignored\n", port
);
315 if (ptran
[off
] == '-' || ptran
[off
] == '/')
319 numlen
= nf_strtou16(ptran
+off
, &port
);
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
);
341 get_skb_tcpdata(skb
, &ptcp
, &tcplen
);
342 ptran
= ptcp
+tranoff
;
344 nextparamoff
-= diff
;
345 nextfieldoff
-= diff
;
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
)
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);
378 DEBUGP("NAT rtsp help_out\n");
380 while (nf_mime_nextline(ptcp
, hdrsoff
+hdrslen
, &off
, &lineoff
, &linelen
))
386 if (off
> hdrsoff
+hdrslen
)
388 INFOP("!! overrun !!");
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");
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
);
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
);
424 case IP_CT_DIR_ORIGINAL
:
425 rc
= help_out(skb
, ctinfo
, matchoff
, matchlen
, prtspexp
, exp
);
427 case IP_CT_DIR_REPLY
:
428 DEBUGP("unmangle ! %u\n", ctinfo
);
433 //UNLOCK_BH(&ip_rtsp_lock);
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;
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
));
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
);
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
;