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