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/ipv4/nf_nat_redirect.h> 30 31 static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; 32 33 static unsigned int 34 redirect_tg6(struct sk_buff *skb, const struct xt_action_param *par) 35 { 36 const struct nf_nat_range *range = par->targinfo; 37 struct nf_nat_range newrange; 38 struct in6_addr newdst; 39 enum ip_conntrack_info ctinfo; 40 struct nf_conn *ct; 41 42 ct = nf_ct_get(skb, &ctinfo); 43 if (par->hooknum == NF_INET_LOCAL_OUT) 44 newdst = loopback_addr; 45 else { 46 struct inet6_dev *idev; 47 struct inet6_ifaddr *ifa; 48 bool addr = false; 49 50 rcu_read_lock(); 51 idev = __in6_dev_get(skb->dev); 52 if (idev != NULL) { 53 list_for_each_entry(ifa, &idev->addr_list, if_list) { 54 newdst = ifa->addr; 55 addr = true; 56 break; 57 } 58 } 59 rcu_read_unlock(); 60 61 if (!addr) 62 return NF_DROP; 63 } 64 65 newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; 66 newrange.min_addr.in6 = newdst; 67 newrange.max_addr.in6 = newdst; 68 newrange.min_proto = range->min_proto; 69 newrange.max_proto = range->max_proto; 70 71 return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); 72 } 73 74 static int redirect_tg6_checkentry(const struct xt_tgchk_param *par) 75 { 76 const struct nf_nat_range *range = par->targinfo; 77 78 if (range->flags & NF_NAT_RANGE_MAP_IPS) 79 return -EINVAL; 80 return 0; 81 } 82 83 /* FIXME: Take multiple ranges --RR */ 84 static int redirect_tg4_check(const struct xt_tgchk_param *par) 85 { 86 const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; 87 88 if (mr->range[0].flags & NF_NAT_RANGE_MAP_IPS) { 89 pr_debug("bad MAP_IPS.\n"); 90 return -EINVAL; 91 } 92 if (mr->rangesize != 1) { 93 pr_debug("bad rangesize %u.\n", mr->rangesize); 94 return -EINVAL; 95 } 96 return 0; 97 } 98 99 static unsigned int 100 redirect_tg4(struct sk_buff *skb, const struct xt_action_param *par) 101 { 102 return nf_nat_redirect_ipv4(skb, par->targinfo, par->hooknum); 103 } 104 105 static struct xt_target redirect_tg_reg[] __read_mostly = { 106 { 107 .name = "REDIRECT", 108 .family = NFPROTO_IPV6, 109 .revision = 0, 110 .table = "nat", 111 .checkentry = redirect_tg6_checkentry, 112 .target = redirect_tg6, 113 .targetsize = sizeof(struct nf_nat_range), 114 .hooks = (1 << NF_INET_PRE_ROUTING) | 115 (1 << NF_INET_LOCAL_OUT), 116 .me = THIS_MODULE, 117 }, 118 { 119 .name = "REDIRECT", 120 .family = NFPROTO_IPV4, 121 .revision = 0, 122 .table = "nat", 123 .target = redirect_tg4, 124 .checkentry = redirect_tg4_check, 125 .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), 126 .hooks = (1 << NF_INET_PRE_ROUTING) | 127 (1 << NF_INET_LOCAL_OUT), 128 .me = THIS_MODULE, 129 }, 130 }; 131 132 static int __init redirect_tg_init(void) 133 { 134 return xt_register_targets(redirect_tg_reg, 135 ARRAY_SIZE(redirect_tg_reg)); 136 } 137 138 static void __exit redirect_tg_exit(void) 139 { 140 xt_unregister_targets(redirect_tg_reg, ARRAY_SIZE(redirect_tg_reg)); 141 } 142 143 module_init(redirect_tg_init); 144 module_exit(redirect_tg_exit); 145 146 MODULE_LICENSE("GPL"); 147 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 148 MODULE_DESCRIPTION("Xtables: Connection redirection to localhost"); 149 MODULE_ALIAS("ip6t_REDIRECT"); 150 MODULE_ALIAS("ipt_REDIRECT"); 151