nf_conntrack_rtsp.c 15 KB

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