1/* 2 * RTSP extension for IP connection tracking 3 * (C) 2003 by Tom Marshall <tmarshall at real.com> 4 * based on ip_conntrack_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 nf_conntrack_rtsp.o ports=port1,port2,...port<MAX_PORTS> 13 * max_outstanding=n setup_timeout=secs 14 * 15 * If no ports are specified, the default will be port 554. 16 * 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). 21 * 22 * 2005-02-13: Harald Welte <laforge at netfilter.org> 23 * - port to 2.6 24 * - update to recent post-2.6.11 api changes 25 * 2006-09-14: Steven Van Acker <deepstar at singularity.be> 26 * - removed calls to NAT code from conntrack helper: NAT no longer needed to use rtsp-conntrack 27 * 2007-04-18: Michael Guntsche <mike at it-loops.com> 28 * - Port to new NF API 29 */ 30 31#include <linux/module.h> 32#include <linux/netfilter.h> 33#include <linux/ip.h> 34#include <linux/inet.h> 35#include <net/tcp.h> 36 37#include <net/netfilter/nf_conntrack.h> 38#include <net/netfilter/nf_conntrack_expect.h> 39#include <net/netfilter/nf_conntrack_helper.h> 40#include <linux/netfilter/nf_conntrack_rtsp.h> 41 42#define NF_NEED_STRNCASECMP 43#define NF_NEED_STRTOU16 44#define NF_NEED_STRTOU32 45#define NF_NEED_NEXTLINE 46#include <linux/netfilter_helpers.h> 47#define NF_NEED_MIME_NEXTLINE 48#include <linux/netfilter_mime.h> 49 50#include <linux/ctype.h> 51#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */ 52 53#define MAX_PORTS 8 54static int ports[MAX_PORTS]; 55static int num_ports = 0; 56static int max_outstanding = 8; 57static unsigned int setup_timeout = 300; 58 59MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>"); 60MODULE_DESCRIPTION("RTSP connection tracking module"); 61MODULE_LICENSE("GPL"); 62module_param_array(ports, int, &num_ports, 0400); 63MODULE_PARM_DESC(ports, "port numbers of RTSP servers"); 64module_param(max_outstanding, int, 0400); 65MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session"); 66module_param(setup_timeout, int, 0400); 67MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels"); 68 69static char *rtsp_buffer; 70static DEFINE_SPINLOCK(rtsp_buffer_lock); 71 72static struct nf_conntrack_expect_policy rtsp_exp_policy; 73 74unsigned int (*nf_nat_rtsp_hook)(struct sk_buff *skb, 75 enum ip_conntrack_info ctinfo, 76 unsigned int matchoff, unsigned int matchlen,struct ip_ct_rtsp_expect* prtspexp, 77 struct nf_conntrack_expect *exp); 78void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp); 79 80EXPORT_SYMBOL_GPL(nf_nat_rtsp_hook); 81 82/* 83 * Max mappings we will allow for one RTSP connection (for RTP, the number 84 * of allocated ports is twice this value). Note that SMIL burns a lot of 85 * ports so keep this reasonably high. If this is too low, you will see a 86 * lot of "no free client map entries" messages. 87 */ 88#define MAX_PORT_MAPS 16 89 90/*** default port list was here in the masq code: 554, 3030, 4040 ***/ 91 92#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; } 93 94/* 95 * Parse an RTSP packet. 96 * 97 * Returns zero if parsing failed. 98 * 99 * Parameters: 100 * IN ptcp tcp data pointer 101 * IN tcplen tcp data len 102 * IN/OUT ptcpoff points to current tcp offset 103 * OUT phdrsoff set to offset of rtsp headers 104 * OUT phdrslen set to length of rtsp headers 105 * OUT pcseqoff set to offset of CSeq header 106 * OUT pcseqlen set to length of CSeq header 107 */ 108static int 109rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff, 110 uint* phdrsoff, uint* phdrslen, 111 uint* pcseqoff, uint* pcseqlen, 112 uint* transoff, uint* translen) 113{ 114 uint entitylen = 0; 115 uint lineoff; 116 uint linelen; 117 118 if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) 119 return 0; 120 121 *phdrsoff = *ptcpoff; 122 while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) { 123 if (linelen == 0) { 124 if (entitylen > 0) 125 *ptcpoff += min(entitylen, tcplen - *ptcpoff); 126 break; 127 } 128 if (lineoff+linelen > tcplen) { 129 pr_info("!! overrun !!\n"); 130 break; 131 } 132 133 if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) { 134 *pcseqoff = lineoff; 135 *pcseqlen = linelen; 136 } 137 138 if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) { 139 *transoff = lineoff; 140 *translen = linelen; 141 } 142 143 if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) { 144 uint off = lineoff+15; 145 SKIP_WSPACE(ptcp+lineoff, linelen, off); 146 nf_strtou32(ptcp+off, &entitylen); 147 } 148 } 149 *phdrslen = (*ptcpoff) - (*phdrsoff); 150 151 return 1; 152} 153 154/* 155 * Find lo/hi client ports (if any) in transport header 156 * In: 157 * ptcp, tcplen = packet 158 * tranoff, tranlen = buffer to search 159 * 160 * Out: 161 * pport_lo, pport_hi = lo/hi ports (host endian) 162 * 163 * Returns nonzero if any client ports found 164 * 165 * Note: it is valid (and expected) for the client to request multiple 166 * transports, so we need to parse the entire line. 167 */ 168static int 169rtsp_parse_transport(char* ptran, uint tranlen, 170 struct ip_ct_rtsp_expect* prtspexp) 171{ 172 int rc = 0; 173 uint off = 0; 174 175 if (tranlen < 10 || !iseol(ptran[tranlen-1]) || 176 nf_strncasecmp(ptran, "Transport:", 10) != 0) { 177 pr_info("sanity check failed\n"); 178 return 0; 179 } 180 181 pr_debug("tran='%.*s'\n", (int)tranlen, ptran); 182 off += 10; 183 SKIP_WSPACE(ptran, tranlen, off); 184 185 /* Transport: tran;field;field=val,tran;field;field=val,... */ 186 while (off < tranlen) { 187 const char* pparamend; 188 uint nextparamoff; 189 190 pparamend = memchr(ptran+off, ',', tranlen-off); 191 pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; 192 nextparamoff = pparamend-ptran; 193 194 while (off < nextparamoff) { 195 const char* pfieldend; 196 uint nextfieldoff; 197 198 pfieldend = memchr(ptran+off, ';', nextparamoff-off); 199 nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; 200 201 if (strncmp(ptran+off, "client_port=", 12) == 0) { 202 u_int16_t port; 203 uint numlen; 204 205 off += 12; 206 numlen = nf_strtou16(ptran+off, &port); 207 off += numlen; 208 if (prtspexp->loport != 0 && prtspexp->loport != port) 209 pr_debug("multiple ports found, port %hu ignored\n", port); 210 else { 211 pr_debug("lo port found : %hu\n", port); 212 prtspexp->loport = prtspexp->hiport = port; 213 if (ptran[off] == '-') { 214 off++; 215 numlen = nf_strtou16(ptran+off, &port); 216 off += numlen; 217 prtspexp->pbtype = pb_range; 218 prtspexp->hiport = port; 219 220 // If we have a range, assume rtp: 221 // loport must be even, hiport must be loport+1 222 if ((prtspexp->loport & 0x0001) != 0 || 223 prtspexp->hiport != prtspexp->loport+1) { 224 pr_debug("incorrect range: %hu-%hu, correcting\n", 225 prtspexp->loport, prtspexp->hiport); 226 prtspexp->loport &= 0xfffe; 227 prtspexp->hiport = prtspexp->loport+1; 228 } 229 } else if (ptran[off] == '/') { 230 off++; 231 numlen = nf_strtou16(ptran+off, &port); 232 off += numlen; 233 prtspexp->pbtype = pb_discon; 234 prtspexp->hiport = port; 235 } 236 rc = 1; 237 } 238 } 239 240 /* 241 * Note we don't look for the destination parameter here. 242 * If we are using NAT, the NAT module will handle it. If not, 243 * and the client is sending packets elsewhere, the expectation 244 * will quietly time out. 245 */ 246 247 off = nextfieldoff; 248 } 249 250 off = nextparamoff; 251 } 252 253 return rc; 254} 255 256void expected(struct nf_conn *ct, struct nf_conntrack_expect *exp) 257{ 258 typeof(nf_nat_rtsp_hook_expectfn) nf_nat_rtsp_expectfn; 259 nf_nat_rtsp_expectfn = rcu_dereference(nf_nat_rtsp_hook_expectfn); 260 if(nf_nat_rtsp_expectfn && ct->master->status & IPS_NAT_MASK) { 261 nf_nat_rtsp_expectfn(ct,exp); 262 } 263} 264 265/*** conntrack functions ***/ 266 267/* outbound packet: client->server */ 268 269static inline int 270help_out(struct sk_buff *skb, unsigned char *rb_ptr, unsigned int datalen, 271 struct nf_conn *ct, enum ip_conntrack_info ctinfo) 272{ 273 struct ip_ct_rtsp_expect expinfo; 274 275 int dir = CTINFO2DIR(ctinfo); /* = IP_CT_DIR_ORIGINAL */ 276 //struct tcphdr* tcph = (void*)iph + iph->ihl * 4; 277 //uint tcplen = pktlen - iph->ihl * 4; 278 char* pdata = rb_ptr; 279 //uint datalen = tcplen - tcph->doff * 4; 280 uint dataoff = 0; 281 int ret = NF_ACCEPT; 282 283 struct nf_conntrack_expect *exp; 284 285 __be16 be_loport; 286 287 typeof(nf_nat_rtsp_hook) nf_nat_rtsp; 288 289 memset(&expinfo, 0, sizeof(expinfo)); 290 291 while (dataoff < datalen) { 292 uint cmdoff = dataoff; 293 uint hdrsoff = 0; 294 uint hdrslen = 0; 295 uint cseqoff = 0; 296 uint cseqlen = 0; 297 uint transoff = 0; 298 uint translen = 0; 299 uint off; 300 301 if (!rtsp_parse_message(pdata, datalen, &dataoff, 302 &hdrsoff, &hdrslen, 303 &cseqoff, &cseqlen, 304 &transoff, &translen)) 305 break; /* not a valid message */ 306 307 if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0) 308 continue; /* not a SETUP message */ 309 pr_debug("found a setup message\n"); 310 311 off = 0; 312 if(translen) { 313 rtsp_parse_transport(pdata+transoff, translen, &expinfo); 314 } 315 316 if (expinfo.loport == 0) { 317 pr_debug("no udp transports found\n"); 318 continue; /* no udp transports found */ 319 } 320 321 pr_debug("udp transport found, ports=(%d,%hu,%hu)\n", 322 (int)expinfo.pbtype, expinfo.loport, expinfo.hiport); 323 324 exp = nf_ct_expect_alloc(ct); 325 if (!exp) { 326 ret = NF_DROP; 327 goto out; 328 } 329 330 be_loport = htons(expinfo.loport); 331 332 nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), 333 /* media stream source can be different from the RTSP server address */ 334 // &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, 335 NULL, &ct->tuplehash[!dir].tuple.dst.u3, 336 IPPROTO_UDP, NULL, &be_loport); 337 338 exp->master = ct; 339 340 exp->expectfn = expected; 341 exp->flags = 0; 342 343 if (expinfo.pbtype == pb_range) { 344 pr_debug("Changing expectation mask to handle multiple ports\n"); 345 //exp->mask.dst.u.udp.port = 0xfffe; 346 } 347 348 pr_debug("expect_related %pI4:%u-%pI4:%u\n", 349 &exp->tuple.src.u3.ip, 350 ntohs(exp->tuple.src.u.udp.port), 351 &exp->tuple.dst.u3.ip, 352 ntohs(exp->tuple.dst.u.udp.port)); 353 354 nf_nat_rtsp = rcu_dereference(nf_nat_rtsp_hook); 355 if (nf_nat_rtsp && ct->status & IPS_NAT_MASK) 356 /* pass the request off to the nat helper */ 357 ret = nf_nat_rtsp(skb, ctinfo, hdrsoff, hdrslen, &expinfo, exp); 358 else if (nf_ct_expect_related(exp) != 0) { 359 pr_info("nf_conntrack_expect_related failed\n"); 360 ret = NF_DROP; 361 } 362 nf_ct_expect_put(exp); 363 goto out; 364 } 365out: 366 367 return ret; 368} 369 370 371static inline int 372help_in(struct sk_buff *skb, size_t pktlen, 373 struct nf_conn* ct, enum ip_conntrack_info ctinfo) 374{ 375 return NF_ACCEPT; 376} 377 378static int help(struct sk_buff *skb, unsigned int protoff, 379 struct nf_conn *ct, enum ip_conntrack_info ctinfo) 380{ 381 struct tcphdr _tcph, *th; 382 unsigned int dataoff, datalen; 383 char *rb_ptr; 384 int ret = NF_DROP; 385 386 /* Until there's been traffic both ways, don't look in packets. */ 387 if (ctinfo != IP_CT_ESTABLISHED && 388 ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { 389 pr_debug("conntrackinfo = %u\n", ctinfo); 390 return NF_ACCEPT; 391 } 392 393 /* Not whole TCP header? */ 394 th = skb_header_pointer(skb,protoff, sizeof(_tcph), &_tcph); 395 396 if (!th) 397 return NF_ACCEPT; 398 399 /* No data ? */ 400 dataoff = protoff + th->doff*4; 401 datalen = skb->len - dataoff; 402 if (dataoff >= skb->len) 403 return NF_ACCEPT; 404 405 spin_lock_bh(&rtsp_buffer_lock); 406 rb_ptr = skb_header_pointer(skb, dataoff, 407 skb->len - dataoff, rtsp_buffer); 408 BUG_ON(rb_ptr == NULL); 409 410#if 0 411 /* Checksum invalid? Ignore. */ 412 /* FIXME: Source route IP option packets --RR */ 413 if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, 414 csum_partial((char*)tcph, tcplen, 0))) 415 { 416 DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", 417 tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); 418 return NF_ACCEPT; 419 } 420#endif 421 422 switch (CTINFO2DIR(ctinfo)) { 423 case IP_CT_DIR_ORIGINAL: 424 ret = help_out(skb, rb_ptr, datalen, ct, ctinfo); 425 break; 426 case IP_CT_DIR_REPLY: 427 pr_debug("IP_CT_DIR_REPLY\n"); 428 /* inbound packet: server->client */ 429 ret = NF_ACCEPT; 430 break; 431 } 432 433 spin_unlock_bh(&rtsp_buffer_lock); 434 435 return ret; 436} 437 438static struct nf_conntrack_helper rtsp_helpers[MAX_PORTS]; 439static char rtsp_names[MAX_PORTS][10]; 440 441/* This function is intentionally _NOT_ defined as __exit */ 442static void 443fini(void) 444{ 445 int i; 446 for (i = 0; i < num_ports; i++) { 447 if (rtsp_helpers[i].me == NULL) 448 continue; 449 pr_debug("unregistering port %d\n", ports[i]); 450 nf_conntrack_helper_unregister(&rtsp_helpers[i]); 451 } 452 kfree(rtsp_buffer); 453} 454 455static int __init 456init(void) 457{ 458 int i, ret; 459 struct nf_conntrack_helper *hlpr; 460 char *tmpname; 461 462 printk("nf_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n"); 463 464 if (max_outstanding < 1) { 465 printk("nf_conntrack_rtsp: max_outstanding must be a positive integer\n"); 466 return -EBUSY; 467 } 468 if (setup_timeout < 0) { 469 printk("nf_conntrack_rtsp: setup_timeout must be a positive integer\n"); 470 return -EBUSY; 471 } 472 473 rtsp_exp_policy.max_expected = max_outstanding; 474 rtsp_exp_policy.timeout = setup_timeout; 475 476 rtsp_buffer = kmalloc(65536, GFP_KERNEL); 477 if (!rtsp_buffer) 478 return -ENOMEM; 479 480 /* If no port given, default to standard rtsp port */ 481 if (ports[0] == 0) { 482 ports[0] = RTSP_PORT; 483 } 484 485 for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { 486 hlpr = &rtsp_helpers[i]; 487 memset(hlpr, 0, sizeof(struct nf_conntrack_helper)); 488 hlpr->tuple.src.l3num = AF_INET; 489 hlpr->tuple.src.u.tcp.port = htons(ports[i]); 490 hlpr->tuple.dst.protonum = IPPROTO_TCP; 491 //hlpr->mask.src.u.tcp.port = 0xFFFF; 492 //hlpr->mask.dst.protonum = 0xFF; 493 hlpr->expect_policy = &rtsp_exp_policy; 494 hlpr->me = THIS_MODULE; 495 hlpr->help = help; 496 497 tmpname = &rtsp_names[i][0]; 498 if (ports[i] == RTSP_PORT) { 499 sprintf(tmpname, "rtsp"); 500 } else { 501 sprintf(tmpname, "rtsp-%d", i); 502 } 503 hlpr->name = tmpname; 504 505 pr_debug("port #%d: %d\n", i, ports[i]); 506 507 ret = nf_conntrack_helper_register(hlpr); 508 509 if (ret) { 510 printk("nf_conntrack_rtsp: ERROR registering port %d\n", ports[i]); 511 fini(); 512 return -EBUSY; 513 } 514 num_ports++; 515 } 516 return 0; 517} 518 519module_init(init); 520module_exit(fini); 521 522EXPORT_SYMBOL(nf_nat_rtsp_hook_expectfn); 523 524