# BRCM_VERSION=3
[bcm963xx.git] / kernel / linux / net / ipv4 / netfilter / ip_nat_rtsp.c
1 /*
2  * RTSP extension for TCP NAT alteration
3  * (C) 2003 by Tom Marshall <tmarshall@real.com>
4  * based on ip_nat_irc.c
5  *
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.
10  *
11  * Module load syntax:
12  *      insmod ip_nat_rtsp.o ports=port1,port2,...port<MAX_PORTS>
13  *                           stunaddr=<address>
14  *                           destaction=[auto|strip|none]
15  *
16  * If no ports are specified, the default will be port 554 only.
17  *
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.
26  *
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).
31  */
32
33 #include <linux/module.h>
34 #include <linux/netfilter_ipv4.h>
35 #include <linux/ip.h>
36 #include <linux/tcp.h>
37 #include <linux/udp.h>
38 #include <linux/kernel.h>
39 #include <net/tcp.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>
46
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>
54
55 /*
56  * To enable debugging, replace the line below with #define IP_NF_RTSP_DEBUG 1
57  */
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__); \
63                         printk(args)
64 #else
65 #define DEBUGP(args...)
66 #endif
67
68 #define MAX_PORTS       8
69 #define DSTACT_AUTO     0
70 #define DSTACT_STRIP    1
71 #define DSTACT_NONE     2
72 #define MAX_NAT_PORTS   16
73
74 static int          ports[MAX_PORTS];
75 static char*        stunaddr = NULL;
76 static char*        destaction = NULL;
77
78 static int          num_ports = 0;
79 static u_int32_t    extip = 0;
80 static int          dstact = 0;
81
82 MODULE_AUTHOR("Tom Marshall <tmarshall@real.com>");
83 MODULE_DESCRIPTION("RTSP network address translation module");
84 MODULE_LICENSE("GPL");
85 #ifdef MODULE_PARM
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)");
92 #endif
93
94 /* protects rtsp part of conntracks */
95 DECLARE_LOCK_EXTERN(ip_rtsp_lock);
96
97 #define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
98
99 /*** helper functions ***/
100
101 static void *
102 rtsp_nat_find_char(void *str, int ch, size_t len)
103 {
104     unsigned char *pStr = NULL;
105     if (len != 0) {
106         pStr = str;
107     }
108     do {
109         if (*pStr++ == ch) {
110             return ((void *)(pStr - 1));
111         }
112     } while (--len != 0);
113     return (NULL);
114 }
115
116 static void
117 get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen)
118 {
119     struct iphdr*   iph  = (struct iphdr*)skb->nh.iph;
120     struct tcphdr*  tcph = (struct tcphdr*)((char*)iph + iph->ihl*4);
121
122     *pptcpdata = (char*)tcph + tcph->doff*4;
123     *ptcpdatalen = ((char*)skb->h.raw + skb->len) - *pptcpdata;
124 }
125
126 /*** nat functions ***/
127
128 /*
129  * Mangle the "Transport:" header:
130  *   - Replace all occurences of "client_port=<spec>"
131  *   - Handle destination parameter
132  *
133  * In:
134  *   ct, ctinfo = conntrack context
135  *   pskb       = packet
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)
140  *
141  * Returns packet size difference.
142  *
143  * Assumes that a complete transport header is present, ending with CR or LF
144  */
145 static int
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)
149 {
150     char*       ptcp;
151     uint        tcplen;
152     char*       ptran;
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) */
157     u_int32_t   newip;
158     u_int16_t   loport, hiport;
159     uint        off = 0;
160     uint        diff;           /* Number of bytes we removed */
161
162     struct ip_ct_rtsp_expect* prtspexp = &exp->help.exp_rtsp_info;
163     struct ip_conntrack_tuple t;
164
165     char        szextaddr[15+1];
166     uint        extaddrlen;
167     int         is_stun;
168
169     get_skb_tcpdata(*pskb, &ptcp, &tcplen);
170     ptran = ptcp+tranoff;
171
172     if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
173         tranlen < 10 || !iseol(ptran[tranlen-1]) ||
174         nf_strncasecmp(ptran, "Transport:", 10) != 0)
175     {
176         INFOP("sanity check failed\n");
177         return 0;
178     }
179     off += 10;
180     SKIP_WSPACE(ptcp+tranoff, tranlen, off);
181
182     newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
183     t = exp->tuple;
184     t.dst.ip = newip;
185
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);
190
191     rbuf1len = rbufalen = 0;
192     switch (prtspexp->pbtype)
193     {
194     case pb_single:
195         for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
196         {
197             DEBUGP("Original UDP PORT value is %hu exp loport %hu hiport %hu\n", t.dst.u.udp.port,
198                     prtspexp->loport, prtspexp->hiport);
199             // Do not transpose the ports yet. If you do, you better register a helper
200             // to mangle them correctly when you receive packets on those ports.
201             //t.dst.u.udp.port = htons(loport);
202             if (ip_conntrack_change_expect(exp, &t) == 0)
203             {
204                 DEBUGP("using port %hu\n", loport);
205                 break;
206             }
207         }
208         if (loport != 0)
209         {
210             rbuf1len = sprintf(rbuf1, "%hu", loport);
211             rbufalen = sprintf(rbufa, "%hu", loport);
212         }
213         break;
214     case pb_range:
215         loport = prtspexp->loport;
216         if (loport != 0)
217         {
218             rbuf1len = sprintf(rbuf1, "%hu", loport);
219             rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1);
220             DEBUGP("MANGLING to ports (%hu-%hu) rbuf1 %s rbufa %s\n", prtspexp->loport, prtspexp->loport+1,
221                     rbuf1, rbufa);
222         }
223         break;
224     case pb_discon:
225         for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
226         {
227             DEBUGP("Original UDP PORT value is %hu exp loport %hu hiport %hu\n", t.dst.u.udp.port,
228                     prtspexp->loport, prtspexp->hiport);
229             // Do not transpose the ports yet. If you do, you better register a helper
230             // to mangle them correctly when you receive packets on those ports.
231             //t.dst.u.udp.port = htons(loport);
232             if (ip_conntrack_change_expect(exp, &t) == 0)
233             {
234                 DEBUGP("using port %hu (1 of 2)\n", loport);
235                 break;
236             }
237         }
238         for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */
239         {
240             t.dst.u.udp.port = htons(hiport);
241             if (ip_conntrack_change_expect(exp, &t) == 0)
242             {
243                 DEBUGP("using port %hu (2 of 2)\n", hiport);
244                 break;
245             }
246         }
247         if (loport != 0 && hiport != 0)
248         {
249             rbuf1len = sprintf(rbuf1, "%hu", loport);
250             if (hiport == loport+1)
251             {
252                 rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
253                 DEBUGP("Ports %hu-%hu\n", loport, hiport);
254             }
255             else
256             {
257                 rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport);
258                 DEBUGP("ports %hu-%hu\n", loport, hiport);
259             }
260         }
261         break;
262     default:
263         /* oops */
264         break;
265     }
266
267     if (rbuf1len == 0)
268     {
269         DEBUGP("Cannot get replacement ports\n");
270         return 0;   /* cannot get replacement port(s) */
271     }
272
273     /* Transport: tran;field;field=val,tran;field;field=val,... */
274     while (off < tranlen)
275     {
276         uint        saveoff;
277         const char* pparamend;
278         uint        nextparamoff;
279
280         pparamend = rtsp_nat_find_char(ptran+off, ',', tranlen-off);
281         pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
282         nextparamoff = pparamend-ptcp;
283
284         /*
285          * We pass over each param twice.  On the first pass, we look for a
286          * destination= field.  It is handled by the security policy.  If it
287          * is present, allowed, and equal to our external address, we assume
288          * that STUN is being used and we leave the client_port= field alone.
289          */
290         is_stun = 0;
291         saveoff = off;
292         while (off < nextparamoff)
293         {
294             const char* pfieldend;
295             uint        nextfieldoff;
296
297             pfieldend = rtsp_nat_find_char(ptran+off, ';', nextparamoff-off);
298             nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
299
300             if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0)
301             {
302                 if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
303                 {
304                     is_stun = 1;
305                 }
306                 if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun))
307                 {
308                     diff = nextfieldoff-off;
309                     if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
310                                                          off, diff, NULL, 0))
311                     {
312                         /* mangle failed, all we can do is bail */
313                         DEBUGP("mangle failed bailing out now\n");
314                         return 0;
315                     }
316                     get_skb_tcpdata(*pskb, &ptcp, &tcplen);
317                     ptran = ptcp+tranoff;
318                     tranlen -= diff;
319                     nextparamoff -= diff;
320                     nextfieldoff -= diff;
321                 }
322             }
323
324             off = nextfieldoff;
325         }
326         if (is_stun)
327         {
328             continue;
329         }
330         off = saveoff;
331         while (off < nextparamoff)
332         {
333             const char* pfieldend;
334             uint        nextfieldoff;
335
336             pfieldend = rtsp_nat_find_char(ptran+off, ';', nextparamoff-off);
337             nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
338
339             DEBUGP("off %d nextparamoff %d %s\n", off, nextparamoff, ptran+off);
340             if (strncmp(ptran+off, "client_port=", 12) == 0)
341             {
342                 u_int16_t   port;
343                 uint        numlen;
344                 uint        origoff;
345                 uint        origlen;
346                 char*       rbuf    = rbuf1;
347                 uint        rbuflen = rbuf1len;
348
349                 off += 12;
350                 origoff = (ptran-ptcp)+off;
351                 origlen = 0;
352                 numlen = nf_strtou16(ptran+off, &port);
353                 off += numlen;
354                 origlen += numlen;
355                 DEBUGP("Checking port %hu expec port %hu rbufa %s rbufalen %d\n",
356                        port, prtspexp->loport, rbufa, rbufalen);
357                 if (ptran[off] == '-' || ptran[off] == '/')
358                 {
359                     off++;
360                     origlen++;
361                     numlen = nf_strtou16(ptran+off, &port);
362                     off += numlen;
363                     origlen += numlen;
364                     rbuf = rbufa;
365                     rbuflen = rbufalen;
366                 }
367
368                 /*
369                  * note we cannot just memcpy() if the sizes are the same.
370                  * the mangle function does skb resizing, checks for a
371                  * cloned skb, and updates the checksums.
372                  *
373                  * parameter 4 below is offset from start of tcp data.
374                  */
375                 diff = origlen-rbuflen;
376                 DEBUGP("Before mangle rbuf %s diff %d ptran %s\n", rbuf, diff, ptran+off);
377                 if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
378                                           origoff, origlen, rbuf, rbuflen))
379                 {
380                     /* mangle failed, all we can do is bail */
381                     DEBUGP("MANGLE Failed\n");
382                     return 0;
383                 }
384                 get_skb_tcpdata(*pskb, &ptcp, &tcplen);
385                 ptran = ptcp+tranoff;
386                 tranlen -= diff;
387                 nextparamoff -= diff;
388                 nextfieldoff -= diff;
389                 DEBUGP("After mangle nextparamoff %d nextfieldoff %d ptran %s\n",
390                        nextparamoff, nextfieldoff, ptran);
391                 break;
392             }
393             off = nextfieldoff;
394         }
395
396             off = nextparamoff;
397     }
398
399     return 1;
400 }
401
402 static unsigned int
403 expected(struct sk_buff** pskb, uint hooknum,
404                 struct ip_conntrack* ct, struct ip_nat_info* info)
405 {
406     struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
407     struct udphdr *udph = (void *)iph + iph->ihl * 4;
408     struct ip_nat_multi_range mr;
409     u_int32_t newdstip, newsrcip, newip;
410
411     struct ip_conntrack *master = master_ct(ct);
412
413     IP_NF_ASSERT(info);
414     IP_NF_ASSERT(master);
415
416     IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));
417
418     newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
419     newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
420     newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip;
421
422     DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
423     DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
424     DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.%u.%u CLIENT PORT %hu\n",
425            NIPQUAD(newsrcip), NIPQUAD(newdstip), NIPQUAD(newip), ct->nat.rtsp_info.orig_port);
426     DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
427     DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple);
428
429     /*
430      * Ignore the UDP checksum for now.
431      */
432     udph->dest = ct->nat.rtsp_info.orig_port;
433     udph->check = 0;
434
435     mr.rangesize = 1;
436     /*
437      * We don't want to manip the per-protocol, just the IPs. Actually we
438      * did manipulate the UDP ports
439      */
440     mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
441     mr.range[0].min_ip = mr.range[0].max_ip = newip;
442
443     return ip_nat_setup_info(ct, &mr, hooknum);
444 }
445
446 static uint
447 help_out(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo,
448                 struct ip_conntrack_expect* exp, struct sk_buff** pskb)
449 {
450     char*   ptcp;
451     uint    tcplen;
452     uint    hdrsoff;
453     uint    hdrslen;
454     uint    lineoff;
455     uint    linelen;
456     uint    off;
457
458     struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
459     struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
460
461     struct ip_ct_rtsp_expect* prtspexp = &exp->help.exp_rtsp_info;
462
463     get_skb_tcpdata(*pskb, &ptcp, &tcplen);
464
465     hdrsoff = exp->seq - ntohl(tcph->seq);
466     hdrslen = prtspexp->len;
467     off = hdrsoff;
468
469     DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
470     DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
471
472     DEBUGP("SRC IP %u.%u.%u.%u DST IP %u.%u.%u.%u PORTS %hu-%hu\n",
473            NIPQUAD(iph->saddr), NIPQUAD(iph->daddr), prtspexp->loport,
474            prtspexp->hiport);
475     while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen))
476     {
477         if (linelen == 0)
478         {
479             break;
480         }
481         if (off > hdrsoff+hdrslen)
482         {
483             INFOP("!! overrun !!\n");
484             break;
485         }
486         DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
487
488         if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0)
489         {
490             uint oldtcplen = tcplen;
491             if (!rtsp_mangle_tran(ct, ctinfo, exp, pskb, lineoff, linelen))
492             {
493                 break;
494             }
495             get_skb_tcpdata(*pskb, &ptcp, &tcplen);
496             hdrslen -= (oldtcplen-tcplen);
497             off -= (oldtcplen-tcplen);
498             lineoff -= (oldtcplen-tcplen);
499             linelen -= (oldtcplen-tcplen);
500             DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
501         }
502     }
503     DEBUGP("SRC IP %u.%u.%u.%u DST IP %u.%u.%u.%u PORTS (%hu-%hu)\n",
504            NIPQUAD(iph->saddr), NIPQUAD(iph->daddr), tcph->source, tcph->dest);
505
506     return NF_ACCEPT;
507 }
508
509 static uint
510 help_in(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo,
511                 struct ip_conntrack_expect* exp, struct sk_buff** pskb)
512 {
513     /* XXX: unmangle */
514     DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
515     DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
516     return NF_ACCEPT;
517 }
518
519 static uint
520 help(struct ip_conntrack* ct, struct ip_conntrack_expect* exp,
521                 struct ip_nat_info* info, enum ip_conntrack_info ctinfo,
522                 unsigned int hooknum, struct sk_buff** pskb)
523 {
524     struct iphdr*  iph  = (struct iphdr*)(*pskb)->nh.iph;
525     struct tcphdr* tcph = (struct tcphdr*)((char*)iph + iph->ihl * 4);
526     uint datalen;
527     int dir;
528     struct ip_ct_rtsp_expect* ct_rtsp_info;
529     int rc = NF_ACCEPT;
530
531     DEBUGP("SRC IP %u.%u.%u.%u DST IP %u.%u.%u.%u PORTS %hu-%hu\n",
532            NIPQUAD(iph->saddr), NIPQUAD(iph->daddr), tcph->source,
533            tcph->dest);
534     if (ct == NULL || exp == NULL || info == NULL || pskb == NULL)
535     {
536         DEBUGP("!! null ptr (%p,%p,%p,%p) !!\n", ct, exp, info, pskb);
537         return NF_ACCEPT;
538     }
539
540     ct_rtsp_info = &exp->help.exp_rtsp_info;
541
542     /*
543      * Only mangle things once: original direction in POST_ROUTING
544      * and reply direction on PRE_ROUTING.
545      */
546     dir = CTINFO2DIR(ctinfo);
547     if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
548           || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY)))
549     {
550         DEBUGP("Not touching dir %s at hook %s\n",
551                dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
552                hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
553                : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
554                : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
555         return NF_ACCEPT;
556     }
557     DEBUGP("got beyond not touching\n");
558
559     datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
560
561     LOCK_BH(&ip_rtsp_lock);
562     /* Ensure the packet contains all of the marked data */
563     if (!between(exp->seq + ct_rtsp_info->len,
564                  ntohl(tcph->seq), ntohl(tcph->seq) + datalen))
565     {
566         /* Partial retransmission?  Probably a hacker. */
567         if (net_ratelimit())
568         {
569             INFOP("partial packet %u/%u in %u/%u\n",
570                    exp->seq, ct_rtsp_info->len, ntohl(tcph->seq), ntohl(tcph->seq) + datalen);
571         }
572         UNLOCK_BH(&ip_rtsp_lock);
573         return NF_DROP;
574     }
575
576     switch (dir)
577     {
578     case IP_CT_DIR_ORIGINAL:
579         rc = help_out(ct, ctinfo, exp, pskb);
580         break;
581     case IP_CT_DIR_REPLY:
582         rc = help_in(ct, ctinfo, exp, pskb);
583         break;
584     default:
585         /* oops */
586         break;
587     }
588     UNLOCK_BH(&ip_rtsp_lock);
589
590     return rc;
591 }
592
593 static struct ip_nat_helper ip_nat_rtsp_helpers[MAX_PORTS];
594 static char rtsp_names[MAX_PORTS][10];
595
596 /* This function is intentionally _NOT_ defined as  __exit */
597 static void
598 fini(void)
599 {
600     int i;
601
602     for (i = 0; i < num_ports; i++)
603     {
604         DEBUGP("unregistering helper for port %d\n", ports[i]);
605         ip_nat_helper_unregister(&ip_nat_rtsp_helpers[i]);
606     }
607 }
608
609 static int __init
610 init(void)
611 {
612     int ret = 0;
613     int i;
614     struct ip_nat_helper* hlpr;
615     char* tmpname;
616
617     printk("ip_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");
618
619     if (ports[0] == 0)
620     {
621         ports[0] = RTSP_PORT;
622     }
623
624     for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++)
625     {
626         hlpr = &ip_nat_rtsp_helpers[i];
627         memset(hlpr, 0, sizeof(struct ip_nat_helper));
628
629         hlpr->tuple.dst.protonum = IPPROTO_TCP;
630         hlpr->tuple.src.u.tcp.port = htons(ports[i]);
631         hlpr->mask.src.u.tcp.port = 0xFFFF;
632         hlpr->mask.dst.protonum = 0xFFFF;
633         hlpr->help = help;
634 #ifdef CONFIG_MIPS_BRCM
635         //hlpr->flags = 0;
636         //hlpr->me = THIS_MODULE;
637 #else
638         hlpr->flags = 0;
639         hlpr->me = THIS_MODULE;
640 #endif
641         hlpr->expect = expected;
642
643         tmpname = &rtsp_names[i][0];
644         if (ports[i] == RTSP_PORT)
645         {
646             sprintf(tmpname, "rtsp");
647         }
648         else
649         {
650             sprintf(tmpname, "rtsp-%d", i);
651         }
652         hlpr->name = tmpname;
653
654         DEBUGP("registering helper for port %d: name %s\n", ports[i], hlpr->name);
655         ret = ip_nat_helper_register(hlpr);
656
657         if (ret)
658         {
659             printk("ip_nat_rtsp: error registering helper for port %d\n", ports[i]);
660             fini();
661             return 1;
662         }
663         num_ports++;
664     }
665     if (stunaddr != NULL)
666     {
667         extip = in_aton(stunaddr);
668     }
669     if (destaction != NULL)
670     {
671         if (strcmp(destaction, "auto") == 0)
672         {
673             dstact = DSTACT_AUTO;
674         }
675         if (strcmp(destaction, "strip") == 0)
676         {
677             dstact = DSTACT_STRIP;
678         }
679         if (strcmp(destaction, "none") == 0)
680         {
681             dstact = DSTACT_NONE;
682         }
683     }
684     return ret;
685 }
686
687 NEEDS_CONNTRACK(rtsp);
688
689 module_init(init);
690 module_exit(fini);