www.usr.com/support/gpl/USR9107_release.1.4.tar.gz
[bcm963xx.git] / kernel / linux / net / ipv4 / netfilter / ip_conntrack_rtsp.c
index 5b2f8e0..4e3536c 100755 (executable)
@@ -47,7 +47,7 @@
 /*
  * To enable debugging, replace the line below with #define IP_NF_RTSP_DEBUG 1
  */
-#undef IP_NF_RTSP_DEBUG
+#undef IP_NF_RTSP_DEBUG 
 #define INFOP(args...) printk(KERN_INFO args)
 #ifdef IP_NF_RTSP_DEBUG
 #define DEBUGP(args...) printk(KERN_DEBUG "%s:%s ", __FILE__, __FUNCTION__); \
@@ -73,8 +73,8 @@ MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
 MODULE_PARM_DESC(ports, "port numbers of RTSP servers");
 MODULE_PARM(max_outstanding, "i");
 MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session");
-MODULE_PARM(setup_timeout, "i");
-MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels");
+//MODULE_PARM(setup_timeout, "i");
+//MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels");
 #endif
 
 DECLARE_LOCK(ip_rtsp_lock);
@@ -87,7 +87,10 @@ struct module* ip_conntrack_rtsp = THIS_MODULE;
  * lot of "no free client map entries" messages.
  */
 #define MAX_PORT_MAPS 16
-static u_int16_t g_tr_port = 10000;
+static u_int16_t g_tr_port = 7000;
+
+#define PAUSE_TIMEOUT      (5 * HZ)
+#define RTSP_PAUSE_TIMEOUT (6 * HZ)
 
 /*** default port list was here in the masq code: 554, 3030, 4040 ***/
 
@@ -99,16 +102,157 @@ static u_int16_t g_tr_port = 10000;
  * process the packets appropriately.
  */
 struct _rtsp_data_ports {
-    u_int32_t   client_ip;
-    u_int16_t   client_tcp_port;
-    u_int16_t   client_udp_lo;
-    u_int16_t   client_udp_hi;
-    portblock_t pbtype;
-    u_int16_t   nat_udp_lo;
-    u_int16_t   nat_udp_hi;
-    int         in_use;
+    u_int32_t           client_ip;
+    u_int16_t           client_tcp_port;
+    u_int16_t           client_udp_lo;
+    u_int16_t           client_udp_hi;
+    portblock_t         pbtype;
+    u_int16_t           nat_udp_lo;
+    u_int16_t           nat_udp_hi;
+    struct timer_list   pause_timeout;
+    struct ip_conntrack *ct_lo;
+    struct ip_conntrack *ct_hi;
+    int                 timeout_active;
+    int                 in_use;
 } rtsp_data_ports[MAX_PORT_MAPS];
 
+static u_int16_t rtsp_nat_to_client_pmap(u_int16_t nat_port);
+
+static void
+save_ct(struct ip_conntrack *ct)
+{
+    int i    = 0;
+    struct ip_conntrack_tuple *tp = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+
+    for (i = 0; i < MAX_PORT_MAPS; i++)
+    {
+        if (!rtsp_data_ports[i].in_use)
+        {
+            continue;
+        }
+        if (rtsp_data_ports[i].nat_udp_lo == ntohs((tp)->dst.u.all))
+        {
+            rtsp_data_ports[i].ct_lo = ct;
+            break;
+        }
+        else if (rtsp_data_ports[i].nat_udp_hi == ntohs((tp)->dst.u.all))
+        {
+            rtsp_data_ports[i].ct_hi = ct;
+            break;
+        }
+    }
+}
+
+static void
+rtsp_pause_timeout(unsigned long data)
+{
+    int    index = (int)data;
+    struct _rtsp_data_ports *rtsp_data = &rtsp_data_ports[index];
+    struct ip_conntrack *ct_lo = rtsp_data->ct_lo;
+
+    if (rtsp_data->in_use) {
+        rtsp_data->pause_timeout.expires = jiffies + PAUSE_TIMEOUT;
+        rtsp_data->pause_timeout.function = rtsp_pause_timeout;
+        rtsp_data->pause_timeout.data = data;
+        rtsp_data->timeout_active = 1;
+        ip_ct_refresh(ct_lo, RTSP_PAUSE_TIMEOUT);
+        add_timer(&rtsp_data->pause_timeout);
+    }
+}
+
+static void
+ip_conntrack_rtsp_proc_play(struct ip_conntrack *ct, const struct iphdr *iph)
+{
+    int i    = 0;
+    struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
+
+    for (i = 0; i < MAX_PORT_MAPS; i++)
+    {
+        if (!rtsp_data_ports[i].in_use)
+        {
+            continue;
+        }
+        DEBUGP("Searching client info IP %u.%u.%u.%u->%hu PORTS (%hu-%hu)\n",
+                NIPQUAD(iph->saddr), tcph->source, rtsp_data_ports[i].client_udp_lo,
+                rtsp_data_ports[i].client_udp_hi);
+        if ((rtsp_data_ports[i].client_ip == iph->saddr) &&
+            (rtsp_data_ports[i].client_tcp_port == tcph->source))
+        {
+            DEBUGP("Found client info SRC IP %u.%u.%u.%u TCP PORT %hu UDP PORTS (%hu-%hu)\n",
+                    NIPQUAD(iph->saddr), tcph->source, rtsp_data_ports[i].client_udp_lo,
+                    rtsp_data_ports[i].client_udp_hi);
+            if (rtsp_data_ports[i].timeout_active)
+            {
+                del_timer(&rtsp_data_ports[i].pause_timeout);
+                rtsp_data_ports[i].timeout_active = 0;
+            }
+        }
+    }
+}
+
+static void
+ip_conntrack_rtsp_proc_pause(struct ip_conntrack *ct, const struct iphdr *iph)
+{
+    int i    = 0;
+    struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
+    struct ip_conntrack_tuple *tp_lo;
+    struct ip_conntrack_tuple *tp_hi;
+    struct ip_conntrack *ct_lo;
+    struct ip_conntrack *ct_hi;
+
+    for (i = 0; i < MAX_PORT_MAPS; i++)
+    {
+        if (!rtsp_data_ports[i].in_use)
+        {
+            continue;
+        }
+        DEBUGP("Searching client info IP %u.%u.%u.%u->%hu PORTS (%hu-%hu)\n",
+                NIPQUAD(iph->saddr), tcph->source, rtsp_data_ports[i].client_udp_lo,
+                rtsp_data_ports[i].client_udp_hi);
+        if ((rtsp_data_ports[i].client_ip == iph->saddr) &&
+            (rtsp_data_ports[i].client_tcp_port == tcph->source))
+        {
+            DEBUGP("Found client info SRC IP %u.%u.%u.%u TCP PORT %hu UDP PORTS (%hu-%hu)\n",
+                    NIPQUAD(iph->saddr), tcph->source, rtsp_data_ports[i].client_udp_lo,
+                    rtsp_data_ports[i].client_udp_hi);
+            if (rtsp_data_ports[i].timeout_active != 0 ||
+                rtsp_data_ports[i].ct_lo == NULL)
+            {
+                break;
+            }
+            rtsp_data_ports[i].pause_timeout.expires = jiffies + PAUSE_TIMEOUT;
+            rtsp_data_ports[i].pause_timeout.function = rtsp_pause_timeout;
+            rtsp_data_ports[i].pause_timeout.data = (unsigned long)i;
+            add_timer(&rtsp_data_ports[i].pause_timeout);
+            rtsp_data_ports[i].timeout_active = 1;
+            rtsp_data_ports[i].ct_lo = ct;
+            tp_lo = &ct_lo->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+            tp_hi = &ct_hi->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+            ip_ct_refresh(ct, RTSP_PAUSE_TIMEOUT);
+        }
+    }
+}
+
+static int
+rtp_expect(struct ip_conntrack *ct)
+{
+    u_int16_t nat_port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port;
+    u_int16_t orig_port = 0;
+    DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+    DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+    orig_port = rtsp_nat_to_client_pmap(nat_port);
+    if (orig_port)
+    {
+        ct->nat.rtsp_info.orig_port = orig_port;
+    } else {
+        return NF_DROP;
+    }
+    DEBUGP("UDP client port %hu\n", ct->nat.rtsp_info.orig_port);
+    save_ct(ct);
+
+    return NF_ACCEPT;
+}
+
 /*
  * Maps client ports that are overlapping with other client UDP transport to
  * new NAT ports that will be tracked and converted back to client assigned
@@ -132,20 +276,30 @@ rtsp_client_to_nat_pmap(struct ip_ct_rtsp_expect *prtspexp, const struct iphdr *
                    rtsp_data_ports[i].in_use, NIPQUAD(rtsp_data_ports[i].client_ip),
                    rtsp_data_ports[i].client_udp_lo, rtsp_data_ports[i].client_udp_hi,
                    rtsp_data_ports[i].nat_udp_lo, rtsp_data_ports[i].nat_udp_hi);
+            if (ntohl(iph->saddr) == rtsp_data_ports[i].client_ip &&
+                ntohs(tcph->source) == rtsp_data_ports[i].client_tcp_port &&
+                ntohs(prtspexp->loport) == rtsp_data_ports[i].client_udp_lo &&
+                ntohs(prtspexp->hiport) == rtsp_data_ports[i].client_udp_hi)
+            {
+                prtspexp->loport  = rtsp_data_ports[i].nat_udp_lo;
+                prtspexp->hiport  = rtsp_data_ports[i].nat_udp_hi;
+                return rc = 2;
+            }
             continue;
         }
-            rtsp_data_ports[i].client_ip       = ntohl(iph->saddr);
-            rtsp_data_ports[i].client_tcp_port = ntohs(tcph->source);
-            rtsp_data_ports[i].client_udp_lo   = ntohs(prtspexp->loport);
-            rtsp_data_ports[i].client_udp_hi   = ntohs(prtspexp->hiport);
-            rtsp_data_ports[i].pbtype          = prtspexp->pbtype;
-            rtsp_data_ports[i].in_use          = 1;
-            DEBUGP("Mapped at index %d ORIGINAL PORTS %hu-%hu\n", i,
-                   ntohs(prtspexp->loport), ntohs(prtspexp->hiport));
-            prtspexp->loport  = rtsp_data_ports[i].nat_udp_lo = g_tr_port++;
-            prtspexp->hiport  = rtsp_data_ports[i].nat_udp_hi = g_tr_port++;
-            DEBUGP("NEW PORTS %hu-%hu\n", ntohs(prtspexp->loport), ntohs(prtspexp->hiport));
-            return rc = 1;
+        rtsp_data_ports[i].client_ip       = ntohl(iph->saddr);
+        rtsp_data_ports[i].client_tcp_port = ntohs(tcph->source);
+        rtsp_data_ports[i].client_udp_lo   = ntohs(prtspexp->loport);
+        rtsp_data_ports[i].client_udp_hi   = ntohs(prtspexp->hiport);
+        rtsp_data_ports[i].pbtype          = prtspexp->pbtype;
+        rtsp_data_ports[i].in_use          = 1;
+        init_timer(&rtsp_data_ports[i].pause_timeout);
+        DEBUGP("Mapped at index %d ORIGINAL PORTS %hu-%hu\n", i,
+               ntohs(prtspexp->loport), ntohs(prtspexp->hiport));
+        prtspexp->loport  = rtsp_data_ports[i].nat_udp_lo = g_tr_port++;
+        prtspexp->hiport  = rtsp_data_ports[i].nat_udp_hi = g_tr_port++;
+        DEBUGP("NEW PORTS %hu-%hu\n", ntohs(prtspexp->loport), ntohs(prtspexp->hiport));
+        return rc = 1;
     }
     return rc;
 }
@@ -172,12 +326,14 @@ rtsp_nat_to_client_pmap(u_int16_t nat_port)
         DEBUGP("Searching at index %d NAT_PORT %hu CLIENT PORTS (%hu-%hu)\n", i,
                ntohs(nat_port), rtsp_data_ports[i].client_udp_lo,
                rtsp_data_ports[i].client_udp_hi);
-        if (ntohs(nat_port) == rtsp_data_ports[i].nat_udp_lo) {
+        if (ntohs(nat_port) == rtsp_data_ports[i].nat_udp_lo ||
+            ntohs(nat_port) == rtsp_data_ports[i].client_udp_lo) {
             tr_port = rtsp_data_ports[i].client_udp_lo;
             DEBUGP("Found at index %d NAT_PORT %hu CLIENT PORTS (%hu-%hu) tr_port %hu\n", i,
                    nat_port, rtsp_data_ports[i].client_udp_lo,
                    rtsp_data_ports[i].client_udp_hi, tr_port);
-        } else if (ntohs(nat_port) == rtsp_data_ports[i].nat_udp_hi) {
+        } else if (ntohs(nat_port) == rtsp_data_ports[i].nat_udp_hi ||
+                   ntohs(nat_port) == rtsp_data_ports[i].client_udp_hi) {
             tr_port = rtsp_data_ports[i].client_udp_hi;
             DEBUGP("Found at index %d NAT_PORT %hu CLIENT PORTS %hu-%hu tr_port %hu\n", i,
                    nat_port, rtsp_data_ports[i].client_udp_lo,
@@ -209,8 +365,14 @@ ip_conntrack_rtsp_proc_teardown(struct iphdr *iph)
             DEBUGP("Found client info SRC IP %u.%u.%u.%u TCP PORT %hu UDP PORTS (%hu-%hu)\n",
                     NIPQUAD(iph->saddr), tcph->source, rtsp_data_ports[i].client_udp_lo,
                     rtsp_data_ports[i].client_udp_hi);
+            if (rtsp_data_ports[i].timeout_active)
+            {
+                del_timer(&rtsp_data_ports[i].pause_timeout);
+                rtsp_data_ports[i].timeout_active = 0;
+            }
+            memset(&rtsp_data_ports[i], 0, sizeof(struct _rtsp_data_ports));
             rtsp_data_ports[i].in_use = 0;
-            break;
+            //break;
         }
     }
 }
@@ -329,7 +491,7 @@ rtsp_parse_transport(char* ptran, uint tranlen,
         const char* pparamend;
         uint        nextparamoff;
 
-        pparamend = memchr(ptran+off, ',', tranlen-off);
+        pparamend = find_char(ptran+off, ',', tranlen-off);
         pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
         nextparamoff = pparamend-ptran;
 
@@ -356,6 +518,7 @@ rtsp_parse_transport(char* ptran, uint tranlen,
                 else
                 {
                     prtspexp->loport = prtspexp->hiport = port;
+                    DEBUGP("DASH or SLASH 0x%x\n", ptran[off]);
                     if (ptran[off] == '-')
                     {
                         off++;
@@ -403,18 +566,6 @@ rtsp_parse_transport(char* ptran, uint tranlen,
     return rc;
 }
 
-static int
-rtp_expect(struct ip_conntrack *ct)
-{
-    u_int16_t nat_port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port;
-    DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
-    DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
-    ct->nat.rtsp_info.orig_port = rtsp_nat_to_client_pmap(nat_port);
-    DEBUGP("UDP client port %hu\n", ct->nat.rtsp_info.orig_port);
-
-    return NF_ACCEPT;
-}
-
 /*** conntrack functions ***/
 
 /* outbound packet: client->server */
@@ -440,8 +591,8 @@ help_out(struct iphdr* iph, char* pdata, size_t datalen,
         uint    off;
         int     rc;
         uint    port = 0;
-        int     i    = 0;
         struct  ip_conntrack_expect *new_exp = NULL;
+        int     ret = 0;
 
         if (!rtsp_parse_message(pdata, datalen, &dataoff,
                                 &hdrsoff, &hdrslen,
@@ -450,6 +601,18 @@ help_out(struct iphdr* iph, char* pdata, size_t datalen,
             break;      /* not a valid message */
         }
 
+        if (strncmp(pdata+cmdoff, "PLAY ", 5) == 0)
+        {
+            ip_conntrack_rtsp_proc_play(ct, iph);
+            continue;
+        }
+
+        if (strncmp(pdata+cmdoff, "PAUSE ", 6) == 0)
+        {
+            ip_conntrack_rtsp_proc_pause(ct, iph);
+            continue;
+        }
+
         if (strncmp(pdata+cmdoff, "TEARDOWN ", 6) == 0)
         {
             ip_conntrack_rtsp_proc_teardown(iph);   /* TEARDOWN message */
@@ -502,15 +665,14 @@ help_out(struct iphdr* iph, char* pdata, size_t datalen,
          * Translate the original ports to the NAT ports and note them
          * down to translate back in the return direction.
          */
-        if (!rtsp_client_to_nat_pmap(&exp.help.exp_rtsp_info, iph, ct))
+        if (!(ret = rtsp_client_to_nat_pmap(&exp.help.exp_rtsp_info, iph, ct)))
         {
             DEBUGP("Dropping the packet. No more space in the mapping table\n");
             UNLOCK_BH(&ip_rtsp_lock);
             return NF_DROP;
         }
-        i = 0;
         port = exp.help.exp_rtsp_info.loport;
-        while (i < 2) {
+        while (port <= exp.help.exp_rtsp_info.hiport) {
             /*
              * Allocate expectation for tracking this connection
              */
@@ -527,11 +689,15 @@ help_out(struct iphdr* iph, char* pdata, size_t datalen,
             DEBUGP("Adding UDP port %hu,%hu\n", htons(port), ntohs(port));
 
             new_exp->tuple = ct->tuplehash[!dir].tuple;
-            new_exp->tuple.dst.u.udp.port = htons(port);
+            if (ret == 2) {
+                    new_exp->tuple.dst.u.udp.port = htons(g_tr_port);
+                    g_tr_port++;
+            } else
+                new_exp->tuple.dst.u.udp.port = htons(port);
             new_exp->tuple.dst.protonum = IPPROTO_UDP;
             new_exp->mask.src.ip  = 0xffffffff;
             new_exp->mask.dst.ip  = 0xffffffff;
-            //exp.mask.dst.u.udp.port  = (exp.help.exp_rtsp_info.pbtype == pb_range) ? 0xfffe : 0xffff;
+            //new_exp->mask.dst.u.udp.port  = (exp.help.exp_rtsp_info.pbtype == pb_range) ? 0xfffe : 0xffff;
             new_exp->mask.dst.u.udp.port  = 0xffff;
             new_exp->expectfn = rtp_expect;
             new_exp->mask.dst.protonum  = 0xffff;
@@ -546,14 +712,13 @@ help_out(struct iphdr* iph, char* pdata, size_t datalen,
             rc = ip_conntrack_expect_related(new_exp, ct);
             if (rc == 0)
             {
-                DEBUGP("ip_conntrack_expect_related succeeded\n");
+                DEBUGP("ip_conntrack_expect_related succeeded loport\n");
             }
             else
             {
-                INFOP("ip_conntrack_expect_related failed\n");
+                DEBUGP("ip_conntrack_expect_related loport failed (%d)\n", rc);
             }
             port++;
-            i++;
         }
         UNLOCK_BH(&ip_rtsp_lock);
     }
@@ -632,6 +797,16 @@ fini(void)
         DEBUGP("unregistering port %d\n", ports[i]);
         ip_conntrack_helper_unregister(&rtsp_helpers[i]);
     }
+    for (i = 0; i < MAX_PORT_MAPS; i++)
+    {
+        if (!rtsp_data_ports[i].in_use)
+        {
+            continue;
+        }
+        if (rtsp_data_ports[i].timeout_active == 1) {
+            del_timer(&rtsp_data_ports[i].pause_timeout);
+        }
+    }
 }
 
 static int __init