2 * RTSP extension for IP connection tracking
3 * (C) 2003 by Tom Marshall <tmarshall@real.com>
4 * based on ip_conntrack_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_conntrack_rtsp.o ports=port1,port2,...port<MAX_PORTS>
13 * max_outstanding=n setup_timeout=secs
15 * If no ports are specified, the default will be port 554.
17 * With max_outstanding you can define the maximum number of not yet
18 * answered SETUP requests per RTSP session (default 8).
19 * With setup_timeout you can specify how long the system waits for
20 * an expected data channel (default 300 seconds).
23 #include <linux/config.h>
24 #include <linux/module.h>
25 #include <linux/netfilter.h>
27 #include <linux/udp.h>
28 #include <net/checksum.h>
31 #include <linux/netfilter_ipv4/lockhelp.h>
32 #include <linux/netfilter_ipv4/ip_nat_rtsp.h>
33 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
34 #include <linux/netfilter_ipv4/ip_conntrack_rtsp.h>
36 #include <linux/ctype.h>
37 #define NF_NEED_STRNCASECMP
38 #define NF_NEED_STRTOU16
39 #define NF_NEED_STRTOU32
40 #define NF_NEED_NEXTLINE
41 #include <linux/netfilter_helpers.h>
42 #define NF_NEED_MIME_NEXTLINE
43 #include <linux/netfilter_mime.h>
45 #define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */
48 * To enable debugging, replace the line below with #define IP_NF_RTSP_DEBUG 1
50 #undef IP_NF_RTSP_DEBUG
51 #define INFOP(args...) printk(KERN_INFO args)
52 #ifdef IP_NF_RTSP_DEBUG
53 #define DEBUGP(args...) printk(KERN_DEBUG "%s:%s ", __FILE__, __FUNCTION__); \
56 #define DEBUGP(args...)
60 static int ports[MAX_PORTS];
61 static int num_ports = 0;
62 static int max_outstanding = 8;
63 static unsigned int setup_timeout = 300;
65 /* This is slow, but it's simple. --RR */
66 static char rtsp_buffer[65536];
68 MODULE_AUTHOR("Tom Marshall <tmarshall@real.com>");
69 MODULE_DESCRIPTION("RTSP connection tracking module");
70 MODULE_LICENSE("GPL");
72 MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
73 MODULE_PARM_DESC(ports, "port numbers of RTSP servers");
74 MODULE_PARM(max_outstanding, "i");
75 MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session");
76 MODULE_PARM(setup_timeout, "i");
77 MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels");
80 DECLARE_LOCK(ip_rtsp_lock);
81 struct module* ip_conntrack_rtsp = THIS_MODULE;
84 * Max mappings we will allow for one RTSP connection (for RTP, the number
85 * of allocated ports is twice this value). Note that SMIL burns a lot of
86 * ports so keep this reasonably high. If this is too low, you will see a
87 * lot of "no free client map entries" messages.
89 #define MAX_PORT_MAPS 16
90 static u_int16_t g_tr_port = 10000;
92 /*** default port list was here in the masq code: 554, 3030, 4040 ***/
94 #define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
96 * Structure to hold the mappings from client to NAT vice versa. If we
97 * mangle UDP ports in the outgoing SETUP message, we must properly
98 * mangle them in the return direction so that the client will
99 * process the packets appropriately.
101 struct _rtsp_data_ports {
103 u_int16_t client_tcp_port;
104 u_int16_t client_udp_lo;
105 u_int16_t client_udp_hi;
107 u_int16_t nat_udp_lo;
108 u_int16_t nat_udp_hi;
110 } rtsp_data_ports[MAX_PORT_MAPS];
113 * Maps client ports that are overlapping with other client UDP transport to
114 * new NAT ports that will be tracked and converted back to client assigned
119 rtsp_client_to_nat_pmap(struct ip_ct_rtsp_expect *prtspexp, const struct iphdr *iph,
120 struct ip_conntrack *ct)
124 struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
126 DEBUGP("IP %u.%u.%u.%u->%u.%u.%u.%u PORTS (%hu-%hu)\n", NIPQUAD(iph->saddr),
127 NIPQUAD(iph->daddr), tcph->source, tcph->dest);
129 for (i = 0; i < MAX_PORT_MAPS; i++) {
130 if (rtsp_data_ports[i].in_use) {
131 DEBUGP("Index %d in_use flag %d IP %u.%u.%u.%u CLIENT %hu-%hu NAT %hu-%hu\n", i,
132 rtsp_data_ports[i].in_use, NIPQUAD(rtsp_data_ports[i].client_ip),
133 rtsp_data_ports[i].client_udp_lo, rtsp_data_ports[i].client_udp_hi,
134 rtsp_data_ports[i].nat_udp_lo, rtsp_data_ports[i].nat_udp_hi);
137 rtsp_data_ports[i].client_ip = ntohl(iph->saddr);
138 rtsp_data_ports[i].client_tcp_port = ntohs(tcph->source);
139 rtsp_data_ports[i].client_udp_lo = ntohs(prtspexp->loport);
140 rtsp_data_ports[i].client_udp_hi = ntohs(prtspexp->hiport);
141 rtsp_data_ports[i].pbtype = prtspexp->pbtype;
142 rtsp_data_ports[i].in_use = 1;
143 DEBUGP("Mapped at index %d ORIGINAL PORTS %hu-%hu\n", i,
144 ntohs(prtspexp->loport), ntohs(prtspexp->hiport));
145 prtspexp->loport = rtsp_data_ports[i].nat_udp_lo = g_tr_port++;
146 prtspexp->hiport = rtsp_data_ports[i].nat_udp_hi = g_tr_port++;
147 DEBUGP("NEW PORTS %hu-%hu\n", ntohs(prtspexp->loport), ntohs(prtspexp->hiport));
154 * Performs NAT to client port mapping. Incoming UDP ports are looked up and
155 * appropriate client ports are extracted from the table and returned.
156 * Return client_udp_port or 0 when no matches found.
159 rtsp_nat_to_client_pmap(u_int16_t nat_port)
162 u_int16_t tr_port = 0;
164 for (i = 0; i < MAX_PORT_MAPS; i++) {
165 if (!rtsp_data_ports[i].in_use) {
169 * Check if the UDP ports match any of our NAT ports and return
170 * the client UDP ports.
172 DEBUGP("Searching at index %d NAT_PORT %hu CLIENT PORTS (%hu-%hu)\n", i,
173 ntohs(nat_port), rtsp_data_ports[i].client_udp_lo,
174 rtsp_data_ports[i].client_udp_hi);
175 if (ntohs(nat_port) == rtsp_data_ports[i].nat_udp_lo) {
176 tr_port = rtsp_data_ports[i].client_udp_lo;
177 DEBUGP("Found at index %d NAT_PORT %hu CLIENT PORTS (%hu-%hu) tr_port %hu\n", i,
178 nat_port, rtsp_data_ports[i].client_udp_lo,
179 rtsp_data_ports[i].client_udp_hi, tr_port);
180 } else if (ntohs(nat_port) == rtsp_data_ports[i].nat_udp_hi) {
181 tr_port = rtsp_data_ports[i].client_udp_hi;
182 DEBUGP("Found at index %d NAT_PORT %hu CLIENT PORTS %hu-%hu tr_port %hu\n", i,
183 nat_port, rtsp_data_ports[i].client_udp_lo,
184 rtsp_data_ports[i].client_udp_hi, tr_port);
192 ip_conntrack_rtsp_proc_teardown(struct iphdr *iph)
195 struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
197 for (i = 0; i < MAX_PORT_MAPS; i++)
199 if (!rtsp_data_ports[i].in_use)
203 DEBUGP("Searching client info IP %u.%u.%u.%u->%hu PORTS (%hu-%hu)\n",
204 NIPQUAD(iph->saddr), tcph->source, rtsp_data_ports[i].client_udp_lo,
205 rtsp_data_ports[i].client_udp_hi);
206 if ((rtsp_data_ports[i].client_ip == iph->saddr) &&
207 (rtsp_data_ports[i].client_tcp_port == tcph->source))
209 DEBUGP("Found client info SRC IP %u.%u.%u.%u TCP PORT %hu UDP PORTS (%hu-%hu)\n",
210 NIPQUAD(iph->saddr), tcph->source, rtsp_data_ports[i].client_udp_lo,
211 rtsp_data_ports[i].client_udp_hi);
212 rtsp_data_ports[i].in_use = 0;
219 find_char(void *str, int ch, size_t len)
221 unsigned char *pStr = NULL;
227 return ((void *)(pStr - 1));
229 } while (--len != 0);
234 * Parse an RTSP packet.
236 * Returns zero if parsing failed.
239 * IN ptcp tcp data pointer
240 * IN tcplen tcp data len
241 * IN/OUT ptcpoff points to current tcp offset
242 * OUT phdrsoff set to offset of rtsp headers
243 * OUT phdrslen set to length of rtsp headers
244 * OUT pcseqoff set to offset of CSeq header
245 * OUT pcseqlen set to length of CSeq header
248 rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff,
249 uint* phdrsoff, uint* phdrslen,
250 uint* pcseqoff, uint* pcseqlen)
256 if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen))
261 *phdrsoff = *ptcpoff;
262 while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen))
268 *ptcpoff += min(entitylen, tcplen - *ptcpoff);
272 if (lineoff+linelen > tcplen)
274 INFOP("!! overrun !!\n");
278 if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0)
283 if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0)
285 uint off = lineoff+15;
286 SKIP_WSPACE(ptcp+lineoff, linelen, off);
287 nf_strtou32(ptcp+off, &entitylen);
290 *phdrslen = (*ptcpoff) - (*phdrsoff);
296 * Find lo/hi client ports (if any) in transport header
298 * ptcp, tcplen = packet
299 * tranoff, tranlen = buffer to search
302 * pport_lo, pport_hi = lo/hi ports (host endian)
304 * Returns nonzero if any client ports found
306 * Note: it is valid (and expected) for the client to request multiple
307 * transports, so we need to parse the entire line.
310 rtsp_parse_transport(char* ptran, uint tranlen,
311 struct ip_ct_rtsp_expect* prtspexp)
316 if (tranlen < 10 || !iseol(ptran[tranlen-1]) ||
317 nf_strncasecmp(ptran, "Transport:", 10) != 0)
319 INFOP("sanity check failed\n");
322 DEBUGP("tran='%.*s'\n", (int)tranlen, ptran);
324 SKIP_WSPACE(ptran, tranlen, off);
326 /* Transport: tran;field;field=val,tran;field;field=val,... */
327 while (off < tranlen)
329 const char* pparamend;
332 pparamend = memchr(ptran+off, ',', tranlen-off);
333 pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
334 nextparamoff = pparamend-ptran;
336 while (off < nextparamoff)
338 const char* pfieldend;
341 pfieldend = find_char(ptran+off, ';', nextparamoff-off);
342 nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
344 if (strncmp(ptran+off, "client_port=", 12) == 0)
350 numlen = nf_strtou16(ptran+off, &port);
352 if (prtspexp->loport != 0 && prtspexp->loport != port)
354 DEBUGP("multiple ports found, port %hu ignored\n", port);
358 prtspexp->loport = prtspexp->hiport = port;
359 if (ptran[off] == '-')
362 numlen = nf_strtou16(ptran+off, &port);
364 prtspexp->pbtype = pb_range;
365 prtspexp->hiport = port;
367 // If we have a range, assume rtp:
368 // loport must be even, hiport must be loport+1
369 if ((prtspexp->loport & 0x0001) != 0 ||
370 prtspexp->hiport != prtspexp->loport+1)
372 DEBUGP("incorrect range: %hu-%hu, correcting\n",
373 prtspexp->loport, prtspexp->hiport);
374 prtspexp->loport &= 0xfffe;
375 prtspexp->hiport = prtspexp->loport+1;
378 else if (ptran[off] == '/')
381 numlen = nf_strtou16(ptran+off, &port);
383 prtspexp->pbtype = pb_discon;
384 prtspexp->hiport = port;
391 * Note we don't look for the destination parameter here.
392 * If we are using NAT, the NAT module will handle it. If not,
393 * and the client is sending packets elsewhere, the expectation
394 * will quietly time out.
407 rtp_expect(struct ip_conntrack *ct)
409 u_int16_t nat_port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port;
410 DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
411 DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
412 ct->nat.rtsp_info.orig_port = rtsp_nat_to_client_pmap(nat_port);
413 DEBUGP("UDP client port %hu\n", ct->nat.rtsp_info.orig_port);
418 /*** conntrack functions ***/
420 /* outbound packet: client->server */
422 help_out(struct iphdr* iph, char* pdata, size_t datalen,
423 struct ip_conntrack* ct, enum ip_conntrack_info ctinfo)
425 int dir = CTINFO2DIR(ctinfo); /* = IP_CT_DIR_ORIGINAL */
427 struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
429 struct ip_conntrack_expect exp;
431 while (dataoff < datalen)
433 uint cmdoff = dataoff;
444 struct ip_conntrack_expect *new_exp = NULL;
446 if (!rtsp_parse_message(pdata, datalen, &dataoff,
450 break; /* not a valid message */
453 if (strncmp(pdata+cmdoff, "TEARDOWN ", 6) == 0)
455 ip_conntrack_rtsp_proc_teardown(iph); /* TEARDOWN message */
459 if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0)
461 continue; /* not a SETUP message */
463 DEBUGP("found a setup message\n");
465 memset(&exp, 0, sizeof(exp));
469 while (nf_mime_nextline(pdata+hdrsoff, hdrslen, &off,
476 if (off > hdrsoff+hdrslen)
478 INFOP("!! overrun !!");
482 if (nf_strncasecmp(pdata+hdrsoff+lineoff, "Transport:", 10) == 0)
484 rtsp_parse_transport(pdata+hdrsoff+lineoff, linelen,
485 &exp.help.exp_rtsp_info);
489 if (exp.help.exp_rtsp_info.loport == 0)
491 DEBUGP("no udp transports found\n");
492 continue; /* no udp transports found */
495 DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n",
496 (int)exp.help.exp_rtsp_info.pbtype,
497 exp.help.exp_rtsp_info.loport,
498 exp.help.exp_rtsp_info.hiport);
500 LOCK_BH(&ip_rtsp_lock);
502 * Translate the original ports to the NAT ports and note them
503 * down to translate back in the return direction.
505 if (!rtsp_client_to_nat_pmap(&exp.help.exp_rtsp_info, iph, ct))
507 DEBUGP("Dropping the packet. No more space in the mapping table\n");
508 UNLOCK_BH(&ip_rtsp_lock);
512 port = exp.help.exp_rtsp_info.loport;
515 * Allocate expectation for tracking this connection
517 new_exp = ip_conntrack_expect_alloc();
519 INFOP("Failed to get a new expectation entry\n");
520 UNLOCK_BH(&ip_rtsp_lock);
523 memcpy(new_exp, &exp, sizeof(struct ip_conntrack_expect));
524 new_exp->seq = ntohl(tcph->seq) + hdrsoff; /* mark all the headers */
525 new_exp->help.exp_rtsp_info.len = hdrslen;
527 DEBUGP("Adding UDP port %hu,%hu\n", htons(port), ntohs(port));
529 new_exp->tuple = ct->tuplehash[!dir].tuple;
530 new_exp->tuple.dst.u.udp.port = htons(port);
531 new_exp->tuple.dst.protonum = IPPROTO_UDP;
532 new_exp->mask.src.ip = 0xffffffff;
533 new_exp->mask.dst.ip = 0xffffffff;
534 //exp.mask.dst.u.udp.port = (exp.help.exp_rtsp_info.pbtype == pb_range) ? 0xfffe : 0xffff;
535 new_exp->mask.dst.u.udp.port = 0xffff;
536 new_exp->expectfn = rtp_expect;
537 new_exp->mask.dst.protonum = 0xffff;
539 DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n",
540 NIPQUAD(new_exp->tuple.src.ip),
541 ntohs(new_exp->tuple.src.u.tcp.port),
542 NIPQUAD(new_exp->tuple.dst.ip),
543 ntohs(new_exp->tuple.dst.u.tcp.port));
545 /* pass the request off to the nat helper */
546 rc = ip_conntrack_expect_related(new_exp, ct);
549 DEBUGP("ip_conntrack_expect_related succeeded\n");
553 INFOP("ip_conntrack_expect_related failed\n");
558 UNLOCK_BH(&ip_rtsp_lock);
564 /* inbound packet: server->client */
566 help_in(struct tcphdr* tcph, char* pdata, size_t datalen,
567 struct ip_conntrack* ct, enum ip_conntrack_info ctinfo)
573 help(struct sk_buff* skb,
574 struct ip_conntrack* ct, enum ip_conntrack_info ctinfo)
577 struct iphdr *iph = skb->nh.iph;
582 /* Until there's been traffic both ways, don't look in packets. */
583 if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY)
585 DEBUGP("conntrackinfo = %u\n", ctinfo);
589 /* Not whole TCP header? */
590 if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) != 0)
596 dataoff = skb->nh.iph->ihl*4 + tcph.doff*4;
597 if ( skb->nh.iph->ihl*4 + tcph.doff*4 >= skb->len)
602 LOCK_BH(&ip_rtsp_lock);
603 skb_copy_bits(skb, dataoff, rtsp_buffer, skb->len - dataoff);
605 datalen = skb->len - dataoff;
606 switch (CTINFO2DIR(ctinfo))
608 case IP_CT_DIR_ORIGINAL:
609 help_out(iph, data, datalen, ct, ctinfo);
611 case IP_CT_DIR_REPLY:
612 help_in(&tcph, data, datalen, ct, ctinfo);
618 UNLOCK_BH(&ip_rtsp_lock);
623 static struct ip_conntrack_helper rtsp_helpers[MAX_PORTS];
624 static char rtsp_names[MAX_PORTS][10];
630 for (i = 0; i < num_ports; i++)
632 DEBUGP("unregistering port %d\n", ports[i]);
633 ip_conntrack_helper_unregister(&rtsp_helpers[i]);
641 struct ip_conntrack_helper *hlpr;
644 printk("ip_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n");
646 if (max_outstanding < 1)
648 printk("ip_conntrack_rtsp: max_outstanding must be a positive integer\n");
651 if (setup_timeout < 0)
653 printk("ip_conntrack_rtsp: setup_timeout must be a positive integer\n");
657 /* If no port given, default to standard rtsp port */
660 ports[0] = RTSP_PORT;
663 for (i = 0; i < MAX_PORT_MAPS; i++)
665 memset(&rtsp_data_ports[i], 0, sizeof(struct _rtsp_data_ports));
666 rtsp_data_ports[i].in_use = 0;
669 for (i = 0; (i < MAX_PORTS) && ports[i]; i++)
671 hlpr = &rtsp_helpers[i];
672 memset(hlpr, 0, sizeof(struct ip_conntrack_helper));
673 hlpr->tuple.src.u.tcp.port = htons(ports[i]);
674 hlpr->tuple.dst.protonum = IPPROTO_TCP;
675 hlpr->mask.src.u.tcp.port = 0xFFFF;
676 hlpr->mask.dst.protonum = 0xFFFF;
677 hlpr->max_expected = max_outstanding;
679 hlpr->flags = IP_CT_HELPER_F_REUSE_EXPECT;
680 hlpr->me = ip_conntrack_rtsp;
683 tmpname = &rtsp_names[i][0];
684 if (ports[i] == RTSP_PORT)
686 sprintf(tmpname, "rtsp");
690 sprintf(tmpname, "rtsp-%d", i);
692 hlpr->name = tmpname;
694 DEBUGP("port #%d: %d\n", i, ports[i]);
696 ret = ip_conntrack_helper_register(hlpr);
700 printk("ip_conntrack_rtsp: ERROR registering port %d\n", ports[i]);
709 PROVIDES_CONNTRACK(rtsp);
710 EXPORT_SYMBOL(ip_rtsp_lock);