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 11 #include <linux/if.h> 12 #include <linux/inetdevice.h> 13 #include <linux/in.h> 14 #include <linux/ip.h> 15 #include <linux/kernel.h> 16 #include <linux/netdevice.h> 17 #include <linux/netfilter.h> 18 #include <linux/types.h> 19 #include <linux/netfilter_ipv4.h> 20 #include <linux/netfilter_ipv6.h> 21 #include <linux/netfilter/x_tables.h> 22 #include <net/addrconf.h> 23 #include <net/checksum.h> 24 #include <net/protocol.h> 25 #include <net/netfilter/nf_nat.h> 26 #include <net/netfilter/nf_nat_redirect.h> 27 28 static unsigned int 29 nf_nat_redirect(struct sk_buff *skb, const struct nf_nat_range2 *range, 30 const union nf_inet_addr *newdst) 31 { 32 struct nf_nat_range2 newrange; 33 enum ip_conntrack_info ctinfo; 34 struct nf_conn *ct; 35 36 ct = nf_ct_get(skb, &ctinfo); 37 38 memset(&newrange, 0, sizeof(newrange)); 39 40 newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; 41 newrange.min_addr = *newdst; 42 newrange.max_addr = *newdst; 43 newrange.min_proto = range->min_proto; 44 newrange.max_proto = range->max_proto; 45 46 return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); 47 } 48 49 unsigned int 50 nf_nat_redirect_ipv4(struct sk_buff *skb, const struct nf_nat_range2 *range, 51 unsigned int hooknum) 52 { 53 union nf_inet_addr newdst = {}; 54 55 WARN_ON(hooknum != NF_INET_PRE_ROUTING && 56 hooknum != NF_INET_LOCAL_OUT); 57 58 /* Local packets: make them go to loopback */ 59 if (hooknum == NF_INET_LOCAL_OUT) { 60 newdst.ip = htonl(INADDR_LOOPBACK); 61 } else { 62 const struct in_device *indev; 63 64 indev = __in_dev_get_rcu(skb->dev); 65 if (indev) { 66 const struct in_ifaddr *ifa; 67 68 ifa = rcu_dereference(indev->ifa_list); 69 if (ifa) 70 newdst.ip = ifa->ifa_local; 71 } 72 73 if (!newdst.ip) 74 return NF_DROP; 75 } 76 77 return nf_nat_redirect(skb, range, &newdst); 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 union nf_inet_addr newdst = {}; 88 89 if (hooknum == NF_INET_LOCAL_OUT) { 90 newdst.in6 = loopback_addr; 91 } else { 92 struct inet6_dev *idev; 93 struct inet6_ifaddr *ifa; 94 bool addr = false; 95 96 idev = __in6_dev_get(skb->dev); 97 if (idev != NULL) { 98 read_lock_bh(&idev->lock); 99 list_for_each_entry(ifa, &idev->addr_list, if_list) { 100 newdst.in6 = ifa->addr; 101 addr = true; 102 break; 103 } 104 read_unlock_bh(&idev->lock); 105 } 106 107 if (!addr) 108 return NF_DROP; 109 } 110 111 return nf_nat_redirect(skb, range, &newdst); 112 } 113 EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv6); 114