nf_nat_redirect.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * (C) 1999-2001 Paul `Rusty' Russell
  4. * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
  5. * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
  6. *
  7. * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6
  8. * NAT funded by Astaro.
  9. */
  10. #include <linux/if.h>
  11. #include <linux/inetdevice.h>
  12. #include <linux/ip.h>
  13. #include <linux/kernel.h>
  14. #include <linux/netdevice.h>
  15. #include <linux/netfilter.h>
  16. #include <linux/types.h>
  17. #include <linux/netfilter_ipv4.h>
  18. #include <linux/netfilter_ipv6.h>
  19. #include <linux/netfilter/x_tables.h>
  20. #include <net/addrconf.h>
  21. #include <net/checksum.h>
  22. #include <net/protocol.h>
  23. #include <net/netfilter/nf_nat.h>
  24. #include <net/netfilter/nf_nat_redirect.h>
  25. unsigned int
  26. nf_nat_redirect_ipv4(struct sk_buff *skb,
  27. const struct nf_nat_ipv4_multi_range_compat *mr,
  28. unsigned int hooknum)
  29. {
  30. struct nf_conn *ct;
  31. enum ip_conntrack_info ctinfo;
  32. __be32 newdst;
  33. struct nf_nat_range2 newrange;
  34. WARN_ON(hooknum != NF_INET_PRE_ROUTING &&
  35. hooknum != NF_INET_LOCAL_OUT);
  36. ct = nf_ct_get(skb, &ctinfo);
  37. WARN_ON(!(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)));
  38. /* Local packets: make them go to loopback */
  39. if (hooknum == NF_INET_LOCAL_OUT) {
  40. newdst = htonl(0x7F000001);
  41. } else {
  42. const struct in_device *indev;
  43. newdst = 0;
  44. indev = __in_dev_get_rcu(skb->dev);
  45. if (indev) {
  46. const struct in_ifaddr *ifa;
  47. ifa = rcu_dereference(indev->ifa_list);
  48. if (ifa)
  49. newdst = ifa->ifa_local;
  50. }
  51. if (!newdst)
  52. return NF_DROP;
  53. }
  54. /* Transfer from original range. */
  55. memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
  56. memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
  57. newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
  58. newrange.min_addr.ip = newdst;
  59. newrange.max_addr.ip = newdst;
  60. newrange.min_proto = mr->range[0].min;
  61. newrange.max_proto = mr->range[0].max;
  62. /* Hand modified range to generic setup. */
  63. return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
  64. }
  65. EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv4);
  66. static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
  67. unsigned int
  68. nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range,
  69. unsigned int hooknum)
  70. {
  71. struct nf_nat_range2 newrange;
  72. struct in6_addr newdst;
  73. enum ip_conntrack_info ctinfo;
  74. struct nf_conn *ct;
  75. ct = nf_ct_get(skb, &ctinfo);
  76. if (hooknum == NF_INET_LOCAL_OUT) {
  77. newdst = loopback_addr;
  78. } else {
  79. struct inet6_dev *idev;
  80. struct inet6_ifaddr *ifa;
  81. bool addr = false;
  82. idev = __in6_dev_get(skb->dev);
  83. if (idev != NULL) {
  84. read_lock_bh(&idev->lock);
  85. list_for_each_entry(ifa, &idev->addr_list, if_list) {
  86. newdst = ifa->addr;
  87. addr = true;
  88. break;
  89. }
  90. read_unlock_bh(&idev->lock);
  91. }
  92. if (!addr)
  93. return NF_DROP;
  94. }
  95. newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
  96. newrange.min_addr.in6 = newdst;
  97. newrange.max_addr.in6 = newdst;
  98. newrange.min_proto = range->min_proto;
  99. newrange.max_proto = range->max_proto;
  100. return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
  101. }
  102. EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv6);