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 indev = __in_dev_get_rcu(skb->dev); 56 if (indev && indev->ifa_list) { 57 ifa = indev->ifa_list; 58 newdst = ifa->ifa_local; 59 } 60 61 if (!newdst) 62 return NF_DROP; 63 } 64 65 /* Transfer from original range. */ 66 memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); 67 memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); 68 newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; 69 newrange.min_addr.ip = newdst; 70 newrange.max_addr.ip = newdst; 71 newrange.min_proto = mr->range[0].min; 72 newrange.max_proto = mr->range[0].max; 73 74 /* Hand modified range to generic setup. */ 75 return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); 76 } 77 EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv4); 78 79 static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; 80 81 unsigned int 82 nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range, 83 unsigned int hooknum) 84 { 85 struct nf_nat_range2 newrange; 86 struct in6_addr newdst; 87 enum ip_conntrack_info ctinfo; 88 struct nf_conn *ct; 89 90 ct = nf_ct_get(skb, &ctinfo); 91 if (hooknum == NF_INET_LOCAL_OUT) { 92 newdst = loopback_addr; 93 } else { 94 struct inet6_dev *idev; 95 struct inet6_ifaddr *ifa; 96 bool addr = false; 97 98 idev = __in6_dev_get(skb->dev); 99 if (idev != NULL) { 100 read_lock_bh(&idev->lock); 101 list_for_each_entry(ifa, &idev->addr_list, if_list) { 102 newdst = ifa->addr; 103 addr = true; 104 break; 105 } 106 read_unlock_bh(&idev->lock); 107 } 108 109 if (!addr) 110 return NF_DROP; 111 } 112 113 newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; 114 newrange.min_addr.in6 = newdst; 115 newrange.max_addr.in6 = newdst; 116 newrange.min_proto = range->min_proto; 117 newrange.max_proto = range->max_proto; 118 119 return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); 120 } 121 EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv6); 122