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/module.h> 19 #include <linux/netdevice.h> 20 #include <linux/netfilter.h> 21 #include <linux/types.h> 22 #include <linux/netfilter_ipv4.h> 23 #include <linux/netfilter_ipv6.h> 24 #include <linux/netfilter/x_tables.h> 25 #include <net/addrconf.h> 26 #include <net/checksum.h> 27 #include <net/protocol.h> 28 #include <net/netfilter/nf_nat.h> 29 #include <net/netfilter/nf_nat_redirect.h> 30 31 unsigned int 32 nf_nat_redirect_ipv4(struct sk_buff *skb, 33 const struct nf_nat_ipv4_multi_range_compat *mr, 34 unsigned int hooknum) 35 { 36 struct nf_conn *ct; 37 enum ip_conntrack_info ctinfo; 38 __be32 newdst; 39 struct nf_nat_range newrange; 40 41 NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING || 42 hooknum == NF_INET_LOCAL_OUT); 43 44 ct = nf_ct_get(skb, &ctinfo); 45 NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); 46 47 /* Local packets: make them go to loopback */ 48 if (hooknum == NF_INET_LOCAL_OUT) { 49 newdst = htonl(0x7F000001); 50 } else { 51 struct in_device *indev; 52 struct in_ifaddr *ifa; 53 54 newdst = 0; 55 56 rcu_read_lock(); 57 indev = __in_dev_get_rcu(skb->dev); 58 if (indev && indev->ifa_list) { 59 ifa = indev->ifa_list; 60 newdst = ifa->ifa_local; 61 } 62 rcu_read_unlock(); 63 64 if (!newdst) 65 return NF_DROP; 66 } 67 68 /* Transfer from original range. */ 69 memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); 70 memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); 71 newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; 72 newrange.min_addr.ip = newdst; 73 newrange.max_addr.ip = newdst; 74 newrange.min_proto = mr->range[0].min; 75 newrange.max_proto = mr->range[0].max; 76 77 /* Hand modified range to generic setup. */ 78 return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); 79 } 80 EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv4); 81 82 static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; 83 84 unsigned int 85 nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range *range, 86 unsigned int hooknum) 87 { 88 struct nf_nat_range newrange; 89 struct in6_addr newdst; 90 enum ip_conntrack_info ctinfo; 91 struct nf_conn *ct; 92 93 ct = nf_ct_get(skb, &ctinfo); 94 if (hooknum == NF_INET_LOCAL_OUT) { 95 newdst = loopback_addr; 96 } else { 97 struct inet6_dev *idev; 98 struct inet6_ifaddr *ifa; 99 bool addr = false; 100 101 rcu_read_lock(); 102 idev = __in6_dev_get(skb->dev); 103 if (idev != NULL) { 104 list_for_each_entry(ifa, &idev->addr_list, if_list) { 105 newdst = ifa->addr; 106 addr = true; 107 break; 108 } 109 } 110 rcu_read_unlock(); 111 112 if (!addr) 113 return NF_DROP; 114 } 115 116 newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; 117 newrange.min_addr.in6 = newdst; 118 newrange.max_addr.in6 = newdst; 119 newrange.min_proto = range->min_proto; 120 newrange.max_proto = range->max_proto; 121 122 return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); 123 } 124 EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv6); 125 126 MODULE_LICENSE("GPL"); 127 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 128