nf_nat_rtsp.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. /*
  2. * RTSP extension for TCP NAT alteration
  3. * (C) 2003 by Tom Marshall <tmarshall at 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 nf_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. #include <linux/module.h>
  33. #include <net/tcp.h>
  34. #include <net/netfilter/nf_nat_helper.h>
  35. #include <net/netfilter/nf_nat_rule.h>
  36. #include "nf_conntrack_rtsp.h"
  37. #include <net/netfilter/nf_conntrack_expect.h>
  38. #include <linux/inet.h>
  39. #include <linux/ctype.h>
  40. #define NF_NEED_STRNCASECMP
  41. #define NF_NEED_STRTOU16
  42. #include "netfilter_helpers.h"
  43. #define NF_NEED_MIME_NEXTLINE
  44. #include "netfilter_mime.h"
  45. #define MAX_PORTS 8
  46. #define DSTACT_AUTO 0
  47. #define DSTACT_STRIP 1
  48. #define DSTACT_NONE 2
  49. static char* stunaddr = NULL;
  50. static char* destaction = NULL;
  51. static u_int32_t extip = 0;
  52. static int dstact = 0;
  53. MODULE_AUTHOR("Tom Marshall <tmarshall at real.com>");
  54. MODULE_DESCRIPTION("RTSP network address translation module");
  55. MODULE_LICENSE("GPL");
  56. module_param(stunaddr, charp, 0644);
  57. MODULE_PARM_DESC(stunaddr, "Address for detecting STUN");
  58. module_param(destaction, charp, 0644);
  59. MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)");
  60. #define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; }
  61. /*** helper functions ***/
  62. static void
  63. get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen)
  64. {
  65. struct iphdr* iph = ip_hdr(skb);
  66. struct tcphdr* tcph = (void *)iph + ip_hdrlen(skb);
  67. *pptcpdata = (char*)tcph + tcph->doff*4;
  68. *ptcpdatalen = ((char*)skb_transport_header(skb) + skb->len) - *pptcpdata;
  69. }
  70. /*** nat functions ***/
  71. /*
  72. * Mangle the "Transport:" header:
  73. * - Replace all occurences of "client_port=<spec>"
  74. * - Handle destination parameter
  75. *
  76. * In:
  77. * ct, ctinfo = conntrack context
  78. * skb = packet
  79. * tranoff = Transport header offset from TCP data
  80. * tranlen = Transport header length (incl. CRLF)
  81. * rport_lo = replacement low port (host endian)
  82. * rport_hi = replacement high port (host endian)
  83. *
  84. * Returns packet size difference.
  85. *
  86. * Assumes that a complete transport header is present, ending with CR or LF
  87. */
  88. static int
  89. rtsp_mangle_tran(enum ip_conntrack_info ctinfo,
  90. struct nf_conntrack_expect* exp,
  91. struct ip_ct_rtsp_expect* prtspexp,
  92. struct sk_buff* skb, uint tranoff, uint tranlen)
  93. {
  94. char* ptcp;
  95. uint tcplen;
  96. char* ptran;
  97. char rbuf1[16]; /* Replacement buffer (one port) */
  98. uint rbuf1len; /* Replacement len (one port) */
  99. char rbufa[16]; /* Replacement buffer (all ports) */
  100. uint rbufalen; /* Replacement len (all ports) */
  101. u_int32_t newip;
  102. u_int16_t loport, hiport;
  103. uint off = 0;
  104. uint diff; /* Number of bytes we removed */
  105. struct nf_conn *ct = exp->master;
  106. struct nf_conntrack_tuple *t;
  107. char szextaddr[15+1];
  108. uint extaddrlen;
  109. int is_stun;
  110. get_skb_tcpdata(skb, &ptcp, &tcplen);
  111. ptran = ptcp+tranoff;
  112. if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen ||
  113. tranlen < 10 || !iseol(ptran[tranlen-1]) ||
  114. nf_strncasecmp(ptran, "Transport:", 10) != 0)
  115. {
  116. pr_debug("sanity check failed\n");
  117. return 0;
  118. }
  119. off += 10;
  120. SKIP_WSPACE(ptcp+tranoff, tranlen, off);
  121. newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
  122. t = &exp->tuple;
  123. t->dst.u3.ip = newip;
  124. extaddrlen = extip ? sprintf(szextaddr, "%pI4", extip)
  125. : sprintf(szextaddr, "%pI4", newip);
  126. pr_debug("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto"));
  127. rbuf1len = rbufalen = 0;
  128. switch (prtspexp->pbtype)
  129. {
  130. case pb_single:
  131. for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
  132. {
  133. t->dst.u.udp.port = htons(loport);
  134. if (nf_ct_expect_related(exp) == 0)
  135. {
  136. pr_debug("using port %hu\n", loport);
  137. break;
  138. }
  139. }
  140. if (loport != 0)
  141. {
  142. rbuf1len = sprintf(rbuf1, "%hu", loport);
  143. rbufalen = sprintf(rbufa, "%hu", loport);
  144. }
  145. break;
  146. case pb_range:
  147. for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */
  148. {
  149. t->dst.u.udp.port = htons(loport);
  150. if (nf_ct_expect_related(exp) == 0)
  151. {
  152. hiport = loport + 1; //~exp->mask.dst.u.udp.port;
  153. pr_debug("using ports %hu-%hu\n", loport, hiport);
  154. break;
  155. }
  156. }
  157. if (loport != 0)
  158. {
  159. rbuf1len = sprintf(rbuf1, "%hu", loport);
  160. rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1);
  161. }
  162. break;
  163. case pb_discon:
  164. for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */
  165. {
  166. t->dst.u.udp.port = htons(loport);
  167. if (nf_ct_expect_related(exp) == 0)
  168. {
  169. pr_debug("using port %hu (1 of 2)\n", loport);
  170. break;
  171. }
  172. }
  173. for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */
  174. {
  175. t->dst.u.udp.port = htons(hiport);
  176. if (nf_ct_expect_related(exp) == 0)
  177. {
  178. pr_debug("using port %hu (2 of 2)\n", hiport);
  179. break;
  180. }
  181. }
  182. if (loport != 0 && hiport != 0)
  183. {
  184. rbuf1len = sprintf(rbuf1, "%hu", loport);
  185. if (hiport == loport+1)
  186. {
  187. rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport);
  188. }
  189. else
  190. {
  191. rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport);
  192. }
  193. }
  194. break;
  195. }
  196. if (rbuf1len == 0)
  197. {
  198. return 0; /* cannot get replacement port(s) */
  199. }
  200. /* Transport: tran;field;field=val,tran;field;field=val,... */
  201. while (off < tranlen)
  202. {
  203. uint saveoff;
  204. const char* pparamend;
  205. uint nextparamoff;
  206. pparamend = memchr(ptran+off, ',', tranlen-off);
  207. pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1;
  208. nextparamoff = pparamend-ptcp;
  209. /*
  210. * We pass over each param twice. On the first pass, we look for a
  211. * destination= field. It is handled by the security policy. If it
  212. * is present, allowed, and equal to our external address, we assume
  213. * that STUN is being used and we leave the client_port= field alone.
  214. */
  215. is_stun = 0;
  216. saveoff = off;
  217. while (off < nextparamoff)
  218. {
  219. const char* pfieldend;
  220. uint nextfieldoff;
  221. pfieldend = memchr(ptran+off, ';', nextparamoff-off);
  222. nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
  223. /*
  224. if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0)
  225. {
  226. if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0)
  227. {
  228. is_stun = 1;
  229. }
  230. if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun))
  231. {
  232. diff = nextfieldoff-off;
  233. if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
  234. off, diff, NULL, 0))
  235. {
  236. nf_ct_unexpect_related(exp);
  237. return 0;
  238. }
  239. get_skb_tcpdata(skb, &ptcp, &tcplen);
  240. ptran = ptcp+tranoff;
  241. tranlen -= diff;
  242. nextparamoff -= diff;
  243. nextfieldoff -= diff;
  244. }
  245. }
  246. */
  247. off = nextfieldoff;
  248. }
  249. if (is_stun)
  250. {
  251. continue;
  252. }
  253. off = saveoff;
  254. while (off < nextparamoff)
  255. {
  256. const char* pfieldend;
  257. const char* pdestport;
  258. uint nextfieldoff;
  259. pfieldend = memchr(ptran+off, ';', nextparamoff-off);
  260. nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1;
  261. if (strncmp(ptran+off, "client_port=", 12) == 0)
  262. {
  263. u_int16_t port;
  264. uint numlen;
  265. uint origoff;
  266. uint origlen;
  267. char* rbuf = rbuf1;
  268. uint rbuflen = rbuf1len;
  269. off += 12;
  270. origoff = (ptran-ptcp)+off;
  271. origlen = 0;
  272. numlen = nf_strtou16(ptran+off, &port);
  273. off += numlen;
  274. origlen += numlen;
  275. if (port != prtspexp->loport)
  276. {
  277. pr_debug("multiple ports found, port %hu ignored\n", port);
  278. }
  279. else
  280. {
  281. if (ptran[off] == '-' || ptran[off] == '/')
  282. {
  283. off++;
  284. origlen++;
  285. numlen = nf_strtou16(ptran+off, &port);
  286. off += numlen;
  287. origlen += numlen;
  288. rbuf = rbufa;
  289. rbuflen = rbufalen;
  290. }
  291. /*
  292. * note we cannot just memcpy() if the sizes are the same.
  293. * the mangle function does skb resizing, checks for a
  294. * cloned skb, and updates the checksums.
  295. *
  296. * parameter 4 below is offset from start of tcp data.
  297. */
  298. diff = origlen-rbuflen;
  299. if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
  300. origoff, origlen, rbuf, rbuflen))
  301. {
  302. /* mangle failed, all we can do is bail */
  303. nf_ct_unexpect_related(exp);
  304. return 0;
  305. }
  306. get_skb_tcpdata(skb, &ptcp, &tcplen);
  307. ptran = ptcp+tranoff;
  308. tranlen -= diff;
  309. nextparamoff -= diff;
  310. nextfieldoff -= diff;
  311. }
  312. }
  313. else if ((strncmp(ptran+off, "destination=", 12) == 0) && ((pdestport = memchr(ptran+off+12, ':', nextparamoff-(off + 12))) != NULL))
  314. {
  315. u_int16_t port;
  316. uint numlen;
  317. uint origoff;
  318. uint origlen;
  319. char rbuf[32];
  320. uint rbuflen = sprintf(rbuf, "%s:%s",szextaddr,rbuf1);
  321. pdestport++;
  322. off += 12;
  323. origoff = (ptran + off) - ptcp;
  324. origlen = pdestport - (ptran + off);
  325. off += origlen;
  326. numlen = nf_strtou16(ptran+off, &port);
  327. off += numlen;
  328. origlen += numlen;
  329. if (port != prtspexp->loport)
  330. {
  331. pr_debug("multiple ports found, port %hu ignored\n", port);
  332. }
  333. else
  334. {
  335. diff = origlen-rbuflen;
  336. if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
  337. origoff, origlen, rbuf, rbuflen))
  338. {
  339. /* mangle failed, all we can do is bail */
  340. nf_ct_unexpect_related(exp);
  341. return 0;
  342. }
  343. get_skb_tcpdata(skb, &ptcp, &tcplen);
  344. ptran = ptcp+tranoff;
  345. tranlen -= diff;
  346. nextparamoff -= diff;
  347. nextfieldoff -= diff;
  348. }
  349. }
  350. off = nextfieldoff;
  351. }
  352. off = nextparamoff;
  353. }
  354. return 1;
  355. }
  356. static uint
  357. help_out(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
  358. unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp,
  359. struct nf_conntrack_expect* exp)
  360. {
  361. char* ptcp;
  362. uint tcplen;
  363. uint hdrsoff;
  364. uint hdrslen;
  365. uint lineoff;
  366. uint linelen;
  367. uint off;
  368. //struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph;
  369. //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4);
  370. get_skb_tcpdata(skb, &ptcp, &tcplen);
  371. hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq);
  372. hdrslen = matchlen;
  373. off = hdrsoff;
  374. pr_debug("NAT rtsp help_out\n");
  375. while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen))
  376. {
  377. if (linelen == 0)
  378. {
  379. break;
  380. }
  381. if (off > hdrsoff+hdrslen)
  382. {
  383. pr_debug("!! overrun !!");
  384. break;
  385. }
  386. pr_debug("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
  387. if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0)
  388. {
  389. uint oldtcplen = tcplen;
  390. pr_debug("hdr: Transport\n");
  391. if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, skb, lineoff, linelen))
  392. {
  393. pr_debug("hdr: Transport mangle failed");
  394. break;
  395. }
  396. get_skb_tcpdata(skb, &ptcp, &tcplen);
  397. hdrslen -= (oldtcplen-tcplen);
  398. off -= (oldtcplen-tcplen);
  399. lineoff -= (oldtcplen-tcplen);
  400. linelen -= (oldtcplen-tcplen);
  401. pr_debug("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff);
  402. }
  403. }
  404. return NF_ACCEPT;
  405. }
  406. static unsigned int
  407. help(struct sk_buff *skb, enum ip_conntrack_info ctinfo,
  408. unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp,
  409. struct nf_conntrack_expect* exp)
  410. {
  411. int dir = CTINFO2DIR(ctinfo);
  412. int rc = NF_ACCEPT;
  413. switch (dir)
  414. {
  415. case IP_CT_DIR_ORIGINAL:
  416. rc = help_out(skb, ctinfo, matchoff, matchlen, prtspexp, exp);
  417. break;
  418. case IP_CT_DIR_REPLY:
  419. pr_debug("unmangle ! %u\n", ctinfo);
  420. /* XXX: unmangle */
  421. rc = NF_ACCEPT;
  422. break;
  423. }
  424. //UNLOCK_BH(&ip_rtsp_lock);
  425. return rc;
  426. }
  427. static void expected(struct nf_conn* ct, struct nf_conntrack_expect *exp)
  428. {
  429. struct nf_nat_multi_range_compat mr;
  430. u_int32_t newdstip, newsrcip, newip;
  431. struct nf_conn *master = ct->master;
  432. newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
  433. newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
  434. //FIXME (how to port that ?)
  435. //code from 2.4 : newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip;
  436. newip = newdstip;
  437. pr_debug("newsrcip=%pI4, newdstip=%pI4, newip=%pI4\n",
  438. newsrcip, newdstip, newip);
  439. mr.rangesize = 1;
  440. // We don't want to manip the per-protocol, just the IPs.
  441. mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
  442. mr.range[0].min_ip = mr.range[0].max_ip = newip;
  443. nf_nat_setup_info(ct, &mr.range[0], IP_NAT_MANIP_DST);
  444. }
  445. static void __exit fini(void)
  446. {
  447. nf_nat_rtsp_hook = NULL;
  448. nf_nat_rtsp_hook_expectfn = NULL;
  449. synchronize_net();
  450. }
  451. static int __init init(void)
  452. {
  453. printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n");
  454. BUG_ON(nf_nat_rtsp_hook);
  455. nf_nat_rtsp_hook = help;
  456. nf_nat_rtsp_hook_expectfn = &expected;
  457. if (stunaddr != NULL)
  458. extip = in_aton(stunaddr);
  459. if (destaction != NULL) {
  460. if (strcmp(destaction, "auto") == 0)
  461. dstact = DSTACT_AUTO;
  462. if (strcmp(destaction, "strip") == 0)
  463. dstact = DSTACT_STRIP;
  464. if (strcmp(destaction, "none") == 0)
  465. dstact = DSTACT_NONE;
  466. }
  467. return 0;
  468. }
  469. module_init(init);
  470. module_exit(fini);