ip_fw_compat_redir.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:8k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /* This is a file to handle the "simple" NAT cases (redirect and
  2.    masquerade) required for the compatibility layer.
  3.    `bind to foreign address' and `getpeername' hacks are not
  4.    supported.
  5.    FIXME: Timing is overly simplistic.  If anyone complains, make it
  6.    use conntrack.
  7. */
  8. #include <linux/config.h>
  9. #include <linux/netfilter.h>
  10. #include <linux/ip.h>
  11. #include <linux/udp.h>
  12. #include <linux/tcp.h>
  13. #include <net/checksum.h>
  14. #include <linux/timer.h>
  15. #include <linux/netdevice.h>
  16. #include <linux/if.h>
  17. #include <linux/in.h>
  18. #include <linux/netfilter_ipv4/lockhelp.h>
  19. /* Very simple timeout pushed back by each packet */
  20. #define REDIR_TIMEOUT (240*HZ)
  21. static DECLARE_LOCK(redir_lock);
  22. #define ASSERT_READ_LOCK(x) MUST_BE_LOCKED(&redir_lock)
  23. #define ASSERT_WRITE_LOCK(x) MUST_BE_LOCKED(&redir_lock)
  24. #include <linux/netfilter_ipv4/listhelp.h>
  25. #if 0
  26. #define DEBUGP printk
  27. #else
  28. #define DEBUGP(format, args...)
  29. #endif
  30. #ifdef CONFIG_NETFILTER_DEBUG
  31. #define IP_NF_ASSERT(x)  
  32. do {  
  33. if (!(x))  
  34. /* Wooah!  I'm tripping my conntrack in a frenzy of  
  35.    netplay... */  
  36. printk("ASSERT: %s:%i(%s)n",  
  37.        __FILE__, __LINE__, __FUNCTION__);  
  38. } while(0);
  39. #else
  40. #define IP_NF_ASSERT(x)
  41. #endif
  42. static u_int16_t
  43. cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck)
  44. {
  45. u_int32_t diffs[] = { oldvalinv, newval };
  46. return csum_fold(csum_partial((char *)diffs, sizeof(diffs),
  47.       oldcheck^0xFFFF));
  48. }
  49. struct redir_core {
  50. u_int32_t orig_srcip, orig_dstip;
  51. u_int16_t orig_sport, orig_dport;
  52. u_int32_t new_dstip;
  53. u_int16_t new_dport;
  54. };
  55. struct redir
  56. {
  57. struct list_head list;
  58. struct redir_core core;
  59. struct timer_list destroyme;
  60. };
  61. static LIST_HEAD(redirs);
  62. static int
  63. redir_cmp(const struct redir *i,
  64.   u_int32_t orig_srcip, u_int32_t orig_dstip,
  65.   u_int16_t orig_sport, u_int16_t orig_dport)
  66. {
  67. return (i->core.orig_srcip == orig_srcip
  68. && i->core.orig_dstip == orig_dstip
  69. && i->core.orig_sport == orig_sport
  70. && i->core.orig_dport == orig_dport);
  71. }
  72. /* Search for an existing redirection of the TCP packet. */
  73. static struct redir *
  74. find_redir(u_int32_t orig_srcip, u_int32_t orig_dstip,
  75.    u_int16_t orig_sport, u_int16_t orig_dport)
  76. {
  77. return LIST_FIND(&redirs, redir_cmp, struct redir *,
  78.  orig_srcip, orig_dstip, orig_sport, orig_dport);
  79. }
  80. static void do_tcp_redir(struct sk_buff *skb, struct redir *redir)
  81. {
  82. struct iphdr *iph = skb->nh.iph;
  83. struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
  84. + iph->ihl);
  85. tcph->check = cheat_check(~redir->core.orig_dstip,
  86.   redir->core.new_dstip,
  87.   cheat_check(redir->core.orig_dport ^ 0xFFFF,
  88.       redir->core.new_dport,
  89.       tcph->check));
  90. iph->check = cheat_check(~redir->core.orig_dstip,
  91.  redir->core.new_dstip, iph->check);
  92. tcph->dest = redir->core.new_dport;
  93. iph->daddr = redir->core.new_dstip;
  94. skb->nfcache |= NFC_ALTERED;
  95. }
  96. static int
  97. unredir_cmp(const struct redir *i,
  98.     u_int32_t new_dstip, u_int32_t orig_srcip,
  99.     u_int16_t new_dport, u_int16_t orig_sport)
  100. {
  101. return (i->core.orig_srcip == orig_srcip
  102. && i->core.new_dstip == new_dstip
  103. && i->core.orig_sport == orig_sport
  104. && i->core.new_dport == new_dport);
  105. }
  106. /* Match reply packet against redir */
  107. static struct redir *
  108. find_unredir(u_int32_t new_dstip, u_int32_t orig_srcip,
  109.      u_int16_t new_dport, u_int16_t orig_sport)
  110. {
  111. return LIST_FIND(&redirs, unredir_cmp, struct redir *,
  112.  new_dstip, orig_srcip, new_dport, orig_sport);
  113. }
  114. /* `unredir' a reply packet. */
  115. static void do_tcp_unredir(struct sk_buff *skb, struct redir *redir)
  116. {
  117. struct iphdr *iph = skb->nh.iph;
  118. struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
  119. + iph->ihl);
  120. tcph->check = cheat_check(~redir->core.new_dstip,
  121.   redir->core.orig_dstip,
  122.   cheat_check(redir->core.new_dport ^ 0xFFFF,
  123.       redir->core.orig_dport,
  124.       tcph->check));
  125. iph->check = cheat_check(~redir->core.new_dstip,
  126.  redir->core.orig_dstip,
  127.  iph->check);
  128. tcph->source = redir->core.orig_dport;
  129. iph->saddr = redir->core.orig_dstip;
  130. skb->nfcache |= NFC_ALTERED;
  131. }
  132. static void destroyme(unsigned long me)
  133. {
  134. LOCK_BH(&redir_lock);
  135. LIST_DELETE(&redirs, (struct redir *)me);
  136. UNLOCK_BH(&redir_lock);
  137. kfree((struct redir *)me);
  138. }
  139. /* REDIRECT a packet. */
  140. unsigned int
  141. do_redirect(struct sk_buff *skb,
  142.     const struct net_device *dev,
  143.     u_int16_t redirpt)
  144. {
  145. struct iphdr *iph = skb->nh.iph;
  146. u_int32_t newdst;
  147. /* Figure out address: not loopback. */
  148. if (!dev)
  149. return NF_DROP;
  150. /* Grab first address on interface. */
  151. newdst = ((struct in_device *)dev->ip_ptr)->ifa_list->ifa_local;
  152. switch (iph->protocol) {
  153. case IPPROTO_UDP: {
  154. /* Simple mangle. */
  155. struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph
  156. + iph->ihl);
  157. /* Must have whole header */
  158. if (skb->len < iph->ihl*4 + sizeof(*udph))
  159. return NF_DROP;
  160. if (udph->check) /* 0 is a special case meaning no checksum */
  161. udph->check = cheat_check(~iph->daddr, newdst,
  162.   cheat_check(udph->dest ^ 0xFFFF,
  163.       redirpt,
  164.       udph->check));
  165. iph->check = cheat_check(~iph->daddr, newdst, iph->check);
  166. udph->dest = redirpt;
  167. iph->daddr = newdst;
  168. skb->nfcache |= NFC_ALTERED;
  169. return NF_ACCEPT;
  170. }
  171. case IPPROTO_TCP: {
  172. /* Mangle, maybe record. */
  173. struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
  174. + iph->ihl);
  175. struct redir *redir;
  176. int ret;
  177. /* Must have whole header */
  178. if (skb->len < iph->ihl*4 + sizeof(*tcph))
  179. return NF_DROP;
  180. DEBUGP("Doing tcp redirect. %08X:%u %08X:%u -> %08X:%un",
  181.        iph->saddr, tcph->source, iph->daddr, tcph->dest,
  182.        newdst, redirpt);
  183. LOCK_BH(&redir_lock);
  184. redir = find_redir(iph->saddr, iph->daddr,
  185.    tcph->source, tcph->dest);
  186. if (!redir) {
  187. redir = kmalloc(sizeof(struct redir), GFP_ATOMIC);
  188. if (!redir) {
  189. ret = NF_DROP;
  190. goto out;
  191. }
  192. list_prepend(&redirs, redir);
  193. init_timer(&redir->destroyme);
  194. redir->destroyme.function = destroyme;
  195. redir->destroyme.data = (unsigned long)redir;
  196. redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
  197. add_timer(&redir->destroyme);
  198. }
  199. /* In case mangling has changed, rewrite this part. */
  200. redir->core = ((struct redir_core)
  201.        { iph->saddr, iph->daddr,
  202.  tcph->source, tcph->dest,
  203.  newdst, redirpt });
  204. do_tcp_redir(skb, redir);
  205. ret = NF_ACCEPT;
  206. out:
  207. UNLOCK_BH(&redir_lock);
  208. return ret;
  209. }
  210. default: /* give up if not TCP or UDP. */
  211. return NF_DROP;
  212. }
  213. }
  214. /* Incoming packet: is it a reply to a masqueraded connection, or
  215.    part of an already-redirected TCP connection? */
  216. void
  217. check_for_redirect(struct sk_buff *skb)
  218. {
  219. struct iphdr *iph = skb->nh.iph;
  220. struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
  221. + iph->ihl);
  222. struct redir *redir;
  223. if (iph->protocol != IPPROTO_TCP)
  224. return;
  225. /* Must have whole header */
  226. if (skb->len < iph->ihl*4 + sizeof(*tcph))
  227. return;
  228. LOCK_BH(&redir_lock);
  229. redir = find_redir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
  230. if (redir) {
  231. DEBUGP("Doing tcp redirect again.n");
  232. do_tcp_redir(skb, redir);
  233. if (del_timer(&redir->destroyme)) {
  234. redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
  235. add_timer(&redir->destroyme);
  236. }
  237. }
  238. UNLOCK_BH(&redir_lock);
  239. }
  240. void
  241. check_for_unredirect(struct sk_buff *skb)
  242. {
  243. struct iphdr *iph = skb->nh.iph;
  244. struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
  245. + iph->ihl);
  246. struct redir *redir;
  247. if (iph->protocol != IPPROTO_TCP)
  248. return;
  249. /* Must have whole header */
  250. if (skb->len < iph->ihl*4 + sizeof(*tcph))
  251. return;
  252. LOCK_BH(&redir_lock);
  253. redir = find_unredir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
  254. if (redir) {
  255. DEBUGP("Doing tcp unredirect.n");
  256. do_tcp_unredir(skb, redir);
  257. if (del_timer(&redir->destroyme)) {
  258. redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
  259. add_timer(&redir->destroyme);
  260. }
  261. }
  262. UNLOCK_BH(&redir_lock);
  263. }